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.namespaces().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 Problems problems;
350 private final String resourceName;
351
352 protected ImportContext( ExecutionContext context,
353 Problems problems,
354 String resourceName ) {
355 // Create a context that has a local namespace registry
356 NamespaceRegistry localNamespaces = new LocalNamespaceRegistry(context.getNamespaceRegistry());
357 this.context = context.with(localNamespaces);
358 this.problems = problems;
359 this.resourceName = resourceName;
360 }
361
362 protected ExecutionContext context() {
363 return this.context;
364 }
365
366 protected NamespaceRegistry namespaces() {
367 return this.context.getNamespaceRegistry();
368 }
369
370 protected NameFactory nameFactory() {
371 return this.context.getValueFactories().getNameFactory();
372 }
373
374 protected PathFactory pathFactory() {
375 return this.context.getValueFactories().getPathFactory();
376 }
377
378 protected ValueFactory<String> stringFactory() {
379 return this.context.getValueFactories().getStringFactory();
380 }
381
382 protected ValueFactory<Boolean> booleanFactory() {
383 return this.context.getValueFactories().getBooleanFactory();
384 }
385
386 protected void recordError( CommonTree node,
387 I18n msg,
388 Object... params ) {
389 String location = CndI18n.locationFromLineNumberAndCharacter.text(node.getLine(), node.getCharPositionInLine());
390 problems.addError(msg, resourceName, location, params);
391 }
392
393 protected void recordError( Throwable throwable,
394 CommonTree node,
395 I18n msg,
396 Object... params ) {
397 String location = CndI18n.locationFromLineNumberAndCharacter.text(node.getLine(), node.getCharPositionInLine());
398 problems.addError(throwable, msg, resourceName, location, params);
399 }
400
401 protected Name nameFrom( CommonTree node,
402 int childType ) {
403 CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
404 if (childNode != null && childNode.getChildCount() > 0) {
405 CommonTree textNode = (CommonTree)childNode.getChild(0);
406 if (textNode.getToken().getTokenIndex() < 0) return null;
407 String text = removeQuotes(childNode.getChild(0).getText());
408 try {
409 return nameFactory().create(text);
410 } catch (ValueFormatException e) {
411 recordError(e, node, CndI18n.expectedValidNameLiteral, text);
412 }
413 }
414 return null;
415 }
416
417 protected Name[] namesFrom( CommonTree node,
418 int childType ) {
419 CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
420 if (childNode != null && childNode.getChildCount() > 0) {
421 List<Name> names = new ArrayList<Name>();
422 for (int i = 0; i != childNode.getChildCount(); ++i) {
423 String text = removeQuotes(childNode.getChild(i).getText());
424 try {
425 names.add(nameFactory().create(text));
426 } catch (ValueFormatException e) {
427 recordError(e, node, CndI18n.expectedValidNameLiteral, text);
428 }
429 }
430 return names.toArray(new Name[names.size()]);
431 }
432 return new Name[] {};
433 }
434
435 protected String stringFrom( CommonTree node,
436 int childType ) {
437 CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
438 if (childNode != null && childNode.getChildCount() > 0) {
439 String text = removeQuotes(childNode.getChild(0).getText().trim());
440 try {
441 return stringFactory().create(text);
442 } catch (ValueFormatException e) {
443 recordError(e, node, CndI18n.expectedStringLiteral, text);
444 }
445 }
446 return null;
447 }
448
449 protected String[] stringsFrom( CommonTree node,
450 int childType ) {
451 CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
452 if (childNode != null && childNode.getChildCount() > 0) {
453 List<String> names = new ArrayList<String>();
454 for (int i = 0; i != childNode.getChildCount(); ++i) {
455 String text = removeQuotes(childNode.getChild(i).getText().trim());
456 try {
457 names.add(stringFactory().create(text));
458 } catch (ValueFormatException e) {
459 recordError(e, node, CndI18n.expectedStringLiteral, text);
460 }
461 }
462 return names.toArray(new String[names.size()]);
463 }
464 return new String[] {};
465 }
466
467 protected boolean booleanFrom( CommonTree node,
468 int childType,
469 boolean defaultValue ) {
470 CommonTree childNode = (CommonTree)node.getFirstChildWithType(childType);
471 if (childNode != null && childNode.getChildCount() > 0) {
472 String text = removeQuotes(childNode.getChild(0).getText());
473 try {
474 return booleanFactory().create(text);
475 } catch (ValueFormatException e) {
476 recordError(e, node, CndI18n.expectedBooleanLiteral, text);
477 }
478 }
479 return defaultValue;
480 }
481
482 protected QueryOperator[] queryOperatorsFrom( CommonTree node,
483 int childType ) {
484 String text = stringFrom(node, childType);
485 if (text != null) {
486 String[] literals = text.split(",");
487 if (literals.length != 0) {
488 Set<QueryOperator> operators = new HashSet<QueryOperator>();
489 for (String literal : literals) {
490 literal = literal.trim();
491 if (literal.length() == 0) continue;
492 QueryOperator operator = QueryOperator.forText(literal);
493 if (operator != null) {
494 operators.add(operator);
495 } else {
496 recordError(node, CndI18n.expectedValidQueryOperator, literal);
497 }
498 }
499 return operators.toArray(new QueryOperator[operators.size()]);
500 }
501 }
502 return new QueryOperator[] {};
503 }
504
505 protected String propertyTypeNameFrom( CommonTree node,
506 int childType ) {
507 String text = stringFrom(node, childType);
508 if (text.equals("*")) text = "undefined";
509 String upperText = text.toUpperCase();
510 if (!VALID_PROPERTY_TYPES.contains(upperText)) {
511 recordError(node, CndI18n.expectedValidPropertyTypeName, text, VALID_PROPERTY_TYPES);
512 return null;
513 }
514 return upperText;
515 }
516
517 protected String onParentVersionFrom( CommonTree node,
518 int childType ) {
519 String text = stringFrom(node, childType);
520 if (text == null) return "COPY";
521 String upperText = text.toUpperCase();
522 if (!VALID_ON_PARENT_VERSION.contains(upperText)) {
523 recordError(node, CndI18n.expectedValidOnParentVersion, text, VALID_ON_PARENT_VERSION);
524 return null;
525 }
526 return upperText;
527 }
528
529 protected Path createNodeType( CommonTree nodeType,
530 Path parentPath ) {
531 Name name = nameFrom(nodeType, CndLexer.NAME);
532 Name[] supertypes = namesFrom(nodeType, CndLexer.SUPERTYPES);
533 boolean isAbstract = booleanFrom(nodeType, CndLexer.IS_ABSTRACT, false);
534 boolean hasOrderableChildNodes = booleanFrom(nodeType, CndLexer.HAS_ORDERABLE_CHILD_NODES, false);
535 boolean isMixin = booleanFrom(nodeType, CndLexer.IS_MIXIN, false);
536 boolean isQueryable = booleanFrom(nodeType, CndLexer.IS_QUERYABLE, true);
537 Name primaryItemName = nameFrom(nodeType, CndLexer.PRIMARY_ITEM_NAME);
538
539 if (primaryItemName == null) {
540 // See if one of the property definitions is marked as the primary ...
541 CommonTree propertyDefinitions = (CommonTree)nodeType.getFirstChildWithType(CndLexer.PROPERTY_DEFINITION);
542 if (propertyDefinitions != null) {
543 // Walk each of the nodes under PROPERTY_DEFINITION ...
544 for (int j = 0; j != propertyDefinitions.getChildCount(); ++j) {
545 CommonTree propDefn = (CommonTree)propertyDefinitions.getChild(j);
546 if (booleanFrom(propDefn, CndLexer.IS_PRIMARY_PROPERTY, false)) {
547 primaryItemName = nameFrom(propDefn, CndLexer.NAME);
548 break;
549 }
550 }
551 }
552 }
553 if (primaryItemName == null) {
554 // See if one of the child definitions is marked as the primary ...
555 CommonTree childNodeDefinitions = (CommonTree)nodeType.getFirstChildWithType(CndLexer.CHILD_NODE_DEFINITION);
556 if (childNodeDefinitions != null) {
557 // Walk each of the nodes under CHILD_NODE_DEFINITION ...
558 for (int j = 0; j != childNodeDefinitions.getChildCount(); ++j) {
559 CommonTree childDefn = (CommonTree)childNodeDefinitions.getChild(j);
560 if (booleanFrom(childDefn, CndLexer.IS_PRIMARY_PROPERTY, false)) {
561 primaryItemName = nameFrom(childDefn, CndLexer.NAME);
562 break;
563 }
564 }
565 }
566 }
567
568 // Create the node for the node type ...
569 if (name == null) return null;
570 Path path = pathFactory().create(parentPath, name);
571
572 PropertyFactory factory = context.getPropertyFactory();
573 destination.create(path,
574 factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.NODE_TYPE),
575 factory.create(JcrLexicon.SUPERTYPES, (Object[])supertypes),
576 factory.create(JcrLexicon.IS_ABSTRACT, isAbstract),
577 factory.create(JcrLexicon.HAS_ORDERABLE_CHILD_NODES, hasOrderableChildNodes),
578 factory.create(JcrLexicon.IS_MIXIN, isMixin),
579 factory.create(JcrLexicon.IS_QUERYABLE, isQueryable),
580 factory.create(JcrLexicon.PRIMARY_ITEM_NAME, primaryItemName));
581
582 return path;
583 }
584
585 protected Path createPropertyDefinition( CommonTree propDefn,
586 Path parentPath ) {
587 Name name = nameFrom(propDefn, CndLexer.NAME);
588 String requiredType = propertyTypeNameFrom(propDefn, CndLexer.REQUIRED_TYPE);
589 String[] defaultValues = stringsFrom(propDefn, CndLexer.DEFAULT_VALUES);
590 boolean multiple = booleanFrom(propDefn, CndLexer.MULTIPLE, false);
591 boolean mandatory = booleanFrom(propDefn, CndLexer.MANDATORY, false);
592 boolean autoCreated = booleanFrom(propDefn, CndLexer.AUTO_CREATED, false);
593 boolean isProtected = booleanFrom(propDefn, CndLexer.PROTECTED, false);
594 String onParentVersion = onParentVersionFrom(propDefn, CndLexer.ON_PARENT_VERSION);
595 /*QueryOperator[] queryOperators =*/queryOperatorsFrom(propDefn, CndLexer.QUERY_OPERATORS);
596 boolean isFullTextSearchable = booleanFrom(propDefn, CndLexer.IS_FULL_TEXT_SEARCHABLE, true);
597 boolean isQueryOrderable = booleanFrom(propDefn, CndLexer.IS_QUERY_ORDERERABLE, true);
598 String[] valueConstraints = stringsFrom(propDefn, CndLexer.VALUE_CONSTRAINTS);
599
600 // Create the node for the node type ...
601 if (name == null) return null;
602 Path path = pathFactory().create(parentPath, name);
603
604 PropertyFactory factory = context.getPropertyFactory();
605 destination.create(path,
606 factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.PROPERTY_DEFINITION),
607 factory.create(JcrLexicon.REQUIRED_TYPE, requiredType),
608 factory.create(JcrLexicon.DEFAULT_VALUES, (Object[])defaultValues),
609 factory.create(JcrLexicon.MULTIPLE, multiple),
610 factory.create(JcrLexicon.MANDATORY, mandatory),
611 factory.create(JcrLexicon.AUTO_CREATED, autoCreated),
612 factory.create(JcrLexicon.PROTECTED, isProtected),
613 factory.create(JcrLexicon.ON_PARENT_VERSION, onParentVersion),
614 // factory.create(DnaLexicon.QUERY_OPERATORS, queryOperators),
615 factory.create(JcrLexicon.IS_FULL_TEXT_SEARCHABLE, isFullTextSearchable),
616 factory.create(JcrLexicon.IS_QUERY_ORDERABLE, isQueryOrderable),
617 factory.create(JcrLexicon.VALUE_CONSTRAINTS, (Object[])valueConstraints));
618
619 return path;
620 }
621
622 protected Path createChildDefinition( CommonTree childDefn,
623 Path parentPath ) {
624 Name name = nameFrom(childDefn, CndLexer.NAME);
625 Name[] requiredPrimaryTypes = namesFrom(childDefn, CndLexer.REQUIRED_PRIMARY_TYPES);
626 Name defaultPrimaryType = nameFrom(childDefn, CndLexer.DEFAULT_PRIMARY_TYPE);
627 boolean mandatory = booleanFrom(childDefn, CndLexer.MANDATORY, false);
628 boolean autoCreated = booleanFrom(childDefn, CndLexer.AUTO_CREATED, false);
629 boolean isProtected = booleanFrom(childDefn, CndLexer.PROTECTED, false);
630 String onParentVersion = onParentVersionFrom(childDefn, CndLexer.ON_PARENT_VERSION);
631 boolean sameNameSiblings = booleanFrom(childDefn, CndLexer.SAME_NAME_SIBLINGS, false);
632
633 // Create the node for the node type ...
634 if (name == null) return null;
635 Path path = pathFactory().create(parentPath, name);
636
637 PropertyFactory factory = context.getPropertyFactory();
638 destination.create(path,
639 factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.CHILD_NODE_DEFINITION),
640 factory.create(JcrLexicon.REQUIRED_PRIMARY_TYPES, (Object[])requiredPrimaryTypes),
641 factory.create(JcrLexicon.DEFAULT_PRIMARY_TYPE, defaultPrimaryType),
642 factory.create(JcrLexicon.MANDATORY, mandatory),
643 factory.create(JcrLexicon.AUTO_CREATED, autoCreated),
644 factory.create(JcrLexicon.PROTECTED, isProtected),
645 factory.create(JcrLexicon.ON_PARENT_VERSION, onParentVersion),
646 factory.create(JcrLexicon.SAME_NAME_SIBLINGS, sameNameSiblings));
647
648 return path;
649 }
650 }
651
652 protected class Parser extends CndParser {
653 private final Problems problems;
654 private final String nameOfResource;
655
656 public Parser( TokenStream input,
657 Problems problems,
658 String nameOfResource ) {
659 super(input);
660 this.problems = problems;
661 this.nameOfResource = nameOfResource;
662 }
663
664 @Override
665 public void displayRecognitionError( String[] tokenNames,
666 RecognitionException e ) {
667 if (problems != null) {
668 String hdr = getErrorHeader(e);
669 String msg = getErrorMessage(e, tokenNames);
670 problems.addError(CndI18n.passthrough, nameOfResource, hdr, msg);
671 } else {
672 super.displayRecognitionError(tokenNames, e);
673 }
674 }
675 }
676
677 /**
678 * Specialization of an {@link ANTLRInputStream} that converts all tokens to lowercase, allowing the grammar to be
679 * case-insensitive. See the <a href="http://www.antlr.org/wiki/pages/viewpage.action?pageId=1782">ANTLR documentation</a>.
680 */
681 protected class CaseInsensitiveInputStream extends ANTLRInputStream {
682 protected CaseInsensitiveInputStream( InputStream stream ) throws IOException {
683 super(stream);
684 }
685
686 /**
687 * {@inheritDoc}
688 *
689 * @see org.antlr.runtime.ANTLRStringStream#LA(int)
690 */
691 @Override
692 public int LA( int i ) {
693 if (i == 0) {
694 return 0; // undefined
695 }
696 if (i < 0) {
697 i++; // e.g., translate LA(-1) to use offset 0
698 }
699
700 if ((p + i - 1) >= n) {
701 return CharStream.EOF;
702 }
703 return Character.toLowerCase(data[p + i - 1]);
704 }
705 }
706
707 /**
708 * Specialization of an {@link ANTLRInputStream} that converts all tokens to lowercase, allowing the grammar to be
709 * case-insensitive. See the <a href="http://www.antlr.org/wiki/pages/viewpage.action?pageId=1782">ANTLR documentation</a>.
710 */
711 protected class CaseInsensitiveFileStream extends ANTLRFileStream {
712 protected CaseInsensitiveFileStream( String fileName ) throws IOException {
713 super(fileName, null);
714 }
715
716 protected CaseInsensitiveFileStream( String fileName,
717 String encoding ) throws IOException {
718 super(fileName, encoding);
719 }
720
721 /**
722 * {@inheritDoc}
723 *
724 * @see org.antlr.runtime.ANTLRStringStream#LA(int)
725 */
726 @Override
727 public int LA( int i ) {
728 if (i == 0) {
729 return 0; // undefined
730 }
731 if (i < 0) {
732 i++; // e.g., translate LA(-1) to use offset 0
733 }
734
735 if ((p + i - 1) >= n) {
736
737 return CharStream.EOF;
738 }
739 return Character.toLowerCase(data[p + i - 1]);
740 }
741 }
742
743 }