View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.jcr;
25  
26  import java.io.File;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.net.URL;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.Collections;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  import javax.jcr.PropertyType;
38  import javax.jcr.Session;
39  import javax.jcr.Value;
40  import javax.jcr.nodetype.ConstraintViolationException;
41  import javax.jcr.nodetype.NodeDefinitionTemplate;
42  import javax.jcr.nodetype.NodeTypeDefinition;
43  import javax.jcr.nodetype.NodeTypeTemplate;
44  import javax.jcr.nodetype.PropertyDefinitionTemplate;
45  import javax.jcr.version.OnParentVersionAction;
46  import net.jcip.annotations.NotThreadSafe;
47  import org.modeshape.common.collection.Problems;
48  import org.modeshape.common.collection.SimpleProblems;
49  import org.modeshape.common.util.CheckArg;
50  import org.modeshape.common.util.IoUtil;
51  import org.modeshape.graph.ExecutionContext;
52  import org.modeshape.graph.Graph;
53  import org.modeshape.graph.Location;
54  import org.modeshape.graph.Subgraph;
55  import org.modeshape.graph.SubgraphNode;
56  import org.modeshape.graph.connector.inmemory.InMemoryRepositorySource;
57  import org.modeshape.graph.io.Destination;
58  import org.modeshape.graph.io.GraphBatchDestination;
59  import org.modeshape.graph.property.Name;
60  import org.modeshape.graph.property.NameFactory;
61  import org.modeshape.graph.property.Path;
62  import org.modeshape.graph.property.PathFactory;
63  import org.modeshape.graph.property.Property;
64  import org.modeshape.graph.property.ValueFactories;
65  import org.modeshape.graph.property.ValueFactory;
66  import org.modeshape.graph.property.basic.LocalNamespaceRegistry;
67  
68  /**
69   * A base class for loading content into a graph with the standard format used by ModeShape.
70   * <p>
71   * The root node of the graph should have zero or more children. Each child of the root node represents a type to be registered
72   * and the name of the node should be the name of the node type to be registered. Additionally, any facets of the node type that
73   * are specified should be set in a manner consistent with the JCR specification for the {@code nt:nodeType} built-in node type.
74   * The {@code jcr:primaryType} property does not need to be set on these nodes, but the nodes must be semantically valid as if the
75   * {@code jcr:primaryType} property was set.
76   * </p>
77   * <p>
78   * Each node type node may have zero or more children, each with the name {@code jcr:propertyDefinition} or {@code
79   * jcr:childNodeDefinition}, as per the definition of the {@code nt:nodeType} built-in type. Each property definition and child
80   * node definition must obey the semantics of {@code jcr:propertyDefinition} and {@code jcr:childNodeDefinition} respectively
81   * However these nodes also do not need to have the {@code jcr:primaryType} property set.
82   * </p>
83   * <p>
84   * For example, one valid graph is:
85   * 
86   * <pre>
87   * &lt;root&gt;
88   * +---- test:testMixinType
89   *        +--- jcr:nodeTypeName               =  test:testMixinType  (PROPERTY)
90   *        +--- jcr:isMixin                    =  true                (PROPERTY)    
91   *        +--- jcr:childNodeDefinition                               (CHILD NODE)
92   *        |     +--- jcr:name                 =  test:childNodeA     (PROPERTY)
93   *        |     +--- jcr:mandatory            =  true                (PROPERTY)
94   *        |     +--- jcr:autoCreated          =  true                (PROPERTY)
95   *        |     +--- jcr:defaultPrimaryType   =  nt:base             (PROPERTY)
96   *        |     +--- jcr:requiredPrimaryTypes =  nt:base             (PROPERTY)
97   *        +--- jcr:propertyDefinition                                (CHILD NODE)
98   *              +--- jcr:name                 =  test:propertyA      (PROPERTY)
99   *              +--- jcr:multiple             =  true                (PROPERTY)
100  *              +--- jcr:requiredType         =  String              (PROPERTY)
101  * </pre>
102  * 
103  * This graph (when registered) would create a mixin node named "test:testMixinType" with a mandatory, autocreated child node
104  * named "test:childNodeA" with a default and required primary type of "nt:base" and a multi-valued string property named
105  * "test:propertyA".
106  * </p>
107  */
108 @NotThreadSafe
109 class GraphNodeTypeReader implements Iterable<NodeTypeDefinition> {
110 
111     private static final Map<String, Integer> PROPERTY_TYPE_VALUES_FROM_NAME;
112 
113     static {
114         Map<String, Integer> temp = new HashMap<String, Integer>();
115 
116         temp.put(PropertyType.TYPENAME_BINARY.toUpperCase(), PropertyType.BINARY);
117         temp.put(PropertyType.TYPENAME_BOOLEAN.toUpperCase(), PropertyType.BOOLEAN);
118         temp.put(PropertyType.TYPENAME_DATE.toUpperCase(), PropertyType.DATE);
119         temp.put(PropertyType.TYPENAME_DECIMAL.toUpperCase(), PropertyType.DECIMAL);
120         temp.put(PropertyType.TYPENAME_DOUBLE.toUpperCase(), PropertyType.DOUBLE);
121         temp.put(PropertyType.TYPENAME_LONG.toUpperCase(), PropertyType.LONG);
122         temp.put(PropertyType.TYPENAME_NAME.toUpperCase(), PropertyType.NAME);
123         temp.put(PropertyType.TYPENAME_PATH.toUpperCase(), PropertyType.PATH);
124         temp.put(PropertyType.TYPENAME_STRING.toUpperCase(), PropertyType.STRING);
125         temp.put(PropertyType.TYPENAME_REFERENCE.toUpperCase(), PropertyType.REFERENCE);
126         temp.put(PropertyType.TYPENAME_WEAKREFERENCE.toUpperCase(), PropertyType.WEAKREFERENCE);
127         temp.put(PropertyType.TYPENAME_UNDEFINED.toUpperCase(), PropertyType.UNDEFINED);
128         temp.put(PropertyType.TYPENAME_URI.toUpperCase(), PropertyType.URI);
129 
130         PROPERTY_TYPE_VALUES_FROM_NAME = Collections.unmodifiableMap(temp);
131     }
132 
133     protected final Problems problems = new SimpleProblems();
134     protected final List<NodeTypeDefinition> types = new ArrayList<NodeTypeDefinition>();
135     protected final List<NodeTypeDefinition> immutableTypes = Collections.unmodifiableList(types);
136     protected final ExecutionContext context;
137     protected final ValueFactories valueFactories;
138     protected final PathFactory pathFactory;
139     protected final NameFactory nameFactory;
140     protected final ValueFactory<Boolean> booleanFactory;
141     protected final ValueFactory<String> stringFactory;
142     protected final Path root;
143 
144     /**
145      * Create a new node type factory that reads CND files.
146      * 
147      * @param session the session that will be used to register the node types; may not be null and must be a ModeShape Session.
148      */
149     protected GraphNodeTypeReader( Session session ) {
150         this(CheckArg.getInstanceOf(session, JcrSession.class, "session").getExecutionContext());
151     }
152 
153     /**
154      * Create a new node type factory that reads CND files.
155      * 
156      * @param executionContext the session that will be used to load the node types; may not be null and must be a ModeShape
157      *        Session.
158      */
159     protected GraphNodeTypeReader( ExecutionContext executionContext ) {
160         LocalNamespaceRegistry localRegistry = new LocalNamespaceRegistry(executionContext.getNamespaceRegistry());
161         context = executionContext.with(localRegistry);
162         valueFactories = context.getValueFactories();
163         pathFactory = valueFactories.getPathFactory();
164         nameFactory = valueFactories.getNameFactory();
165         booleanFactory = valueFactories.getBooleanFactory();
166         stringFactory = valueFactories.getStringFactory();
167         root = pathFactory.createRootPath();
168     }
169 
170     /**
171      * Import the node types from the supplied stream and add all of the node type definitions to this factory's list.
172      * 
173      * @param stream the stream containing the node types
174      * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
175      *        useful name
176      * @throws IOException if there is a problem reading from the supplied stream
177      */
178     public void read( InputStream stream,
179                       String resourceName ) throws IOException {
180         read(IoUtil.read(stream), resourceName);
181     }
182 
183     /**
184      * Import the node types from the supplied file and add all of the node type definitions to this factory's list.
185      * 
186      * @param file the file containing the node types
187      * @throws IOException if there is a problem reading from the supplied stream
188      */
189     public void read( File file ) throws IOException {
190         read(IoUtil.read(file), file.getCanonicalPath());
191     }
192 
193     /**
194      * Import the node types from the file at the supplied URL and add all of the node type definitions to this factory's list.
195      * 
196      * @param url the URL to the file containing the node types
197      * @throws IOException if there is a problem reading from the supplied stream
198      */
199     public void read( URL url ) throws IOException {
200         InputStream stream = url.openStream();
201         try {
202             read(IoUtil.read(stream), url.toString());
203         } finally {
204             stream.close();
205         }
206     }
207 
208     /**
209      * Import the node types from the supplied file and add all of the node type definitions to this factory's list.
210      * 
211      * @param resourceFile the name of the resource file on the classpath containing the node types
212      * @throws IOException if there is a problem reading from the supplied resource
213      */
214     public void read( String resourceFile ) throws IOException {
215         InputStream stream = getClass().getResourceAsStream(resourceFile);
216         if (stream != null) {
217             try {
218                 read(IoUtil.read(stream), resourceFile);
219             } finally {
220                 stream.close();
221             }
222         }
223     }
224 
225     /**
226      * Import the node types from the supplied string and add all of the node type definitions to this factory's list.
227      * 
228      * @param content the string containing the node types
229      * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
230      *        useful name
231      */
232     @SuppressWarnings( "cast" )
233     public void read( String content,
234                       String resourceName ) {
235         InMemoryRepositorySource source = new InMemoryRepositorySource();
236         source.setName("Node Type Import Source");
237         Graph graph = Graph.create(source, context);
238 
239         // Create the batch and load the graph ...
240         Graph.Batch batch = graph.batch();
241         Destination destination = new GraphBatchDestination(batch);
242         try {
243             importFrom(destination, root, content, resourceName);
244             List<NodeTypeDefinition> types = readTypesFrom(graph, root, null);
245             this.types.addAll(types);
246         } catch (Throwable t) {
247             problems.addError(t, JcrI18n.errorImportingNodeTypeContent, (Object)resourceName, t.getMessage());
248         }
249     }
250 
251     /**
252      * Import the node types from the supplied location in the specified graph.
253      * 
254      * @param graph the graph containing the standard ModeShape CND content
255      * @param parentOfTypes the path to the parent of the node type definition nodes
256      * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
257      *        useful name
258      */
259     public void read( Graph graph,
260                       Path parentOfTypes,
261                       String resourceName ) {
262         this.types.addAll(readTypesFrom(graph, parentOfTypes, null));
263     }
264 
265     /**
266      * Import the node types from the supplied location in the specified graph.
267      * 
268      * @param graph the graph containing the standard ModeShape CND content
269      * @param parentOfTypes the path to the parent of the node type definition nodes
270      * @param nodeTypesToRead the names of the node types that should be read; null means that all node types should be read
271      * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
272      *        useful name
273      */
274     public void read( Graph graph,
275                       Path parentOfTypes,
276                       Collection<Name> nodeTypesToRead,
277                       String resourceName ) {
278         this.types.addAll(readTypesFrom(graph, parentOfTypes, nodeTypesToRead));
279     }
280 
281     /**
282      * Import the node types from the supplied location in the specified graph.
283      * 
284      * @param subgraph the subgraph containing the standard ModeShape CND content
285      * @param locationOfParent the location to the parent of the node type definition nodes
286      * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
287      *        useful name
288      */
289     public void read( Subgraph subgraph,
290                       Location locationOfParent,
291                       String resourceName ) {
292         this.types.addAll(readTypesFrom(subgraph, locationOfParent, null));
293     }
294 
295     /**
296      * Get the problems where warnings and error messages were recorded by this factory.
297      * 
298      * @return the problems; never null
299      */
300     public Problems getProblems() {
301         return problems;
302     }
303 
304     /**
305      * Returns the node type definitions created by this factory.
306      * 
307      * @return the {@link NodeTypeDefinition}s
308      */
309     public NodeTypeDefinition[] getNodeTypeDefinitions() {
310         return types.toArray(new NodeTypeDefinition[types.size()]);
311     }
312 
313     /**
314      * {@inheritDoc}
315      * 
316      * @see java.lang.Iterable#iterator()
317      */
318     @Override
319     public Iterator<NodeTypeDefinition> iterator() {
320         return immutableTypes.iterator();
321     }
322 
323     protected List<NodeTypeDefinition> readTypesFrom( Graph graph,
324                                                       Path parentOfTypes,
325                                                       Collection<Name> nodeTypesToRead ) {
326         Subgraph subgraph = graph.getSubgraphOfDepth(5).at(parentOfTypes);
327         return readTypesFrom(subgraph, subgraph.getLocation(), nodeTypesToRead);
328     }
329 
330     protected List<NodeTypeDefinition> readTypesFrom( Subgraph nodeTypeSubgraph,
331                                                       Location locationOfParentOfNodeTypes,
332                                                       Collection<Name> nodeTypesToRead ) {
333         List<Location> nodeTypeLocations = nodeTypeSubgraph.getNode(locationOfParentOfNodeTypes).getChildren();
334         List<NodeTypeDefinition> results = new ArrayList<NodeTypeDefinition>(nodeTypeLocations.size());
335         boolean shouldFilterNodes = locationOfParentOfNodeTypes.hasPath() && nodeTypesToRead != null;
336 
337         for (Location location : nodeTypeLocations) {
338             SubgraphNode nodeTypeNode = nodeTypeSubgraph.getNode(location);
339             assert location.getPath() != null;
340 
341             Path relativeNodeTypePath = shouldFilterNodes ? location.getPath().relativeTo(locationOfParentOfNodeTypes.getPath()) : null;
342             if (shouldFilterNodes && !nodeTypesToRead.contains(relativeNodeTypePath.getSegment(0).getName())) {
343                 continue;
344             }
345 
346             try {
347                 NodeTypeDefinition nodeType = nodeTypeFrom(nodeTypeNode, nodeTypeSubgraph);
348                 results.add(nodeType);
349             } catch (ConstraintViolationException e) {
350                 String resource = stringFactory.create(locationOfParentOfNodeTypes.getPath());
351                 problems.addError(e, JcrI18n.errorImportingNodeTypeContent, resource, e.getMessage());
352             }
353         }
354         return results;
355     }
356 
357     @SuppressWarnings( "unchecked" )
358     protected NodeTypeTemplate nodeTypeFrom( SubgraphNode nodeTypeNode,
359                                              Subgraph subgraph ) throws ConstraintViolationException {
360         List<Location> children = nodeTypeNode.getChildren();
361 
362         String name = readString(nodeTypeNode, JcrLexicon.NODE_TYPE_NAME, null);
363         String primaryItemName = readString(nodeTypeNode, JcrLexicon.PRIMARY_ITEM_NAME, null);
364 
365         boolean mixin = readBoolean(nodeTypeNode, JcrLexicon.IS_MIXIN, false);
366         boolean isAbstract = readBoolean(nodeTypeNode, JcrLexicon.IS_ABSTRACT, false);
367         boolean queryable = readBoolean(nodeTypeNode, JcrLexicon.IS_QUERYABLE, true);
368         boolean orderableChildNodes = readBoolean(nodeTypeNode, JcrLexicon.HAS_ORDERABLE_CHILD_NODES, false);
369         List<String> supertypes = readStrings(nodeTypeNode, JcrLexicon.SUPERTYPES);
370 
371         NodeTypeTemplate template = new JcrNodeTypeTemplate(context);
372         template.setAbstract(isAbstract);
373         template.setDeclaredSuperTypeNames(supertypes.toArray(new String[supertypes.size()]));
374         template.setMixin(mixin);
375         template.setName(name);
376         template.setOrderableChildNodes(orderableChildNodes);
377         template.setPrimaryItemName(primaryItemName);
378         template.setQueryable(queryable);
379 
380         for (Location childLocation : children) {
381             if (JcrLexicon.PROPERTY_DEFINITION.equals(childLocation.getPath().getLastSegment().getName())) {
382                 template.getPropertyDefinitionTemplates().add(propertyDefinitionFrom(subgraph, childLocation));
383             } else if (JcrLexicon.CHILD_NODE_DEFINITION.equals(childLocation.getPath().getLastSegment().getName())) {
384                 template.getNodeDefinitionTemplates().add(childNodeDefinitionFrom(subgraph, childLocation));
385             } else {
386                 throw new IllegalStateException("Unexpected child of node type at: " + childLocation);
387             }
388         }
389 
390         return template;
391     }
392 
393     protected PropertyDefinitionTemplate propertyDefinitionFrom( Subgraph nodeTypeGraph,
394                                                                  Location propertyLocation ) throws ConstraintViolationException {
395         SubgraphNode propertyDefinitionNode = nodeTypeGraph.getNode(propertyLocation);
396 
397         String name = readString(propertyDefinitionNode, JcrLexicon.NAME, null);
398         String onParentVersionName = readString(propertyDefinitionNode,
399                                                 JcrLexicon.ON_PARENT_VERSION,
400                                                 OnParentVersionAction.nameFromValue(OnParentVersionAction.COPY));
401         int onParentVersion = OnParentVersionAction.valueFromName(onParentVersionName);
402 
403         int requiredType = PROPERTY_TYPE_VALUES_FROM_NAME.get(readString(propertyDefinitionNode, JcrLexicon.REQUIRED_TYPE, null));
404 
405         boolean mandatory = readBoolean(propertyDefinitionNode, JcrLexicon.MANDATORY, false);
406         boolean multiple = readBoolean(propertyDefinitionNode, JcrLexicon.MULTIPLE, false);
407         boolean autoCreated = readBoolean(propertyDefinitionNode, JcrLexicon.AUTO_CREATED, false);
408         boolean isProtected = readBoolean(propertyDefinitionNode, JcrLexicon.PROTECTED, false);
409         boolean isSearchable = readBoolean(propertyDefinitionNode, JcrLexicon.IS_FULL_TEXT_SEARCHABLE, true);
410         boolean isOrderable = readBoolean(propertyDefinitionNode, JcrLexicon.IS_QUERY_ORDERABLE, true);
411         List<Value> defaultValues = readValues(propertyDefinitionNode, JcrLexicon.DEFAULT_VALUES, requiredType);
412         List<String> constraints = readStrings(propertyDefinitionNode, JcrLexicon.VALUE_CONSTRAINTS);
413         List<String> queryOps = readStrings(propertyDefinitionNode, JcrLexicon.QUERY_OPERATORS);
414 
415         PropertyDefinitionTemplate template = new JcrPropertyDefinitionTemplate(context);
416         if (name != null) {
417             template.setName(name);
418         }
419         template.setAutoCreated(autoCreated);
420         template.setMandatory(mandatory);
421         template.setMultiple(multiple);
422         template.setProtected(isProtected);
423         template.setFullTextSearchable(isSearchable);
424         template.setAvailableQueryOperators(queryOps.toArray(new String[queryOps.size()]));
425         template.setQueryOrderable(isOrderable);
426         template.setProtected(isProtected);
427         template.setOnParentVersion(onParentVersion);
428         template.setDefaultValues(defaultValues.toArray(new Value[defaultValues.size()]));
429         template.setRequiredType(requiredType);
430         template.setValueConstraints(constraints.toArray(new String[constraints.size()]));
431         return template;
432     }
433 
434     protected NodeDefinitionTemplate childNodeDefinitionFrom( Subgraph nodeTypeGraph,
435                                                               Location childNodeLocation ) throws ConstraintViolationException {
436         SubgraphNode childNodeDefinitionNode = nodeTypeGraph.getNode(childNodeLocation);
437 
438         String childNodeName = readString(childNodeDefinitionNode, JcrLexicon.NAME, null);
439         String defaultPrimaryTypeName = readString(childNodeDefinitionNode, JcrLexicon.DEFAULT_PRIMARY_TYPE, null);
440         String onParentVersionName = readString(childNodeDefinitionNode,
441                                                 JcrLexicon.ON_PARENT_VERSION,
442                                                 OnParentVersionAction.nameFromValue(OnParentVersionAction.COPY));
443         int onParentVersion = OnParentVersionAction.valueFromName(onParentVersionName);
444 
445         boolean mandatory = readBoolean(childNodeDefinitionNode, JcrLexicon.MANDATORY, false);
446         boolean allowsSns = readBoolean(childNodeDefinitionNode, JcrLexicon.SAME_NAME_SIBLINGS, false);
447         boolean autoCreated = readBoolean(childNodeDefinitionNode, JcrLexicon.AUTO_CREATED, false);
448         boolean isProtected = readBoolean(childNodeDefinitionNode, JcrLexicon.PROTECTED, false);
449         List<String> requiredTypes = readStrings(childNodeDefinitionNode, JcrLexicon.REQUIRED_PRIMARY_TYPES);
450 
451         NodeDefinitionTemplate template = new JcrNodeDefinitionTemplate(context);
452         if (childNodeName != null) {
453             template.setName(childNodeName);
454         }
455         template.setAutoCreated(autoCreated);
456         template.setMandatory(mandatory);
457         template.setSameNameSiblings(allowsSns);
458         template.setProtected(isProtected);
459         template.setOnParentVersion(onParentVersion);
460         template.setDefaultPrimaryTypeName(defaultPrimaryTypeName);
461         template.setRequiredPrimaryTypeNames(requiredTypes.toArray(new String[requiredTypes.size()]));
462         return template;
463     }
464 
465     protected Name nameFrom( SubgraphNode node ) {
466         return node.getLocation().getPath().getLastSegment().getName();
467     }
468 
469     protected Name name( String name ) {
470         return nameFactory.create(name);
471     }
472 
473     protected String string( Object value ) {
474         return stringFactory.create(value);
475     }
476 
477     protected boolean readBoolean( SubgraphNode node,
478                                    String propertyName,
479                                    boolean defaultValue ) {
480         return readBoolean(node, nameFactory.create(propertyName), defaultValue);
481     }
482 
483     protected boolean readBoolean( SubgraphNode node,
484                                    Name propertyName,
485                                    boolean defaultValue ) {
486         Property property = node.getProperty(propertyName);
487         return property != null ? booleanFactory.create(property.getFirstValue()) : defaultValue;
488     }
489 
490     protected String readString( SubgraphNode node,
491                                  String propertyName,
492                                  String defaultValue ) {
493         return readString(node, nameFactory.create(propertyName), defaultValue);
494     }
495 
496     protected String readString( SubgraphNode node,
497                                  Name propertyName,
498                                  String defaultValue ) {
499         Property property = node.getProperty(propertyName);
500         return property != null ? stringFactory.create(property.getFirstValue()) : defaultValue;
501     }
502 
503     protected List<String> readStrings( SubgraphNode node,
504                                         String propertyName ) {
505         return readStrings(node, nameFactory.create(propertyName));
506     }
507 
508     protected List<String> readStrings( SubgraphNode node,
509                                         Name propertyName ) {
510         List<String> results = new ArrayList<String>();
511         Property property = node.getProperty(propertyName);
512         if (property != null) {
513             for (Object value : property) {
514                 String str = stringFactory.create(value);
515                 if (str != null && str.length() != 0) results.add(str);
516             }
517         }
518         return results;
519     }
520 
521     protected List<Value> readValues( SubgraphNode node,
522                                       Name propertyName,
523                                       int requiredType ) {
524         List<String> results = readStrings(node, propertyName);
525         List<Value> values = new ArrayList<Value>(results.size());
526         for (String result : results) {
527             values.add(new JcrValue(valueFactories, null, requiredType, result));
528         }
529         return values;
530     }
531 
532     protected Name readName( SubgraphNode node,
533                              String propertyName,
534                              Name defaultValue ) {
535         return readName(node, nameFactory.create(propertyName), defaultValue);
536     }
537 
538     protected Name readName( SubgraphNode node,
539                              Name propertyName,
540                              Name defaultValue ) {
541         Property property = node.getProperty(propertyName);
542         if (property != null && !property.isEmpty()) {
543             String firstValue = stringFactory.create(property.getFirstValue());
544             if (firstValue != null && firstValue.trim().length() != 0) {
545                 return valueFactories.getNameFactory().create(firstValue);
546             }
547         }
548         return defaultValue;
549     }
550 
551     protected List<Name> readNames( SubgraphNode node,
552                                     String propertyName,
553                                     Name defaultIfNone ) {
554         return readNames(node, nameFactory.create(propertyName), defaultIfNone);
555     }
556 
557     protected List<Name> readNames( SubgraphNode node,
558                                     Name propertyName,
559                                     Name defaultIfNone ) {
560         List<Name> results = new ArrayList<Name>();
561         Property property = node.getProperty(propertyName);
562         if (property != null) {
563             for (Object value : property) {
564                 Name name = nameFactory.create(value);
565                 if (name != null) results.add(name);
566             }
567         }
568         if (results.isEmpty() && defaultIfNone != null) results.add(defaultIfNone);
569         return results;
570     }
571 
572     /**
573      * Method that loads into the graph destination the content containing the node type definitions.
574      * 
575      * @param graphDestination the destination to which the node type content should be written; never null
576      * @param path the path within the destination at which the node type content should be rooted; never null
577      * @param content the content containing some string representation of the node types to be imported; never null
578      * @param resourceName a descriptive name for this import (used only for error messages); may be null
579      * @throws Exception if there is a problem importing from the content; this will be automatically recorded in the problems
580      */
581     protected void importFrom( Destination graphDestination,
582                                Path path,
583                                String content,
584                                String resourceName ) throws Exception {
585         throw new UnsupportedOperationException();
586     }
587 
588 }