use of org.apache.jackrabbit.spi.QNodeDefinition in project jackrabbit by apache.
the class NodeTypeRegistry method checkForCircularNodeAutoCreation.
static void checkForCircularNodeAutoCreation(EffectiveNodeType childNodeENT, Stack<Name> definingParentNTs, EffectiveNodeTypeCache anEntCache, Map<Name, QNodeTypeDefinition> ntDefCache) throws InvalidNodeTypeDefException {
// check for circularity through default node types of auto-created child nodes
// (node type 'a' defines auto-created child node with default node type 'a')
Name[] childNodeNTs = childNodeENT.getAllNodeTypes();
for (Name nt : childNodeNTs) {
int pos = definingParentNTs.lastIndexOf(nt);
if (pos >= 0) {
StringBuilder buf = new StringBuilder();
for (int j = 0; j < definingParentNTs.size(); j++) {
if (j == pos) {
buf.append("--> ");
}
buf.append("node type ");
buf.append(definingParentNTs.get(j));
buf.append(" defines auto-created child node with default ");
}
buf.append("--> ");
buf.append("node type ");
buf.append(nt);
throw new InvalidNodeTypeDefException("circular node auto-creation detected: " + buf.toString());
}
}
QNodeDefinition[] nodeDefs = childNodeENT.getAutoCreateNodeDefs();
for (QNodeDefinition nodeDef : nodeDefs) {
Name dnt = nodeDef.getDefaultPrimaryType();
Name definingNT = nodeDef.getDeclaringNodeType();
try {
if (dnt != null) {
// check recursively
definingParentNTs.push(definingNT);
checkForCircularNodeAutoCreation(getEffectiveNodeType(dnt, anEntCache, ntDefCache), definingParentNTs, anEntCache, ntDefCache);
definingParentNTs.pop();
}
} catch (NoSuchNodeTypeException nsnte) {
String msg = definingNT + " defines invalid default node type for child node " + nodeDef.getName();
log.debug(msg);
throw new InvalidNodeTypeDefException(msg, nsnte);
}
}
}
use of org.apache.jackrabbit.spi.QNodeDefinition in project jackrabbit by apache.
the class NodeTypeRegistry method validateNodeTypeDef.
/**
* Validates the specified <code>NodeTypeDef</code> within the context of
* the two other given collections and returns an <code>EffectiveNodeType</code>.
*
* @param ntd node type definition
* @param entCache effective node type cache
* @param ntdCache cache of 'known' node type definitions, used to resolve dependencies
* @param nsReg namespace registry used for validatingatch names
* @param lenient flag governing whether validation can be lenient or has to be strict
* @return an effective node type representation of the specified <code>QNodeTypeDefinition</code>
* @throws InvalidNodeTypeDefException if the node type is not valid
* @throws RepositoryException if another error occurs
*/
private static EffectiveNodeType validateNodeTypeDef(QNodeTypeDefinition ntd, EffectiveNodeTypeCache entCache, Map<Name, QNodeTypeDefinition> ntdCache, NamespaceRegistry nsReg, boolean lenient) throws InvalidNodeTypeDefException, RepositoryException {
/**
* the effective (i.e. merged and resolved) node type resulting from
* the specified node type definition;
* the effective node type will finally be created after the definition
* has been verified and checked for conflicts etc.; in some cases it
* will be created already at an earlier stage during the validation
* of child node definitions
*/
EffectiveNodeType ent = null;
Name name = ntd.getName();
if (name == null) {
String msg = "no name specified";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
checkNamespace(name, nsReg);
// validate supertypes
Name[] supertypes = ntd.getSupertypes();
if (supertypes.length > 0) {
for (Name supertype : supertypes) {
checkNamespace(supertype, nsReg);
/**
* simple check for infinite recursion
* (won't trap recursion on a deeper inheritance level)
*/
if (name.equals(supertype)) {
String msg = "[" + name + "] invalid supertype: " + supertype + " (infinite recursion))";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
if (!ntdCache.containsKey(supertype)) {
String msg = "[" + name + "] invalid supertype: " + supertype;
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
}
/**
* check for circularity in inheritance chain
* ('a' extends 'b' extends 'a')
*/
Stack<Name> inheritanceChain = new Stack<Name>();
inheritanceChain.push(name);
checkForCircularInheritance(supertypes, inheritanceChain, ntdCache);
}
/**
* build effective (i.e. merged and resolved) node type from supertypes
* and check for conflicts
*/
if (supertypes.length > 0) {
try {
EffectiveNodeType est = getEffectiveNodeType(supertypes, entCache, ntdCache);
// a supertypes's primaryItem -> illegal (JCR-1947)
if (ntd.getPrimaryItemName() != null && est.getPrimaryItemName() != null) {
String msg = "[" + name + "] primaryItemName is already specified by a supertype and must therefore not be overridden.";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
} catch (NodeTypeConflictException ntce) {
String msg = "[" + name + "] failed to validate supertypes";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg, ntce);
} catch (NoSuchNodeTypeException nsnte) {
String msg = "[" + name + "] failed to validate supertypes";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg, nsnte);
}
}
checkNamespace(ntd.getPrimaryItemName(), nsReg);
// validate property definitions
QPropertyDefinition[] pda = ntd.getPropertyDefs();
for (QPropertyDefinition pd : pda) {
/**
* sanity check:
* make sure declaring node type matches name of node type definition
*/
if (!name.equals(pd.getDeclaringNodeType())) {
String msg = "[" + name + "#" + pd.getName() + "] invalid declaring node type specified";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
checkNamespace(pd.getName(), nsReg);
// check that auto-created properties specify a name
if (pd.definesResidual() && pd.isAutoCreated()) {
String msg = "[" + name + "#" + pd.getName() + "] auto-created properties must specify a name";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
// check that auto-created properties specify a type
if (pd.getRequiredType() == PropertyType.UNDEFINED && pd.isAutoCreated()) {
String msg = "[" + name + "#" + pd.getName() + "] auto-created properties must specify a type";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
/**
* check default values:
* make sure type of value is consistent with required property type
*/
QValue[] defVals = pd.getDefaultValues();
if (defVals != null && defVals.length != 0) {
int reqType = pd.getRequiredType();
for (QValue defVal : defVals) {
if (reqType == PropertyType.UNDEFINED) {
reqType = defVal.getType();
} else {
if (defVal.getType() != reqType) {
String msg = "[" + name + "#" + pd.getName() + "] type of default value(s) is not consistent with required property type";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
}
}
} else {
// no default values specified
if (!lenient) {
// auto-created properties must have a default value
if (pd.isAutoCreated()) {
String msg = "[" + name + "#" + pd.getName() + "] auto-created property must have a default value";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
}
}
// check that default values satisfy value constraints
QValueConstraint[] constraints = pd.getValueConstraints();
if (constraints != null && constraints.length > 0) {
if (defVals != null && defVals.length > 0) {
// check value constraints on every value
for (QValue defVal : defVals) {
// constraints are OR-ed together
boolean satisfied = false;
ConstraintViolationException cve = null;
for (QValueConstraint constraint : constraints) {
try {
constraint.check(defVal);
// at least one constraint is satisfied
satisfied = true;
break;
} catch (ConstraintViolationException e) {
cve = e;
}
}
if (!satisfied) {
// report last exception we encountered
String msg = "[" + name + "#" + pd.getName() + "] default value does not satisfy value constraint";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg, cve);
}
}
}
/**
* ReferenceConstraint:
* the specified node type must be registered, with one notable
* exception: the node type just being registered
*/
if (pd.getRequiredType() == PropertyType.REFERENCE || pd.getRequiredType() == PropertyType.WEAKREFERENCE) {
for (QValueConstraint constraint : constraints) {
Name ntName = NameFactoryImpl.getInstance().create(constraint.getString());
if (!name.equals(ntName) && !ntdCache.containsKey(ntName)) {
String msg = "[" + name + "#" + pd.getName() + "] invalid " + (pd.getRequiredType() == PropertyType.REFERENCE ? "REFERENCE" : "WEAKREFERENCE") + " value constraint '" + ntName + "' (unknown node type)";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
}
}
}
}
// validate child-node definitions
QNodeDefinition[] cnda = ntd.getChildNodeDefs();
for (QNodeDefinition cnd : cnda) {
/**
* sanity check:
* make sure declaring node type matches name of node type definition
*/
if (!name.equals(cnd.getDeclaringNodeType())) {
String msg = "[" + name + "#" + cnd.getName() + "] invalid declaring node type specified";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
checkNamespace(cnd.getName(), nsReg);
// check that auto-created child-nodes specify a name
if (cnd.definesResidual() && cnd.isAutoCreated()) {
String msg = "[" + name + "#" + cnd.getName() + "] auto-created child-nodes must specify a name";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
// check that auto-created child-nodes specify a default primary type
if (cnd.getDefaultPrimaryType() == null && cnd.isAutoCreated()) {
String msg = "[" + name + "#" + cnd.getName() + "] auto-created child-nodes must specify a default primary type";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
// check default primary type
Name dpt = cnd.getDefaultPrimaryType();
checkNamespace(dpt, nsReg);
boolean referenceToSelf = false;
EffectiveNodeType defaultENT = null;
if (dpt != null) {
// check if this node type specifies itself as default primary type
if (name.equals(dpt)) {
referenceToSelf = true;
}
/**
* the default primary type must be registered, with one notable
* exception: the node type just being registered
*/
if (!name.equals(dpt) && !ntdCache.containsKey(dpt)) {
String msg = "[" + name + "#" + cnd.getName() + "] invalid default primary type '" + dpt + "'";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
/**
* build effective (i.e. merged and resolved) node type from
* default primary type and check for conflicts
*/
try {
if (!referenceToSelf) {
defaultENT = getEffectiveNodeType(dpt, entCache, ntdCache);
} else {
/**
* the default primary type is identical with the node
* type just being registered; we have to instantiate it
* 'manually'
*/
ent = EffectiveNodeType.create(ntd, entCache, ntdCache);
defaultENT = ent;
}
if (cnd.isAutoCreated()) {
/**
* check for circularity through default primary types
* of auto-created child nodes (node type 'a' defines
* auto-created child node with default primary type 'a')
*/
Stack<Name> definingNTs = new Stack<Name>();
definingNTs.push(name);
checkForCircularNodeAutoCreation(defaultENT, definingNTs, entCache, ntdCache);
}
} catch (NodeTypeConflictException ntce) {
String msg = "[" + name + "#" + cnd.getName() + "] failed to validate default primary type";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg, ntce);
} catch (NoSuchNodeTypeException nsnte) {
String msg = "[" + name + "#" + cnd.getName() + "] failed to validate default primary type";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg, nsnte);
}
}
// check required primary types
Name[] reqTypes = cnd.getRequiredPrimaryTypes();
if (reqTypes != null && reqTypes.length > 0) {
for (Name rpt : reqTypes) {
// skip nt:base required types
if (NameConstants.NT_BASE.equals(rpt)) {
continue;
}
checkNamespace(rpt, nsReg);
referenceToSelf = false;
/**
* check if this node type specifies itself as required
* primary type
*/
if (name.equals(rpt)) {
referenceToSelf = true;
}
/**
* the required primary type must be registered, with one
* notable exception: the node type just being registered
*/
if (!name.equals(rpt) && !ntdCache.containsKey(rpt)) {
String msg = "[" + name + "#" + cnd.getName() + "] invalid required primary type: " + rpt;
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
/**
* check if default primary type satisfies the required
* primary type constraint
*/
if (defaultENT != null && !defaultENT.includesNodeType(rpt)) {
String msg = "[" + name + "#" + cnd.getName() + "] default primary type does not satisfy required primary type constraint " + rpt;
log.debug(msg);
throw new InvalidNodeTypeDefException(msg);
}
/**
* build effective (i.e. merged and resolved) node type from
* required primary type constraint and check for conflicts
*/
try {
if (!referenceToSelf) {
getEffectiveNodeType(rpt, entCache, ntdCache);
} else {
/**
* the required primary type is identical with the
* node type just being registered; we have to
* instantiate it 'manually'
*/
if (ent == null) {
ent = EffectiveNodeType.create(ntd, entCache, ntdCache);
}
}
} catch (NodeTypeConflictException ntce) {
String msg = "[" + name + "#" + cnd.getName() + "] failed to validate required primary type constraint";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg, ntce);
} catch (NoSuchNodeTypeException nsnte) {
String msg = "[" + name + "#" + cnd.getName() + "] failed to validate required primary type constraint";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg, nsnte);
}
}
}
}
/**
* now build effective (i.e. merged and resolved) node type from
* this node type definition; this will potentially detect more
* conflicts or problems
*/
if (ent == null) {
try {
ent = EffectiveNodeType.create(ntd, entCache, ntdCache);
} catch (NodeTypeConflictException ntce) {
String msg = "[" + name + "] failed to resolve node type definition";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg, ntce);
} catch (NoSuchNodeTypeException nsnte) {
String msg = "[" + name + "] failed to resolve node type definition";
log.debug(msg);
throw new InvalidNodeTypeDefException(msg, nsnte);
}
}
return ent;
}
use of org.apache.jackrabbit.spi.QNodeDefinition in project jackrabbit by apache.
the class NodeTypeReader method getNodeTypeDef.
/**
* Returns the node type definition specified by the current element.
*
* @return node type definition
* @throws InvalidNodeTypeDefException if the definition is invalid
* @throws NameException if the definition contains an
* illegal name
* @throws NamespaceException if a namespace is not defined
*/
private QNodeTypeDefinition getNodeTypeDef() throws InvalidNodeTypeDefException, NameException, NamespaceException {
QNodeTypeDefinitionBuilder type = new QNodeTypeDefinitionBuilder();
type.setName(resolver.getQName(walker.getAttribute(Constants.NAME_ATTRIBUTE)));
type.setMixin(Boolean.valueOf(walker.getAttribute(Constants.ISMIXIN_ATTRIBUTE)));
type.setOrderableChildNodes(Boolean.valueOf(walker.getAttribute(Constants.HASORDERABLECHILDNODES_ATTRIBUTE)));
type.setAbstract(Boolean.valueOf(walker.getAttribute(Constants.ISABSTRACT_ATTRIBUTE)));
if (walker.getAttribute(Constants.ISQUERYABLE_ATTRIBUTE) != null) {
type.setQueryable(Boolean.valueOf(walker.getAttribute(Constants.ISQUERYABLE_ATTRIBUTE)));
}
String primaryItemName = walker.getAttribute(Constants.PRIMARYITEMNAME_ATTRIBUTE);
if (primaryItemName != null && primaryItemName.length() > 0) {
type.setPrimaryItemName(resolver.getQName(primaryItemName));
}
// supertype declarations
if (walker.enterElement(Constants.SUPERTYPES_ELEMENT)) {
List<Name> supertypes = new ArrayList<Name>();
while (walker.iterateElements(Constants.SUPERTYPE_ELEMENT)) {
supertypes.add(resolver.getQName(walker.getContent()));
}
type.setSupertypes(supertypes.toArray(new Name[supertypes.size()]));
walker.leaveElement();
}
// property definitions
List<QPropertyDefinition> properties = new ArrayList<QPropertyDefinition>();
while (walker.iterateElements(Constants.PROPERTYDEFINITION_ELEMENT)) {
QPropertyDefinitionBuilder def = getPropDef();
def.setDeclaringNodeType(type.getName());
properties.add(def.build());
}
type.setPropertyDefs(properties.toArray(new QPropertyDefinition[properties.size()]));
// child node definitions
List<QNodeDefinition> nodes = new ArrayList<QNodeDefinition>();
while (walker.iterateElements(Constants.CHILDNODEDEFINITION_ELEMENT)) {
QNodeDefinitionBuilder def = getChildNodeDef();
def.setDeclaringNodeType(type.getName());
nodes.add(def.build());
}
type.setChildNodeDefs(nodes.toArray(new QNodeDefinition[nodes.size()]));
return type.build();
}
use of org.apache.jackrabbit.spi.QNodeDefinition in project jackrabbit by apache.
the class EffectiveNodeType method internalMerge.
/**
* Internal helper method which merges another <code>EffectiveNodeType</code>
* instance with <i>this</i> instance.
* <p>
* Warning: This instance might be in an inconsistent state if an exception
* is thrown.
*
* @param other
* @param supertype true if the merge is a result of inheritance, i.e. <code>other</code>
* represents one or more supertypes of this instance; otherwise false, i.e.
* the merge is the result of an explicit aggregation
* @throws NodeTypeConflictException
*/
private synchronized void internalMerge(EffectiveNodeType other, boolean supertype) throws NodeTypeConflictException {
Name[] nta = other.getAllNodeTypes();
int includedCount = 0;
for (Name aNta : nta) {
if (includesNodeType(aNta)) {
// redundant node type
log.debug("node type '" + aNta + "' is already contained.");
includedCount++;
}
}
if (includedCount == nta.length) {
// total overlap, ignore
return;
}
// named item definitions
QItemDefinition[] defs = other.getNamedItemDefs();
for (QItemDefinition def : defs) {
if (includesNodeType(def.getDeclaringNodeType())) {
// ignore redundant definitions
continue;
}
Name name = def.getName();
List<QItemDefinition> existingDefs = namedItemDefs.get(name);
if (existingDefs != null) {
if (existingDefs.size() > 0) {
// there already exists at least one definition with that name
for (QItemDefinition existingDef : existingDefs) {
// make sure none of them is auto-create
if (def.isAutoCreated() || existingDef.isAutoCreated()) {
// conflict
String msg = "The item definition for '" + name + "' in node type '" + def.getDeclaringNodeType() + "' conflicts with node type '" + existingDef.getDeclaringNodeType() + "': name collision with auto-create definition";
log.debug(msg);
throw new NodeTypeConflictException(msg);
}
// check ambiguous definitions
if (def.definesNode() == existingDef.definesNode()) {
if (!def.definesNode()) {
// property definition
QPropertyDefinition pd = (QPropertyDefinition) def;
QPropertyDefinition epd = (QPropertyDefinition) existingDef;
// compare type & multiValued flag
if (pd.getRequiredType() == epd.getRequiredType() && pd.isMultiple() == epd.isMultiple()) {
// conflict
String msg = "The property definition for '" + name + "' in node type '" + def.getDeclaringNodeType() + "' conflicts with node type '" + existingDef.getDeclaringNodeType() + "': ambiguous property definition";
log.debug(msg);
throw new NodeTypeConflictException(msg);
}
} else {
// child node definition
// conflict
String msg = "The child node definition for '" + name + "' in node type '" + def.getDeclaringNodeType() + "' conflicts with node type '" + existingDef.getDeclaringNodeType() + "': ambiguous child node definition";
log.debug(msg);
throw new NodeTypeConflictException(msg);
}
}
}
}
} else {
existingDefs = new ArrayList<QItemDefinition>();
namedItemDefs.put(name, existingDefs);
}
existingDefs.add(def);
}
// residual item definitions
defs = other.getUnnamedItemDefs();
for (QItemDefinition def : defs) {
if (includesNodeType(def.getDeclaringNodeType())) {
// ignore redundant definitions
continue;
}
for (QItemDefinition existing : unnamedItemDefs) {
// compare with existing definition
if (def.definesNode() == existing.definesNode()) {
if (!def.definesNode()) {
// property definition
QPropertyDefinition pd = (QPropertyDefinition) def;
QPropertyDefinition epd = (QPropertyDefinition) existing;
// compare type & multiValued flag
if (pd.getRequiredType() == epd.getRequiredType() && pd.isMultiple() == epd.isMultiple()) {
// conflict
String msg = "A property definition in node type '" + def.getDeclaringNodeType() + "' conflicts with node type '" + existing.getDeclaringNodeType() + "': ambiguous residual property definition";
log.debug(msg);
throw new NodeTypeConflictException(msg);
}
} else {
// child node definition
QNodeDefinition nd = (QNodeDefinition) def;
QNodeDefinition end = (QNodeDefinition) existing;
// compare required & default primary types
if (Arrays.equals(nd.getRequiredPrimaryTypes(), end.getRequiredPrimaryTypes()) && (nd.getDefaultPrimaryType() == null ? end.getDefaultPrimaryType() == null : nd.getDefaultPrimaryType().equals(end.getDefaultPrimaryType()))) {
// conflict
String msg = "A child node definition in node type '" + def.getDeclaringNodeType() + "' conflicts with node type '" + existing.getDeclaringNodeType() + "': ambiguous residual child node definition";
log.debug(msg);
throw new NodeTypeConflictException(msg);
}
}
}
}
unnamedItemDefs.add(def);
}
allNodeTypes.addAll(Arrays.asList(nta));
if (supertype) {
// implicit merge as result of inheritance
// add other merged node types as supertypes
nta = other.getMergedNodeTypes();
inheritedNodeTypes.addAll(Arrays.asList(nta));
// add supertypes of other merged node types as supertypes
nta = other.getInheritedNodeTypes();
inheritedNodeTypes.addAll(Arrays.asList(nta));
} else {
// explicit merge
// merge with other merged node types
nta = other.getMergedNodeTypes();
mergedNodeTypes.addAll(Arrays.asList(nta));
// add supertypes of other merged node types as supertypes
nta = other.getInheritedNodeTypes();
inheritedNodeTypes.addAll(Arrays.asList(nta));
}
// update 'orderable child nodes' attribute value (JCR-1947)
if (other.hasOrderableChildNodes()) {
orderableChildNodes = true;
}
// update 'primary item' attribute value (JCR-1947)
if (primaryItemName == null && other.getPrimaryItemName() != null) {
primaryItemName = other.getPrimaryItemName();
}
}
use of org.apache.jackrabbit.spi.QNodeDefinition in project jackrabbit by apache.
the class NodeTypeWriter method addNodeTypeDef.
/**
* Builds a node type definition element under the current element.
*
* @param def node type definition
* @throws RepositoryException if the default property values
* cannot be serialized
* @throws NamespaceException if the node type definition contains
* invalid namespace references
*/
private void addNodeTypeDef(QNodeTypeDefinition def) throws NamespaceException, RepositoryException {
builder.startElement(Constants.NODETYPE_ELEMENT);
// simple attributes
builder.setAttribute(Constants.NAME_ATTRIBUTE, resolver.getJCRName(def.getName()));
builder.setAttribute(Constants.ISMIXIN_ATTRIBUTE, def.isMixin());
builder.setAttribute(Constants.ISQUERYABLE_ATTRIBUTE, def.isQueryable());
builder.setAttribute(Constants.ISABSTRACT_ATTRIBUTE, def.isAbstract());
builder.setAttribute(Constants.HASORDERABLECHILDNODES_ATTRIBUTE, def.hasOrderableChildNodes());
// primary item name
Name item = def.getPrimaryItemName();
if (item != null) {
builder.setAttribute(Constants.PRIMARYITEMNAME_ATTRIBUTE, resolver.getJCRName(item));
} else {
builder.setAttribute(Constants.PRIMARYITEMNAME_ATTRIBUTE, "");
}
// supertype declarations
Name[] supertypes = def.getSupertypes();
if (supertypes.length > 0) {
builder.startElement(Constants.SUPERTYPES_ELEMENT);
for (Name supertype : supertypes) {
builder.addContentElement(Constants.SUPERTYPE_ELEMENT, resolver.getJCRName(supertype));
}
builder.endElement();
}
// property definitions
QPropertyDefinition[] properties = def.getPropertyDefs();
for (QPropertyDefinition property : properties) {
addPropDef(property);
}
// child node definitions
QNodeDefinition[] nodes = def.getChildNodeDefs();
for (QNodeDefinition node : nodes) {
addChildNodeDef(node);
}
builder.endElement();
}
Aggregations