/**
* Copyright 2004-2016 Riccardo Solmi. All rights reserved.
* This file is part of the Whole Platform.
*
* The Whole Platform is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Whole Platform is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whole.lang.xsd.builders;
import static org.whole.lang.xml.reflect.XmlEntityDescriptorEnum.*;
import static org.whole.lang.xsd.mapping.util.MappingStrategyUtils.*;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.whole.lang.bindings.BindingManagerFactory;
import org.whole.lang.bindings.IBindingManager;
import org.whole.lang.builders.GenericEventTrackingBuilder;
import org.whole.lang.builders.GenericIdentityBuilder;
import org.whole.lang.builders.IBuilder;
import org.whole.lang.builders.IBuilderOperation;
import org.whole.lang.builders.ModelBuilderOperation;
import org.whole.lang.commons.factories.CommonsEntityAdapterFactory;
import org.whole.lang.commons.reflect.CommonsEntityDescriptorEnum;
import org.whole.lang.factories.GenericEntityFactory;
import org.whole.lang.iterators.IEntityIterator;
import org.whole.lang.iterators.IteratorFactory;
import org.whole.lang.model.IEntity;
import org.whole.lang.reflect.EntityDescriptor;
import org.whole.lang.reflect.FeatureDescriptor;
import org.whole.lang.reflect.ILanguageKit;
import org.whole.lang.templates.ModelTemplate;
import org.whole.lang.util.DataTypeUtils;
import org.whole.lang.util.EntityUtils;
import org.whole.lang.xml.builders.XmlSpecificBuilderAdapter;
import org.whole.lang.xml.factories.XmlEntityFactory;
import org.whole.lang.xml.model.Attribute;
import org.whole.lang.xml.model.Attributes;
import org.whole.lang.xml.model.Element;
import org.whole.lang.xml.model.IName;
import org.whole.lang.xml.reflect.XmlFeatureDescriptorEnum;
import org.whole.lang.xml.reflect.XmlLanguageKit;
import org.whole.lang.xml.util.QName;
import org.whole.lang.xml.util.XmlUtils;
import org.whole.lang.xsd.builders.utils.AttributesPreprocessor;
import org.whole.lang.xsd.builders.utils.MappingContext;
import org.whole.lang.xsd.builders.utils.MappingContextStack;
import org.whole.lang.xsd.codebase.IMappingStrategy;
import org.whole.lang.xsd.mapping.util.MappingStrategyUtils;
import org.whole.lang.xsd.util.NamespaceUtils;
import org.whole.lang.xsd.util.SchemaUtils;
/**
* @author Enrico Persiani
*/
public class XmlSpecific2XsiBuilderAdapter extends XmlSpecificBuilderAdapter {
protected IBuilderOperation op;
protected IBindingManager ns;
protected AttributesPreprocessor ap;
protected GenericEventTrackingBuilder builder;
public XmlSpecific2XsiBuilderAdapter(IBuilderOperation op, IBindingManager bm) {
super(new GenericIdentityBuilder());
wSetBuilderStrategy(new ElementBuilder());
this.ns = NamespaceUtils.initializeNampespaceBindings(BindingManagerFactory.instance.createBindingManager());
this.op = op;
this.ap = new AttributesPreprocessor(bm, ns);
this.builder = new GenericEventTrackingBuilder(getTargetBuilder(), CommonsEntityDescriptorEnum.RootFragment);
}
protected IBuilder getTargetBuilder() {
return ModelBuilderOperation.ID.equals(op.wGetOperationId()) ?
op.wGetBuilder(XmlLanguageKit.URI, false) : //FIXME workaround, should use a generic builder
op.wGetBuilder();
}
protected FeatureDescriptor calculateFeatureDescriptor(EntityDescriptor<?> context, QName name) {
Collection<FeatureDescriptor> fds = getElementFeatureMappings(context, name);
if (fds.size() > 1) {
List<FeatureDescriptor> contextFeatures = context.getEntityFeatureDescriptors();
FeatureDescriptor contextFeature;
int index = builder.wCurrentIndexOf();
while (index < contextFeatures.size() && !fds.contains(contextFeature = contextFeatures.get(index)) && contextFeature.isOptional())
index++;
return context.getEntityFeatureDescriptor(index);
} else
return fds.iterator().next();
}
protected MappingContext elementEntity_(EntityDescriptor<?> context, QName name, Attributes attributes) {
//TODO test!!!
// EntityDescriptor<?> ed = ap.hasExplicitType() ? ap.getExplicitType() : getElementEntityMapping(context, name);
EntityDescriptor<?> ed = getElementEntityMapping(context, name);
if (SchemaUtils.isAnyType(ed))
ed = getElementEntityMapping(CommonsEntityDescriptorEnum.RootFragment, name);
if (ed == null)
throw new IllegalStateException("missing mapping");
FeatureDescriptor fd = calculateFeatureDescriptor(context, name);
if (!EntityUtils.isComposite(context))
builder.wFeature(fd);
//FIXME abstract/union types
if (!EntityUtils.isData(ed) && !(ed.isAbstract() && ed.isPolymorphic())) {;//was getConcreteSubtypesInLanguage().size() > 1)) {
builder.wEntity_(ed);
attributeEntities(ed, attributes);
}
return MappingContext.create(ed, fd, false);
}
protected void _elementEntity(EntityDescriptor<?> context) {
//FIXME unions
if (!EntityUtils.isData(context) && !(context.isAbstract() && context.isPolymorphic())) {;//was getConcreteSubtypesInLanguage().size() > 1)) {
// sets an empty string if content entity maps to a string data type
FeatureDescriptor fd = getContentFeatureMapping(context);
if (fd != null && builder.wCurrentIndexOf() <= context.indexOf(fd)) {
EntityDescriptor<?> ed = getContentEntityMapping(context);
//FIXME lists/unions
if (ed.getDataKind().isString()) {
builder.wFeature(fd);
DataTypeUtils.buildFromPersistenceString(builder, ed, "");
}
}
builder._wEntity(context);
}
}
protected void attributeEntities(EntityDescriptor<?> context, Attributes attributes) {
String uri = context.getLanguageKit().getURI();
boolean isAttributeFormQualified = MappingStrategyUtils.hasMappingStrategy(uri) &&
MappingStrategyUtils.getMappingStrategy(uri).isAttributesFormQualified();
IEntityIterator<Attribute> iterator = IteratorFactory.<Attribute>childIterator();
iterator.reset(attributes);
// add attribute features
while (iterator.hasNext()) {
Attribute attribute = iterator.next();
QName name = QName.create(ns, isAttributeFormQualified ? NamespaceUtils.getDefaultNamespace(ns) : uri, attribute.getName());
String value = attribute.getValue().getValue();
if (hasAttributeEntityMapping(context, name)) {
FeatureDescriptor fd = getAttributeFeatureMapping(context, name);
EntityDescriptor<?> ed = getAttributeEntityMapping(context, name);
attributeEntity(fd, ed, value);
iterator.remove();
}
}
// add any attributes feature
anyAttributeEntities(context, attributes);
}
protected void anyAttributeEntities(EntityDescriptor<?> context, Attributes anyAttributes) {
//TODO implement any attribute mappings
}
protected void anyAttributeEntity(EntityDescriptor<?> context, Attribute anyAttribute) {
builder.wEntity_(CommonsEntityDescriptorEnum.SameStageFragment);
new ModelTemplate(anyAttribute).apply(builder);
builder._wEntity(CommonsEntityDescriptorEnum.SameStageFragment);
}
protected void attributeEntity(FeatureDescriptor fd, EntityDescriptor<?> ed, String value) {
builder.wFeature(fd);
DataTypeUtils.buildFromPersistenceString(builder, ed, value);
}
protected void dataEntity(EntityDescriptor<?> context, String value) {
if (context.isAbstract() && context.isPolymorphic()) {//was getConcreteSubtypesInLanguage().size() > 1) {
Collection<EntityDescriptor<?>> unionTypes = getUnionEntityMappings(context);
Iterator<EntityDescriptor<?>> iterator = unionTypes.iterator();
while (iterator.hasNext()) {
try {
dataEntity(iterator.next(), value);
return;
} catch (Exception e) {
}
}
throw new IllegalStateException("cannot find a suitable type");
}
if (EntityUtils.isData(context))
DataTypeUtils.buildFromPersistenceString(builder, context, value);
else if (EntityUtils.isComposite(context)) {
String[] values = value.split("["+XmlUtils.IGNORABLE_WHITESPACE_CHARS+"]++");
EntityDescriptor<?> ed = context.getEntityDescriptor(0);
builder.wEntity_(context);
for (String v : values)
dataEntity(ed, v);
builder._wEntity(context);
} else {
EntityDescriptor<?> ed = getContentEntityMapping(context);
builder.wFeature(getContentFeatureMapping(context));
DataTypeUtils.buildFromPersistenceString(builder, ed, value);
}
}
protected MappingContext structuralEntity_(EntityDescriptor<?> context, QName name) {
if (!hasStructuralEntityMapping(context, name))
return null;
FeatureDescriptor fd = getStructuralFeatureMapping(context, name);
EntityDescriptor<?> ed = getStructuralEntityMapping(context, name);
if (!EntityUtils.isComposite(context))
builder.wFeature(fd);
builder.wEntity_(ed);
return MappingContext.create(ed, fd, true);
}
protected MappingContext mixedStructuralEntity_(EntityDescriptor<?> context) {
if (!hasMixedStructuralMapping(context))
return null;
FeatureDescriptor fd = getMixedStructuralFeature(context);
EntityDescriptor<?> ed = getMixedStructuralType(context);
builder.wFeature(fd);
builder.wEntity_(ed);
return MappingContext.create(ed, fd, true, true);
}
protected void mixedDataEntity(EntityDescriptor<?> context, int lastChildIndex, String value) {
EntityDescriptor<?> ed = getMixedDataType(context);
// find next mixed feature
if (!EntityUtils.isComposite(context)) {
for (int i=lastChildIndex, size=context.featureSize(); i<size; i++)
if (ed.equals(context.getEntityDescriptor(i))) {
builder.wFeature(context.getEntityFeatureDescriptor(i));
break;
}
}
builder.wEntity(ed, value);
}
protected org.whole.lang.xml.model.Element normalizeXmlFragment(org.whole.lang.xml.model.Element xmlFragment) {
XmlFragmentNormalizer normalizer = new XmlFragmentNormalizer(true);
xmlFragment.accept(normalizer);
// add needed namespace declarations
XmlEntityFactory xef = XmlEntityFactory.instance;
IEntity attributes = xmlFragment.getAttributes();
for (String prefix : normalizer.getNeededNamespaces())
attributes.wAdd(xef.createAttribute(xef.createQualifiedName(
xef.createNameSpace("xmlns"), xef.createName(prefix)),
xef.createValue(ns.wStringValue(prefix))));
return xmlFragment;
}
private final class ElementBuilder extends GenericIdentityBuilder {
private IName name;
private MappingContextStack mcs;
private org.whole.lang.xml.model.Element xmlFragment;
public ElementBuilder() {
this.mcs = new MappingContextStack();
}
public void wEntity(EntityDescriptor<?> ed) {
if (ed.getOrdinal() == Attributes_ord)
mapElementEntity_(XmlEntityFactory.instance.createAttributes(0));
}
public void wEntity(EntityDescriptor<?> ed, String value) {
switch (ed.getOrdinal()) {
case Name_ord:
name = (IName) GenericEntityFactory.instance.create(ed, value);
break;
case CharData_ord:
mapElementEntity(value);
}
}
public void wEntity_(EntityDescriptor<?> ed) {
wEntity_(ed, 0);
}
public void wEntity_(EntityDescriptor<?> ed, int initialCapacity) {
switch (ed.getOrdinal()) {
case Attributes_ord:
case QualifiedName_ord:
wBuildEntity_(ed);
break;
case Element_ord:
ns.wEnterScope();
break;
case Content_ord:
if (isBuildingXmlFragment())
wBuildEntity_(ed);
}
}
public void _wEntity(EntityDescriptor<?> ed) {
switch (ed.getOrdinal()) {
case Attributes_ord:
Attributes attributes = (Attributes) _wBuildEntity(ed);
ap.preprocess(attributes);
mapElementEntity_(attributes);
break;
case QualifiedName_ord:
name = (IName) _wBuildEntity(ed);
break;
case Element_ord:
_mapElementEntity();
ns.wExitScope();
break;
case Content_ord:
if (isBuildingXmlFragment())
break;
// close as many containing element as needed
while (mcs.isStructural())
builder._wEntity(mcs.pop().ed);
}
}
protected void mapElementEntity(String value) {
try {
MappingContext mc = mcs.peek();
if (hasMixedContent(mc.ed))
mixedDataEntity(mc.ed, mc.getLastChildIndex(), value);
else
dataEntity(mc.ed, value);
} catch (Exception e) {
xmlContent(value);
}
}
protected void mapElementEntity_(Attributes attributes) {
try {
MappingContext mc;
ILanguageKit languageKit = mcs.peekContextType().getLanguageKit();
IMappingStrategy strategy = getMappingStrategy(languageKit);
boolean isElementsFormQualified = strategy != null && strategy.isElementsFormQualified();
QName qname = QName.create(ns, isElementsFormQualified ? NamespaceUtils.getDefaultNamespace(ns) : languageKit.getURI(), name);
if (!hasMappingStrategy(qname))
throw new IllegalStateException("missing mapping strategy");
if (!hasElementEntityMapping(mcs.peekContextType(), qname)) {
// terminate structural entities if we cannot further map to another container
if (mcs.isStructural() && !hasStructuralEntityMapping(mcs.peekContextType(), qname))
builder._wEntity(mcs.pop().ed);//_anyStructuralEntity(mcs.pop().ed);
// begin as many structural entities as needed
while ((mc = structuralEntity_(mcs.peekContextType(), qname)) != null) {
mcs.push(mc);
if (hasElementEntityMapping(mcs.peekContextType(), qname))
break;
}
}
// maps the current element
mcs.push(mc = elementEntity_(mcs.peekContextType(), qname, attributes));
// begin a mixed structural entity if needed
if ((mc = mixedStructuralEntity_(mcs.peekContextType())) != null)
mcs.push(mc);
} catch (Exception e) {
xmlContent_(attributes);
}
}
protected void _mapElementEntity() {
if (isBuildingXmlFragment())
_xmlContent();
else {
// terminate the mixed structural entity, if any
if (mcs.isMixedStructural())
builder._wEntity(mcs.pop().ed);
_elementEntity(mcs.pop().ed);
}
}
protected void skipAttributeFeatures() {
EntityDescriptor<?> context = builder.wGetEntityDescriptor();
int index = builder.wCurrentIndexOf();
if (EntityUtils.isComposite(context))
return;
IMappingStrategy mappingStrategy = getMappingStrategy(context.getLanguageKit());
while (mappingStrategy.isAttributeMapping(context,
context.getEntityDescriptor(index),
context.getEntityFeatureDescriptor(index)))
index++;
builder.wFeature(index);
}
protected boolean isBuildingXmlFragment() {
return xmlFragment != null;
}
protected void xmlContent(String value) {
skipAttributeFeatures();
XmlEntityFactory xef = XmlEntityFactory.instance;
Element element = xef.createElement(name,
CommonsEntityAdapterFactory.createResolver(Attributes),
xef.createCharData(value));
builder.wEntity_(CommonsEntityDescriptorEnum.SameStageFragment);
new ModelTemplate(normalizeXmlFragment(element)).apply(builder);
builder._wEntity(CommonsEntityDescriptorEnum.SameStageFragment);
}
protected void xmlContent_(Attributes attributes) {
skipAttributeFeatures();
builder.wEntity_(CommonsEntityDescriptorEnum.SameStageFragment);
xmlFragment = XmlEntityFactory.instance.createElement(name, attributes,
CommonsEntityAdapterFactory.createResolver(Content));
}
protected void _xmlContent() {
xmlFragment.wSet(XmlFeatureDescriptorEnum.content, _wBuildEntity(Content));
new ModelTemplate(normalizeXmlFragment(xmlFragment)).apply(builder);
builder._wEntity(CommonsEntityDescriptorEnum.SameStageFragment);
xmlFragment = null;
}
}
}