View Javadoc

1   /**
2    * This file Copyright (c) 2003-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.cms.util;
35  
36  import info.magnolia.cms.beans.config.ContentRepository;
37  import info.magnolia.cms.core.Content;
38  import info.magnolia.cms.core.NodeData;
39  import info.magnolia.cms.i18n.Messages;
40  import info.magnolia.cms.i18n.MessagesManager;
41  import info.magnolia.cms.i18n.MessagesUtil;
42  import info.magnolia.cms.security.AccessDeniedException;
43  import info.magnolia.context.MgnlContext;
44  import info.magnolia.cms.core.HierarchyManager;
45  
46  import java.io.InputStream;
47  import java.util.ArrayList;
48  import java.util.Calendar;
49  import java.util.Date;
50  import java.util.GregorianCalendar;
51  import java.util.List;
52  import java.util.TimeZone;
53  
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.slf4j.Logger;
63  import org.slf4j.LoggerFactory;
64  
65  /**
66   * Util to work with {@link NodeData}.
67   * @author Sameer Charles
68   * @version $Revision: 48455 $ ($Author: dlipp $)
69   */
70  public class NodeDataUtil {
71      private static final Logger log = LoggerFactory.getLogger(NodeDataUtil.class);
72  
73      /**
74       * Same as getValueString(nd, dateFormat) but using the users short date format.
75       */
76      public static String getValueString(NodeData nodeData) {
77          String dateFormat = null;
78          if(nodeData.getType() == PropertyType.DATE){
79              try{
80                  dateFormat = FastDateFormat.getDateInstance(
81                  FastDateFormat.SHORT,
82                  MgnlContext.getLocale()).getPattern();
83              }
84              // this happens if the context is not (yet) set
85              catch(IllegalStateException e){
86                  dateFormat = DateUtil.YYYY_MM_DD;
87              }
88          }
89          return getValueString(nodeData, dateFormat);
90      }
91  
92      /**
93       * Returns a String representation of the value. In case of a binary the path including filename and extension is returned
94       * @param nodeData
95       * @param dateFormat date format to use in the case it is a date
96       * @return
97       */
98      public static String getValueString(NodeData nodeData, String dateFormat) {
99          // we can't use FileProperties since this class is in the GUI package
100         if(nodeData.getType()==PropertyType.BINARY){
101             String filename = nodeData.getAttribute("fileName");
102             String ext = nodeData.getAttribute("extension");
103             String fullName = filename;
104             String fullExt = StringUtils.EMPTY;
105             if (StringUtils.isNotEmpty(ext)) {
106                 fullExt = "." + ext;
107                 fullName += fullExt;
108             }
109             return nodeData.getHandle() + "/" + fullName;
110         }
111         else if (nodeData.isMultiValue() == NodeData.MULTIVALUE_TRUE){
112             return StringUtils.join(getValuesStringList(nodeData.getValues()), ",");
113         } else {
114             return getValueString(nodeData.getValue(), dateFormat);
115         }
116     }
117 
118     /**
119      * Same as value.getString(), but using custom date format.
120      */
121     public static String getValueString(Value value, String dateFormat) {
122         try{
123             switch (value.getType()) {
124                 case (PropertyType.STRING):
125                     return value.getString();
126                 case (PropertyType.DOUBLE):
127                     return Double.toString(value.getDouble());
128                 case (PropertyType.LONG):
129                     return Long.toString(value.getLong());
130                 case (PropertyType.BOOLEAN):
131                     return Boolean.toString(value.getBoolean());
132                 case (PropertyType.DATE):
133                     Date valueDate = value.getDate().getTime();
134                     return DateUtil.format(valueDate, dateFormat);
135                 case (PropertyType.BINARY):
136                     // ???
137                 default:
138                     return StringUtils.EMPTY;
139             }
140         }
141         catch (Exception e) {
142             log.debug("Exception caught: " + e.getMessage(), e); //$NON-NLS-1$
143         }
144         return StringUtils.EMPTY;
145     }
146 
147     /**
148      * Inherit a value. Uses the string value. The "inherit" means that the method will look for the value in the content itself and if not found
149      * it will go up in the tree and try to locate value in one of the parents of the content until reaching the root. The first value found while
150      * traversing the tree way up is the one that will be returned.
151      * @param node Node expected to define or inherit the searched node value.
152      * @param name Name of the nodeData.
153      */
154     public static String inheritString(Content node, String name) throws RepositoryException{
155         String value = "";
156 
157         if (node.hasNodeData(name)) {
158             value = NodeDataUtil.getString(node, name);
159         }
160         if(StringUtils.isEmpty(value) && node.getLevel() > 0){
161             value = inheritString(node.getParent(), name);
162         }
163         return value;
164     }
165 
166     /**
167      * Inherit a value. You can provide a default value if not found
168      */
169     public static String inheritString(Content node, String name, String dflt) throws RepositoryException{
170         String value = inheritString(node, name);
171         if(StringUtils.isEmpty(value)){
172             return dflt;
173         }
174         return value;
175     }
176 
177     /**
178      * Inherit a value. Uses the string value
179      */
180     public static Object inherit(Content node, String name) throws RepositoryException{
181         Object value = null;
182 
183         if (node.hasNodeData(name)) {
184             value = NodeDataUtil.getValueObject(node.getNodeData(name));
185         }
186         if(value == null && node.getLevel() > 0){
187             value = inherit(node.getParent(), name);
188         }
189         return value;
190     }
191 
192     /**
193      * Inherit a value. You can provide a default value if not found
194      */
195     public static Object inherit(Content node, String name, Object dflt) throws RepositoryException{
196         Object value = inherit(node, name);
197         if(value == null){
198             return dflt;
199         }
200         return value;
201     }
202 
203     /**
204      * Returns the value as an Object.
205      * @return Object
206      */
207     public static Object getValueObject(NodeData nd) {
208         try {
209             switch (nd.getType()) {
210                 case (PropertyType.STRING):
211                     return nd.getString();
212                 case (PropertyType.DOUBLE):
213                     return new Double(nd.getDouble());
214                 case (PropertyType.LONG):
215                     return new Long(nd.getLong());
216                 case (PropertyType.BOOLEAN):
217                     return Boolean.valueOf(nd.getBoolean());
218                 case (PropertyType.DATE):
219                     return nd.getDate().getTime();
220                 case (PropertyType.BINARY):
221                     return null;
222                 default:
223                     return null;
224             }
225         }
226         catch (Exception e) {
227             log.debug("Exception caught: " + e.getMessage(), e); //$NON-NLS-1$
228         }
229         return null;
230     }
231 
232     /**
233      * Calls the correct setValue method based on object type. If the value is null an empty string is set.
234      */
235     public static NodeData setValue(NodeData nodeData, Object valueObj) throws AccessDeniedException, RepositoryException{
236         if(valueObj == null){
237             nodeData.setValue(StringUtils.EMPTY);
238         }
239         else{
240             switch (getJCRPropertyType(valueObj)) {
241                 case PropertyType.STRING:
242                     nodeData.setValue((String)valueObj);
243                     break;
244                 case PropertyType.BOOLEAN:
245                     nodeData.setValue(((Boolean)valueObj).booleanValue());
246                     break;
247                 case PropertyType.DATE:
248                     nodeData.setValue((Calendar)valueObj);
249                     break;
250                 case PropertyType.LONG:
251                     // can either be a Long or Integer - see #getJCRPropertyType(Object)
252                     long longToSet = (valueObj instanceof Integer) ? ((Integer) valueObj).longValue() : ((Long) valueObj).longValue();
253                     nodeData.setValue(longToSet);
254                     break;
255                 case PropertyType.DOUBLE:
256                     // can either be a Double or Float - see #getJCRPropertyType(Object)
257                     double doubleToSet = (valueObj instanceof Float) ? ((Float) valueObj).doubleValue() : ((Double) valueObj).doubleValue();
258                     nodeData.setValue(doubleToSet);
259                     break;
260                 case PropertyType.BINARY:
261                     nodeData.setValue((InputStream)valueObj);
262                     break;
263                 case PropertyType.REFERENCE:
264                     nodeData.setValue((Content)valueObj);
265                     break;
266                 default:
267                     nodeData.setValue(valueObj.toString());
268             }
269         }
270         return nodeData;
271     }
272 
273 
274     /**
275      * String representation of the jcr property type.
276      */
277     public static String getTypeName(NodeData nd) {
278         return PropertyType.nameFromValue(nd.getType());
279     }
280 
281     /**
282      * Simple method to get strings like configuration informations.
283      */
284     public static String getString(String repository, String path) {
285         return getString(repository, path, null);
286     }
287 
288     /**
289      * Get the string or the empty string if not existing.
290      */
291     public static String getString(Content node, String name) {
292         return getString(node, name, "");
293     }
294 
295     /**
296      * You can define a default value if not found.
297      */
298     public static String getString(String repository, String path, String defaultValue) {
299         try {
300             String name = StringUtils.substringAfterLast(path, "/");
301             String nodeHandle = StringUtils.substringBeforeLast(path, "/");
302             Content node = MgnlContext.getHierarchyManager(repository).getContent(nodeHandle);
303             return getString(node, name);
304         }
305         catch (Exception e) {
306             return defaultValue;
307         }
308     }
309 
310     /**
311      * You can define a default value if not found.
312      */
313     public static String getString(Content node, String name, String defaultValue) {
314         try {
315             if (node.hasNodeData(name)) {
316                 return getValueString(node.getNodeData(name));
317             }
318 
319             return defaultValue;
320         }
321         catch (Exception e) {
322             return defaultValue;
323         }
324     }
325 
326     public static long getLong(Content node, String name, long defaultValue) {
327         try {
328             if(node.hasNodeData(name)){
329                 return node.getNodeData(name).getLong();
330             }
331         }
332         // should not happen
333         catch (RepositoryException e) {
334             log.error("can't read value will return default value", e);
335         }
336         return defaultValue;
337     }
338 
339     public static Calendar getDate(Content node, String name, Calendar defaultValue) {
340         try {
341             if(node.hasNodeData(name)){
342                 return node.getNodeData(name).getDate();
343             }
344         }
345         // should not happen
346         catch (RepositoryException e) {
347             log.error("can't read value will return default value", e);
348         }
349         return defaultValue;
350     }
351 
352     public static boolean getBoolean(Content node, String name, boolean defaultValue) {
353         try {
354             if(node.hasNodeData(name)){
355                 return node.getNodeData(name).getBoolean();
356             }
357         }
358         // should not happen
359         catch (RepositoryException e) {
360             log.error("can't read value will return default value", e);
361         }
362         return defaultValue;
363     }
364 
365     /**
366      * If the NodeData does not exist yet, just create it.
367      * @param node
368      * @param name
369      * @return the found or created NodeData
370      * @throws AccessDeniedException
371      * @throws PathNotFoundException
372      * @throws RepositoryException
373      */
374     public static NodeData getOrCreate(Content node, String name) throws AccessDeniedException, RepositoryException {
375         return getOrCreate(node, name, PropertyType.STRING);
376     }
377 
378     /**
379      * If the NodeData does not exist yet, just create it.
380      * @param node
381      * @param name
382      * @return the found or created NodeData
383      * @throws AccessDeniedException
384      * @throws PathNotFoundException
385      * @throws RepositoryException
386      */
387     public static NodeData getOrCreate(Content node, String name, int type) throws AccessDeniedException,
388         RepositoryException {
389         if (node.hasNodeData(name)) {
390             return node.getNodeData(name);
391         }
392 
393         return node.createNodeData(name, type);
394     }
395 
396     public static NodeData getOrCreate(Content node, String name, Object obj) throws AccessDeniedException,
397         RepositoryException {
398 
399         return getOrCreate(node, name, getJCRPropertyType(obj));
400     }
401 
402 
403     public static NodeData getOrCreateAndSet(Content node, String name, Object obj) throws AccessDeniedException, RepositoryException {
404         // TODO we should not use the jcr node
405         ValueFactory valueFactory = node.getJCRNode().getSession().getValueFactory();
406         NodeData nd = getOrCreate(node, name, getJCRPropertyType(obj));
407         nd.setValue(createValue(obj, valueFactory));
408         return nd;
409     }
410 
411     public static NodeData getOrCreateAndSet(Content node, String name, long value) throws AccessDeniedException, RepositoryException {
412         return getOrCreateAndSet(node, name, new Long(value));
413     }
414 
415     public static NodeData getOrCreateAndSet(Content node, String name, Value[] value) throws AccessDeniedException, RepositoryException {
416         if (node.hasNodeData(name)) {
417             node.setNodeData(name, value);
418             return node.getNodeData(name);
419         }
420 
421         return node.createNodeData(name, value);
422 
423     }
424 
425     public static NodeData getOrCreateAndSet(Content node, String name, int value) throws AccessDeniedException, RepositoryException {
426         return getOrCreateAndSet(node, name, new Long(value));
427     }
428 
429     public static NodeData getOrCreateAndSet(Content node, String name, boolean value) throws AccessDeniedException, RepositoryException {
430         return getOrCreateAndSet(node, name, new Boolean(value));
431     }
432 
433     /**
434      * Uses the i18n mechanism to translate the message if the resulting string is a key.
435      */
436     public static String getI18NString(Content node, String str) {
437         Messages msgs = MessagesManager.getMessages();
438         String key = getString(node, str);
439         String i18nBasename = null;
440         try {
441             i18nBasename = NodeDataUtil.inheritString(node, "i18nBasename");
442         }
443         catch (RepositoryException e) {
444             log.error("can't read i18nBasename value, will default back", e);
445         }
446 
447         if (StringUtils.isNotEmpty(i18nBasename)) {
448             msgs = MessagesUtil.chainWithDefault(i18nBasename);
449         }
450 
451         return msgs.getWithDefault(key, key);
452     }
453 
454     /**
455      * Uses the i18n mechanism to translate the message if the resulting string is a key.
456      */
457     public static String getI18NString(Content node, String str, String basename) {
458         String key = getString(node, str);
459         return MessagesManager.getMessages(basename).getWithDefault(key, key);
460     }
461 
462     /**
463      * Uses the default value factory.
464      */
465     public static Value createValue(String valueStr, int type) throws RepositoryException {
466         HierarchyManager hm = MgnlContext.getSystemContext().getHierarchyManager(ContentRepository.CONFIG);
467         ValueFactory valueFactory = hm.getWorkspace().getSession().getValueFactory();
468         return createValue(valueStr, type, valueFactory);
469     }
470 
471     public static Value createValue(Object obj, ValueFactory valueFactory) throws RepositoryException {
472         switch (getJCRPropertyType(obj)) {
473             case PropertyType.STRING:
474                 return valueFactory.createValue((String)obj);
475             case PropertyType.BOOLEAN:
476                 return valueFactory.createValue(((Boolean)obj).booleanValue());
477             case PropertyType.DATE:
478                 return valueFactory.createValue((Calendar)obj);
479             case PropertyType.LONG:
480                 return obj instanceof Long ? valueFactory.createValue(((Long)obj).longValue()) : valueFactory.createValue(((Integer)obj).longValue());
481             case PropertyType.DOUBLE:
482                 return obj instanceof Double ? valueFactory.createValue(((Double)obj).doubleValue()) : valueFactory.createValue(((Float)obj).doubleValue());
483             case PropertyType.BINARY:
484                 return valueFactory.createValue((InputStream)obj);
485             case PropertyType.REFERENCE:
486                 return valueFactory.createValue(((Content)obj).getJCRNode());
487             default:
488                 return (obj != null ? valueFactory.createValue(obj.toString()) : valueFactory.createValue(StringUtils.EMPTY));
489         }
490     }
491 
492     /**
493      * Transforms a string to a jcr value object.
494      */
495     public static Value createValue(String valueStr, int type, ValueFactory valueFactory) {
496         Value value = null;
497         if (type == PropertyType.STRING) {
498             value = valueFactory.createValue(valueStr);
499         }
500         else if (type == PropertyType.BOOLEAN) {
501             value = valueFactory.createValue(BooleanUtils.toBoolean(valueStr));
502         }
503         else if (type == PropertyType.DOUBLE) {
504             try {
505                 value = valueFactory.createValue(Double.parseDouble(valueStr));
506             }
507             catch (NumberFormatException e) {
508                 value = valueFactory.createValue(0d);
509             }
510         }
511         else if (type == PropertyType.LONG) {
512             try {
513                 value = valueFactory.createValue(Long.parseLong(valueStr));
514             }
515             catch (NumberFormatException e) {
516                 value = valueFactory.createValue(0L);
517             }
518         }
519         else if (type == PropertyType.DATE) {
520             try {
521                 Calendar date = new GregorianCalendar();
522                 try {
523                     String newDateAndTime = valueStr;
524                     String[] dateAndTimeTokens = newDateAndTime.split("T"); //$NON-NLS-1$
525                     String newDate = dateAndTimeTokens[0];
526                     String[] dateTokens = newDate.split("-"); //$NON-NLS-1$
527                     int hour = 0;
528                     int minute = 0;
529                     int second = 0;
530                     int year = Integer.parseInt(dateTokens[0]);
531                     int month = Integer.parseInt(dateTokens[1]) - 1;
532                     int day = Integer.parseInt(dateTokens[2]);
533                     if (dateAndTimeTokens.length > 1) {
534                         String newTime = dateAndTimeTokens[1];
535                         String[] timeTokens = newTime.split(":"); //$NON-NLS-1$
536                         hour = Integer.parseInt(timeTokens[0]);
537                         minute = Integer.parseInt(timeTokens[1]);
538                         second = Integer.parseInt(timeTokens[2]);
539                     }
540                     date.set(year, month, day, hour, minute, second);
541                     // this is used in the searching
542                     date.set(Calendar.MILLISECOND, 0);
543                     date.setTimeZone(TimeZone.getTimeZone("GMT"));
544                 }
545                 // todo time zone??
546                 catch (Exception e) {
547                     // ignore, it sets the current date / time
548                 }
549                 value = valueFactory.createValue(date);
550             }
551             catch (Exception e) {
552                 log.debug("Exception caught: " + e.getMessage(), e); //$NON-NLS-1$
553             }
554         }
555 
556         return value;
557 
558     }
559 
560     public static int getJCRPropertyType(Object obj) {
561         if(obj instanceof String) {
562             return PropertyType.STRING;
563         }
564         if(obj instanceof Double) {
565             return PropertyType.DOUBLE;
566         }
567         if(obj instanceof Float) {
568             return PropertyType.DOUBLE;
569         }
570         if(obj instanceof Long) {
571             return PropertyType.LONG;
572         }
573         if(obj instanceof Integer) {
574             return PropertyType.LONG;
575         }
576         if(obj instanceof Boolean) {
577             return PropertyType.BOOLEAN;
578         }
579         if(obj instanceof Calendar) {
580             return PropertyType.DATE;
581         }
582         if(obj instanceof InputStream) {
583             return PropertyType.BINARY;
584         }
585         if(obj instanceof Content) {
586             return PropertyType.REFERENCE;
587         }
588         return PropertyType.UNDEFINED;
589     }
590 
591     public static List<String> getValuesStringList(Value[] values) {
592         ArrayList<String> list = new ArrayList<String>();
593         try{
594             Value value;
595             for( int i = 0; i < values.length; i++) {
596                 value = values[i];
597                 switch (value.getType()) {
598                     case (PropertyType.STRING):
599                         list.add(value.getString());
600                         break;
601                     case (PropertyType.DOUBLE):
602                         list.add(Double.toString(value.getDouble()));
603                         break;
604                     case (PropertyType.LONG):
605                         list.add(Long.toString(value.getLong()));
606                         break;
607                     case (PropertyType.BOOLEAN):
608                         list.add(Boolean.toString(value.getBoolean()));
609                         break;
610                     case (PropertyType.DATE):
611                         Date valueDate = value.getDate().getTime();
612                         list.add(DateUtil.format(valueDate, NodeDataUtil.getDateFormat()));
613                         break;
614                     case (PropertyType.BINARY):
615                         // for lack of better solution, fall through to the default - empty string
616                     default:
617                         list.add(StringUtils.EMPTY);
618                 }
619             }
620         }
621         catch (Exception e) {
622             log.debug("Exception caught: " + e.getMessage(), e); //$NON-NLS-1$
623         }
624         return list;
625     }
626 
627     public static String getDateFormat() {
628         String dateFormat = null;
629         try{
630             dateFormat = FastDateFormat.getDateInstance(
631             FastDateFormat.SHORT,
632             MgnlContext.getLocale()).getPattern();
633         }
634         // this happens if the context is not (yet) set
635         catch(IllegalStateException e){
636             dateFormat = DateUtil.YYYY_MM_DD;
637         }
638         return dateFormat;
639     }
640 }