View Javadoc

1   /**
2    * This file Copyright (c) 2011-2014 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.jcr.util;
35  
36  import info.magnolia.cms.core.Content;
37  import info.magnolia.cms.util.DateUtil;
38  import info.magnolia.context.MgnlContext;
39  
40  import java.io.InputStream;
41  import java.math.BigDecimal;
42  import java.util.ArrayList;
43  import java.util.Calendar;
44  import java.util.Date;
45  import java.util.GregorianCalendar;
46  import java.util.List;
47  import java.util.TimeZone;
48  
49  import javax.jcr.Binary;
50  import javax.jcr.Node;
51  import javax.jcr.Property;
52  import javax.jcr.PropertyType;
53  import javax.jcr.RepositoryException;
54  import javax.jcr.Value;
55  import javax.jcr.ValueFactory;
56  
57  import org.apache.commons.lang.BooleanUtils;
58  import org.apache.commons.lang.StringUtils;
59  import org.apache.commons.lang.time.FastDateFormat;
60  import org.apache.jackrabbit.value.ValueFactoryImpl;
61  import org.slf4j.Logger;
62  import org.slf4j.LoggerFactory;
63  
64  /**
65   * Property-related utility methods.
66   */
67  public class PropertyUtil {
68  
69      private static final Logger log = LoggerFactory.getLogger(PropertyUtil.class);
70  
71      public static void renameProperty(Property property, String newName) throws RepositoryException {
72          Node node = property.getParent();
73          node.setProperty(newName, property.getValue());
74          property.remove();
75      }
76  
77      /**
78       * Allows setting a Node's property from an object.
79       */
80      public static void setProperty(Node node, String propertyName, Object propertyValue) throws RepositoryException {
81          if (node == null) {
82              throw new IllegalArgumentException("Cannot set a property on a null-node!");
83          }
84          if (propertyName == null) {
85              throw new IllegalArgumentException("Cannot set a property without a provided name");
86          }
87  
88          if (propertyValue == null){
89              node.setProperty(propertyName, (Value) null);
90          } else if (propertyValue instanceof Value) {
91              node.setProperty(propertyName, (Value) propertyValue);
92          } else if (propertyValue instanceof Node) {
93              node.setProperty(propertyName, (Node) propertyValue);
94          } else if (propertyValue instanceof Binary) {
95              node.setProperty(propertyName, (Binary) propertyValue);
96          } else if (propertyValue instanceof Calendar) {
97              node.setProperty(propertyName, (Calendar) propertyValue);
98          } else if (propertyValue instanceof BigDecimal) {
99              node.setProperty(propertyName, (BigDecimal) propertyValue);
100         } else if (propertyValue instanceof String) {
101             node.setProperty(propertyName, (String) propertyValue);
102         } else if (propertyValue instanceof Long) {
103             node.setProperty(propertyName, ((Long) propertyValue).longValue());
104         } else if (propertyValue instanceof Double) {
105             node.setProperty(propertyName, (Double) propertyValue);
106         } else if (propertyValue instanceof Boolean) {
107             node.setProperty(propertyName, (Boolean) propertyValue);
108         } else if (propertyValue instanceof InputStream) {
109             node.setProperty(propertyName, (InputStream) propertyValue);
110         } else {
111             // TODO dlipp: verify if this is desired default-behavior: NodeDataUtil#setValue sets propertyValue.toString() as default!
112             throw new IllegalArgumentException("Cannot set property to a value of type " + propertyValue.getClass());
113         }
114     }
115 
116     /**
117      * Transforms a string to a jcr value object.
118      */
119     public static Value createValue(String valueStr, int type, ValueFactory valueFactory) {
120         Value value = null;
121         if (type == PropertyType.STRING) {
122             value = valueFactory.createValue(valueStr);
123         } else if (type == PropertyType.BOOLEAN) {
124             value = valueFactory.createValue(BooleanUtils.toBoolean(valueStr));
125         } else if (type == PropertyType.DOUBLE) {
126             try {
127                 value = valueFactory.createValue(Double.parseDouble(valueStr));
128             } catch (NumberFormatException e) {
129                 value = valueFactory.createValue(0d);
130             }
131         } else if (type == PropertyType.LONG) {
132             try {
133                 value = valueFactory.createValue(Long.parseLong(valueStr));
134             } catch (NumberFormatException e) {
135                 value = valueFactory.createValue(0L);
136             }
137         } else if (type == PropertyType.DATE) {
138             try {
139                 Calendar date = new GregorianCalendar();
140                 try {
141                     String newDateAndTime = valueStr;
142                     String[] dateAndTimeTokens = newDateAndTime.split("T");
143                     String newDate = dateAndTimeTokens[0];
144                     String[] dateTokens = newDate.split("-");
145                     int hour = 0;
146                     int minute = 0;
147                     int second = 0;
148                     int year = Integer.parseInt(dateTokens[0]);
149                     int month = Integer.parseInt(dateTokens[1]) - 1;
150                     int day = Integer.parseInt(dateTokens[2]);
151                     if (dateAndTimeTokens.length > 1) {
152                         String newTime = dateAndTimeTokens[1];
153                         String[] timeTokens = newTime.split(":");
154                         hour = Integer.parseInt(timeTokens[0]);
155                         minute = Integer.parseInt(timeTokens[1]);
156                         second = Integer.parseInt(timeTokens[2]);
157                     }
158                     date.set(year, month, day, hour, minute, second);
159                     // this is used in the searching
160                     date.set(Calendar.MILLISECOND, 0);
161                     date.setTimeZone(TimeZone.getTimeZone("GMT"));
162                 }
163                 // todo time zone??
164                 catch (Exception e) {
165                     // ignore, it sets the current date / time
166                 }
167                 value = valueFactory.createValue(date);
168             } catch (Exception e) {
169                 log.debug("Exception caught: " + e.getMessage(), e);
170             }
171         }
172 
173         return value;
174 
175     }
176 
177     /**
178      * @return JCR-PropertyType corresponding to provided Object.
179      */
180     public static int getJCRPropertyType(Object obj) {
181         if (obj instanceof String) {
182             return PropertyType.STRING;
183         }
184         if (obj instanceof Double) {
185             return PropertyType.DOUBLE;
186         }
187         if (obj instanceof Float) {
188             return PropertyType.DOUBLE;
189         }
190         if (obj instanceof Long) {
191             return PropertyType.LONG;
192         }
193         if (obj instanceof Integer) {
194             return PropertyType.LONG;
195         }
196         if (obj instanceof Boolean) {
197             return PropertyType.BOOLEAN;
198         }
199         if (obj instanceof Calendar) {
200             return PropertyType.DATE;
201         }
202         if (obj instanceof Binary) {
203             return PropertyType.BINARY;
204         }
205         if (obj instanceof InputStream) {
206             return PropertyType.BINARY;
207         }
208         if (obj instanceof Content) {
209             return PropertyType.REFERENCE;
210         }
211         return PropertyType.UNDEFINED;
212     }
213 
214     /**
215      * Updates existing property or creates a new one if it doesn't exist already.
216      */
217     public static void updateOrCreate(Node node, String string, GregorianCalendar gregorianCalendar) throws RepositoryException {
218         if (node.hasProperty(string)) {
219             node.getProperty(string).setValue(gregorianCalendar);
220         } else {
221             node.setProperty(string, gregorianCalendar);
222         }
223     }
224 
225     public static String getDateFormat() {
226         try {
227             return FastDateFormat.getDateInstance(
228                     FastDateFormat.SHORT,
229                     MgnlContext.getLocale()).getPattern();
230         } catch (IllegalStateException e) {
231             // this happens if the context is not (yet) set
232             return DateUtil.YYYY_MM_DD;
233         }
234     }
235 
236     public static List<String> getValuesStringList(Value[] values) {
237         ArrayList<String> list = new ArrayList<String>();
238         for (Value value : values) {
239             list.add(getValueString(value));
240         }
241         return list;
242     }
243 
244     /**
245      * Returns value of the property converted to string no matter what it's type actually is. In case of dates, value if formatted according to format returned by {@link #getDateFormat()}. Binary and reference values are converted to empty string. In case of error during conversion, null will be returned instead. Works only for single value properties.
246      */
247     public static String getValueString(Property property) {
248         try {
249             return getValueString(property.getValue());
250         } catch (RepositoryException e) {
251             log.debug("RepositoryException caught: " + e.getMessage(), e);
252             return null;
253         }
254     }
255 
256     /**
257      * Returns value converted to string no matter what it's type actually is. In case of dates, value if formatted according to format returned by {@link #getDateFormat()}. Binary and reference values are converted to empty string. In case of error during conversion, null will be returned instead.
258      */
259     public static String getValueString(Value value) {
260         try {
261             switch (value.getType()) {
262             case (PropertyType.STRING):
263                 return value.getString();
264             case (PropertyType.DOUBLE):
265                 return Double.toString(value.getDouble());
266             case (PropertyType.LONG):
267                 return Long.toString(value.getLong());
268             case (PropertyType.BOOLEAN):
269                 return Boolean.toString(value.getBoolean());
270             case (PropertyType.DATE):
271                 Date valueDate = value.getDate().getTime();
272             return DateUtil.format(valueDate, PropertyUtil.getDateFormat());
273             case (PropertyType.BINARY):
274                 // for lack of better solution, fall through to the default - empty string
275             default:
276                 return StringUtils.EMPTY;
277             }
278         } catch (RepositoryException e) {
279             log.debug("RepositoryException caught: " + e.getMessage(), e);
280         }
281         return null;
282 
283     }
284 
285     public static Value createValue(Object obj, ValueFactory valueFactory) throws RepositoryException {
286         switch (PropertyUtil.getJCRPropertyType(obj)) {
287         case PropertyType.STRING:
288             return valueFactory.createValue((String) obj);
289         case PropertyType.BOOLEAN:
290             return valueFactory.createValue((Boolean) obj);
291         case PropertyType.DATE:
292             return valueFactory.createValue((Calendar) obj);
293         case PropertyType.LONG:
294             return obj instanceof Long ? valueFactory.createValue(((Long) obj).longValue()) : valueFactory.createValue(((Integer) obj).longValue());
295         case PropertyType.DOUBLE:
296             return obj instanceof Double ? valueFactory.createValue((Double) obj) : valueFactory.createValue(((Float) obj).doubleValue());
297         case PropertyType.BINARY:
298             return valueFactory.createValue((InputStream) obj);
299         case PropertyType.REFERENCE:
300             return valueFactory.createValue(((Content) obj).getJCRNode());
301         default:
302             return (obj != null ? valueFactory.createValue(obj.toString()) : valueFactory.createValue(StringUtils.EMPTY));
303         }
304     }
305 
306     /**
307      * Return the Calendar representing the node property value.
308      * If the Node did not contain such a Property,
309      * then return <b>null</b>.
310      */
311     public static Calendar getDate(Node node, String name) {
312         return getDate(node, name, null);
313     }
314 
315     /**
316      * Return the Calendar representing the node property value.
317      * If the Node did not contain such a Property,
318      * then return the default value.
319      */
320     public static Calendar getDate(Node node, String name, Calendar defaultValue) {
321         try {
322             if (node.hasProperty(name)) {
323                 return node.getProperty(name).getDate();
324             }
325         } catch (RepositoryException e) {
326             log.error("can't read value '" + name + "' of the Node '" + node.toString() + "' will return default value", e);
327         }
328         return defaultValue;
329     }
330 
331     /**
332      * Return the String representing the node property value.
333      * If the Node did not contain such a Property or if the Node is null,
334      * then return <b>null</b>.
335      */
336     public static String getString(Node node, String name) {
337         return getString(node, name, null);
338     }
339 
340     /**
341      * Return the String representing the node property value.
342      * If the Node did not contain such a Property or if the Node is null,
343      * then return the default value.
344      */
345     public static String getString(Node node, String name, String defaultValue) {
346         if (node != null) {
347             try {
348                 if (node.hasProperty(name)) {
349                     return node.getProperty(name).getString();
350                 }
351             } catch (RepositoryException e) {
352                 log.error("can't read value '" + name + "' of the Node '" + node.toString() + "' will return default value", e);
353             }
354         }
355         return defaultValue;
356     }
357 
358     public static Long getLong(Node node, String name) {
359         return getLong(node, name, null);
360     }
361     
362     public static Long getLong(Node node, String name, Long defaultValue) {
363         if (node != null) {
364             try {
365                 if (node.hasProperty(name)) {
366                     return node.getProperty(name).getLong();
367                 }
368             } catch (RepositoryException e) {
369                 log.error("can't read value '" + name + "' of the Node '" + node.toString() + "' will return default value", e);
370             }
371         }
372         return defaultValue;
373     }
374     
375     /**
376      * Return the boolean representing the node property value.
377      * If the Node did not contain such a Property,
378      * then return the default value.
379      */
380     public static boolean getBoolean(Node node, String name, boolean defaultValue) {
381         try {
382             if (node.hasProperty(name)) {
383                 return node.getProperty(name).getBoolean();
384             }
385         } catch (RepositoryException e) {
386             log.error("can't read value '" + name + "' of the Node '" + node.toString() + "' will return default value", e);
387         }
388         return defaultValue;
389     }
390     
391 
392     /**
393      * Return the Property relative to the Node or null if it's not existing or in case of any RepositoryException.
394      */
395     public static Property getPropertyOrNull(Node node, String relativePath) {
396         try {
397             return node.hasProperty(relativePath) ? node.getProperty(relativePath) : null;
398         }
399         catch (RepositoryException e) {
400             log.debug("Could not retrieve property " + relativePath, e);
401         }
402         return null;
403     }
404 
405     /**
406      * @deprecated since 4.5 - use getPropertyOrNull instead.
407      */
408     public static Property getProperty(Node node, String relativePath) {
409         return getPropertyOrNull(node, relativePath);
410     }
411 
412     /**
413      * Return the Value Object from a {@link Value}.
414      * Return null in case of exception.
415      */
416     public static Object getValueObject(Value value) {
417         try {
418             switch (value.getType()) {
419             case (PropertyType.DECIMAL):
420                 return value.getDecimal();
421             case (PropertyType.STRING):
422                 return value.getString();
423             case (PropertyType.DOUBLE):
424                 return Double.valueOf(value.getDouble());
425             case (PropertyType.LONG):
426                 return Long.valueOf(value.getLong());
427             case (PropertyType.BOOLEAN):
428                 return Boolean.valueOf(value.getBoolean());
429             case (PropertyType.DATE):
430                 return value.getDate().getTime();
431             case (PropertyType.BINARY):
432                 return ValueFactoryImpl.getInstance().createBinary(value.getBinary().getStream());
433             default:
434                 return value.getString();
435             }
436         } catch (Exception e) {
437             log.warn("Exception during casting the property value", e);
438         }
439         return null;
440     }
441 
442 }