org.modeshape.jcr.CndImporter Maven / Gradle / Ivy
/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.jcr;
import static org.modeshape.common.text.TokenStream.ANY_VALUE;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NodeTypeDefinition;
import javax.jcr.version.OnParentVersionAction;
import org.infinispan.commons.util.FileLookup;
import org.infinispan.commons.util.FileLookupFactory;
import org.modeshape.common.annotation.NotThreadSafe;
import org.modeshape.common.collection.Problems;
import org.modeshape.common.text.ParsingException;
import org.modeshape.common.text.Position;
import org.modeshape.common.text.TokenStream;
import org.modeshape.common.text.TokenStream.Tokenizer;
import org.modeshape.common.util.IoUtil;
import org.modeshape.jcr.cache.PropertyTypeUtil;
import org.modeshape.jcr.value.Name;
import org.modeshape.jcr.value.NameFactory;
import org.modeshape.jcr.value.NamespaceException;
import org.modeshape.jcr.value.NamespaceRegistry;
import org.modeshape.jcr.value.Property;
import org.modeshape.jcr.value.PropertyType;
import org.modeshape.jcr.value.ValueFormatException;
import org.modeshape.jcr.value.basic.LocalNamespaceRegistry;
/**
* A class that imports the node types contained in a JCR Compact Node Definition (CND) file into {@link NodeTypeDefinition}
* instances.
*/
@NotThreadSafe
public class CndImporter {
protected final List VALID_PROPERTY_TYPES = Collections.unmodifiableList(Arrays.asList("STRING",
"BINARY", "LONG", "DOUBLE",
"BOOLEAN", "DATE", "NAME",
"PATH", "REFERENCE",
"WEAKREFERENCE",
"SIMPLEREFERENCE",
"DECIMAL", "URI", "UNDEFINED",
"*", "?"));
protected final List VALID_ON_PARENT_VERSION = Collections.unmodifiableList(Arrays.asList("COPY",
"VERSION", "INITIALIZE",
"COMPUTE", "IGNORE",
"ABORT"));
protected final Set VALID_QUERY_OPERATORS = Collections.unmodifiableSet(new HashSet(
Arrays.asList(new String[] {
"=", "<>", "<", "<=",
">", ">=", "LIKE"})));
protected static final String MODESHAPE_BUILT_INS = "org/modeshape/jcr/modeshape_builtins.cnd";
protected static final String JSR283_BUILTINS = "org/modeshape/jcr/jsr_283_builtins.cnd";
protected final List BUILT_INS = Collections.unmodifiableList(Arrays.asList(JSR283_BUILTINS, MODESHAPE_BUILT_INS));
/**
* The default flag for using vendor extensions is {@value} .
*/
public static final boolean DEFAULT_USE_VENDOR_EXTENSIONS = true;
/**
* The default flag for supporting pre-JCR 2.0 CND format is {@value} .
*/
public static final boolean DEFAULT_COMPATIBLE_WITH_PREJCR2 = true;
/**
* The regular expression used to capture the vendor property name and the value. The expression is "
* ([^\s]+)(\s+(.*))
".
*/
protected final String VENDOR_PATTERN_STRING = "([^\\s]+)(\\s+(.*))";
protected final Pattern VENDOR_PATTERN = Pattern.compile(VENDOR_PATTERN_STRING);
protected final ExecutionContext context;
protected final LocalNamespaceRegistry localRegistry;
protected final NameFactory nameFactory;
protected final org.modeshape.jcr.value.ValueFactory stringFactory;
protected final ValueFactory valueFactory;
protected final List nodeTypes;
/**
* Create a new importer that will place the content in the supplied destination under the supplied path.
*
* @param context the context in which the importing should be performed; may not be null
*
*/
public CndImporter( ExecutionContext context ) {
assert context != null;
this.localRegistry = new LocalNamespaceRegistry(context.getNamespaceRegistry());
this.context = context.with(this.localRegistry);
this.valueFactory = new JcrValueFactory(this.context);
this.nameFactory = this.context.getValueFactories().getNameFactory();
this.stringFactory = this.context.getValueFactories().getStringFactory();
this.nodeTypes = new LinkedList();
}
/**
* Import the CND content from the supplied stream, placing the content into the importer's destination.
*
* @param stream the stream containing the CND content
* @param problems where any problems encountered during import should be reported
* @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
* useful name
* @throws IOException if there is a problem reading from the supplied stream
*/
public void importFrom( InputStream stream,
Problems problems,
String resourceName ) throws IOException {
importFrom(IoUtil.read(stream), problems, resourceName);
}
/**
* Import the CND content from the supplied stream, placing the content into the importer's destination.
*
* @param file the file containing the CND content
* @param problems where any problems encountered during import should be reported
* @throws IOException if there is a problem reading from the supplied stream
*/
public void importFrom( File file,
Problems problems ) throws IOException {
importFrom(IoUtil.read(file), problems, file.getCanonicalPath());
}
/**
* Import the CND content from the supplied stream, placing the content into the importer's destination.
*
* @param content the string containing the CND content
* @param problems where any problems encountered during import should be reported
* @param resourceName a logical name for the resource name to be used when reporting problems; may be null if there is no
* useful name
*/
public void importFrom( String content,
Problems problems,
String resourceName ) {
try {
parse(content);
} catch (RuntimeException e) {
problems.addError(e, CndI18n.errorImportingCndContent, resourceName, e.getMessage());
}
}
public void importBuiltIns( Problems problems ) throws IOException {
for (String resource : BUILT_INS) {
FileLookup factory = FileLookupFactory.newInstance();
InputStream stream = factory.lookupFile(resource, Thread.currentThread().getContextClassLoader());
if (stream == null) {
stream = factory.lookupFile(resource, getClass().getClassLoader());
}
importFrom(stream, problems, resource);
}
}
public Set getNamespaces() {
return new HashSet(this.localRegistry.getLocalNamespaces());
}
/**
* @return nodeTypes
*/
public List getNodeTypeDefinitions() {
return Collections.unmodifiableList(new ArrayList(nodeTypes));
}
/**
* Parse the CND content.
*
* @param content the content
* @throws ParsingException if there is a problem parsing the content
*/
protected void parse( String content ) {
Tokenizer tokenizer = new CndTokenizer(false, true);
TokenStream tokens = new TokenStream(content, tokenizer, false);
tokens.start();
while (tokens.hasNext()) {
// Keep reading while we can recognize one of the two types of statements ...
if (tokens.matches("<", ANY_VALUE, "=", ANY_VALUE, ">")) {
parseNamespaceMapping(tokens);
} else if (tokens.matches("[", ANY_VALUE, "]")) {
parseNodeTypeDefinition(tokens);
} else {
Position position = tokens.previousPosition();
throw new ParsingException(position, CndI18n.expectedNamespaceOrNodeDefinition.text(tokens.consume(),
position.getLine(),
position.getColumn()));
}
}
}
/**
* Parse the namespace mapping statement that is next on the token stream.
*
* @param tokens the tokens containing the namespace statement; never null
* @throws ParsingException if there is a problem parsing the content
*/
protected void parseNamespaceMapping( TokenStream tokens ) {
tokens.consume('<');
String prefix = removeQuotes(tokens.consume());
tokens.consume('=');
String uri = removeQuotes(tokens.consume());
tokens.consume('>');
// Register the namespace ...
context.getNamespaceRegistry().register(prefix, uri);
}
/**
* Parse the node type definition that is next on the token stream.
*
* @param tokens the tokens containing the node type definition; never null
* @throws ParsingException if there is a problem parsing the content
*/
protected void parseNodeTypeDefinition( TokenStream tokens ) {
// Parse the name, and create the path and a property for the name ...
Name name = parseNodeTypeName(tokens);
JcrNodeTypeTemplate nodeType = new JcrNodeTypeTemplate(context);
try {
nodeType.setName(string(name));
} catch (ConstraintViolationException e) {
assert false : "Names should always be syntactically valid";
}
// Read the (optional) supertypes ...
List supertypes = parseSupertypes(tokens);
try {
nodeType.setDeclaredSuperTypeNames(names(supertypes));
// Read the node type options (and vendor extensions) ...
parseNodeTypeOptions(tokens, nodeType);
// Parse property and child node definitions ...
parsePropertyOrChildNodeDefinitions(tokens, nodeType);
} catch (ConstraintViolationException e) {
assert false : "Names should always be syntactically valid";
}
this.nodeTypes.add(nodeType);
}
/**
* Parse a node type name that appears next on the token stream.
*
* @param tokens the tokens containing the node type name; never null
* @return the node type name
* @throws ParsingException if there is a problem parsing the content
*/
protected Name parseNodeTypeName( TokenStream tokens ) {
tokens.consume('[');
Name name = parseName(tokens);
tokens.consume(']');
return name;
}
/**
* Parse an optional list of supertypes if they appear next on the token stream.
*
* @param tokens the tokens containing the supertype names; never null
* @return the list of supertype names; never null, but possibly empty
* @throws ParsingException if there is a problem parsing the content
*/
protected List parseSupertypes( TokenStream tokens ) {
if (tokens.canConsume('>')) {
// There is at least one supertype ...
return parseNameList(tokens);
}
return Collections.emptyList();
}
/**
* Parse a list of strings, separated by commas. Any quotes surrounding the strings are removed.
*
* @param tokens the tokens containing the comma-separated strings; never null
* @return the list of string values; never null, but possibly empty
* @throws ParsingException if there is a problem parsing the content
*/
protected List parseStringList( TokenStream tokens ) {
List strings = new ArrayList();
if (tokens.canConsume('?')) {
// This list is variant ...
strings.add("?");
} else {
// Read names until we see a ','
do {
strings.add(removeQuotes(tokens.consume()));
} while (tokens.canConsume(','));
}
return strings;
}
/**
* Parse a list of names, separated by commas. Any quotes surrounding the names are removed.
*
* @param tokens the tokens containing the comma-separated strings; never null
* @return the list of string values; never null, but possibly empty
* @throws ParsingException if there is a problem parsing the content
*/
protected List parseNameList( TokenStream tokens ) {
List names = new ArrayList();
if (!tokens.canConsume('?')) {
// Read names until we see a ','
do {
names.add(parseName(tokens));
} while (tokens.canConsume(','));
}
return names;
}
/**
* Parse the options for the node types, including whether the node type is orderable, a mixin, abstract, whether it supports
* querying, and which property/child node (if any) is the primary item for the node type.
*
* @param tokens the tokens containing the comma-separated strings; never null
* @param nodeType the node type being created; may not be null
* @throws ParsingException if there is a problem parsing the content
* @throws ConstraintViolationException not expected
*/
protected void parseNodeTypeOptions( TokenStream tokens,
JcrNodeTypeTemplate nodeType ) throws ConstraintViolationException {
// Set up the defaults ...
boolean isOrderable = false;
boolean isMixin = false;
boolean isAbstract = false;
boolean isQueryable = true;
String primaryItem = null;
while (true) {
// Keep reading while we see a valid option ...
if (tokens.canConsumeAnyOf("ORDERABLE", "ORD", "O")) {
tokens.canConsume('?');
isOrderable = true;
} else if (tokens.canConsumeAnyOf("MIXIN", "MIX", "M")) {
tokens.canConsume('?');
isMixin = true;
} else if (tokens.canConsumeAnyOf("ABSTRACT", "ABS", "A")) {
tokens.canConsume('?');
isAbstract = true;
} else if (tokens.canConsumeAnyOf("NOQUERY", "NOQ")) {
tokens.canConsume('?');
isQueryable = false;
} else if (tokens.canConsumeAnyOf("QUERY", "Q")) {
tokens.canConsume('?');
isQueryable = true;
} else if (tokens.canConsumeAnyOf("PRIMARYITEM", "!")) {
primaryItem = removeQuotes(tokens.consume());
tokens.canConsume('?');
} else if (tokens.matches(CndTokenizer.VENDOR_EXTENSION)) {
List properties = new LinkedList();
parseVendorExtensions(tokens, properties);
applyVendorExtensions(nodeType, properties);
} else {
// No more valid options on the stream, so stop ...
break;
}
}
nodeType.setAbstract(isAbstract);
nodeType.setMixin(isMixin);
nodeType.setOrderableChildNodes(isOrderable);
nodeType.setQueryable(isQueryable);
// nodeType.setOnParentVersion();
if (primaryItem != null) {
nodeType.setPrimaryItemName(string(primaryItem));
}
}
/**
* Parse a node type's property or child node definitions that appear next on the token stream.
*
* @param tokens the tokens containing the definitions; never null
* @param nodeType the node type being created; never null
* @throws ParsingException if there is a problem parsing the content
* @throws ConstraintViolationException not expected
*/
protected void parsePropertyOrChildNodeDefinitions( TokenStream tokens,
JcrNodeTypeTemplate nodeType ) throws ConstraintViolationException {
while (true) {
// Keep reading while we see a property definition or child node definition ...
if (tokens.matches('-')) {
parsePropertyDefinition(tokens, nodeType);
} else if (tokens.matches('+')) {
parseChildNodeDefinition(tokens, nodeType);
} else {
// The next token does not signal either one of these, so stop ...
break;
}
}
}
/**
* Parse a node type's property definition from the next tokens on the stream.
*
* @param tokens the tokens containing the definition; never null
* @param nodeType the node type definition; never null
* @throws ParsingException if there is a problem parsing the content
* @throws ConstraintViolationException not expected
*/
protected void parsePropertyDefinition( TokenStream tokens,
JcrNodeTypeTemplate nodeType ) throws ConstraintViolationException {
tokens.consume('-');
Name name = parseName(tokens);
JcrPropertyDefinitionTemplate propDefn = new JcrPropertyDefinitionTemplate(context);
propDefn.setName(string(name));
// Parse the (optional) required type ...
parsePropertyType(tokens, propDefn, PropertyType.STRING.getName());
// Parse the default values ...
parseDefaultValues(tokens, propDefn);
// Parse the property attributes (and vendor extensions) ...
parsePropertyAttributes(tokens, propDefn, nodeType);
// Parse the property constraints ...
parseValueConstraints(tokens, propDefn);
// Parse the vendor extensions (appearing after the constraints) ...
List properties = new LinkedList();
parseVendorExtensions(tokens, properties);
applyVendorExtensions(nodeType, properties);
nodeType.getPropertyDefinitionTemplates().add(propDefn);
}
/**
* Parse the property type, if a valid one appears next on the token stream.
*
* @param tokens the tokens containing the definition; never null
* @param propDefn the property definition; never null
* @param defaultPropertyType the default property type if none is actually found
* @throws ParsingException if there is a problem parsing the content
*/
protected void parsePropertyType( TokenStream tokens,
JcrPropertyDefinitionTemplate propDefn,
String defaultPropertyType ) {
if (tokens.canConsume('(')) {
// Parse the (optional) property type ...
String propertyType = defaultPropertyType;
if (tokens.matchesAnyOf(VALID_PROPERTY_TYPES)) {
propertyType = tokens.consume();
if ("*".equals(propertyType)) propertyType = "UNDEFINED";
}
tokens.consume(')');
PropertyType type = PropertyType.valueFor(propertyType.toLowerCase());
int jcrType = PropertyTypeUtil.jcrPropertyTypeFor(type);
propDefn.setRequiredType(jcrType);
}
}
/**
* Parse the property definition's default value, if they appear next on the token stream.
*
* @param tokens the tokens containing the definition; never null
* @param propDefn the property definition; never null
* @throws ParsingException if there is a problem parsing the content
*/
protected void parseDefaultValues( TokenStream tokens,
JcrPropertyDefinitionTemplate propDefn ) {
if (tokens.canConsume('=')) {
List defaultValues = parseStringList(tokens);
if (!defaultValues.isEmpty()) {
propDefn.setDefaultValues(values(defaultValues));
}
}
}
/**
* Parse the property definition's value constraints, if they appear next on the token stream.
*
* @param tokens the tokens containing the definition; never null
* @param propDefn the property definition; never null
* @throws ParsingException if there is a problem parsing the content
*/
protected void parseValueConstraints( TokenStream tokens,
JcrPropertyDefinitionTemplate propDefn ) {
if (tokens.canConsume('<')) {
List defaultValues = parseStringList(tokens);
if (!defaultValues.isEmpty()) {
propDefn.setValueConstraints(strings(defaultValues));
}
}
}
/**
* Parse the property definition's attributes, if they appear next on the token stream.
*
* @param tokens the tokens containing the attributes; never null
* @param propDefn the property definition; never null
* @param nodeType the node type; never null
* @throws ParsingException if there is a problem parsing the content
* @throws ConstraintViolationException not expected
*/
protected void parsePropertyAttributes( TokenStream tokens,
JcrPropertyDefinitionTemplate propDefn,
JcrNodeTypeTemplate nodeType ) throws ConstraintViolationException {
boolean autoCreated = false;
boolean mandatory = false;
boolean isProtected = false;
boolean multiple = false;
boolean isFullTextSearchable = true;
boolean isQueryOrderable = true;
String onParentVersion = "COPY";
while (true) {
if (tokens.canConsumeAnyOf("AUTOCREATED", "AUT", "A")) {
tokens.canConsume('?');
autoCreated = true;
} else if (tokens.canConsumeAnyOf("MANDATORY", "MAN", "M")) {
tokens.canConsume('?');
mandatory = true;
} else if (tokens.canConsumeAnyOf("PROTECTED", "PRO", "P")) {
tokens.canConsume('?');
isProtected = true;
} else if (tokens.canConsumeAnyOf("MULTIPLE", "MUL", "*")) {
tokens.canConsume('?');
multiple = true;
} else if (tokens.matchesAnyOf(VALID_ON_PARENT_VERSION)) {
onParentVersion = tokens.consume();
tokens.canConsume('?');
} else if (tokens.matches("OPV")) {
// variant on-parent-version
onParentVersion = tokens.consume();
tokens.canConsume('?');
} else if (tokens.canConsumeAnyOf("NOFULLTEXT", "NOF")) {
tokens.canConsume('?');
isFullTextSearchable = false;
} else if (tokens.canConsumeAnyOf("NOQUERYORDER", "NQORD")) {
tokens.canConsume('?');
isQueryOrderable = false;
} else if (tokens.canConsumeAnyOf("QUERYOPS", "QOP")) {
parseQueryOperators(tokens, propDefn);
} else if (tokens.canConsumeAnyOf("PRIMARYITEM", "PRIMARY", "PRI", "!")) {
Position pos = tokens.previousPosition();
int line = pos.getLine();
int column = pos.getColumn();
throw new ParsingException(tokens.previousPosition(),
CndI18n.primaryKeywordNotValidInJcr2CndFormat.text(line, column));
} else if (tokens.matches(CndTokenizer.VENDOR_EXTENSION)) {
List properties = new LinkedList();
parseVendorExtensions(tokens, properties);
applyVendorExtensions(propDefn, properties);
} else {
break;
}
}
propDefn.setAutoCreated(autoCreated);
propDefn.setMandatory(mandatory);
propDefn.setProtected(isProtected);
propDefn.setOnParentVersion(OnParentVersionAction.valueFromName(onParentVersion.toUpperCase()));
propDefn.setMultiple(multiple);
propDefn.setFullTextSearchable(isFullTextSearchable);
propDefn.setQueryOrderable(isQueryOrderable);
}
/**
* Parse the property definition's query operators, if they appear next on the token stream.
*
* @param tokens the tokens containing the definition; never null
* @param propDefn the property definition; never null
* @throws ParsingException if there is a problem parsing the content
*/
protected void parseQueryOperators( TokenStream tokens,
JcrPropertyDefinitionTemplate propDefn ) {
if (tokens.canConsume('?')) {
return;
}
// The query operators are expected to be enclosed in a single quote, so therefore will be a single token ...
List operators = new ArrayList();
String operatorList = removeQuotes(tokens.consume());
// Now split this string on ',' ...
for (String operatorValue : operatorList.split(",")) {
String operator = operatorValue.trim();
if (!VALID_QUERY_OPERATORS.contains(operator)) {
throw new ParsingException(tokens.previousPosition(), CndI18n.expectedValidQueryOperator.text(operator));
}
operators.add(operator);
}
if (operators.isEmpty()) {
operators.addAll(VALID_QUERY_OPERATORS);
}
propDefn.setAvailableQueryOperators(strings(operators));
}
/**
* Parse a node type's child node definition from the next tokens on the stream.
*
* @param tokens the tokens containing the definition; never null
* @param nodeType the node type being created; never null
* @throws ParsingException if there is a problem parsing the content
* @throws ConstraintViolationException not expected
*/
protected void parseChildNodeDefinition( TokenStream tokens,
JcrNodeTypeTemplate nodeType ) throws ConstraintViolationException {
tokens.consume('+');
Name name = parseName(tokens);
JcrNodeDefinitionTemplate childDefn = new JcrNodeDefinitionTemplate(context);
childDefn.setName(string(name));
parseRequiredPrimaryTypes(tokens, childDefn);
parseDefaultType(tokens, childDefn);
parseNodeAttributes(tokens, childDefn, nodeType);
nodeType.getNodeDefinitionTemplates().add(childDefn);
}
/**
* Parse the child node definition's list of required primary types, if they appear next on the token stream.
*
* @param tokens the tokens containing the definition; never null
* @param childDefn the child node definition; never null
* @throws ParsingException if there is a problem parsing the content
* @throws ConstraintViolationException not expected
*/
protected void parseRequiredPrimaryTypes( TokenStream tokens,
JcrNodeDefinitionTemplate childDefn ) throws ConstraintViolationException {
if (tokens.canConsume('(')) {
List requiredTypes = parseNameList(tokens);
if (requiredTypes.isEmpty()) {
requiredTypes.add(JcrNtLexicon.BASE);
}
childDefn.setRequiredPrimaryTypeNames(names(requiredTypes));
tokens.consume(')');
}
}
/**
* Parse the child node definition's default type, if they appear next on the token stream.
*
* @param tokens the tokens containing the definition; never null
* @param childDefn the child node definition; never null
* @throws ParsingException if there is a problem parsing the content
* @throws ConstraintViolationException not expected
*/
protected void parseDefaultType( TokenStream tokens,
JcrNodeDefinitionTemplate childDefn ) throws ConstraintViolationException {
if (tokens.canConsume('=')) {
if (!tokens.canConsume('?')) {
Name defaultType = parseName(tokens);
childDefn.setDefaultPrimaryTypeName(string(defaultType));
}
}
}
/**
* Parse the child node definition's attributes, if they appear next on the token stream.
*
* @param tokens the tokens containing the attributes; never null
* @param childDefn the child node definition; never null
* @param nodeType the node type being created; never null
* @throws ParsingException if there is a problem parsing the content
* @throws ConstraintViolationException not expected
*/
protected void parseNodeAttributes( TokenStream tokens,
JcrNodeDefinitionTemplate childDefn,
JcrNodeTypeTemplate nodeType ) throws ConstraintViolationException {
boolean autoCreated = false;
boolean mandatory = false;
boolean isProtected = false;
boolean sns = false;
String onParentVersion = "COPY";
while (true) {
if (tokens.canConsumeAnyOf("AUTOCREATED", "AUT", "A")) {
tokens.canConsume('?');
autoCreated = true;
} else if (tokens.canConsumeAnyOf("MANDATORY", "MAN", "M")) {
tokens.canConsume('?');
mandatory = true;
} else if (tokens.canConsumeAnyOf("PROTECTED", "PRO", "P")) {
tokens.canConsume('?');
isProtected = true;
} else if (tokens.canConsumeAnyOf("SNS", "*")) { // standard JCR 2.0 keywords for SNS ...
tokens.canConsume('?');
sns = true;
} else if (tokens.canConsumeAnyOf("MULTIPLE", "MUL", "*")) { // from pre-JCR 2.0 ref impl
Position pos = tokens.previousPosition();
int line = pos.getLine();
int column = pos.getColumn();
throw new ParsingException(tokens.previousPosition(),
CndI18n.multipleKeywordNotValidInJcr2CndFormat.text(line, column));
} else if (tokens.matchesAnyOf(VALID_ON_PARENT_VERSION)) {
onParentVersion = tokens.consume();
tokens.canConsume('?');
} else if (tokens.matches("OPV")) {
// variant on-parent-version
onParentVersion = tokens.consume();
tokens.canConsume('?');
} else if (tokens.canConsumeAnyOf("PRIMARYITEM", "PRIMARY", "PRI", "!")) {
Position pos = tokens.previousPosition();
int line = pos.getLine();
int column = pos.getColumn();
throw new ParsingException(tokens.previousPosition(),
CndI18n.primaryKeywordNotValidInJcr2CndFormat.text(line, column));
} else if (tokens.matches(CndTokenizer.VENDOR_EXTENSION)) {
List properties = new LinkedList();
parseVendorExtensions(tokens, properties);
applyVendorExtensions(childDefn, properties);
} else {
break;
}
}
childDefn.setAutoCreated(autoCreated);
childDefn.setMandatory(mandatory);
childDefn.setProtected(isProtected);
childDefn.setOnParentVersion(OnParentVersionAction.valueFromName(onParentVersion.toUpperCase()));
childDefn.setSameNameSiblings(sns);
}
/**
* Parse the name that is expected to be next on the token stream.
*
* @param tokens the tokens containing the name; never null
* @return the name; never null
* @throws ParsingException if there is a problem parsing the content
*/
protected Name parseName( TokenStream tokens ) {
String value = tokens.consume();
try {
return nameFactory.create(removeQuotes(value));
} catch (ValueFormatException e) {
if (e.getCause() instanceof NamespaceException) {
throw (NamespaceException)e.getCause();
}
throw new ParsingException(tokens.previousPosition(), CndI18n.expectedValidNameLiteral.text(value));
}
}
protected final String removeQuotes( String text ) {
// Remove leading and trailing quotes, if there are any ...
return text.replaceFirst("^['\"]+", "").replaceAll("['\"]+$", "");
}
/**
* Parse the vendor extensions that may appear next on the tokenzied stream.
*
* @param tokens token stream; may not be null
* @param properties the list of properties to which any vendor extension properties should be added
*/
protected final void parseVendorExtensions( TokenStream tokens,
List properties ) {
while (tokens.matches(CndTokenizer.VENDOR_EXTENSION)) {
Property extension = parseVendorExtension(tokens.consume());
if (extension != null) properties.add(extension);
}
}
/**
* Parse the vendor extension, including the curly braces in the CND content.
*
* @param vendorExtension the vendor extension string
* @return the property representing the vendor extension, or null if the vendor extension is incomplete
*/
protected final Property parseVendorExtension( String vendorExtension ) {
if (vendorExtension == null) return null;
// Remove the curly braces ...
String extension = vendorExtension.replaceFirst("^[{]", "").replaceAll("[}]$", "");
if (extension.trim().length() == 0) return null;
return parseVendorExtensionContent(extension);
}
/**
* Parse the content of the vendor extension excluding the curly braces in the CND content.
*
* @param vendorExtension the vendor extension string; never null
* @return the property representing the vendor extension, or null if the vendor extension is incomplete
*/
protected final Property parseVendorExtensionContent( String vendorExtension ) {
Matcher matcher = VENDOR_PATTERN.matcher(vendorExtension);
if (!matcher.find()) return null;
String vendorName = removeQuotes(matcher.group(1));
String vendorValue = removeQuotes(matcher.group(3));
assert vendorName != null;
assert vendorValue != null;
assert vendorName.length() != 0;
assert vendorValue.length() != 0;
return context.getPropertyFactory().create(nameFactory.create(vendorName), vendorValue);
}
/**
* Method that is responsible for setting the vendor extensions on the supplied node type template. By default this method
* does nothing; subclasses should override this method for custom extensions.
*
* @param nodeType the node type definition; never null
* @param extensions the extensions; never null but possibly empty
*/
protected void applyVendorExtensions( JcrNodeTypeTemplate nodeType,
List extensions ) {
}
/**
* Method that is responsible for setting the vendor extensions on the supplied child node type template. By default this
* method does nothing; subclasses should override this method for custom extensions.
*
* @param childDefn the child definition; never null
* @param extensions the extensions; never null but possibly empty
*/
protected void applyVendorExtensions( JcrNodeDefinitionTemplate childDefn,
List extensions ) {
}
/**
* Method that is responsible for setting the vendor extensions on the supplied property definition template. By default this
* method does nothing; subclasses should override this method for custom extensions.
*
* @param propDefn the property definition; never null
* @param extensions the extensions; never null but possibly empty
*/
protected void applyVendorExtensions( JcrPropertyDefinitionTemplate propDefn,
List extensions ) {
}
protected final String string( Object name ) {
return stringFactory.create(name);
}
protected final String[] names( Collection names ) {
String[] result = new String[names.size()];
int i = 0;
for (Name name : names) {
result[i++] = string(name);
}
return result;
}
protected final String[] strings( Collection values ) {
String[] result = new String[values.size()];
int i = 0;
for (String value : values) {
result[i++] = value;
}
return result;
}
protected final Value[] values( Collection values ) {
Value[] result = new Value[values.size()];
int i = 0;
for (String value : values) {
result[i++] = valueFactory.createValue(value);
}
return result;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy