001 /*
002 * JBoss DNA (http://www.jboss.org/dna)
003 * See the COPYRIGHT.txt file distributed with this work for information
004 * regarding copyright ownership. Some portions may be licensed
005 * to Red Hat, Inc. under one or more contributor license agreements.
006 * See the AUTHORS.txt file in the distribution for a full listing of
007 * individual contributors.
008 *
009 * Unless otherwise indicated, all code in JBoss DNA is licensed
010 * to you under the terms of the GNU Lesser General Public License as
011 * published by the Free Software Foundation; either version 2.1 of
012 * the License, or (at your option) any later version.
013 *
014 * JBoss DNA is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this software; if not, write to the Free
021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023 */
024 package org.jboss.dna.cnd;
025
026 import java.io.ByteArrayInputStream;
027 import java.io.File;
028 import java.io.IOException;
029 import java.io.InputStream;
030 import java.util.ArrayList;
031 import java.util.Arrays;
032 import java.util.Collections;
033 import java.util.HashSet;
034 import java.util.List;
035 import java.util.Set;
036 import net.jcip.annotations.NotThreadSafe;
037 import org.antlr.runtime.ANTLRFileStream;
038 import org.antlr.runtime.ANTLRInputStream;
039 import org.antlr.runtime.CharStream;
040 import org.antlr.runtime.CommonTokenStream;
041 import org.antlr.runtime.RecognitionException;
042 import org.antlr.runtime.TokenStream;
043 import org.antlr.runtime.tree.CommonTree;
044 import org.antlr.runtime.tree.RewriteCardinalityException;
045 import org.jboss.dna.common.collection.Problems;
046 import org.jboss.dna.common.i18n.I18n;
047 import org.jboss.dna.common.util.CheckArg;
048 import org.jboss.dna.graph.ExecutionContext;
049 import org.jboss.dna.graph.JcrLexicon;
050 import org.jboss.dna.graph.JcrNtLexicon;
051 import org.jboss.dna.graph.io.Destination;
052 import org.jboss.dna.graph.property.Name;
053 import org.jboss.dna.graph.property.NameFactory;
054 import org.jboss.dna.graph.property.NamespaceRegistry;
055 import org.jboss.dna.graph.property.Path;
056 import org.jboss.dna.graph.property.PathFactory;
057 import org.jboss.dna.graph.property.PropertyFactory;
058 import org.jboss.dna.graph.property.ValueFactory;
059 import org.jboss.dna.graph.property.ValueFormatException;
060 import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
061
062 /**
063 * A class that imports the node types contained in a JCR Compact Node Definition (CND) file into graph content. The content is
064 * written using the graph structured defined by JCR and the "{@code nt:nodeType}", "{@code nt:propertyDefinition}", and "{@code
065 * nt:childNodeDefinition}" node types.
066 * <p>
067 * Although instances of this class never change their behavior and all processing is done in local contexts, {@link Destination}
068 * is not thread-safe and therefore this component may not be considered thread-safe.
069 * </p>
070 */
071 @NotThreadSafe
072 public class CndImporter {
073
074 private static final Set<String> VALID_PROPERTY_TYPES = Collections.unmodifiableSet(new HashSet<String>(
075 Arrays.asList(new String[] {
076 "STRING",
077 "BINARY", "LONG",
078 "DOUBLE",
079 "BOOLEAN",
080 "DECIMAL",
081 "DATE", "NAME",
082 "PATH",
083 "REFERENCE",
084 "WEAKREFERENCE",
085 "URI",
086 "UNDEFINED"})));
087 private static final Set<String> VALID_ON_PARENT_VERSION = Collections.unmodifiableSet(new HashSet<String>(
088 Arrays.asList(new String[] {
089 "COPY",
090 "VERSION",
091 "INITIALIZE",
092 "COMPUTE",
093 "IGNORE",
094 "ABORT"})));
095 protected final Destination destination;
096 protected final Path parentPath;
097 private boolean debug = false;
098
099 /**
100 * Create a new importer that will place the content in the supplied destination under the supplied path.
101 *
102 * @param destination the destination where content is to be written
103 * @param parentPath the path in the destination below which the generated content is to appear
104 * @throws IllegalArgumentException if either parameter is null
105 */
106 public CndImporter( Destination destination,
107 Path parentPath ) {
108 CheckArg.isNotNull(destination, "destination");
109 CheckArg.isNotNull(parentPath, "parentPath");
110 this.destination = destination;
111 this.parentPath = parentPath;
112 }
113
114 void setDebug( boolean value ) {
115 this.debug = value;
116 }
117
118 protected ExecutionContext context() {
119 return this.destination.getExecutionContext();
120 }
121
122 protected NamespaceRegistry namespaces() {
123 return context().getNamespaceRegistry();
124 }
125
126 protected NameFactory nameFactory() {
127 return context().getValueFactories().getNameFactory();
128 }
129
130 protected ValueFactory<String> stringFactory() {
131 return context().getValueFactories().getStringFactory();
132 }
133
134 protected ValueFactory<Boolean> booleanFactory() {
135 return context().getValueFactories().getBooleanFactory();
136 }
137
138 /**
139 * Import the CND content from the supplied stream, placing the content into the importer's destination.
140 *
141 * @param stream the stream containing the CND content
142 * @param problems where any problems encountered during import should be reported
143 * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
144 * useful name
145 * @throws IOException if there is a problem reading from the supplied stream
146 */
147 public void importFrom( InputStream stream,
148 Problems problems,
149 String resourceName ) throws IOException {
150 CndLexer lex = new CndLexer(new CaseInsensitiveInputStream(stream));
151 importFrom(lex, resourceName, problems);
152 }
153
154 /**
155 * Import the CND content from the supplied stream, placing the content into the importer's destination.
156 *
157 * @param content the string containing the CND content
158 * @param problems where any problems encountered during import should be reported
159 * @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
160 * useful name
161 * @throws IOException if there is a problem reading from the supplied stream
162 */
163 public void importFrom( String content,
164 Problems problems,
165 String resourceName ) throws IOException {
166 ByteArrayInputStream stream = new ByteArrayInputStream(content.getBytes());
167 importFrom(stream, problems, resourceName);
168 }
169
170 /**
171 * Import the CND content from the supplied stream, placing the content into the importer's destination.
172 *
173 * @param file the file containing the CND content
174 * @param problems where any problems encountered during import should be reported
175 * @throws IOException if there is a problem reading from the supplied stream
176 */
177 public void importFrom( File file,
178 Problems problems ) throws IOException {
179 CndLexer lex = new CndLexer(new CaseInsensitiveFileStream(file.getAbsolutePath()));
180 importFrom(lex, file.getCanonicalPath(), problems);
181 }
182
183 protected void importFrom( CndLexer lexer,
184 String resourceName,
185 Problems problems ) {
186 CommonTokenStream tokens = new CommonTokenStream(lexer);
187 CndParser parser = new Parser(tokens, problems, resourceName);
188
189 // Create a new context with our own namespace registry ...
190 ImportContext context = new ImportContext(context(), problems, resourceName);
191 CommonTree ast = null;
192 try {
193 ast = (CommonTree)parser.cnd().getTree();
194 } catch (RecognitionException e) {
195 // already handled by Parser, so we should not handle twice
196 } catch (RewriteCardinalityException e) {
197 // already handled by Parser, so we should not handle twice
198 } catch (RuntimeException e) {
199 problems.addError(e, CndI18n.errorImportingCndContent, (Object)resourceName, e.getMessage());
200 }
201
202 if (ast != null && problems.isEmpty()) {
203
204 // --------------
205 // Namespaces ...
206 // --------------
207
208 /*
209 NAMESPACES
210 +- NODE (multiple)
211 +- PREFIX
212 +- string value
213 +- URI
214 +- string value
215 */
216
217 // Get the namespaces before we do anything else ...
218 CommonTree namespaces = (CommonTree)ast.getFirstChildWithType(CndLexer.NAMESPACES);
219 if (namespaces != null) {
220 for (int i = 0; i != namespaces.getChildCount(); ++i) {
221 CommonTree namespace = (CommonTree)namespaces.getChild(i);
222 String prefix = namespace.getFirstChildWithType(CndLexer.PREFIX).getChild(0).getText();
223 String uri = namespace.getFirstChildWithType(CndLexer.URI).getChild(0).getText();
224 // Register the namespace ...
225 context.register(removeQuotes(prefix), removeQuotes(uri));
226 }
227 }
228
229 // --------------
230 // Node Types ...
231 // --------------
232
233 /*
234 NODE_TYPES
235 +- NODE (multiple)
236 +- NAME [nt:nodeType/@jcr:name]
237 +- string value
238 +- PRIMARY_TYPE [nt:base/@jcr:primaryType]
239 +- string with value 'nt:nodeType'
240 +- SUPERTYPES [nt:nodeType/@jcr:supertypes]
241 +- string value(s)
242 +- IS_ABSTRACT [nt:nodeType/@jcr:isAbstract]
243 +- string containing boolean value (or false if not present)
244 +- HAS_ORDERABLE_CHILD_NODES [nt:nodeType/@jcr:hasOrderableChildNodes]
245 +- string containing boolean value (or false if not present)
246 +- IS_MIXIN [nt:nodeType/@jcr:isMixin]
247 +- string containing boolean value (or false if not present)
248 +- IS_QUERYABLE [nt:nodeType/@jcr:isQueryable]
249 +- string containing boolean value (or true if not present)
250 +- PRIMARY_ITEM_NAME [nt:nodeType/@jcr:primaryItemName]
251 +- string containing string value
252 +- PROPERTY_DEFINITION [nt:nodeType/@jcr:propertyDefinition]
253 +- NODE (multiple)
254 +- NAME [nt:propertyDefinition/@jcr:name]
255 +- string value
256 +- PRIMARY_TYPE [nt:base/@jcr:primaryType]
257 +- string with value 'nt:propertyDefinition'
258 +- REQUIRED_TYPE [nt:propertyDefinition/@jcr:propertyType]
259 +- string value (limited to one of the predefined types)
260 +- DEFAULT_VALUES [nt:propertyDefinition/@jcr:defaultValues]
261 +- string value(s)
262 +- MULTIPLE [nt:propertyDefinition/@jcr:multiple]
263 +- string containing boolean value (or false if not present)
264 +- MANDATORY [nt:propertyDefinition/@jcr:mandatory]
265 +- string containing boolean value (or false if not present)
266 +- AUTO_CREATED [nt:propertyDefinition/@jcr:autoCreated]
267 +- string containing boolean value (or false if not present)
268 +- PROTECTED [nt:propertyDefinition/@jcr:protected]
269 +- string containing boolean value (or false if not present)
270 +- ON_PARENT_VERSION [nt:propertyDefinition/@jcr:onParentVersion]
271 +- string value (limited to one of the predefined literal values)
272 +- QUERY_OPERATORS
273 +- string value (containing a comma-separated list of operator literals)
274 +- IS_FULL_TEXT_SEARCHABLE [nt:propertyDefinition/@jcr:isFullTextSearchable]
275 +- string containing boolean value (or true if not present)
276 +- IS_QUERY_ORDERABLE [nt:propertyDefinition/@jcr:isQueryOrderable]
277 +- string containing boolean value (or true if not present)
278 +- VALUE_CONSTRAINTS [nt:propertyDefinition/@jcr:valueConstraints]
279 +- string value(s)
280 +- CHILD_NODE_DEFINITION [nt:nodeType/@jcr:childNodeDefinition]
281 +- NODE (multiple)
282 +- NAME [nt:childNodeDefinition/@jcr:name]
283 +- string value
284 +- PRIMARY_TYPE [nt:base/@jcr:primaryType]
285 +- string with value 'nt:childNodeDefinition'
286 +- REQUIRED_PRIMARY_TYPES [nt:childNodeDefinition/@jcr:requiredPrimaryTypes]
287 +- string values (limited to names)
288 +- DEFAULT_PRIMARY_TYPE [nt:childNodeDefinition/@jcr:defaultPrimaryType]
289 +- string value (limited to a name)
290 +- MANDATORY [nt:childNodeDefinition/@jcr:mandatory]
291 +- string containing boolean value (or false if not present)
292 +- AUTO_CREATED [nt:childNodeDefinition/@jcr:autoCreated]
293 +- string containing boolean value (or false if not present)
294 +- PROTECTED [nt:childNodeDefinition/@jcr:protected]
295 +- string containing boolean value (or false if not present)
296 +- SAME_NAME_SIBLINGS [nt:childNodeDefinition/@jcr:sameNameSiblings]
297 +- string containing boolean value (or false if not present)
298 +- ON_PARENT_VERSION [nt:childNodeDefinition/@jcr:onParentVersion]
299 +- string value (limited to one of the predefined literal values)
300 */
301
302 // Get the node types ...
303 CommonTree nodeTypes = (CommonTree)ast.getFirstChildWithType(CndLexer.NODE_TYPES);
304 if (nodeTypes != null) {
305 int numNodeTypes = 0;
306 // Walk each of the nodes underneath the NODE_TYPES parent node ...
307 for (int i = 0; i != nodeTypes.getChildCount(); ++i) {
308 CommonTree nodeType = (CommonTree)nodeTypes.getChild(i);
309 if (this.debug) System.out.println(nodeType.toStringTree());
310 Path nodeTypePath = context.createNodeType(nodeType, parentPath);
311 if (nodeTypePath == null) continue;
312 ++numNodeTypes;
313
314 CommonTree propertyDefinitions = (CommonTree)nodeType.getFirstChildWithType(CndLexer.PROPERTY_DEFINITION);
315 if (propertyDefinitions != null) {
316 // Walk each of the nodes under PROPERTY_DEFINITION ...
317 for (int j = 0; j != propertyDefinitions.getChildCount(); ++j) {
318 CommonTree propDefn = (CommonTree)propertyDefinitions.getChild(j);
319 context.createPropertyDefinition(propDefn, nodeTypePath);
320 }
321 }
322
323 CommonTree childNodeDefinitions = (CommonTree)nodeType.getFirstChildWithType(CndLexer.CHILD_NODE_DEFINITION);
324 if (childNodeDefinitions != null) {
325 // Walk each of the nodes under CHILD_NODE_DEFINITION ...
326 for (int j = 0; j != childNodeDefinitions.getChildCount(); ++j) {
327 CommonTree childDefn = (CommonTree)childNodeDefinitions.getChild(j);
328 context.createChildDefinition(childDefn, nodeTypePath);
329 }
330 }
331 }
332
333 // Submit the destination
334 destination.submit();
335 }
336 }
337 }
338
339 protected final String removeQuotes( String text ) {
340 // Remove leading and trailing quotes, if there are any ...
341 return text.replaceFirst("^['\"]+", "").replaceAll("['\"]+$", "");
342 }
343
344 /**
345 * Utility class that uses a context with a local namespace registry, along with the problems and resource name.
346 */
347 protected final class ImportContext {
348 private final ExecutionContext context;
349 private final ExecutionContext originalContext;
350 private final Problems problems;
351 private final String resourceName;
352
353 protected ImportContext( ExecutionContext context,
354 Problems problems,
355 String resourceName ) {
356 // Create a context that has a local namespace registry
357 NamespaceRegistry localNamespaces = new LocalNamespaceRegistry(context.getNamespaceRegistry());
358 this.originalContext = context;
359 this.context = context.with(localNamespaces);
360 this.problems = problems;
361 this.resourceName = resourceName;
362 }
363
364 protected ExecutionContext context() {
365 return this.context;
366 }
367
368 protected void register( String prefix,
369 String uri ) {
370 // Register it in the local registry with the supplied prefix ...
371 context.getNamespaceRegistry().register(prefix, uri);
372
373 // See if it is already registered in the original context ...
374 NamespaceRegistry registry = originalContext.getNamespaceRegistry();
375 if (!registry.isRegisteredNamespaceUri(uri)) {
376 // It is not, so register it ...
377 registry.register(prefix, uri);
378 }
379 }
380
381 protected NameFactory nameFactory() {
382 return this.context.getValueFactories().getNameFactory();
383 }
384
385 protected PathFactory pathFactory() {
386 return this.context.getValueFactories().getPathFactory();
387 }
388
389 protected ValueFactory<String> stringFactory() {
390 return this.context.getValueFactories().getStringFactory();
391 }
392
393 protected ValueFactory<Boolean> booleanFactory() {
394 return this.context.getValueFactories().getBooleanFactory();
395 }
396
397 protected void recordError( CommonTree node,
398 I18n msg,
399 Object... params ) {
400 String location = CndI18n.locationFromLineNumberAndCharacter.text(node.getLine(), node.getCharPositionInLine());
401 problems.addError(msg, resourceName, location, params);
402 }
403
404 protected void recordError( Throwable throwable,
405 CommonTree node,
406 I18n msg,
407 Object... params ) {
408 String location = CndI18n.locationFromLineNumberAndCharacter.text(node.getLine(), node.getCharPositionInLine());
409 problems.addError(throwable, msg, resourceName, location, params);
410 }
411
412 protected Name nameFrom( CommonTree node,
413 int childType ) {
414 CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
415 if (childNode != null && childNode.getChildCount() > 0) {
416 CommonTree textNode = (CommonTree)childNode.getChild(0);
417 if (textNode.getToken().getTokenIndex() < 0) return null;
418 String text = removeQuotes(childNode.getChild(0).getText());
419 try {
420 return nameFactory().create(text);
421 } catch (ValueFormatException e) {
422 recordError(e, node, CndI18n.expectedValidNameLiteral, text);
423 }
424 }
425 return null;
426 }
427
428 protected Name[] namesFrom( CommonTree node,
429 int childType ) {
430 CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
431 if (childNode != null && childNode.getChildCount() > 0) {
432 List<Name> names = new ArrayList<Name>();
433 for (int i = 0; i != childNode.getChildCount(); ++i) {
434 String text = removeQuotes(childNode.getChild(i).getText());
435 try {
436 names.add(nameFactory().create(text));
437 } catch (ValueFormatException e) {
438 recordError(e, node, CndI18n.expectedValidNameLiteral, text);
439 }
440 }
441 return names.toArray(new Name[names.size()]);
442 }
443 return new Name[] {};
444 }
445
446 protected String stringFrom( CommonTree node,
447 int childType ) {
448 CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
449 if (childNode != null && childNode.getChildCount() > 0) {
450 String text = removeQuotes(childNode.getChild(0).getText().trim());
451 try {
452 return stringFactory().create(text);
453 } catch (ValueFormatException e) {
454 recordError(e, node, CndI18n.expectedStringLiteral, text);
455 }
456 }
457 return null;
458 }
459
460 protected String[] stringsFrom( CommonTree node,
461 int childType ) {
462 CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
463 if (childNode != null && childNode.getChildCount() > 0) {
464 List<String> names = new ArrayList<String>();
465 for (int i = 0; i != childNode.getChildCount(); ++i) {
466 String text = removeQuotes(childNode.getChild(i).getText().trim());
467 try {
468 names.add(stringFactory().create(text));
469 } catch (ValueFormatException e) {
470 recordError(e, node, CndI18n.expectedStringLiteral, text);
471 }
472 }
473 return names.toArray(new String[names.size()]);
474 }
475 return new String[] {};
476 }
477
478 protected boolean booleanFrom( CommonTree node,
479 int childType,
480 boolean defaultValue ) {
481 CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
482 if (childNode != null && childNode.getChildCount() > 0) {
483 String text = removeQuotes(childNode.getChild(0).getText());
484 try {
485 return booleanFactory().create(text);
486 } catch (ValueFormatException e) {
487 recordError(e, node, CndI18n.expectedBooleanLiteral, text);
488 }
489 }
490 return defaultValue;
491 }
492
493 protected QueryOperator[] queryOperatorsFrom( CommonTree node,
494 int childType ) {
495 String text = stringFrom(node, childType);
496 if (text != null) {
497 String[] literals = text.split(",");
498 if (literals.length != 0) {
499 Set<QueryOperator> operators = new HashSet<QueryOperator>();
500 for (String literal : literals) {
501 literal = literal.trim();
502 if (literal.length() == 0) continue;
503 QueryOperator operator = QueryOperator.forText(literal);
504 if (operator != null) {
505 operators.add(operator);
506 } else {
507 recordError(node, CndI18n.expectedValidQueryOperator, literal);
508 }
509 }
510 return operators.toArray(new QueryOperator[operators.size()]);
511 }
512 }
513 return new QueryOperator[] {};
514 }
515
516 protected String propertyTypeNameFrom( CommonTree node,
517 int childType ) {
518 String text = stringFrom(node, childType);
519 if ("*".equals(text)) text = "undefined";
520 String upperText = text.toUpperCase();
521 if (!VALID_PROPERTY_TYPES.contains(upperText)) {
522 recordError(node, CndI18n.expectedValidPropertyTypeName, text, VALID_PROPERTY_TYPES);
523 return null;
524 }
525 return upperText;
526 }
527
528 protected String onParentVersionFrom( CommonTree node,
529 int childType ) {
530 String text = stringFrom(node, childType);
531 if (text == null) return "COPY";
532 String upperText = text.toUpperCase();
533 if (!VALID_ON_PARENT_VERSION.contains(upperText)) {
534 recordError(node, CndI18n.expectedValidOnParentVersion, text, VALID_ON_PARENT_VERSION);
535 return null;
536 }
537 return upperText;
538 }
539
540 protected Path createNodeType( CommonTree nodeType,
541 Path parentPath ) {
542 Name name = nameFrom(nodeType, CndLexer.NAME);
543 Name[] supertypes = namesFrom(nodeType, CndLexer.SUPERTYPES);
544 boolean isAbstract = booleanFrom(nodeType, CndLexer.IS_ABSTRACT, false);
545 boolean hasOrderableChildNodes = booleanFrom(nodeType, CndLexer.HAS_ORDERABLE_CHILD_NODES, false);
546 boolean isMixin = booleanFrom(nodeType, CndLexer.IS_MIXIN, false);
547 boolean isQueryable = booleanFrom(nodeType, CndLexer.IS_QUERYABLE, true);
548 Name primaryItemName = nameFrom(nodeType, CndLexer.PRIMARY_ITEM_NAME);
549
550 if (primaryItemName == null) {
551 // See if one of the property definitions is marked as the primary ...
552 CommonTree propertyDefinitions = (CommonTree)nodeType.getFirstChildWithType(CndLexer.PROPERTY_DEFINITION);
553 if (propertyDefinitions != null) {
554 // Walk each of the nodes under PROPERTY_DEFINITION ...
555 for (int j = 0; j != propertyDefinitions.getChildCount(); ++j) {
556 CommonTree propDefn = (CommonTree)propertyDefinitions.getChild(j);
557 if (booleanFrom(propDefn, CndLexer.IS_PRIMARY_PROPERTY, false)) {
558 primaryItemName = nameFrom(propDefn, CndLexer.NAME);
559 break;
560 }
561 }
562 }
563 }
564 if (primaryItemName == null) {
565 // See if one of the child definitions is marked as the primary ...
566 CommonTree childNodeDefinitions = (CommonTree)nodeType.getFirstChildWithType(CndLexer.CHILD_NODE_DEFINITION);
567 if (childNodeDefinitions != null) {
568 // Walk each of the nodes under CHILD_NODE_DEFINITION ...
569 for (int j = 0; j != childNodeDefinitions.getChildCount(); ++j) {
570 CommonTree childDefn = (CommonTree)childNodeDefinitions.getChild(j);
571 if (booleanFrom(childDefn, CndLexer.IS_PRIMARY_PROPERTY, false)) {
572 primaryItemName = nameFrom(childDefn, CndLexer.NAME);
573 break;
574 }
575 }
576 }
577 }
578
579 // Create the node for the node type ...
580 if (name == null) return null;
581 Path path = pathFactory().create(parentPath, name);
582
583 PropertyFactory factory = context.getPropertyFactory();
584 destination.create(path,
585 factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.NODE_TYPE),
586 factory.create(JcrLexicon.SUPERTYPES, (Object[])supertypes),
587 factory.create(JcrLexicon.IS_ABSTRACT, isAbstract),
588 factory.create(JcrLexicon.HAS_ORDERABLE_CHILD_NODES, hasOrderableChildNodes),
589 factory.create(JcrLexicon.IS_MIXIN, isMixin),
590 factory.create(JcrLexicon.IS_QUERYABLE, isQueryable),
591 factory.create(JcrLexicon.NODE_TYPE_NAME, name),
592 factory.create(JcrLexicon.PRIMARY_ITEM_NAME, primaryItemName));
593
594 return path;
595 }
596
597 protected Path createPropertyDefinition( CommonTree propDefn,
598 Path parentPath ) {
599 Name name = nameFrom(propDefn, CndLexer.NAME);
600 String requiredType = propertyTypeNameFrom(propDefn, CndLexer.REQUIRED_TYPE);
601 String[] defaultValues = stringsFrom(propDefn, CndLexer.DEFAULT_VALUES);
602 boolean multiple = booleanFrom(propDefn, CndLexer.MULTIPLE, false);
603 boolean mandatory = booleanFrom(propDefn, CndLexer.MANDATORY, false);
604 boolean autoCreated = booleanFrom(propDefn, CndLexer.AUTO_CREATED, false);
605 boolean isProtected = booleanFrom(propDefn, CndLexer.PROTECTED, false);
606 String onParentVersion = onParentVersionFrom(propDefn, CndLexer.ON_PARENT_VERSION);
607 /*QueryOperator[] queryOperators =*/queryOperatorsFrom(propDefn, CndLexer.QUERY_OPERATORS);
608 boolean isFullTextSearchable = booleanFrom(propDefn, CndLexer.IS_FULL_TEXT_SEARCHABLE, true);
609 boolean isQueryOrderable = booleanFrom(propDefn, CndLexer.IS_QUERY_ORDERERABLE, true);
610 String[] valueConstraints = stringsFrom(propDefn, CndLexer.VALUE_CONSTRAINTS);
611
612 // Create the node for the node type ...
613 if (name == null) return null;
614 Path path = pathFactory().create(parentPath, JcrLexicon.PROPERTY_DEFINITION);
615
616 PropertyFactory factory = context.getPropertyFactory();
617 destination.create(path,
618 factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.PROPERTY_DEFINITION),
619 factory.create(JcrLexicon.REQUIRED_TYPE, requiredType),
620 factory.create(JcrLexicon.DEFAULT_VALUES, (Object[])defaultValues),
621 factory.create(JcrLexicon.MULTIPLE, multiple),
622 factory.create(JcrLexicon.MANDATORY, mandatory),
623 factory.create(JcrLexicon.NAME, name),
624 factory.create(JcrLexicon.AUTO_CREATED, autoCreated),
625 factory.create(JcrLexicon.PROTECTED, isProtected),
626 factory.create(JcrLexicon.ON_PARENT_VERSION, onParentVersion),
627 // factory.create(DnaLexicon.QUERY_OPERATORS, queryOperators),
628 factory.create(JcrLexicon.IS_FULL_TEXT_SEARCHABLE, isFullTextSearchable),
629 factory.create(JcrLexicon.IS_QUERY_ORDERABLE, isQueryOrderable),
630 factory.create(JcrLexicon.VALUE_CONSTRAINTS, (Object[])valueConstraints));
631
632 return path;
633 }
634
635 protected Path createChildDefinition( CommonTree childDefn,
636 Path parentPath ) {
637 Name name = nameFrom(childDefn, CndLexer.NAME);
638 Name[] requiredPrimaryTypes = namesFrom(childDefn, CndLexer.REQUIRED_PRIMARY_TYPES);
639 Name defaultPrimaryType = nameFrom(childDefn, CndLexer.DEFAULT_PRIMARY_TYPE);
640 boolean mandatory = booleanFrom(childDefn, CndLexer.MANDATORY, false);
641 boolean autoCreated = booleanFrom(childDefn, CndLexer.AUTO_CREATED, false);
642 boolean isProtected = booleanFrom(childDefn, CndLexer.PROTECTED, false);
643 String onParentVersion = onParentVersionFrom(childDefn, CndLexer.ON_PARENT_VERSION);
644 boolean sameNameSiblings = booleanFrom(childDefn, CndLexer.SAME_NAME_SIBLINGS, false);
645
646 // Create the node for the node type ...
647 if (name == null) return null;
648 Path path = pathFactory().create(parentPath, JcrLexicon.CHILD_NODE_DEFINITION);
649
650 PropertyFactory factory = context.getPropertyFactory();
651 destination.create(path,
652 factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.CHILD_NODE_DEFINITION),
653 factory.create(JcrLexicon.REQUIRED_PRIMARY_TYPES, (Object[])requiredPrimaryTypes),
654 factory.create(JcrLexicon.DEFAULT_PRIMARY_TYPE, defaultPrimaryType),
655 factory.create(JcrLexicon.MANDATORY, mandatory),
656 factory.create(JcrLexicon.NAME, name),
657 factory.create(JcrLexicon.AUTO_CREATED, autoCreated),
658 factory.create(JcrLexicon.PROTECTED, isProtected),
659 factory.create(JcrLexicon.ON_PARENT_VERSION, onParentVersion),
660 factory.create(JcrLexicon.SAME_NAME_SIBLINGS, sameNameSiblings));
661
662 return path;
663 }
664 }
665
666 protected class Parser extends CndParser {
667 private final Problems problems;
668 private final String nameOfResource;
669
670 public Parser( TokenStream input,
671 Problems problems,
672 String nameOfResource ) {
673 super(input);
674 this.problems = problems;
675 this.nameOfResource = nameOfResource;
676 }
677
678 @Override
679 public void displayRecognitionError( String[] tokenNames,
680 RecognitionException e ) {
681 if (problems != null) {
682 String hdr = getErrorHeader(e);
683 String msg = getErrorMessage(e, tokenNames);
684 problems.addError(CndI18n.passthrough, nameOfResource, hdr, msg);
685 } else {
686 super.displayRecognitionError(tokenNames, e);
687 }
688 }
689 }
690
691 /**
692 * Specialization of an {@link ANTLRInputStream} that converts all tokens to lowercase, allowing the grammar to be
693 * case-insensitive. See the <a href="http://www.antlr.org/wiki/pages/viewpage.action?pageId=1782">ANTLR documentation</a>.
694 */
695 protected class CaseInsensitiveInputStream extends ANTLRInputStream {
696 protected CaseInsensitiveInputStream( InputStream stream ) throws IOException {
697 super(stream);
698 }
699
700 /**
701 * {@inheritDoc}
702 *
703 * @see org.antlr.runtime.ANTLRStringStream#LA(int)
704 */
705 @Override
706 public int LA( int i ) {
707 if (i == 0) {
708 return 0; // undefined
709 }
710 if (i < 0) {
711 i++; // e.g., translate LA(-1) to use offset 0
712 }
713
714 if ((p + i - 1) >= n) {
715 return CharStream.EOF;
716 }
717 return Character.toLowerCase(data[p + i - 1]);
718 }
719 }
720
721 /**
722 * Specialization of an {@link ANTLRInputStream} that converts all tokens to lowercase, allowing the grammar to be
723 * case-insensitive. See the <a href="http://www.antlr.org/wiki/pages/viewpage.action?pageId=1782">ANTLR documentation</a>.
724 */
725 protected class CaseInsensitiveFileStream extends ANTLRFileStream {
726 protected CaseInsensitiveFileStream( String fileName ) throws IOException {
727 super(fileName, null);
728 }
729
730 protected CaseInsensitiveFileStream( String fileName,
731 String encoding ) throws IOException {
732 super(fileName, encoding);
733 }
734
735 /**
736 * {@inheritDoc}
737 *
738 * @see org.antlr.runtime.ANTLRStringStream#LA(int)
739 */
740 @Override
741 public int LA( int i ) {
742 if (i == 0) {
743 return 0; // undefined
744 }
745 if (i < 0) {
746 i++; // e.g., translate LA(-1) to use offset 0
747 }
748
749 if ((p + i - 1) >= n) {
750
751 return CharStream.EOF;
752 }
753 return Character.toLowerCase(data[p + i - 1]);
754 }
755 }
756
757 }