This project has retired. For details please refer to its Attic page.
JcrConverter xref

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.chemistry.opencmis.jcr;
21  
22  import org.apache.chemistry.opencmis.commons.PropertyIds;
23  import org.apache.chemistry.opencmis.commons.data.PropertyBoolean;
24  import org.apache.chemistry.opencmis.commons.data.PropertyData;
25  import org.apache.chemistry.opencmis.commons.data.PropertyDateTime;
26  import org.apache.chemistry.opencmis.commons.data.PropertyDecimal;
27  import org.apache.chemistry.opencmis.commons.data.PropertyHtml;
28  import org.apache.chemistry.opencmis.commons.data.PropertyId;
29  import org.apache.chemistry.opencmis.commons.data.PropertyInteger;
30  import org.apache.chemistry.opencmis.commons.data.PropertyString;
31  import org.apache.chemistry.opencmis.commons.data.PropertyUri;
32  import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
33  import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractPropertyData;
34  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanImpl;
35  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeImpl;
36  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalImpl;
37  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl;
38  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
39  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriImpl;
40  import org.apache.chemistry.opencmis.jcr.util.Util;
41  
42  import javax.jcr.Node;
43  import javax.jcr.Property;
44  import javax.jcr.PropertyType;
45  import javax.jcr.RepositoryException;
46  import javax.jcr.Value;
47  import javax.jcr.ValueFactory;
48  import javax.jcr.ValueFormatException;
49  import javax.jcr.nodetype.NodeType;
50  import java.math.BigDecimal;
51  import java.math.BigInteger;
52  import java.util.ArrayList;
53  import java.util.GregorianCalendar;
54  import java.util.List;
55  
56  /**
57   * Utility class providing methods for converting various entities from/to their respective representation
58   * in JCR/CMIS.
59   */
60  public final class JcrConverter {
61      private JcrConverter() {}
62  
63      /**
64       * Escapes all illegal JCR name characters of a string.
65       * The encoding is loosely modeled after URI encoding, but only encodes
66       * the characters it absolutely needs to in order to make the resulting
67       * string a valid JCR name.
68       * <p/>
69       * QName EBNF:<br>
70       * <pre>
71       * simplename ::= onecharsimplename | twocharsimplename | threeormorecharname
72       * onecharsimplename ::= (* Any Unicode character except: '.', '/', ':', '[', ']', '*', '|' or any whitespace character *)
73       * twocharsimplename ::= '.' onecharsimplename | onecharsimplename '.' | onecharsimplename onecharsimplename
74       * threeormorecharname ::= nonspace string nonspace
75       * string ::= char | string char
76       * char ::= nonspace | ' '
77       * nonspace ::= (* Any Unicode character except: '/', ':', '[', ']', '*', '|' or any whitespace character *)
78       * </pre>
79       *
80       * @param cmisName the name to escape
81       * @return the escaped name
82       */    
83      public static String toJcrName(String cmisName) {
84          StringBuilder buffer = new StringBuilder(cmisName.length() * 2);
85          for (int i = 0; i < cmisName.length(); i++) {
86              char ch = cmisName.charAt(i);
87              if (ch == '%' || ch == '/' || ch == ':' || ch == '[' || ch == ']' || ch == '*' || ch == '|'
88                      || ch == '\t' || ch == '\r' || ch == '\n'
89                      || ch == '.' && cmisName.length() < 3
90                      || ch == ' ' && (i == 0 || i == cmisName.length() - 1)) {
91                  buffer.append('%');
92                  buffer.append(Character.toUpperCase(Character.forDigit(ch / 16, 16)));
93                  buffer.append(Character.toUpperCase(Character.forDigit(ch % 16, 16)));
94              }
95              else {
96                  buffer.append(ch);
97              }
98          }
99          return buffer.toString();
100     }
101 
102     /**
103      * Checks if the given name is valid a valid JCR name
104      *
105      * @param name  the name to check
106      * @return <code>true</code> if the name is valid, <code>false</code> otherwise.
107      */
108     public static boolean isValidJcrName(String name) { 
109         if (name == null || name.length() == 0) {
110             return false;
111         }
112 
113         for (int i = 0; i < name.length(); i++) {
114             char ch = name.charAt(i);
115             if (ch == '%' || ch == '/' || ch == ':' || ch == '[' || ch == ']' || ch == '*' || ch == '|'
116                     || ch == '\t' || ch == '\r' || ch == '\n'
117                     || ch == '.' && name.length() < 3
118                     || ch == ' ' && (i == 0 || i == name.length() - 1)) {
119                 return false;
120             }
121         }
122 
123         return true;
124     }
125 
126     /**
127      * Convert a JCR <code>Property</code> to a CMIS <code>PropertyData</code>.
128      * 
129      * @param jcrProperty
130      * @return  
131      * @throws RepositoryException
132      */
133     public static PropertyData<?> convert(Property jcrProperty) throws RepositoryException {
134         AbstractPropertyData<?> propertyData;
135 
136         switch (jcrProperty.getType()) {
137             case PropertyType.BINARY:
138             case PropertyType.NAME:
139             case PropertyType.PATH:
140             case PropertyType.REFERENCE:
141             case PropertyType.WEAKREFERENCE:
142             case PropertyType.STRING:
143                 propertyData = jcrProperty.isMultiple()
144                     ? new PropertyStringImpl(jcrProperty.getName(), toStrings(jcrProperty.getValues()))
145                     : new PropertyStringImpl(jcrProperty.getName(), jcrProperty.getString());
146                 break;
147 
148             case PropertyType.LONG:
149                 propertyData = jcrProperty.isMultiple()
150                     ? new PropertyIntegerImpl(jcrProperty.getName(), toInts(jcrProperty.getValues()))
151                     : new PropertyIntegerImpl(jcrProperty.getName(), BigInteger.valueOf(jcrProperty.getLong()));
152                 break;
153 
154             case PropertyType.DECIMAL:
155                 propertyData = jcrProperty.isMultiple()
156                     ? new PropertyDecimalImpl(jcrProperty.getName(), toDecs(jcrProperty.getValues()))
157                     : new PropertyDecimalImpl(jcrProperty.getName(), jcrProperty.getDecimal());
158                 break;
159 
160             case PropertyType.DOUBLE:
161                 propertyData = jcrProperty.isMultiple()
162                     ? new PropertyDecimalImpl(jcrProperty.getName(), doublesToDecs(jcrProperty.getValues()))
163                     : new PropertyDecimalImpl(jcrProperty.getName(), BigDecimal.valueOf(jcrProperty.getDouble()));
164                 break;
165 
166             case PropertyType.DATE:
167                 propertyData = jcrProperty.isMultiple()
168                     ? new PropertyDateTimeImpl(jcrProperty.getName(), toDates(jcrProperty.getValues()))
169                     : new PropertyDateTimeImpl(jcrProperty.getName(), Util.toCalendar(jcrProperty.getDate()));
170                 break;
171 
172             case PropertyType.BOOLEAN:
173                 propertyData = jcrProperty.isMultiple()
174                     ? new PropertyBooleanImpl(jcrProperty.getName(), toBools(jcrProperty.getValues()))
175                     : new PropertyBooleanImpl(jcrProperty.getName(), jcrProperty.getBoolean());
176                 break;
177 
178             case PropertyType.URI:
179                 propertyData = jcrProperty.isMultiple()
180                     ? new PropertyUriImpl(jcrProperty.getName(), toStrings(jcrProperty.getValues()))
181                     : new PropertyUriImpl(jcrProperty.getName(), jcrProperty.getString());
182                 break;
183 
184             default:
185                 throw new CmisInvalidArgumentException("Invalid property type: " + jcrProperty.getType());
186         }
187 
188         propertyData.setDisplayName(jcrProperty.getName());
189         propertyData.setLocalName(jcrProperty.getName());
190         propertyData.setQueryName(jcrProperty.getName());
191         
192         return propertyData;
193     }
194 
195     /**
196      * Set a property on a JCR node. 
197      *
198      * @param node  the node to set the property
199      * @param propertyData  the property to set
200      * @throws RepositoryException
201      */
202     public static void setProperty(Node node, PropertyData<?> propertyData) throws RepositoryException {
203         Value[] values;
204         int propertyType;
205 
206         if (propertyData instanceof PropertyBoolean) {
207             values = toValue((PropertyBoolean) propertyData, node.getSession().getValueFactory());
208             propertyType = PropertyType.BOOLEAN;
209         }
210         else if (propertyData instanceof PropertyDateTime) {
211             values = toValue((PropertyDateTime) propertyData, node.getSession().getValueFactory());
212             propertyType = PropertyType.DATE;
213         }
214         else if (propertyData instanceof PropertyDecimal) {
215             values = toValue((PropertyDecimal) propertyData, node.getSession().getValueFactory());
216             propertyType = PropertyType.DECIMAL;
217         }
218         else if (propertyData instanceof PropertyHtml) {
219             values = toValue((PropertyHtml) propertyData, node.getSession().getValueFactory());
220             propertyType = PropertyType.STRING;
221         }
222         else if (propertyData instanceof PropertyId) {
223             values = toValue((PropertyId) propertyData, node.getSession().getValueFactory());
224             propertyType = PropertyType.STRING;
225         }
226         else if (propertyData instanceof PropertyInteger) {
227             values = toValue((PropertyInteger) propertyData, node.getSession().getValueFactory());
228             propertyType = PropertyType.DECIMAL;
229         }
230         else if (propertyData instanceof PropertyString) {
231             values = toValue((PropertyString) propertyData, node.getSession().getValueFactory());
232             propertyType = PropertyType.STRING;
233         }
234         else if (propertyData instanceof PropertyUri) {
235             values = toValue((PropertyUri) propertyData, node.getSession().getValueFactory());
236             propertyType = PropertyType.URI;
237         }
238         else {
239             throw new CmisInvalidArgumentException("Invalid property type: " + propertyData);
240         }
241 
242         String id = propertyData.getId();
243         String name;
244         if (PropertyIds.NAME.equals(id)) {
245             node.addMixin(NodeType.MIX_TITLE);
246             name = Property.JCR_TITLE;
247         }
248         else if (PropertyIds.CONTENT_STREAM_MIME_TYPE.equals(id)) {
249             name = Property.JCR_MIMETYPE;
250         }
251         else {
252             name = toJcrName(propertyData.getId());
253         }
254 
255         if (values.length == 1) {
256             node.setProperty(name, values[0]);
257         }
258         else {
259             node.setProperty(name, values, propertyType);
260         }
261     }
262 
263     /**
264      * Remove a property from a JCR node
265      *
266      * @param node  the node from which to remove the property
267      * @param propertyData  the property to remove
268      * @throws RepositoryException
269      */
270     public static void removeProperty(Node node, PropertyData<?> propertyData) throws RepositoryException {
271         String id = propertyData.getId();
272         String name = PropertyIds.NAME.equals(id)
273                 ? Property.JCR_TITLE :
274                 toJcrName(propertyData.getId());
275 
276         if (node.hasProperty(name)) {
277             node.getProperty(name).remove();
278         }
279     }
280 
281     //------------------------------------------< private >---
282 
283     /**
284      * Convert an array of <code>Value</code>s to a list of <code>String</code>s.
285      */
286     private static List<String> toStrings(Value[] values) throws RepositoryException {
287         ArrayList<String> strings = new ArrayList<String>(values.length);
288 
289         for (Value v : values) {
290             strings.add(v.getString());
291         }
292 
293         return strings;
294     }
295 
296     /**
297      * Convert an array of <code>Value</code>s to a list of <code>BigInteger</code>s.
298      */
299     private static List<BigInteger> toInts(Value[] values) throws RepositoryException {
300         ArrayList<BigInteger> ints = new ArrayList<BigInteger>(values.length);
301 
302         for (Value v : values) {
303             ints.add(BigInteger.valueOf(v.getLong()));
304         }
305 
306         return ints;
307     }
308 
309     /**
310      * Convert an array of <code>Value</code>s to a list of <code>BigDecimal</code>s.
311      */
312     private static List<BigDecimal> toDecs(Value[] values) throws RepositoryException {
313         ArrayList<BigDecimal> decs = new ArrayList<BigDecimal>(values.length);
314 
315         for (Value v : values) {
316             decs.add(v.getDecimal());
317         }
318 
319         return decs;
320     }
321 
322     /**
323      * Convert an array of double <code>Value</code>s to a list of <code>BigInteger</code>s.
324      */
325     private static List<BigDecimal> doublesToDecs(Value[] values) throws RepositoryException {
326         ArrayList<BigDecimal> decs = new ArrayList<BigDecimal>(values.length);
327 
328         for (Value v : values) {
329             decs.add(BigDecimal.valueOf(v.getDouble()));
330         }
331 
332         return decs;
333     }
334 
335     /**
336      * Convert an array of <code>Value</code>s to a list of <code>Booleans</code>s.
337      */
338     private static List<Boolean> toBools(Value[] values) throws RepositoryException {
339         ArrayList<Boolean> bools = new ArrayList<Boolean>(values.length);
340 
341         for (Value v : values) {
342             bools.add(v.getBoolean());
343         }
344 
345         return bools;
346     }
347 
348     /**
349      * Convert an array of <code>Value</code>s to a list of <code>GregorianCalendar</code>s.
350      */
351     private static List<GregorianCalendar> toDates(Value[] values) throws RepositoryException {
352         ArrayList<GregorianCalendar> dates = new ArrayList<GregorianCalendar>(values.length);
353 
354         for (Value v : values) {
355             dates.add(Util.toCalendar(v.getDate()));
356         }
357 
358         return dates;
359     }
360 
361     /**
362      * Convert a <code>PropertyBoolean</code> to an array of JCR <code>Values</code>.
363      */
364     private static Value[] toValue(PropertyBoolean propertyData, ValueFactory valueFactory) {
365         List<Boolean> values = propertyData.getValues();
366         if (values == null) {
367             return new Value[0];
368         }
369 
370         Value[] result = new Value[values.size()];
371         int k = 0;
372         for (Boolean v : values) {
373             result[k++] = valueFactory.createValue(v);
374         }
375 
376         return result;
377     }
378 
379     /**
380      * Convert a <code>PropertyDateTime</code> to an array of JCR <code>Values</code>.
381      */
382     private static Value[] toValue(PropertyDateTime propertyData, ValueFactory valueFactory) {
383         List<GregorianCalendar> values = propertyData.getValues();
384         if (values == null) {
385             return new Value[0];
386         }
387 
388         Value[] result = new Value[values.size()];
389         int k = 0;
390         for (GregorianCalendar v : values) {
391             result[k++] = valueFactory.createValue(v);
392         }
393 
394         return result;
395     }
396 
397     /**
398      * Convert a <code>PropertyDecimal</code> to an array of JCR <code>Values</code>.
399      */
400     private static Value[] toValue(PropertyDecimal propertyData, ValueFactory valueFactory) {
401         List<BigDecimal> values = propertyData.getValues();
402         if (values == null) {
403             return new Value[0];
404         }
405 
406         Value[] result = new Value[values.size()];
407         int k = 0;
408         for (BigDecimal v : values) {
409             result[k++] = valueFactory.createValue(v);
410         }
411 
412         return result;
413     }
414 
415     /**
416      * Convert a <code>PropertyHtml</code> to an array of JCR <code>Values</code>.
417      */
418     private static Value[] toValue(PropertyHtml propertyData, ValueFactory valueFactory) {
419         List<String> values = propertyData.getValues();
420         if (values == null) {
421             return new Value[0];
422         }
423 
424         Value[] result = new Value[values.size()];
425         int k = 0;
426         for (String v : values) {
427             result[k++] = valueFactory.createValue(v);
428         }
429 
430         return result;
431     }
432 
433     /**
434      * Convert a <code>PropertyId</code> to an array of JCR <code>Values</code>.
435      */
436     private static Value[] toValue(PropertyId propertyData, ValueFactory valueFactory) {
437         List<String> values = propertyData.getValues();
438         if (values == null) {
439             return new Value[0];
440         }
441 
442         Value[] result = new Value[values.size()];
443         int k = 0;
444         for (String v : values) {
445             result[k++] = valueFactory.createValue(v);
446         }
447 
448         return result;
449     }
450 
451     /**
452      * Convert a <code>PropertyInteger</code> to an array of JCR <code>Values</code>.
453      */
454     private static Value[] toValue(PropertyInteger propertyData, ValueFactory valueFactory) {
455         List<BigInteger> values = propertyData.getValues();
456         if (values == null) {
457             return new Value[0];
458         }
459 
460         Value[] result = new Value[values.size()];
461         int k = 0;
462         for (BigInteger v : values) {
463             result[k++] = valueFactory.createValue(new BigDecimal(v));
464         }
465 
466         return result;
467     }
468 
469     /**
470      * Convert a <code>PropertyString</code> to an array of JCR <code>Values</code>.
471      */
472     private static Value[] toValue(PropertyString propertyData, ValueFactory valueFactory) {
473         List<String> values = propertyData.getValues();
474         if (values == null) {
475             return new Value[0];
476         }
477 
478         Value[] result = new Value[values.size()];
479         int k = 0;
480         for (String v : values) {
481             result[k++] = valueFactory.createValue(v);
482         }
483 
484         return result;
485     }
486 
487     /**
488      * Convert a <code>PropertyUri</code> to an array of JCR <code>Values</code>.
489      */
490     private static Value[] toValue(PropertyUri propertyData, ValueFactory valueFactory) throws ValueFormatException {
491         List<String> values = propertyData.getValues();
492         if (values == null) {
493             return new Value[0];
494         }
495 
496         Value[] result = new Value[values.size()];
497         int k = 0;
498         for (String v : values) {
499             result[k++] = valueFactory.createValue(v, PropertyType.URI);
500         }
501 
502         return result;
503     }
504 
505 }