View Javadoc

1   /**
2    * This file Copyright (c) 2003-2010 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: 36903 $ ($Author: pbaerfuss $)
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                     nodeData.setValue(((Long)valueObj).longValue());
252                     break;
253                 case PropertyType.DOUBLE:
254                     nodeData.setValue(((Double)valueObj).doubleValue());
255                     break;
256                 case PropertyType.BINARY:
257                     nodeData.setValue((InputStream)valueObj);
258                     break;
259                 case PropertyType.REFERENCE:
260                     nodeData.setValue((Content)valueObj);
261                     break;
262                 default:
263                     nodeData.setValue(valueObj.toString());
264             }
265         }
266         return nodeData;
267     }
268 
269 
270     /**
271      * String representation of the jcr property type.
272      */
273     public static String getTypeName(NodeData nd) {
274         return PropertyType.nameFromValue(nd.getType());
275     }
276 
277     /**
278      * Simple method to get strings like configuration informations.
279      */
280     public static String getString(String repository, String path) {
281         return getString(repository, path, null);
282     }
283 
284     /**
285      * Get the string or the empty string if not existing.
286      */
287     public static String getString(Content node, String name) {
288         return getString(node, name, "");
289     }
290 
291     /**
292      * You can define a default value if not found.
293      */
294     public static String getString(String repository, String path, String defaultValue) {
295         try {
296             String name = StringUtils.substringAfterLast(path, "/");
297             String nodeHandle = StringUtils.substringBeforeLast(path, "/");
298             Content node = MgnlContext.getHierarchyManager(repository).getContent(nodeHandle);
299             return getString(node, name);
300         }
301         catch (Exception e) {
302             return defaultValue;
303         }
304     }
305 
306     /**
307      * You can define a default value if not found.
308      */
309     public static String getString(Content node, String name, String defaultValue) {
310         try {
311             if (node.hasNodeData(name)) {
312                 return getValueString(node.getNodeData(name));
313             }
314 
315             return defaultValue;
316         }
317         catch (Exception e) {
318             return defaultValue;
319         }
320     }
321 
322     public static long getLong(Content node, String name, long defaultValue) {
323         try {
324             if(node.hasNodeData(name)){
325                 return node.getNodeData(name).getLong();
326             }
327         }
328         // should not happen
329         catch (RepositoryException e) {
330             log.error("can't read value will return default value", e);
331         }
332         return defaultValue;
333     }
334 
335     public static Calendar getDate(Content node, String name, Calendar defaultValue) {
336         try {
337             if(node.hasNodeData(name)){
338                 return node.getNodeData(name).getDate();
339             }
340         }
341         // should not happen
342         catch (RepositoryException e) {
343             log.error("can't read value will return default value", e);
344         }
345         return defaultValue;
346     }
347 
348     public static boolean getBoolean(Content node, String name, boolean defaultValue) {
349         try {
350             if(node.hasNodeData(name)){
351                 return node.getNodeData(name).getBoolean();
352             }
353         }
354         // should not happen
355         catch (RepositoryException e) {
356             log.error("can't read value will return default value", e);
357         }
358         return defaultValue;
359     }
360 
361     /**
362      * If the NodeData does not exist yet, just create it.
363      * @param node
364      * @param name
365      * @return the found or created NodeData
366      * @throws AccessDeniedException
367      * @throws PathNotFoundException
368      * @throws RepositoryException
369      */
370     public static NodeData getOrCreate(Content node, String name) throws AccessDeniedException, RepositoryException {
371         return getOrCreate(node, name, PropertyType.STRING);
372     }
373 
374     /**
375      * If the NodeData does not exist yet, just create it.
376      * @param node
377      * @param name
378      * @return the found or created NodeData
379      * @throws AccessDeniedException
380      * @throws PathNotFoundException
381      * @throws RepositoryException
382      */
383     public static NodeData getOrCreate(Content node, String name, int type) throws AccessDeniedException,
384         RepositoryException {
385         if (node.hasNodeData(name)) {
386             return node.getNodeData(name);
387         }
388 
389         return node.createNodeData(name, type);
390     }
391 
392     public static NodeData getOrCreate(Content node, String name, Object obj) throws AccessDeniedException,
393         RepositoryException {
394 
395         return getOrCreate(node, name, getJCRPropertyType(obj));
396     }
397 
398 
399     public static NodeData getOrCreateAndSet(Content node, String name, Object obj) throws AccessDeniedException, RepositoryException {
400         // TODO we should not use the jcr node
401         ValueFactory valueFactory = node.getJCRNode().getSession().getValueFactory();
402         NodeData nd = getOrCreate(node, name, getJCRPropertyType(obj));
403         nd.setValue(createValue(obj, valueFactory));
404         return nd;
405     }
406 
407     public static NodeData getOrCreateAndSet(Content node, String name, long value) throws AccessDeniedException, RepositoryException {
408         return getOrCreateAndSet(node, name, new Long(value));
409     }
410 
411     public static NodeData getOrCreateAndSet(Content node, String name, Value[] value) throws AccessDeniedException, RepositoryException {
412         if (node.hasNodeData(name)) {
413             node.setNodeData(name, value);
414             return node.getNodeData(name);
415         }
416 
417         return node.createNodeData(name, value);
418 
419     }
420 
421     public static NodeData getOrCreateAndSet(Content node, String name, int value) throws AccessDeniedException, RepositoryException {
422         return getOrCreateAndSet(node, name, new Long(value));
423     }
424 
425     public static NodeData getOrCreateAndSet(Content node, String name, boolean value) throws AccessDeniedException, RepositoryException {
426         return getOrCreateAndSet(node, name, new Boolean(value));
427     }
428 
429     /**
430      * Uses the i18n mechanism to translate the message if the resulting string is a key.
431      */
432     public static String getI18NString(Content node, String str) {
433         Messages msgs = MessagesManager.getMessages();
434         String key = getString(node, str);
435         String i18nBasename = null;
436         try {
437             i18nBasename = NodeDataUtil.inheritString(node, "i18nBasename");
438         }
439         catch (RepositoryException e) {
440             log.error("can't read i18nBasename value, will default back", e);
441         }
442 
443         if (StringUtils.isNotEmpty(i18nBasename)) {
444             msgs = MessagesUtil.chainWithDefault(i18nBasename);
445         }
446 
447         return msgs.getWithDefault(key, key);
448     }
449 
450     /**
451      * Uses the i18n mechanism to translate the message if the resulting string is a key.
452      */
453     public static String getI18NString(Content node, String str, String basename) {
454         String key = getString(node, str);
455         return MessagesManager.getMessages(basename).getWithDefault(key, key);
456     }
457 
458     /**
459      * Uses the default value factory.
460      */
461     public static Value createValue(String valueStr, int type) throws RepositoryException {
462         HierarchyManager hm = MgnlContext.getSystemContext().getHierarchyManager(ContentRepository.CONFIG);
463         ValueFactory valueFactory = hm.getWorkspace().getSession().getValueFactory();
464         return createValue(valueStr, type, valueFactory);
465     }
466 
467     public static Value createValue(Object obj, ValueFactory valueFactory) throws RepositoryException {
468         switch (getJCRPropertyType(obj)) {
469             case PropertyType.STRING:
470                 return valueFactory.createValue((String)obj);
471             case PropertyType.BOOLEAN:
472                 return valueFactory.createValue(((Boolean)obj).booleanValue());
473             case PropertyType.DATE:
474                 return valueFactory.createValue((Calendar)obj);
475             case PropertyType.LONG:
476                 return valueFactory.createValue(((Long)obj).longValue());
477             case PropertyType.DOUBLE:
478                 return valueFactory.createValue(((Double)obj).doubleValue());
479             case PropertyType.BINARY:
480                 return valueFactory.createValue((InputStream)obj);
481             case PropertyType.REFERENCE:
482                 return valueFactory.createValue(((Content)obj).getJCRNode());
483             default:
484                 return (obj != null ? valueFactory.createValue(obj.toString()) : valueFactory.createValue(StringUtils.EMPTY));
485         }
486     }
487 
488     /**
489      * Transforms a string to a jcr value object.
490      */
491     public static Value createValue(String valueStr, int type, ValueFactory valueFactory) {
492         Value value = null;
493         if (type == PropertyType.STRING) {
494             value = valueFactory.createValue(valueStr);
495         }
496         else if (type == PropertyType.BOOLEAN) {
497             value = valueFactory.createValue(BooleanUtils.toBoolean(valueStr));
498         }
499         else if (type == PropertyType.DOUBLE) {
500             try {
501                 value = valueFactory.createValue(Double.parseDouble(valueStr));
502             }
503             catch (NumberFormatException e) {
504                 value = valueFactory.createValue(0d);
505             }
506         }
507         else if (type == PropertyType.LONG) {
508             try {
509                 value = valueFactory.createValue(Long.parseLong(valueStr));
510             }
511             catch (NumberFormatException e) {
512                 value = valueFactory.createValue(0L);
513             }
514         }
515         else if (type == PropertyType.DATE) {
516             try {
517                 Calendar date = new GregorianCalendar();
518                 try {
519                     String newDateAndTime = valueStr;
520                     String[] dateAndTimeTokens = newDateAndTime.split("T"); //$NON-NLS-1$
521                     String newDate = dateAndTimeTokens[0];
522                     String[] dateTokens = newDate.split("-"); //$NON-NLS-1$
523                     int hour = 0;
524                     int minute = 0;
525                     int second = 0;
526                     int year = Integer.parseInt(dateTokens[0]);
527                     int month = Integer.parseInt(dateTokens[1]) - 1;
528                     int day = Integer.parseInt(dateTokens[2]);
529                     if (dateAndTimeTokens.length > 1) {
530                         String newTime = dateAndTimeTokens[1];
531                         String[] timeTokens = newTime.split(":"); //$NON-NLS-1$
532                         hour = Integer.parseInt(timeTokens[0]);
533                         minute = Integer.parseInt(timeTokens[1]);
534                         second = Integer.parseInt(timeTokens[2]);
535                     }
536                     date.set(year, month, day, hour, minute, second);
537                     // this is used in the searching
538                     date.set(Calendar.MILLISECOND, 0);
539                     date.setTimeZone(TimeZone.getTimeZone("GMT"));
540                 }
541                 // todo time zone??
542                 catch (Exception e) {
543                     // ignore, it sets the current date / time
544                 }
545                 value = valueFactory.createValue(date);
546             }
547             catch (Exception e) {
548                 log.debug("Exception caught: " + e.getMessage(), e); //$NON-NLS-1$
549             }
550         }
551 
552         return value;
553 
554     }
555 
556     public static int getJCRPropertyType(Object obj) {
557         if(obj instanceof String) {
558             return PropertyType.STRING;
559         }
560         if(obj instanceof Double) {
561             return PropertyType.DOUBLE;
562         }
563         if(obj instanceof Long) {
564             return PropertyType.LONG;
565         }
566         if(obj instanceof Integer) {
567             return PropertyType.LONG;
568         }
569         if(obj instanceof Boolean) {
570             return PropertyType.BOOLEAN;
571         }
572         if(obj instanceof Calendar) {
573             return PropertyType.DATE;
574         }
575         if(obj instanceof InputStream) {
576             return PropertyType.BINARY;
577         }
578         if(obj instanceof Content) {
579             return PropertyType.REFERENCE;
580         }
581         return PropertyType.UNDEFINED;
582     }
583 
584     public static List<String> getValuesStringList(Value[] values) {
585         ArrayList<String> list = new ArrayList<String>();
586         try{
587             Value value;
588             for( int i = 0; i < values.length; i++) {
589                 value = values[i];
590                 switch (value.getType()) {
591                     case (PropertyType.STRING):
592                         list.add(value.getString());
593                         break;
594                     case (PropertyType.DOUBLE):
595                         list.add(Double.toString(value.getDouble()));
596                         break;
597                     case (PropertyType.LONG):
598                         list.add(Long.toString(value.getLong()));
599                         break;
600                     case (PropertyType.BOOLEAN):
601                         list.add(Boolean.toString(value.getBoolean()));
602                         break;
603                     case (PropertyType.DATE):
604                         Date valueDate = value.getDate().getTime();
605                         list.add(DateUtil.format(valueDate, NodeDataUtil.getDateFormat()));
606                         break;
607                     case (PropertyType.BINARY):
608                         // for lack of better solution, fall through to the default - empty string
609                     default:
610                         list.add(StringUtils.EMPTY);
611                 }
612             }
613         }
614         catch (Exception e) {
615             log.debug("Exception caught: " + e.getMessage(), e); //$NON-NLS-1$
616         }
617         return list;
618     }
619 
620     public static String getDateFormat() {
621         String dateFormat = null;
622         try{
623             dateFormat = FastDateFormat.getDateInstance(
624             FastDateFormat.SHORT,
625             MgnlContext.getLocale()).getPattern();
626         }
627         // this happens if the context is not (yet) set
628         catch(IllegalStateException e){
629             dateFormat = DateUtil.YYYY_MM_DD;
630         }
631         return dateFormat;
632     }
633 }