|
|
|
|
|
*/ |
|
/* |
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
* contributor license agreements. See the NOTICE file distributed with |
|
* this work for additional information regarding copyright ownership. |
|
* The ASF licenses this file to You 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 com.sun.org.apache.xerces.internal.impl.xs.models; |
|
|
|
import com.sun.org.apache.xerces.internal.impl.dtd.models.CMNode; |
|
import com.sun.org.apache.xerces.internal.impl.xs.SchemaSymbols; |
|
import com.sun.org.apache.xerces.internal.impl.xs.XSComplexTypeDecl; |
|
import com.sun.org.apache.xerces.internal.impl.xs.XSDeclarationPool; |
|
import com.sun.org.apache.xerces.internal.impl.xs.XSElementDecl; |
|
import com.sun.org.apache.xerces.internal.impl.xs.XSModelGroupImpl; |
|
import com.sun.org.apache.xerces.internal.impl.xs.XSParticleDecl; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class CMBuilder { |
|
|
|
|
|
private XSDeclarationPool fDeclPool = null; |
|
|
|
|
|
private static XSEmptyCM fEmptyCM = new XSEmptyCM(); |
|
|
|
|
|
private int fLeafCount; |
|
|
|
private int fParticleCount; |
|
|
|
private CMNodeFactory fNodeFactory ; |
|
|
|
public CMBuilder(CMNodeFactory nodeFactory) { |
|
fDeclPool = null; |
|
fNodeFactory = nodeFactory ; |
|
} |
|
|
|
public void setDeclPool(XSDeclarationPool declPool) { |
|
fDeclPool = declPool; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public XSCMValidator getContentModel(XSComplexTypeDecl typeDecl, boolean forUPA) { |
|
|
|
// for complex type with empty or simple content, |
|
|
|
short contentType = typeDecl.getContentType(); |
|
if (contentType == XSComplexTypeDecl.CONTENTTYPE_SIMPLE || |
|
contentType == XSComplexTypeDecl.CONTENTTYPE_EMPTY) { |
|
return null; |
|
} |
|
|
|
XSParticleDecl particle = (XSParticleDecl)typeDecl.getParticle(); |
|
|
|
// if the content is element only or mixed, but no particle |
|
|
|
if (particle == null) |
|
return fEmptyCM; |
|
|
|
// if the content model contains "all" model group, |
|
|
|
XSCMValidator cmValidator = null; |
|
if (particle.fType == XSParticleDecl.PARTICLE_MODELGROUP && |
|
((XSModelGroupImpl)particle.fValue).fCompositor == XSModelGroupImpl.MODELGROUP_ALL) { |
|
cmValidator = createAllCM(particle); |
|
} |
|
else { |
|
cmValidator = createDFACM(particle, forUPA); |
|
} |
|
|
|
//now we are throught building content model and have passed sucessfully of the nodecount check |
|
|
|
fNodeFactory.resetNodeCount() ; |
|
|
|
// if the validator returned is null, it means there is nothing in |
|
|
|
if (cmValidator == null) |
|
cmValidator = fEmptyCM; |
|
|
|
return cmValidator; |
|
} |
|
|
|
XSCMValidator createAllCM(XSParticleDecl particle) { |
|
if (particle.fMaxOccurs == 0) |
|
return null; |
|
|
|
|
|
XSModelGroupImpl group = (XSModelGroupImpl)particle.fValue; |
|
// create an all content model. the parameter indicates whether |
|
|
|
XSAllCM allContent = new XSAllCM(particle.fMinOccurs == 0, group.fParticleCount); |
|
for (int i = 0; i < group.fParticleCount; i++) { |
|
|
|
allContent.addElement((XSElementDecl)group.fParticles[i].fValue, |
|
group.fParticles[i].fMinOccurs == 0); |
|
} |
|
return allContent; |
|
} |
|
|
|
XSCMValidator createDFACM(XSParticleDecl particle, boolean forUPA) { |
|
fLeafCount = 0; |
|
fParticleCount = 0; |
|
|
|
CMNode node = useRepeatingLeafNodes(particle) ? buildCompactSyntaxTree(particle) : buildSyntaxTree(particle, forUPA, true); |
|
if (node == null) |
|
return null; |
|
|
|
return new XSDFACM(node, fLeafCount); |
|
} |
|
|
|
// 1. convert particle tree to CM tree: |
|
// 2. expand all occurrence values: a{n, unbounded} -> a, a, ..., a+ |
|
// a{n, m} -> a, a, ..., a?, a?, ... |
|
// 3. convert model groups (a, b, c, ...) or (a | b | c | ...) to |
|
// binary tree: (((a,b),c),...) or (((a|b)|c)|...) |
|
|
|
private CMNode buildSyntaxTree(XSParticleDecl particle, boolean forUPA, boolean optimize) { |
|
|
|
int maxOccurs = particle.fMaxOccurs; |
|
int minOccurs = particle.fMinOccurs; |
|
|
|
boolean compactedForUPA = false; |
|
if (forUPA) { |
|
// When doing UPA, we reduce the size of the minOccurs/maxOccurs values to make |
|
|
|
if (minOccurs > 1) { |
|
if (maxOccurs > minOccurs || particle.getMaxOccursUnbounded()) { |
|
minOccurs = 1; |
|
compactedForUPA = true; |
|
} |
|
else { |
|
minOccurs = 2; |
|
compactedForUPA = true; |
|
} |
|
} |
|
if (maxOccurs > 1) { |
|
maxOccurs = 2; |
|
compactedForUPA = true; |
|
} |
|
} |
|
|
|
short type = particle.fType; |
|
CMNode nodeRet = null; |
|
|
|
if ((type == XSParticleDecl.PARTICLE_WILDCARD) || |
|
(type == XSParticleDecl.PARTICLE_ELEMENT)) { |
|
// (task 1) element and wildcard particles should be converted to |
|
// leaf nodes |
|
// REVISIT: Make a clone of the leaf particle, so that if there |
|
// are two references to the same group, we have two different |
|
// leaf particles for the same element or wildcard decl. |
|
|
|
nodeRet = fNodeFactory.getCMLeafNode(particle.fType, particle.fValue, fParticleCount++, fLeafCount++); |
|
|
|
nodeRet = expandContentModel(nodeRet, minOccurs, maxOccurs, optimize); |
|
if (nodeRet != null) { |
|
nodeRet.setIsCompactUPAModel(compactedForUPA); |
|
} |
|
} |
|
else if (type == XSParticleDecl.PARTICLE_MODELGROUP) { |
|
|
|
XSModelGroupImpl group = (XSModelGroupImpl)particle.fValue; |
|
CMNode temp = null; |
|
// when the model group is a choice of more than one particles, but |
|
// only one of the particle is not empty, (for example |
|
// <choice> |
|
// <sequence/> |
|
// <element name="e"/> |
|
// </choice> |
|
// ) we can't not return that one particle ("e"). instead, we should |
|
// treat such particle as optional ("e?"). |
|
// the following boolean variable is true when there are at least |
|
|
|
boolean twoChildren = false; |
|
for (int i = 0; i < group.fParticleCount; i++) { |
|
|
|
temp = buildSyntaxTree(group.fParticles[i], |
|
forUPA, |
|
optimize && |
|
minOccurs == 1 && maxOccurs == 1 && |
|
(group.fCompositor == XSModelGroupImpl.MODELGROUP_SEQUENCE || |
|
group.fParticleCount == 1)); |
|
|
|
if (temp != null) { |
|
compactedForUPA |= temp.isCompactedForUPA(); |
|
if (nodeRet == null) { |
|
nodeRet = temp; |
|
} |
|
else { |
|
nodeRet = fNodeFactory.getCMBinOpNode(group.fCompositor, nodeRet, temp); |
|
|
|
twoChildren = true; |
|
} |
|
} |
|
} |
|
|
|
if (nodeRet != null) { |
|
// when the group is "choice", there is only one non-empty |
|
// child, and the group had more than one children, we need |
|
// to create a zero-or-one (optional) node for the non-empty |
|
|
|
if (group.fCompositor == XSModelGroupImpl.MODELGROUP_CHOICE && |
|
!twoChildren && group.fParticleCount > 1) { |
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ZERO_OR_ONE, nodeRet); |
|
} |
|
nodeRet = expandContentModel(nodeRet, minOccurs, maxOccurs, false); |
|
nodeRet.setIsCompactUPAModel(compactedForUPA); |
|
} |
|
} |
|
|
|
return nodeRet; |
|
} |
|
|
|
// 2. expand all occurrence values: a{n, unbounded} -> a, a, ..., a+ |
|
// a{n, m} -> a, a, ..., a?, a?, ... |
|
|
|
private CMNode expandContentModel(CMNode node, |
|
int minOccurs, int maxOccurs, boolean optimize) { |
|
|
|
CMNode nodeRet = null; |
|
|
|
if (minOccurs==1 && maxOccurs==1) { |
|
nodeRet = node; |
|
} |
|
else if (minOccurs==0 && maxOccurs==1) { |
|
|
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ZERO_OR_ONE, node); |
|
} |
|
else if (minOccurs == 0 && maxOccurs==SchemaSymbols.OCCURRENCE_UNBOUNDED) { |
|
|
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ZERO_OR_MORE, node); |
|
} |
|
else if (minOccurs == 1 && maxOccurs==SchemaSymbols.OCCURRENCE_UNBOUNDED) { |
|
|
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ONE_OR_MORE, node); |
|
} |
|
else if (optimize && node.type() == XSParticleDecl.PARTICLE_ELEMENT || |
|
node.type() == XSParticleDecl.PARTICLE_WILDCARD) { |
|
// Only for elements and wildcards, subsume e{n,m} and e{n,unbounded} to e* |
|
// or e+ and, once the DFA reaches a final state, check if the actual number |
|
// of elements is between minOccurs and maxOccurs. This new algorithm runs |
|
// in constant space. |
|
|
|
|
|
nodeRet = fNodeFactory.getCMUniOpNode( |
|
minOccurs == 0 ? XSParticleDecl.PARTICLE_ZERO_OR_MORE |
|
: XSParticleDecl.PARTICLE_ONE_OR_MORE, node); |
|
nodeRet.setUserData(new int[] { minOccurs, maxOccurs }); |
|
} |
|
else if (maxOccurs == SchemaSymbols.OCCURRENCE_UNBOUNDED) { |
|
// => a,a,..,a+ |
|
// create a+ node first, then put minOccurs-1 a's in front of it |
|
// for the first time "node" is used, we don't need to make a copy |
|
|
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ONE_OR_MORE, node); |
|
// (task 4) we need to call copyNode here, so that we append |
|
// an entire new copy of the node (a subtree). this is to ensure |
|
// all leaf nodes have distinct position |
|
|
|
nodeRet = fNodeFactory.getCMBinOpNode(XSModelGroupImpl.MODELGROUP_SEQUENCE, |
|
multiNodes(node, minOccurs-1, true), nodeRet); |
|
} |
|
else { |
|
// {n,m} => a,a,a,...(a),(a),... |
|
// first n a's, then m-n a?'s. |
|
|
|
if (minOccurs > 0) { |
|
nodeRet = multiNodes(node, minOccurs, false); |
|
} |
|
if (maxOccurs > minOccurs) { |
|
node = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ZERO_OR_ONE, node); |
|
if (nodeRet == null) { |
|
nodeRet = multiNodes(node, maxOccurs-minOccurs, false); |
|
} |
|
else { |
|
nodeRet = fNodeFactory.getCMBinOpNode(XSModelGroupImpl.MODELGROUP_SEQUENCE, |
|
nodeRet, multiNodes(node, maxOccurs-minOccurs, true)); |
|
} |
|
} |
|
} |
|
|
|
return nodeRet; |
|
} |
|
|
|
private CMNode multiNodes(CMNode node, int num, boolean copyFirst) { |
|
if (num == 0) { |
|
return null; |
|
} |
|
if (num == 1) { |
|
return copyFirst ? copyNode(node) : node; |
|
} |
|
int num1 = num/2; |
|
return fNodeFactory.getCMBinOpNode(XSModelGroupImpl.MODELGROUP_SEQUENCE, |
|
multiNodes(node, num1, copyFirst), |
|
multiNodes(node, num-num1, true)); |
|
} |
|
|
|
|
|
private CMNode copyNode(CMNode node) { |
|
int type = node.type(); |
|
|
|
if (type == XSModelGroupImpl.MODELGROUP_CHOICE || |
|
type == XSModelGroupImpl.MODELGROUP_SEQUENCE) { |
|
XSCMBinOp bin = (XSCMBinOp)node; |
|
node = fNodeFactory.getCMBinOpNode(type, copyNode(bin.getLeft()), |
|
copyNode(bin.getRight())); |
|
} |
|
|
|
else if (type == XSParticleDecl.PARTICLE_ZERO_OR_MORE || |
|
type == XSParticleDecl.PARTICLE_ONE_OR_MORE || |
|
type == XSParticleDecl.PARTICLE_ZERO_OR_ONE) { |
|
XSCMUniOp uni = (XSCMUniOp)node; |
|
node = fNodeFactory.getCMUniOpNode(type, copyNode(uni.getChild())); |
|
} |
|
// for element/wildcard (leaf), make a new leaf node, |
|
|
|
else if (type == XSParticleDecl.PARTICLE_ELEMENT || |
|
type == XSParticleDecl.PARTICLE_WILDCARD) { |
|
XSCMLeaf leaf = (XSCMLeaf)node; |
|
node = fNodeFactory.getCMLeafNode(leaf.type(), leaf.getLeaf(), leaf.getParticleId(), fLeafCount++); |
|
} |
|
|
|
return node; |
|
} |
|
|
|
// A special version of buildSyntaxTree() which builds a compact syntax tree |
|
// containing compound leaf nodes which carry occurence information. This method |
|
// for building the syntax tree is chosen over buildSyntaxTree() when |
|
|
|
private CMNode buildCompactSyntaxTree(XSParticleDecl particle) { |
|
int maxOccurs = particle.fMaxOccurs; |
|
int minOccurs = particle.fMinOccurs; |
|
short type = particle.fType; |
|
CMNode nodeRet = null; |
|
|
|
if ((type == XSParticleDecl.PARTICLE_WILDCARD) || |
|
(type == XSParticleDecl.PARTICLE_ELEMENT)) { |
|
return buildCompactSyntaxTree2(particle, minOccurs, maxOccurs); |
|
} |
|
else if (type == XSParticleDecl.PARTICLE_MODELGROUP) { |
|
XSModelGroupImpl group = (XSModelGroupImpl)particle.fValue; |
|
if (group.fParticleCount == 1 && (minOccurs != 1 || maxOccurs != 1)) { |
|
return buildCompactSyntaxTree2(group.fParticles[0], minOccurs, maxOccurs); |
|
} |
|
else { |
|
CMNode temp = null; |
|
|
|
// when the model group is a choice of more than one particles, but |
|
// only one of the particle is not empty, (for example |
|
// <choice> |
|
// <sequence/> |
|
// <element name="e"/> |
|
// </choice> |
|
// ) we can't not return that one particle ("e"). instead, we should |
|
// treat such particle as optional ("e?"). |
|
|
|
int count = 0; |
|
for (int i = 0; i < group.fParticleCount; i++) { |
|
|
|
temp = buildCompactSyntaxTree(group.fParticles[i]); |
|
|
|
if (temp != null) { |
|
++count; |
|
if (nodeRet == null) { |
|
nodeRet = temp; |
|
} |
|
else { |
|
nodeRet = fNodeFactory.getCMBinOpNode(group.fCompositor, nodeRet, temp); |
|
} |
|
} |
|
} |
|
if (nodeRet != null) { |
|
// when the group is "choice" and the group has one or more empty children, |
|
|
|
if (group.fCompositor == XSModelGroupImpl.MODELGROUP_CHOICE && count < group.fParticleCount) { |
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ZERO_OR_ONE, nodeRet); |
|
} |
|
} |
|
} |
|
} |
|
return nodeRet; |
|
} |
|
|
|
private CMNode buildCompactSyntaxTree2(XSParticleDecl particle, int minOccurs, int maxOccurs) { |
|
|
|
CMNode nodeRet = null; |
|
if (minOccurs == 1 && maxOccurs == 1) { |
|
nodeRet = fNodeFactory.getCMLeafNode(particle.fType, particle.fValue, fParticleCount++, fLeafCount++); |
|
} |
|
else if (minOccurs == 0 && maxOccurs == 1) { |
|
|
|
nodeRet = fNodeFactory.getCMLeafNode(particle.fType, particle.fValue, fParticleCount++, fLeafCount++); |
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ZERO_OR_ONE, nodeRet); |
|
} |
|
else if (minOccurs == 0 && maxOccurs==SchemaSymbols.OCCURRENCE_UNBOUNDED) { |
|
|
|
nodeRet = fNodeFactory.getCMLeafNode(particle.fType, particle.fValue, fParticleCount++, fLeafCount++); |
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ZERO_OR_MORE, nodeRet); |
|
} |
|
else if (minOccurs == 1 && maxOccurs==SchemaSymbols.OCCURRENCE_UNBOUNDED) { |
|
|
|
nodeRet = fNodeFactory.getCMLeafNode(particle.fType, particle.fValue, fParticleCount++, fLeafCount++); |
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ONE_OR_MORE, nodeRet); |
|
} |
|
else { |
|
// {n,m}: Instead of expanding this out, create a compound leaf node which carries the |
|
|
|
nodeRet = fNodeFactory.getCMRepeatingLeafNode(particle.fType, particle.fValue, minOccurs, maxOccurs, fParticleCount++, fLeafCount++); |
|
if (minOccurs == 0) { |
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ZERO_OR_MORE, nodeRet); |
|
} |
|
else { |
|
nodeRet = fNodeFactory.getCMUniOpNode(XSParticleDecl.PARTICLE_ONE_OR_MORE, nodeRet); |
|
} |
|
} |
|
return nodeRet; |
|
} |
|
|
|
// This method checks if this particle can be transformed into a compact syntax |
|
// tree containing compound leaf nodes which carry occurence information. Currently |
|
// it returns true if each model group has minOccurs/maxOccurs == 1 or |
|
|
|
private boolean useRepeatingLeafNodes(XSParticleDecl particle) { |
|
int maxOccurs = particle.fMaxOccurs; |
|
int minOccurs = particle.fMinOccurs; |
|
short type = particle.fType; |
|
|
|
if (type == XSParticleDecl.PARTICLE_MODELGROUP) { |
|
XSModelGroupImpl group = (XSModelGroupImpl) particle.fValue; |
|
if (minOccurs != 1 || maxOccurs != 1) { |
|
if (group.fParticleCount == 1) { |
|
XSParticleDecl particle2 = group.fParticles[0]; |
|
short type2 = particle2.fType; |
|
return ((type2 == XSParticleDecl.PARTICLE_ELEMENT || |
|
type2 == XSParticleDecl.PARTICLE_WILDCARD) && |
|
particle2.fMinOccurs == 1 && |
|
particle2.fMaxOccurs == 1); |
|
} |
|
return (group.fParticleCount == 0); |
|
} |
|
for (int i = 0; i < group.fParticleCount; ++i) { |
|
if (!useRepeatingLeafNodes(group.fParticles[i])) { |
|
return false; |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
} |