View Javadoc

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