View Javadoc

1   /**
2    * This file Copyright (c) 2011-2013 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.Collection;
45  import java.util.Date;
46  import java.util.GregorianCalendar;
47  import java.util.LinkedList;
48  import java.util.List;
49  import java.util.TimeZone;
50  
51  import javax.jcr.Binary;
52  import javax.jcr.Node;
53  import javax.jcr.Property;
54  import javax.jcr.PropertyType;
55  import javax.jcr.RepositoryException;
56  import javax.jcr.Value;
57  import javax.jcr.ValueFactory;
58  
59  import org.apache.commons.lang.BooleanUtils;
60  import org.apache.commons.lang.StringUtils;
61  import org.apache.commons.lang.time.FastDateFormat;
62  import org.apache.jackrabbit.value.ValueFactoryImpl;
63  import org.slf4j.Logger;
64  import org.slf4j.LoggerFactory;
65  
66  /**
67   * Property-related utility methods.
68   */
69  public class PropertyUtil {
70  
71      private static final Logger log = LoggerFactory.getLogger(PropertyUtil.class);
72  
73      public static Property renameProperty(Property property, String newName) throws RepositoryException {
74          // Do nothing if the property already has this name, otherwise we would remove the property
75          if (property.getName().equals(newName)) {
76              return property;
77          }
78          Node node = property.getParent();
79          Property newProperty = node.setProperty(newName, property.getValue());
80          property.remove();
81          return newProperty;
82      }
83  
84      /**
85       * Allows setting a Node's property from an object.
86       */
87      @SuppressWarnings("unchecked")
88      public static void setProperty(Node node, String propertyName, Object propertyValue) throws RepositoryException {
89          if (node == null) {
90              throw new IllegalArgumentException("Cannot set a property on a null-node!");
91          }
92          if (propertyName == null) {
93              throw new IllegalArgumentException("Cannot set a property without a provided name");
94          }
95  
96          if (propertyValue == null){
97              node.setProperty(propertyName, (Value) null);
98          } else if (propertyValue instanceof Value) {
99              node.setProperty(propertyName, (Value) propertyValue);
100         } else if (propertyValue instanceof Node) {
101             node.setProperty(propertyName, (Node) propertyValue);
102         } else if (propertyValue instanceof Binary) {
103             node.setProperty(propertyName, (Binary) propertyValue);
104         } else if (propertyValue instanceof Calendar) {
105             node.setProperty(propertyName, (Calendar) propertyValue);
106         } else if (propertyValue instanceof Date) {
107             Calendar cal = Calendar.getInstance();
108             cal.setTime((Date) propertyValue);
109             node.setProperty(propertyName, cal);
110         } else if (propertyValue instanceof BigDecimal) {
111             node.setProperty(propertyName, (BigDecimal) propertyValue);
112         } else if (propertyValue instanceof String) {
113             node.setProperty(propertyName, (String) propertyValue);
114         } else if (propertyValue instanceof Long) {
115             node.setProperty(propertyName, ((Long) propertyValue).longValue());
116         } else if (propertyValue instanceof Double) {
117             node.setProperty(propertyName, (Double) propertyValue);
118         } else if (propertyValue instanceof Boolean) {
119             node.setProperty(propertyName, (Boolean) propertyValue);
120         } else if (propertyValue instanceof InputStream) {
121             node.setProperty(propertyName, (InputStream) propertyValue);
122         } else if (propertyValue instanceof Collection) {
123             String[] list = new String[((Collection<Object>)propertyValue).size()];
124             int pos = 0;
125             for (Object value : (Collection<Object>)propertyValue) {
126                 list[pos] = value.toString();
127                 pos +=1;
128             }
129             node.setProperty(propertyName, list);
130         } else {
131             // TODO dlipp: verify if this is desired default-behavior: NodeDataUtil#setValue sets propertyValue.toString() as default!
132             throw new IllegalArgumentException("Cannot set property to a value of type " + propertyValue.getClass());
133         }
134     }
135 
136     /**
137      * Transforms a string to a jcr value object.
138      */
139     public static Value createValue(String valueStr, int type, ValueFactory valueFactory) {
140         Value value = null;
141         if (type == PropertyType.STRING) {
142             value = valueFactory.createValue(valueStr);
143         } else if (type == PropertyType.BOOLEAN) {
144             value = valueFactory.createValue(BooleanUtils.toBoolean(valueStr));
145         } else if (type == PropertyType.DOUBLE) {
146             try {
147                 value = valueFactory.createValue(Double.parseDouble(valueStr));
148             } catch (NumberFormatException e) {
149                 value = valueFactory.createValue(0d);
150             }
151         } else if (type == PropertyType.LONG) {
152             try {
153                 value = valueFactory.createValue(Long.parseLong(valueStr));
154             } catch (NumberFormatException e) {
155                 value = valueFactory.createValue(0L);
156             }
157         } else if (type == PropertyType.DATE) {
158             try {
159                 Calendar date = new GregorianCalendar();
160                 try {
161                     String newDateAndTime = valueStr;
162                     String[] dateAndTimeTokens = newDateAndTime.split("T");
163                     String newDate = dateAndTimeTokens[0];
164                     String[] dateTokens = newDate.split("-");
165                     int hour = 0;
166                     int minute = 0;
167                     int second = 0;
168                     int year = Integer.parseInt(dateTokens[0]);
169                     int month = Integer.parseInt(dateTokens[1]) - 1;
170                     int day = Integer.parseInt(dateTokens[2]);
171                     if (dateAndTimeTokens.length > 1) {
172                         String newTime = dateAndTimeTokens[1];
173                         String[] timeTokens = newTime.split(":");
174                         hour = Integer.parseInt(timeTokens[0]);
175                         minute = Integer.parseInt(timeTokens[1]);
176                         second = Integer.parseInt(timeTokens[2]);
177                     }
178                     date.set(year, month, day, hour, minute, second);
179                     // this is used in the searching
180                     date.set(Calendar.MILLISECOND, 0);
181                     date.setTimeZone(TimeZone.getTimeZone("GMT"));
182                 }
183                 // todo time zone??
184                 catch (Exception e) {
185                     // ignore, it sets the current date / time
186                 }
187                 value = valueFactory.createValue(date);
188             } catch (Exception e) {
189                 log.debug("Exception caught: " + e.getMessage(), e);
190             }
191         }
192 
193         return value;
194 
195     }
196 
197     /**
198      * @return JCR-PropertyType corresponding to provided Object.
199      */
200     public static int getJCRPropertyType(Object obj) {
201         if (obj instanceof String) {
202             return PropertyType.STRING;
203         }
204         if (obj instanceof Double) {
205             return PropertyType.DOUBLE;
206         }
207         if (obj instanceof Float) {
208             return PropertyType.DOUBLE;
209         }
210         if (obj instanceof Long) {
211             return PropertyType.LONG;
212         }
213         if (obj instanceof Integer) {
214             return PropertyType.LONG;
215         }
216         if (obj instanceof Boolean) {
217             return PropertyType.BOOLEAN;
218         }
219         if (obj instanceof Calendar) {
220             return PropertyType.DATE;
221         }
222         if (obj instanceof Binary) {
223             return PropertyType.BINARY;
224         }
225         if (obj instanceof InputStream) {
226             return PropertyType.BINARY;
227         }
228         if (obj instanceof Content) {
229             return PropertyType.REFERENCE;
230         }
231         return PropertyType.UNDEFINED;
232     }
233 
234     /**
235      * Updates existing property or creates a new one if it doesn't exist already.
236      */
237     public static void updateOrCreate(Node node, String string, GregorianCalendar gregorianCalendar) throws RepositoryException {
238         if (node.hasProperty(string)) {
239             node.getProperty(string).setValue(gregorianCalendar);
240         } else {
241             node.setProperty(string, gregorianCalendar);
242         }
243     }
244 
245     public static String getDateFormat() {
246         try {
247             return FastDateFormat.getDateInstance(
248                     FastDateFormat.SHORT,
249                     MgnlContext.getLocale()).getPattern();
250         } catch (IllegalStateException e) {
251             // this happens if the context is not (yet) set
252             return DateUtil.YYYY_MM_DD;
253         }
254     }
255 
256     public static List<String> getValuesStringList(Value[] values) {
257         ArrayList<String> list = new ArrayList<String>();
258         for (Value value : values) {
259             list.add(getValueString(value));
260         }
261         return list;
262     }
263 
264     /**
265      * 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.
266      */
267     public static String getValueString(Property property) {
268         try {
269             return getValueString(property.getValue());
270         } catch (RepositoryException e) {
271             log.debug("RepositoryException caught: " + e.getMessage(), e);
272             return null;
273         }
274     }
275 
276     /**
277      * 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.
278      */
279     public static String getValueString(Value value) {
280         try {
281             switch (value.getType()) {
282             case (PropertyType.STRING):
283                 return value.getString();
284             case (PropertyType.DOUBLE):
285                 return Double.toString(value.getDouble());
286             case (PropertyType.LONG):
287                 return Long.toString(value.getLong());
288             case (PropertyType.BOOLEAN):
289                 return Boolean.toString(value.getBoolean());
290             case (PropertyType.DATE):
291                 Date valueDate = value.getDate().getTime();
292             return DateUtil.format(valueDate, PropertyUtil.getDateFormat());
293             case (PropertyType.BINARY):
294                 // for lack of better solution, fall through to the default - empty string
295             default:
296                 return StringUtils.EMPTY;
297             }
298         } catch (RepositoryException e) {
299             log.debug("RepositoryException caught: " + e.getMessage(), e);
300         }
301         return null;
302 
303     }
304 
305     public static Value createValue(Object obj, ValueFactory valueFactory) throws RepositoryException {
306         switch (PropertyUtil.getJCRPropertyType(obj)) {
307         case PropertyType.STRING:
308             return valueFactory.createValue((String) obj);
309         case PropertyType.BOOLEAN:
310             return valueFactory.createValue((Boolean) obj);
311         case PropertyType.DATE:
312             return valueFactory.createValue((Calendar) obj);
313         case PropertyType.LONG:
314             return obj instanceof Long ? valueFactory.createValue(((Long) obj).longValue()) : valueFactory.createValue(((Integer) obj).longValue());
315         case PropertyType.DOUBLE:
316             return obj instanceof Double ? valueFactory.createValue((Double) obj) : valueFactory.createValue(((Float) obj).doubleValue());
317         case PropertyType.BINARY:
318             return valueFactory.createValue((InputStream) obj);
319         case PropertyType.REFERENCE:
320             return valueFactory.createValue(((Content) obj).getJCRNode());
321         default:
322             return (obj != null ? valueFactory.createValue(obj.toString()) : valueFactory.createValue(StringUtils.EMPTY));
323         }
324     }
325 
326     /**
327      * Return the Calendar representing the node property value.
328      * If the Node did not contain such a Property,
329      * then return <b>null</b>.
330      */
331     public static Calendar getDate(Node node, String name) {
332         return getDate(node, name, null);
333     }
334 
335     /**
336      * Return the Calendar representing the node property value.
337      * If the Node did not contain such a Property,
338      * then return the default value.
339      */
340     public static Calendar getDate(Node node, String name, Calendar defaultValue) {
341         try {
342             if (node.hasProperty(name)) {
343                 return node.getProperty(name).getDate();
344             }
345         } catch (RepositoryException e) {
346             log.error("can't read value '" + name + "' of the Node '" + node.toString() + "' will return default value", e);
347         }
348         return defaultValue;
349     }
350 
351     /**
352      * Return the String representing the node property value.
353      * If the Node did not contain such a Property or if the Node is null,
354      * then return <b>null</b>.
355      */
356     public static String getString(Node node, String name) {
357         return getString(node, name, null);
358     }
359 
360     /**
361      * Return the String representing the node property value.
362      * If the Node did not contain such a Property or if the Node is null,
363      * then return the default value.
364      */
365     public static String getString(Node node, String name, String defaultValue) {
366         if (node != null) {
367             try {
368                 if (node.hasProperty(name)) {
369                     return node.getProperty(name).getString();
370                 }
371             } catch (RepositoryException e) {
372                 log.error("can't read value '" + name + "' of the Node '" + node.toString() + "' will return default value", e);
373             }
374         }
375         return defaultValue;
376     }
377 
378     /**
379      * Return the boolean representing the node property value.
380      * If the Node did not contain such a Property,
381      * then return the default value.
382      */
383     public static boolean getBoolean(Node node, String name, boolean defaultValue) {
384         try {
385             if (node.hasProperty(name)) {
386                 return node.getProperty(name).getBoolean();
387             }
388         } catch (RepositoryException e) {
389             log.error("can't read value '" + name + "' of the Node '" + node.toString() + "' will return default value", e);
390         }
391         return defaultValue;
392     }
393 
394     /**
395      * Return the Property relative to the Node or null if it's not existing or in case of any RepositoryException.
396      */
397     public static Property getPropertyOrNull(Node node, String relativePath) {
398         try {
399             return node.hasProperty(relativePath) ? node.getProperty(relativePath) : null;
400         }
401         catch (RepositoryException e) {
402             log.debug("Could not retrieve property " + relativePath, e);
403         }
404         return null;
405     }
406 
407     /**
408      * @deprecated since 4.5 - use getPropertyOrNull instead
409      */
410     public static Property getProperty(Node node, String relativePath) {
411         return getPropertyOrNull(node, relativePath);
412     }
413 
414     /**
415      * Return the Value Object from a property.
416      * Return null in case of exception.
417      * The returned Object could be a basic {@link PropertyType} type or in case
418      * of multivalue, a LinkedList of {@link PropertyType} type objects.
419      */
420     public static Object getPropertyValueObject(Node node, String relativePath) {
421         final Property property = getPropertyOrNull(node, relativePath);
422         if(property != null) {
423             try {
424                 //Handle Multivalue fields
425                 if(property.isMultiple()) {
426                     Value[] values = property.getValues();
427                     List<Object> res = new LinkedList<Object>();
428                     for(Value value:values) {
429                         res.add(getValueObject(value));
430                     }
431                     return res;
432                 } else {
433                     return getValueObject(property.getValue());
434                 }
435 
436             } catch (Exception e) {
437                 log.warn("Exception during casting the property value", e);
438             }
439         }
440         return null;
441     }
442 
443     /**
444      * Return the Value Object from a {@link Value}.
445      * Return null in case of exception.
446      */
447     public static Object getValueObject(Value value) {
448         try {
449             switch (value.getType()) {
450                 case (PropertyType.DECIMAL):
451                     return value.getDecimal();
452                 case (PropertyType.STRING):
453                     return value.getString();
454                 case (PropertyType.DOUBLE):
455                     return Double.valueOf(value.getDouble());
456                 case (PropertyType.LONG):
457                     return Long.valueOf(value.getLong());
458                 case (PropertyType.BOOLEAN):
459                     return Boolean.valueOf(value.getBoolean());
460                 case (PropertyType.DATE):
461                     return value.getDate().getTime();
462                 case (PropertyType.BINARY):
463                 return ValueFactoryImpl.getInstance().createBinary(value.getBinary().getStream());
464                 default:
465                     return null;
466             }
467         } catch (Exception e) {
468             log.warn("Exception during casting the property value", e);
469         }
470         return null;
471     }
472 
473 }