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