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 }