View Javadoc

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