blob: cb9e31120ef92bc8c400ab1b530dbc388ee17d42 [file] [log] [blame]
/*
* Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package com.sun.tools.xjc.reader.xmlschema;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import com.sun.tools.xjc.model.CClassInfo;
import com.sun.tools.xjc.model.CPropertyInfo;
import com.sun.tools.xjc.model.CReferencePropertyInfo;
import com.sun.tools.xjc.reader.xmlschema.bindinfo.BIProperty;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSModelGroup;
import com.sun.xml.xsom.XSModelGroupDecl;
import com.sun.xml.xsom.XSParticle;
import com.sun.xml.xsom.XSTerm;
import com.sun.xml.xsom.XSWildcard;
import com.sun.xml.xsom.visitor.XSTermVisitor;
import java.math.BigInteger;
/**
* {@link ParticleBinder} that follows the JAXB spec.
*
* @author Kohsuke Kawaguchi
*/
final class DefaultParticleBinder extends ParticleBinder {
@Override
public void build( XSParticle p, Collection<XSParticle> forcedProps ) {
Checker checker = checkCollision(p,forcedProps);
if(checker.hasNameCollision()) {
CReferencePropertyInfo prop = new CReferencePropertyInfo(
getCurrentBean().getBaseClass()==null?"Content":"Rest",
true, false, false, p,
builder.getBindInfo(p).toCustomizationList(),
p.getLocator(), false, false, false);
RawTypeSetBuilder.build(p,false).addTo(prop);
prop.javadoc = Messages.format( Messages.MSG_FALLBACK_JAVADOC,
checker.getCollisionInfo().toString() );
getCurrentBean().addProperty(prop);
} else {
new Builder(checker.markedParticles).particle(p);
}
}
@Override
public boolean checkFallback( XSParticle p ) {
return checkCollision(p,Collections.<XSParticle>emptyList()).hasNameCollision();
}
private Checker checkCollision( XSParticle p, Collection<XSParticle> forcedProps ) {
// scan the tree by a checker.
Checker checker = new Checker(forcedProps);
CClassInfo superClass = getCurrentBean().getBaseClass();
if(superClass!=null)
checker.readSuperClass(superClass);
checker.particle(p);
return checker;
}
/**
* Marks particles that need to be mapped to properties,
* by reading customization info.
*/
private final class Checker implements XSTermVisitor {
Checker(Collection<XSParticle> forcedProps) {
this.forcedProps = forcedProps;
}
boolean hasNameCollision() {
return collisionInfo!=null;
}
CollisionInfo getCollisionInfo() {
return collisionInfo;
}
/**
* If a collision is found, this field will be non-null.
*/
private CollisionInfo collisionInfo = null;
/** Used to check name collision. */
private final NameCollisionChecker cchecker = new NameCollisionChecker();
/**
* @see DefaultParticleBinder#build(XSParticle, Collection<com.sun.xml.xsom.XSParticle>)
*/
private final Collection<XSParticle> forcedProps;
public void particle( XSParticle p ) {
if(getLocalPropCustomization(p)!=null
|| builder.getLocalDomCustomization(p)!=null) {
// if a property customization is specfied,
// check that value and turn around.
check(p);
mark(p);
return;
}
XSTerm t = p.getTerm();
if(p.isRepeated() && (t.isModelGroup() || t.isModelGroupDecl())) {
// a repeated model group gets its own property
mark(p);
return;
}
if(forcedProps.contains(p)) {
// this particle must become a property
mark(p);
return;
}
outerParticle = p;
t.visit(this);
}
/**
* This field points to the parent XSParticle.
* The value is only valid when we are processing XSTerm.
*/
private XSParticle outerParticle;
public void elementDecl(XSElementDecl decl) {
check(outerParticle);
mark(outerParticle);
}
public void modelGroup(XSModelGroup mg) {
// choice gets mapped to a property
if(mg.getCompositor()== XSModelGroup.Compositor.CHOICE && builder.getGlobalBinding().isChoiceContentPropertyEnabled()) {
mark(outerParticle);
return;
}
for( XSParticle child : mg.getChildren() )
particle(child);
}
public void modelGroupDecl(XSModelGroupDecl decl) {
modelGroup(decl.getModelGroup());
}
public void wildcard(XSWildcard wc) {
mark(outerParticle);
}
void readSuperClass( CClassInfo ci ) {
cchecker.readSuperClass(ci);
}
/**
* Checks the name collision of a newly found particle.
*/
private void check( XSParticle p ) {
if( collisionInfo==null )
collisionInfo = cchecker.check(p);
}
/**
* Marks a particle that it's going to be mapped to a property.
*/
private void mark( XSParticle p ) {
markedParticles.put(p,computeLabel(p));
}
/**
* Marked particles.
*
* A map from XSParticle to its label.
*/
public final Map<XSParticle,String> markedParticles = new HashMap<>();
/**
* Checks name collisions among particles that belong to sequences.
*/
private final class NameCollisionChecker {
/**
* Checks the label conflict of a particle.
* This method shall be called for each marked particle.
*
* @return
* a description of a collision if a name collision is
* found. Otherwise null.
*/
CollisionInfo check( XSParticle p ) {
// this can be used for particles with a property customization,
// which may not have element declaration as its term.
// // we only check particles with element declarations.
// _assert( p.getTerm().isElementDecl() );
String label = computeLabel(p);
if( occupiedLabels.containsKey(label) ) {
// collide with occupied labels
return new CollisionInfo(label,p.getLocator(),
occupiedLabels.get(label).locator);
}
for( XSParticle jp : particles ) {
if(!check( p, jp )) {
// problem was found. no need to check further
return new CollisionInfo( label, p.getLocator(), jp.getLocator() );
}
}
particles.add(p);
return null;
}
/** List of particles reported through the check method. */
private final List<XSParticle> particles = new ArrayList<>();
/**
* Label names already used in the base type.
*/
private final Map<String,CPropertyInfo> occupiedLabels = new HashMap<>();
/**
* Checks the conflict of two particles.
* @return
* true if the check was successful.
*/
private boolean check( XSParticle p1, XSParticle p2 ) {
return !computeLabel(p1).equals(computeLabel(p2));
}
/**
* Reads fields of the super class and includes them
* to name collision tests.
*/
void readSuperClass( CClassInfo base ) {
for( ; base!=null; base=base.getBaseClass() ) {
for( CPropertyInfo p : base.getProperties() )
occupiedLabels.put(p.getName(true),p);
}
}
}
/** Keep the computed label names for particles. */
private final Map<XSParticle,String> labelCache = new Hashtable<>();
/**
* Hides the computeLabel method of the outer class
* and adds caching.
*/
private String computeLabel( XSParticle p ) {
String label = labelCache.get(p);
if(label==null)
labelCache.put( p, label=DefaultParticleBinder.this.computeLabel(p) );
return label;
}
}
/**
* Builds properties by using the result computed by Checker
*/
private final class Builder implements XSTermVisitor {
Builder( Map<XSParticle,String> markedParticles ) {
this.markedParticles = markedParticles;
}
/** All marked particles. */
private final Map<XSParticle,String/*label*/> markedParticles;
/**
* When we are visiting inside an optional particle, this flag
* is set to true.
*
* <p>
* This allows us to correctly generate primitive/boxed types.
*/
private boolean insideOptionalParticle;
/** Returns true if a particle is marked. */
private boolean marked( XSParticle p ) {
return markedParticles.containsKey(p);
}
/** Gets a label of a particle. */
private String getLabel( XSParticle p ) {
return markedParticles.get(p);
}
public void particle( XSParticle p ) {
XSTerm t = p.getTerm();
if(marked(p)) {
BIProperty cust = BIProperty.getCustomization(p);
CPropertyInfo prop = cust.createElementOrReferenceProperty(
getLabel(p), false, p, RawTypeSetBuilder.build(p,insideOptionalParticle));
getCurrentBean().addProperty(prop);
} else {
// repeated model groups should have been marked already
assert !p.isRepeated();
boolean oldIOP = insideOptionalParticle;
insideOptionalParticle |= BigInteger.ZERO.equals(p.getMinOccurs());
// this is an unmarked particle
t.visit(this);
insideOptionalParticle = oldIOP;
}
}
public void elementDecl( XSElementDecl e ) {
// because the corresponding particle must be marked.
assert false;
}
public void wildcard( XSWildcard wc ) {
// because the corresponding particle must be marked.
assert false;
}
public void modelGroupDecl( XSModelGroupDecl decl ) {
modelGroup(decl.getModelGroup());
}
public void modelGroup( XSModelGroup mg ) {
boolean oldIOP = insideOptionalParticle;
insideOptionalParticle |= mg.getCompositor()==XSModelGroup.CHOICE;
for( XSParticle p : mg.getChildren())
particle(p);
insideOptionalParticle = oldIOP;
}
}
}