/*
* Copyright 2003, 2004 The Apache Software Foundation
*
* 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.apache.ws.jaxme.generator.sg.impl;
import java.util.ArrayList;
import java.util.List;
import org.apache.ws.jaxme.generator.sg.ComplexContentSG;
import org.apache.ws.jaxme.generator.sg.ComplexContentSGChain;
import org.apache.ws.jaxme.generator.sg.ComplexTypeSG;
import org.apache.ws.jaxme.generator.sg.GroupSG;
import org.apache.ws.jaxme.generator.sg.ObjectSG;
import org.apache.ws.jaxme.generator.sg.ParticleSG;
import org.apache.ws.jaxme.generator.sg.PropertySG;
import org.apache.ws.jaxme.js.JavaSource;
import org.apache.ws.jaxme.logging.Logger;
import org.apache.ws.jaxme.logging.LoggerAccess;
import org.apache.ws.jaxme.xs.XSGroup;
import org.apache.ws.jaxme.xs.XSParticle;
import org.apache.ws.jaxme.xs.XSType;
import org.apache.ws.jaxme.xs.types.XSAnyType;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/** Default implementation of
* {@link org.apache.ws.jaxme.generator.sg.ComplexContentSG}.
*/
public class JAXBComplexContentTypeSG implements ComplexContentSGChain {
private static final Logger log = LoggerAccess.getLogger(JAXBComplexContentTypeSG.class);
private static class Particle {
private final GroupSG[] stack;
private final ParticleSG particle;
Particle(GroupSG[] pStack, ParticleSG pParticle) {
stack = pStack;
particle = pParticle;
}
ParticleSG getParticleSG() { return particle; }
Object[] getStack() { return stack; }
GroupSG getClosestCommonAnchestor(Particle pParticle) {
int len = Math.min(stack.length, pParticle.stack.length);
GroupSG anchestor = stack[0];
for (int i = 1; i < len; i++) {
if (stack[i] != pParticle.stack[i]) {
break;
}
}
return anchestor;
}
}
private final GroupSG groupSG;
private final boolean isEmpty, isMixed;
private boolean isInitialized;
private final Locator locator;
private final List elementParticles = new ArrayList();
private Particle[] elementParticleArray;
private final List mixedContentParticles = new ArrayList();
private final List groupParticlesWithMultiplicityGreaterOne = new ArrayList();
private final List wildcardParticles = new ArrayList();
private final List stack = new ArrayList();
protected JAXBComplexContentTypeSG(ComplexTypeSG pComplexTypeSG, XSType pType) throws SAXException {
if (pType == XSAnyType.getInstance()) {
throw new SAXException("The type xs:anyType is not supported.");
}
locator = pType.getLocator();
if (pType.getComplexType().isEmpty()) {
groupSG = null;
isEmpty = true;
isMixed = false;
} else {
XSParticle particle = pType.getComplexType().getParticle();
if (particle == null) {
throw new NullPointerException("Missing group particle for type = " + pType.getName());
}
if (particle.isGroup()) {
XSGroup group = particle.getGroup();
groupSG = pComplexTypeSG.getTypeSG().getFactory().getGroupSG(group, pComplexTypeSG.getClassContext());
} else {
throw new IllegalStateException("Expected internal group");
}
isEmpty = false;
isMixed = pType.getComplexType().isMixed();
}
}
public Locator getLocator(ComplexContentSG pController) { return locator; }
public void init(ComplexContentSG pController) throws SAXException {
}
private void initialize(ComplexContentSG pController) throws SAXException {
if (!isInitialized && !isEmpty) {
isInitialized = true;
verify(pController);
}
}
private Particle[] getLocalElementParticles(ComplexContentSG pController)
throws SAXException {
initialize(pController);
return elementParticleArray;
}
public ParticleSG[] getElementParticles(ComplexContentSG pController)
throws SAXException {
Particle[] particles = getLocalElementParticles(pController);
if (particles == null) {
return new ParticleSG[0];
}
ParticleSG[] result = new ParticleSG[particles.length];
for (int i = 0; i < result.length; i++) {
result[i] = particles[i].getParticleSG();
}
return result;
}
public GroupSG getGroupSG(ComplexContentSG pController) {
return groupSG;
}
public boolean isEmpty(ComplexContentSG pController) {
return isEmpty;
}
public boolean isMixed(ComplexContentSG pController) {
return isMixed;
}
private void findParticles(GroupSG[] pStack, ParticleSG pParticle) throws SAXException {
if (pParticle.isGroup()) {
if (pParticle.getMaxOccurs() > 1) {
groupParticlesWithMultiplicityGreaterOne.add(new Particle(pStack, pParticle));
} else {
findParticles(pParticle.getGroupSG());
}
} else if (pParticle.isWildcard()) {
wildcardParticles.add(new Particle(pStack, pParticle));
} else if (pParticle.isElement()) {
Particle p = new Particle(pStack, pParticle);
elementParticles.add(p);
} else {
throw new SAXParseException("Invalid particle type", pParticle.getLocator());
}
}
private void findParticles(GroupSG pGroup) throws SAXException {
stack.add(pGroup);
ParticleSG[] particles = pGroup.getParticles();
GroupSG[] groups = (GroupSG[]) stack.toArray(new GroupSG[stack.size()]);
for (int i = 0; i < particles.length; i++) {
findParticles(groups, particles[i]);
}
stack.remove(stack.size()-1);
}
/** Verifies the contents of a complex type with complex content, according
* to the JAXB 1.0 specification, 5.9.7.
*/
private void verify(ComplexContentSG pController) throws SAXException {
findParticles(pController.getGroupSG());
/* 5.9.7, 1) If {content type} is mixed, bind the entire content model
* to a general content property with the content-property name "content".
* See Section 5.9.4, "Bind mixed content" for more details.
*/
if (pController.isMixed()) {
throw new SAXParseException("Mixed content is not yet supported.",
pController.getLocator());
}
/* 5.9.7, 2) If (1) a particle has {max occurs} > 1 and (2) its term
* is a model group, then that particle and its descendants are mapped
* to one general content property that represents them. See Section
* 5.9.6, "Bind a repeating occurrence model group" for details.
*/
if (groupParticlesWithMultiplicityGreaterOne.size() > 0) {
Particle particle = (Particle) groupParticlesWithMultiplicityGreaterOne.get(0);
throw new SAXParseException("Model groups with maxOccurs > 1 are not yet supported.",
particle.getParticleSG().getLocator());
}
/* 5.9.7, 3) Process all remaining particles 1) whose {term} are
* wildcard particles and (2) that did not belong to a repeating
* occurrence model group bound in step 2. If there is only one
* wildcard, bind it as specified in Section 5.9.5, "Bind wildcard
* schema component". If there is more than one, then fallback to
* representing the entire content model as a single general
* content property.
*/
if (wildcardParticles.size() > 0) {
Particle particle = (Particle) wildcardParticles.get(0);
throw new SAXParseException("Wildcards are unsupported",
particle.getParticleSG().getLocator());
}
/* 5.9.7, 4) Process all particles (1) whose {term} are element
* declarations and (2) that do not belong to a repeating occurrence
* model group bound in step 2
*
* First we say a particle has a label L, if it refers to an element
* declaration whose {name} is L. Then, for all the possible pair of
* particles P and Q in this set, ensure the following constraints are
* met:
* a) If P and Q have the same label, then they must refer to the same
* element declaration.
* b) If P and Q refer to the same element reference, then its closest
* common ancestor particle may not have sequence as its {term}.
*
* If either of the above constraints are violated, then the binding
* compiler must report a property naming collision, that can be
* corrected via customization.
*/
elementParticleArray = (Particle[]) elementParticles.toArray(new Particle[elementParticles.size()]);
for (int i = 0; i < elementParticleArray.length; i++) {
Particle pParticle = elementParticleArray[i];
ParticleSG p = pParticle.getParticleSG();
String name = p.getPropertySG().getXMLFieldName();
for (int j = i+1; j < elementParticles.size(); j++) {
Particle qParticle = elementParticleArray[j];
ParticleSG q = qParticle.getParticleSG();
if (name.equals(q.getPropertySG().getXMLFieldName())) {
ObjectSG pObject = p.getObjectSG();
ObjectSG qObject = q.getObjectSG();
if (!pObject.isGlobal() || !qObject.isGlobal() ||
pObject != qObject) {
throw new SAXParseException("Multiple element particles named " + name
+ ", which aren't references to "
+ " a common global element, are present in a common"
+ " complex type. (JAXB 1.0, 5.9.7.4.a) Use jaxb:property/@name"
+ " for customization.", pController.getLocator());
}
GroupSG group = pParticle.getClosestCommonAnchestor(pParticle);
if (group.isSequence()) {
throw new SAXParseException("Multiple element particles named " + name
+ " are contained in a common sequence."
+ " (JAXB 1.0, 5.9.7.4.b) Use jaxb:property/@name"
+ " for customization.", pController.getLocator());
}
}
}
}
}
public void generateProperties(ComplexContentSG pController, JavaSource pSource)
throws SAXException {
final String mName = "generateProperties(JavaSource)";
log.finest(mName, "->", pSource.getQName());
if (isEmpty) {
return;
}
Particle[] particles = getLocalElementParticles(pController);
for (int i = 0; i < elementParticleArray.length; i++) {
ParticleSG particle = particles[i].getParticleSG();
PropertySG elementSG = particle.getPropertySG();
elementSG.generate(pSource);
}
log.finest(mName, "<-");
}
}