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 }