1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.jcr.util;
35
36 import info.magnolia.cms.util.OrderedProperties;
37 import info.magnolia.jcr.predicate.AbstractPredicate;
38
39 import java.io.ByteArrayInputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.util.Arrays;
43 import java.util.Calendar;
44 import java.util.Properties;
45
46 import javax.jcr.Node;
47 import javax.jcr.Property;
48 import javax.jcr.PropertyIterator;
49 import javax.jcr.PropertyType;
50 import javax.jcr.RepositoryException;
51 import javax.jcr.Value;
52
53 import org.apache.commons.beanutils.ConvertUtils;
54 import org.apache.commons.io.IOUtils;
55 import org.apache.commons.lang3.StringUtils;
56 import org.apache.jackrabbit.util.ISO8601;
57
58
59
60
61
62
63
64
65
66
67 public class PropertiesImportExport {
68
69
70
71
72 public void createNodes(Node root, String... properties) throws IOException, RepositoryException {
73 createNodes(root, IOUtils.toInputStream(StringUtils.join(Arrays.asList(properties), "\n")));
74 }
75
76
77
78
79 public void createNodes(Node root, InputStream propertiesStream) throws IOException, RepositoryException {
80 Properties properties = new OrderedProperties();
81
82 properties.load(propertiesStream);
83
84 properties = keysToInnerFormat(properties);
85
86 for (Object o : properties.keySet()) {
87 String key = (String) o;
88 String valueStr = properties.getProperty(key);
89
90 String propertyName = StringUtils.substringAfterLast(key, ".");
91 String path = StringUtils.substringBeforeLast(key, ".");
92
93 String type = null;
94 if (propertyName.equals("@type")) {
95 type = valueStr;
96 } else if (properties.containsKey(path + ".@type")) {
97 type = properties.getProperty(path + ".@type");
98 }
99
100 type = StringUtils.defaultIfEmpty(type, NodeTypes.ContentNode.NAME);
101 Node c = NodeUtil.createPath(root, path, type);
102 populateNode(c, propertyName, valueStr);
103 }
104 }
105
106
107
108
109
110 private Properties keysToInnerFormat(Properties properties) {
111 Properties cleaned = new OrderedProperties();
112
113 for (Object o : properties.keySet()) {
114 String orgKey = (String) o;
115
116 if (!orgKey.startsWith("/")) {
117 throw new IllegalArgumentException("Missing trailing '/' for key: " + orgKey);
118 }
119 if (StringUtils.countMatches(orgKey, ".") > 1) {
120 throw new IllegalArgumentException("Key must not contain more than one '.': " + orgKey);
121 }
122 if (orgKey.contains("@") && !orgKey.contains(".@")) {
123 throw new IllegalArgumentException("Key containing '@' must be preceded by a '.': " + orgKey);
124 }
125
126 String newKey = orgKey;
127
128 String propertyName = StringUtils.substringAfterLast(newKey, ".");
129 String keySuffix = StringUtils.substringBeforeLast(newKey, ".");
130 String path = StringUtils.removeStart(keySuffix, "/");
131
132
133 if (StringUtils.isEmpty(propertyName)) {
134
135 if (StringUtils.isEmpty(properties.getProperty(orgKey))) {
136
137 if (!properties.containsKey(orgKey + ".@type")) {
138 cleaned.put(path + ".@type", NodeTypes.ContentNode.NAME);
139 }
140 continue;
141 }
142 throw new IllegalArgumentException("Key for a path (everything without a '.' is considered to be a path) must not contain a value ('='): " + orgKey);
143 }
144 cleaned.put(path + "." + propertyName, properties.get(orgKey));
145 }
146 return cleaned;
147 }
148
149 protected void populateNode(Node node, String name, String valueStr) throws RepositoryException {
150 if (StringUtils.isEmpty(name) && StringUtils.isEmpty(valueStr)) {
151
152 return;
153 }
154 if (name.equals("@type")) {
155
156 } else if (name.equals("@uuid") || name.equals("uuid")) {
157 setIdentifier(node, valueStr);
158 } else {
159 Object valueObj = convertPropertyStringToObject(valueStr);
160 PropertyUtil.setProperty(node, name, valueObj);
161 }
162 }
163
164
165
166
167
168 protected void setIdentifier(Node ignoredNode, String ignoredString) {
169 throw new UnsupportedOperationException("Can't see UUIDs on real node.");
170 }
171
172 protected Object convertPropertyStringToObject(String valueStr) {
173 if (contains(valueStr, ':')) {
174 final String type = StringUtils.substringBefore(valueStr, ":");
175 final String value = StringUtils.substringAfter(valueStr, ":");
176
177
178 if (type.equalsIgnoreCase("date")) {
179 return ISO8601.parse(value);
180 } else if (type.equalsIgnoreCase("binary")) {
181 return new ByteArrayInputStream(value.getBytes());
182 } else {
183 try {
184 final Class<?> typeCl;
185 if (type.equals("int")) {
186 typeCl = Integer.class;
187 } else {
188 typeCl = Class.forName("java.lang." + StringUtils.capitalize(type));
189 }
190 return ConvertUtils.convert(value, typeCl);
191 } catch (ClassNotFoundException e) {
192
193 return valueStr;
194 }
195 }
196 }
197
198 return valueStr;
199 }
200
201 private static boolean contains(String s, char ch) {
202 return s.indexOf(ch) > -1;
203 }
204
205 public Properties toProperties(Node node, final AbstractPredicate<Node> nodePredicate) throws RepositoryException {
206 final Properties out = new OrderedProperties();
207 NodeUtil.visit(
208 node,
209 new NodeVisitor() {
210 @Override
211 public void visit(Node node) throws RepositoryException {
212 appendNodeTypeAndIdentifier(node, out);
213 appendNodeProperties(node, out);
214 }
215 }, nodePredicate
216 );
217 return out;
218 }
219
220 private void appendNodeTypeAndIdentifier(Node node, Properties out) throws RepositoryException {
221
222
223 if (node.getDepth() == 0) {
224 return;
225 }
226
227 String path = getExportPath(node);
228
229 String nodeTypeName = node.getPrimaryNodeType().getName();
230 if (nodeTypeName != null && StringUtils.isNotEmpty(nodeTypeName)) {
231 out.put(path + ".@type", nodeTypeName);
232 }
233
234 String nodeIdentifier = node.getIdentifier();
235 if (nodeIdentifier != null && StringUtils.isNotEmpty(nodeIdentifier)) {
236 out.put(path + ".@uuid", nodeIdentifier);
237 }
238 }
239
240 private void appendNodeProperties(Node node, Properties out) throws RepositoryException {
241 PropertyIterator propertyIterator = node.getProperties();
242 while (propertyIterator.hasNext()) {
243 Property property = propertyIterator.nextProperty();
244 String path = getExportPath(node) + "." + property.getName();
245
246 String propertyValue = getPropertyString(property);
247
248 if (propertyValue != null) {
249 out.setProperty(path, propertyValue);
250 }
251 }
252 }
253
254 private String getExportPath(Node node) throws RepositoryException {
255 return node.getPath();
256 }
257
258 private String getPropertyString(Property property) throws RepositoryException {
259
260 switch (property.getType()) {
261 case (PropertyType.STRING): {
262 return property.getString();
263 }
264 case (PropertyType.BOOLEAN): {
265 return convertBooleanToExportString(property.getBoolean());
266 }
267 case (PropertyType.BINARY): {
268 return convertBinaryToExportString(property.getValue());
269 }
270 case (PropertyType.PATH): {
271 return property.getString();
272 }
273 case (PropertyType.DATE): {
274 return convertCalendarToExportString(property.getDate());
275 }
276 case (PropertyType.LONG): {
277 return "" + property.getLong();
278 }
279 case (PropertyType.DOUBLE): {
280 return "" + property.getDouble();
281 }
282 default: {
283 return property.getString();
284 }
285 }
286 }
287
288 private String convertBooleanToExportString(boolean b) {
289 return "boolean:" + (b ? "true" : "false");
290 }
291
292 private String convertBinaryToExportString(Value value) throws RepositoryException {
293 return "binary:" + ConvertUtils.convert(value.getString());
294 }
295
296 private String convertCalendarToExportString(Calendar calendar) {
297 return "date:" + ISO8601.format(calendar);
298 }
299 }