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. This method
172      * will close the stream.
173      * 
174      * @param stream the stream containing the node types
175      * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
176      *        useful name
177      * @throws IOException if there is a problem reading from the supplied stream
178      */
179     public void read( InputStream stream,
180                       String resourceName ) throws IOException {
181         read(IoUtil.read(stream), resourceName);
182     }
183 
184     /**
185      * Import the node types from the supplied file and add all of the node type definitions to this factory's list.
186      * 
187      * @param file the file containing the node types
188      * @throws IllegalArgumentException if the supplied file reference is null, or if the file does not exist or is not readable
189      * @throws IOException if there is a problem reading from the supplied stream
190      */
191     public void read( File file ) throws IOException {
192         CheckArg.isNotNull(file, "file");
193         if (!file.exists() || !file.canRead()) {
194             throw new IOException(JcrI18n.fileMustExistAndBeReadable.text(file.getCanonicalPath()));
195         }
196         read(IoUtil.read(file), file.getCanonicalPath());
197     }
198 
199     /**
200      * Import the node types from the file at the supplied URL and add all of the node type definitions to this factory's list.
201      * 
202      * @param url the URL to the file containing the node types
203      * @throws IllegalArgumentException if the supplied URL is null
204      * @throws IOException if there is a problem opening or reading the stream to the supplied URL
205      */
206     public void read( URL url ) throws IOException {
207         CheckArg.isNotNull(url, "url");
208         InputStream stream = url.openStream();
209         boolean error = false;
210         try {
211             read(IoUtil.read(stream), url.toString());
212         } catch (IOException e) {
213             error = true;
214             throw e;
215         } catch (RuntimeException e) {
216             error = true;
217             throw e;
218         } finally {
219             try {
220                 stream.close();
221             } catch (IOException e) {
222                 if (!error) throw e;
223             } catch (RuntimeException e) {
224                 if (!error) throw e;
225             }
226         }
227     }
228 
229     /**
230      * Import the node types from the file at the supplied path, and add all of the node type definitions to this factory's list.
231      * This method first attempts to resolve the supplied path to a resource on the classpath. If such a resource could not be
232      * found, this method considers the supplied argument as the path to an existing and readable file. If that does not succeed,
233      * this method treats the supplied argument as a valid and resolvable URL.
234      * 
235      * @param resourceFile the name of the resource file on the classpath containing the node types
236      * @throws IllegalArgumentException if the supplied string is null or empty
237      * @throws IOException if there is a problem reading from the supplied resource, or if the resource could not be found
238      */
239     public void read( String resourceFile ) throws IOException {
240         CheckArg.isNotEmpty(resourceFile, "resourceFile");
241         ClassLoader classLoader = context.getClassLoader();
242         InputStream stream = IoUtil.getResourceAsStream(resourceFile, classLoader, getClass());
243         if (stream == null) {
244             throw new IOException(JcrI18n.unableToFindResourceOnClasspathOrFileOrUrl.text(resourceFile));
245         }
246         boolean error = false;
247         try {
248             read(IoUtil.read(stream), resourceFile);
249         } catch (IOException e) {
250             error = true;
251             throw e;
252         } catch (RuntimeException e) {
253             error = true;
254             throw e;
255         } finally {
256             try {
257                 stream.close();
258             } catch (IOException e) {
259                 if (!error) throw e;
260             } catch (RuntimeException e) {
261                 if (!error) throw e;
262             }
263         }
264     }
265 
266     /**
267      * Import the node types from the supplied string and add all of the node type definitions to this factory's list.
268      * 
269      * @param content the string containing the node types
270      * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
271      *        useful name
272      */
273     @SuppressWarnings( "cast" )
274     public void read( String content,
275                       String resourceName ) {
276         InMemoryRepositorySource source = new InMemoryRepositorySource();
277         source.setName("Node Type Import Source");
278         Graph graph = Graph.create(source, context);
279 
280         // Create the batch and load the graph ...
281         Graph.Batch batch = graph.batch();
282         Destination destination = new GraphBatchDestination(batch);
283         try {
284             importFrom(destination, root, content, resourceName);
285             List<NodeTypeDefinition> types = readTypesFrom(graph, root, null);
286             this.types.addAll(types);
287         } catch (Throwable t) {
288             problems.addError(t, JcrI18n.errorImportingNodeTypeContent, (Object)resourceName, t.getMessage());
289         }
290     }
291 
292     /**
293      * Import the node types from the supplied location in the specified graph.
294      * 
295      * @param graph the graph containing the standard ModeShape CND content
296      * @param parentOfTypes the path to the parent of the node type definition nodes
297      * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
298      *        useful name
299      */
300     public void read( Graph graph,
301                       Path parentOfTypes,
302                       String resourceName ) {
303         this.types.addAll(readTypesFrom(graph, parentOfTypes, null));
304     }
305 
306     /**
307      * Import the node types from the supplied location in the specified graph.
308      * 
309      * @param graph the graph containing the standard ModeShape CND content
310      * @param parentOfTypes the path to the parent of the node type definition nodes
311      * @param nodeTypesToRead the names of the node types that should be read; null means that all node types should be read
312      * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
313      *        useful name
314      */
315     public void read( Graph graph,
316                       Path parentOfTypes,
317                       Collection<Name> nodeTypesToRead,
318                       String resourceName ) {
319         this.types.addAll(readTypesFrom(graph, parentOfTypes, nodeTypesToRead));
320     }
321 
322     /**
323      * Import the node types from the supplied location in the specified graph.
324      * 
325      * @param subgraph the subgraph containing the standard ModeShape CND content
326      * @param locationOfParent the location to the parent of the node type definition nodes
327      * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
328      *        useful name
329      */
330     public void read( Subgraph subgraph,
331                       Location locationOfParent,
332                       String resourceName ) {
333         this.types.addAll(readTypesFrom(subgraph, locationOfParent, null));
334     }
335 
336     /**
337      * Get the problems where warnings and error messages were recorded by this factory.
338      * 
339      * @return the problems; never null
340      */
341     public Problems getProblems() {
342         return problems;
343     }
344 
345     /**
346      * Returns the node type definitions created by this factory.
347      * 
348      * @return the {@link NodeTypeDefinition}s
349      */
350     public NodeTypeDefinition[] getNodeTypeDefinitions() {
351         return types.toArray(new NodeTypeDefinition[types.size()]);
352     }
353 
354     /**
355      * {@inheritDoc}
356      * 
357      * @see java.lang.Iterable#iterator()
358      */
359     @Override
360     public Iterator<NodeTypeDefinition> iterator() {
361         return immutableTypes.iterator();
362     }
363 
364     protected List<NodeTypeDefinition> readTypesFrom( Graph graph,
365                                                       Path parentOfTypes,
366                                                       Collection<Name> nodeTypesToRead ) {
367         Subgraph subgraph = graph.getSubgraphOfDepth(5).at(parentOfTypes);
368         return readTypesFrom(subgraph, subgraph.getLocation(), nodeTypesToRead);
369     }
370 
371     protected List<NodeTypeDefinition> readTypesFrom( Subgraph nodeTypeSubgraph,
372                                                       Location locationOfParentOfNodeTypes,
373                                                       Collection<Name> nodeTypesToRead ) {
374         List<Location> nodeTypeLocations = nodeTypeSubgraph.getNode(locationOfParentOfNodeTypes).getChildren();
375         List<NodeTypeDefinition> results = new ArrayList<NodeTypeDefinition>(nodeTypeLocations.size());
376         boolean shouldFilterNodes = locationOfParentOfNodeTypes.hasPath() && nodeTypesToRead != null;
377 
378         for (Location location : nodeTypeLocations) {
379             SubgraphNode nodeTypeNode = nodeTypeSubgraph.getNode(location);
380             assert location.getPath() != null;
381 
382             Path relativeNodeTypePath = shouldFilterNodes ? location.getPath().relativeTo(locationOfParentOfNodeTypes.getPath()) : null;
383             if (shouldFilterNodes && !nodeTypesToRead.contains(relativeNodeTypePath.getSegment(0).getName())) {
384                 continue;
385             }
386 
387             try {
388                 NodeTypeDefinition nodeType = nodeTypeFrom(nodeTypeNode, nodeTypeSubgraph);
389                 results.add(nodeType);
390             } catch (ConstraintViolationException e) {
391                 String resource = stringFactory.create(locationOfParentOfNodeTypes.getPath());
392                 problems.addError(e, JcrI18n.errorImportingNodeTypeContent, resource, e.getMessage());
393             }
394         }
395         return results;
396     }
397 
398     @SuppressWarnings( "unchecked" )
399     protected NodeTypeTemplate nodeTypeFrom( SubgraphNode nodeTypeNode,
400                                              Subgraph subgraph ) throws ConstraintViolationException {
401         List<Location> children = nodeTypeNode.getChildren();
402 
403         String name = readString(nodeTypeNode, JcrLexicon.NODE_TYPE_NAME, null);
404         String primaryItemName = readString(nodeTypeNode, JcrLexicon.PRIMARY_ITEM_NAME, null);
405 
406         boolean mixin = readBoolean(nodeTypeNode, JcrLexicon.IS_MIXIN, false);
407         boolean isAbstract = readBoolean(nodeTypeNode, JcrLexicon.IS_ABSTRACT, false);
408         boolean queryable = readBoolean(nodeTypeNode, JcrLexicon.IS_QUERYABLE, true);
409         boolean orderableChildNodes = readBoolean(nodeTypeNode, JcrLexicon.HAS_ORDERABLE_CHILD_NODES, false);
410         List<String> supertypes = readStrings(nodeTypeNode, JcrLexicon.SUPERTYPES);
411 
412         NodeTypeTemplate template = new JcrNodeTypeTemplate(context);
413         template.setAbstract(isAbstract);
414         template.setDeclaredSuperTypeNames(supertypes.toArray(new String[supertypes.size()]));
415         template.setMixin(mixin);
416         template.setName(name);
417         template.setOrderableChildNodes(orderableChildNodes);
418         template.setPrimaryItemName(primaryItemName);
419         template.setQueryable(queryable);
420 
421         for (Location childLocation : children) {
422             if (JcrLexicon.PROPERTY_DEFINITION.equals(childLocation.getPath().getLastSegment().getName())) {
423                 template.getPropertyDefinitionTemplates().add(propertyDefinitionFrom(subgraph, childLocation));
424             } else if (JcrLexicon.CHILD_NODE_DEFINITION.equals(childLocation.getPath().getLastSegment().getName())) {
425                 template.getNodeDefinitionTemplates().add(childNodeDefinitionFrom(subgraph, childLocation));
426             } else {
427                 throw new IllegalStateException("Unexpected child of node type at: " + childLocation);
428             }
429         }
430 
431         return template;
432     }
433 
434     protected PropertyDefinitionTemplate propertyDefinitionFrom( Subgraph nodeTypeGraph,
435                                                                  Location propertyLocation ) throws ConstraintViolationException {
436         SubgraphNode propertyDefinitionNode = nodeTypeGraph.getNode(propertyLocation);
437 
438         String name = readString(propertyDefinitionNode, JcrLexicon.NAME, null);
439         String onParentVersionName = readString(propertyDefinitionNode,
440                                                 JcrLexicon.ON_PARENT_VERSION,
441                                                 OnParentVersionAction.nameFromValue(OnParentVersionAction.COPY));
442         int onParentVersion = OnParentVersionAction.valueFromName(onParentVersionName);
443 
444         int requiredType = PROPERTY_TYPE_VALUES_FROM_NAME.get(readString(propertyDefinitionNode, JcrLexicon.REQUIRED_TYPE, null));
445 
446         boolean mandatory = readBoolean(propertyDefinitionNode, JcrLexicon.MANDATORY, false);
447         boolean multiple = readBoolean(propertyDefinitionNode, JcrLexicon.MULTIPLE, false);
448         boolean autoCreated = readBoolean(propertyDefinitionNode, JcrLexicon.AUTO_CREATED, false);
449         boolean isProtected = readBoolean(propertyDefinitionNode, JcrLexicon.PROTECTED, false);
450         boolean isSearchable = readBoolean(propertyDefinitionNode, JcrLexicon.IS_FULL_TEXT_SEARCHABLE, true);
451         boolean isOrderable = readBoolean(propertyDefinitionNode, JcrLexicon.IS_QUERY_ORDERABLE, true);
452         List<Value> defaultValues = readValues(propertyDefinitionNode, JcrLexicon.DEFAULT_VALUES, requiredType);
453         List<String> constraints = readStrings(propertyDefinitionNode, JcrLexicon.VALUE_CONSTRAINTS);
454         List<String> queryOps = readStrings(propertyDefinitionNode, JcrLexicon.QUERY_OPERATORS);
455 
456         PropertyDefinitionTemplate template = new JcrPropertyDefinitionTemplate(context);
457         if (name != null) {
458             template.setName(name);
459         }
460         template.setAutoCreated(autoCreated);
461         template.setMandatory(mandatory);
462         template.setMultiple(multiple);
463         template.setProtected(isProtected);
464         template.setFullTextSearchable(isSearchable);
465         template.setAvailableQueryOperators(queryOps.toArray(new String[queryOps.size()]));
466         template.setQueryOrderable(isOrderable);
467         template.setProtected(isProtected);
468         template.setOnParentVersion(onParentVersion);
469         template.setDefaultValues(defaultValues.toArray(new Value[defaultValues.size()]));
470         template.setRequiredType(requiredType);
471         template.setValueConstraints(constraints.toArray(new String[constraints.size()]));
472         return template;
473     }
474 
475     protected NodeDefinitionTemplate childNodeDefinitionFrom( Subgraph nodeTypeGraph,
476                                                               Location childNodeLocation ) throws ConstraintViolationException {
477         SubgraphNode childNodeDefinitionNode = nodeTypeGraph.getNode(childNodeLocation);
478 
479         String childNodeName = readString(childNodeDefinitionNode, JcrLexicon.NAME, null);
480         String defaultPrimaryTypeName = readString(childNodeDefinitionNode, JcrLexicon.DEFAULT_PRIMARY_TYPE, null);
481         String onParentVersionName = readString(childNodeDefinitionNode,
482                                                 JcrLexicon.ON_PARENT_VERSION,
483                                                 OnParentVersionAction.nameFromValue(OnParentVersionAction.COPY));
484         int onParentVersion = OnParentVersionAction.valueFromName(onParentVersionName);
485 
486         boolean mandatory = readBoolean(childNodeDefinitionNode, JcrLexicon.MANDATORY, false);
487         boolean allowsSns = readBoolean(childNodeDefinitionNode, JcrLexicon.SAME_NAME_SIBLINGS, false);
488         boolean autoCreated = readBoolean(childNodeDefinitionNode, JcrLexicon.AUTO_CREATED, false);
489         boolean isProtected = readBoolean(childNodeDefinitionNode, JcrLexicon.PROTECTED, false);
490         List<String> requiredTypes = readStrings(childNodeDefinitionNode, JcrLexicon.REQUIRED_PRIMARY_TYPES);
491 
492         NodeDefinitionTemplate template = new JcrNodeDefinitionTemplate(context);
493         if (childNodeName != null) {
494             template.setName(childNodeName);
495         }
496         template.setAutoCreated(autoCreated);
497         template.setMandatory(mandatory);
498         template.setSameNameSiblings(allowsSns);
499         template.setProtected(isProtected);
500         template.setOnParentVersion(onParentVersion);
501         template.setDefaultPrimaryTypeName(defaultPrimaryTypeName);
502         template.setRequiredPrimaryTypeNames(requiredTypes.toArray(new String[requiredTypes.size()]));
503         return template;
504     }
505 
506     protected Name nameFrom( SubgraphNode node ) {
507         return node.getLocation().getPath().getLastSegment().getName();
508     }
509 
510     protected Name name( String name ) {
511         return nameFactory.create(name);
512     }
513 
514     protected String string( Object value ) {
515         return stringFactory.create(value);
516     }
517 
518     protected boolean readBoolean( SubgraphNode node,
519                                    String propertyName,
520                                    boolean defaultValue ) {
521         return readBoolean(node, nameFactory.create(propertyName), defaultValue);
522     }
523 
524     protected boolean readBoolean( SubgraphNode node,
525                                    Name propertyName,
526                                    boolean defaultValue ) {
527         Property property = node.getProperty(propertyName);
528         return property != null ? booleanFactory.create(property.getFirstValue()) : defaultValue;
529     }
530 
531     protected String readString( SubgraphNode node,
532                                  String propertyName,
533                                  String defaultValue ) {
534         return readString(node, nameFactory.create(propertyName), defaultValue);
535     }
536 
537     protected String readString( SubgraphNode node,
538                                  Name propertyName,
539                                  String defaultValue ) {
540         Property property = node.getProperty(propertyName);
541         return property != null ? stringFactory.create(property.getFirstValue()) : defaultValue;
542     }
543 
544     protected List<String> readStrings( SubgraphNode node,
545                                         String propertyName ) {
546         return readStrings(node, nameFactory.create(propertyName));
547     }
548 
549     protected List<String> readStrings( SubgraphNode node,
550                                         Name propertyName ) {
551         List<String> results = new ArrayList<String>();
552         Property property = node.getProperty(propertyName);
553         if (property != null) {
554             for (Object value : property) {
555                 String str = stringFactory.create(value);
556                 if (str != null && str.length() != 0) results.add(str);
557             }
558         }
559         return results;
560     }
561 
562     protected List<Value> readValues( SubgraphNode node,
563                                       Name propertyName,
564                                       int requiredType ) {
565         List<String> results = readStrings(node, propertyName);
566         List<Value> values = new ArrayList<Value>(results.size());
567         for (String result : results) {
568             values.add(new JcrValue(valueFactories, null, requiredType, result));
569         }
570         return values;
571     }
572 
573     protected Name readName( SubgraphNode node,
574                              String propertyName,
575                              Name defaultValue ) {
576         return readName(node, nameFactory.create(propertyName), defaultValue);
577     }
578 
579     protected Name readName( SubgraphNode node,
580                              Name propertyName,
581                              Name defaultValue ) {
582         Property property = node.getProperty(propertyName);
583         if (property != null && !property.isEmpty()) {
584             String firstValue = stringFactory.create(property.getFirstValue());
585             if (firstValue != null && firstValue.trim().length() != 0) {
586                 return valueFactories.getNameFactory().create(firstValue);
587             }
588         }
589         return defaultValue;
590     }
591 
592     protected List<Name> readNames( SubgraphNode node,
593                                     String propertyName,
594                                     Name defaultIfNone ) {
595         return readNames(node, nameFactory.create(propertyName), defaultIfNone);
596     }
597 
598     protected List<Name> readNames( SubgraphNode node,
599                                     Name propertyName,
600                                     Name defaultIfNone ) {
601         List<Name> results = new ArrayList<Name>();
602         Property property = node.getProperty(propertyName);
603         if (property != null) {
604             for (Object value : property) {
605                 Name name = nameFactory.create(value);
606                 if (name != null) results.add(name);
607             }
608         }
609         if (results.isEmpty() && defaultIfNone != null) results.add(defaultIfNone);
610         return results;
611     }
612 
613     /**
614      * Method that loads into the graph destination the content containing the node type definitions.
615      * 
616      * @param graphDestination the destination to which the node type content should be written; never null
617      * @param path the path within the destination at which the node type content should be rooted; never null
618      * @param content the content containing some string representation of the node types to be imported; never null
619      * @param resourceName a descriptive name for this import (used only for error messages); may be null
620      * @throws Exception if there is a problem importing from the content; this will be automatically recorded in the problems
621      */
622     protected void importFrom( Destination graphDestination,
623                                Path path,
624                                String content,
625                                String resourceName ) throws Exception {
626         throw new UnsupportedOperationException();
627     }
628 
629 }