All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xmlbeans.impl.inst2xsd.RussianDollStrategy Maven / Gradle / Ivy

Go to download

The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal. In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.

The newest version!
/*   Copyright 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.xmlbeans.impl.inst2xsd;

import org.apache.xmlbeans.*;
import org.apache.xmlbeans.impl.common.PrefixResolver;
import org.apache.xmlbeans.impl.common.ValidationContext;
import org.apache.xmlbeans.impl.common.XmlWhitespace;
import org.apache.xmlbeans.impl.inst2xsd.util.Attribute;
import org.apache.xmlbeans.impl.inst2xsd.util.Element;
import org.apache.xmlbeans.impl.inst2xsd.util.TypeSystemHolder;
import org.apache.xmlbeans.impl.inst2xsd.util.Type;
import org.apache.xmlbeans.impl.util.XsTypeConverter;
import org.apache.xmlbeans.impl.values.*;

import javax.xml.namespace.QName;
import java.util.*;

/**
 * @author Cezar Andrei ( cezar.andrei at bea.com )
 * Date: Jul 26, 2004
 */
public class RussianDollStrategy
    implements XsdGenStrategy
{
    static final String _xsi         = "http://www.w3.org/2001/XMLSchema-instance";

    static final QName _xsiNil          = new QName( _xsi, "nil", "xsi" );
    static final QName _xsiType         = new QName( _xsi, "type", "xsi" );

    public void processDoc(XmlObject[] instances, Inst2XsdOptions options, TypeSystemHolder typeSystemHolder)
    {
        for (int i = 0; i < instances.length; i++)
        {
            XmlObject instance = instances[i];
            XmlCursor xc = instance.newCursor();
            // xc on start doc

            StringBuffer comment = new StringBuffer();

            while( !xc.isStart() )
            {
                xc.toNextToken();
                if( xc.isComment() )
                    comment.append(xc.getTextValue());
                else if (xc.isEnddoc())
                    return;
            }
            // xc now on the root element

            Element withElem = processElement(xc, comment.toString(), options, typeSystemHolder);
            withElem.setGlobal(true);

            addGlobalElement(withElem, typeSystemHolder, options);
        }
    }

    protected Element addGlobalElement(Element withElem, TypeSystemHolder typeSystemHolder, Inst2XsdOptions options)
    {
        assert withElem.isGlobal();
        Element intoElem = typeSystemHolder.getGlobalElement(withElem.getName());

        if (intoElem==null)
        {
            typeSystemHolder.addGlobalElement(withElem);
            return withElem;
        }
        else
        {
            combineTypes(intoElem.getType(), withElem.getType(), options);
            combineElementComments(intoElem, withElem);
            return intoElem;
        }
    }

    protected Element processElement(XmlCursor xc, String comment,
        Inst2XsdOptions options, TypeSystemHolder typeSystemHolder)
    {
        assert xc.isStart();
        Element element = new Element();
        element.setName(xc.getName());
        element.setGlobal(false);

        Type elemType = Type.createUnnamedType(Type.SIMPLE_TYPE_SIMPLE_CONTENT); //assume simple, set later
        element.setType(elemType);

        StringBuffer textBuff = new StringBuffer();
        StringBuffer commentBuff = new StringBuffer();
        List children = new ArrayList();
        List attributes = new ArrayList();

        loop: do
        {
            XmlCursor.TokenType tt = xc.toNextToken();
            switch (tt.intValue())
            {
                case XmlCursor.TokenType.INT_ATTR:
                    // todo check for xsi:type
                    // ignore xsi:... attributes other than xsi:nil
                    QName attName = xc.getName();
                    if (!_xsiNil.getNamespaceURI().equals(attName.getNamespaceURI()))
                        attributes.add(processAttribute(xc, options, element.getName().getNamespaceURI(), typeSystemHolder));
                    else if (_xsiNil.equals(attName))
                        element.setNillable(true);

                    break;

                case XmlCursor.TokenType.INT_START:
                    children.add(processElement(xc, commentBuff.toString(), options, typeSystemHolder));
                    commentBuff.delete(0, commentBuff.length());
                    break;

                case XmlCursor.TokenType.INT_TEXT:
                    textBuff.append(xc.getChars());
                    break;

                case XmlCursor.TokenType.INT_COMMENT:
                    commentBuff.append(xc.getTextValue());
                    break;

                case XmlCursor.TokenType.INT_NAMESPACE:
                    // ignore,
                    // each element and attribute will take care to define itself in the right targetNamespace
                    break;

                case XmlCursor.TokenType.INT_END:
                    break loop;

                case XmlCursor.TokenType.INT_PROCINST:
                    // ignore
                    break;

                case XmlCursor.TokenType.INT_ENDDOC:
                    break loop;

                case XmlCursor.TokenType.INT_NONE:
                    break loop;

                case XmlCursor.TokenType.INT_STARTDOC:
                    throw new IllegalStateException();

                default:
                    throw new IllegalStateException("Unknown TokenType.");
            }
        }
        while( true );

        String collapsedText =  XmlWhitespace.collapse(textBuff.toString(), XmlWhitespace.WS_COLLAPSE);

        String commnetStr = (comment == null ?
            ( commentBuff.length() == 0 ? null : commentBuff.toString() ) :
            ( commentBuff.length() == 0 ? comment : commentBuff.insert(0, comment).toString()) );
        element.setComment(commnetStr);

        if (children.size()>0)
        {
            // complex content
            if (collapsedText.length()>0)
            {
                elemType.setContentType(Type.COMPLEX_TYPE_MIXED_CONTENT);
            }
            else
            {
                elemType.setContentType(Type.COMPLEX_TYPE_COMPLEX_CONTENT);
            }
            processElementsInComplexType(elemType, children, element.getName().getNamespaceURI(), typeSystemHolder, options);
            processAttributesInComplexType(elemType, attributes);
        }
        else
        {
            // simple content
            // hack workaround for being able to call xc.getNamespaceForPrefix()
            XmlCursor xcForNamespaces = xc.newCursor();
            xcForNamespaces.toParent();

            if (attributes.size()>0)
            {
                elemType.setContentType(Type.COMPLEX_TYPE_SIMPLE_CONTENT);

                Type extendedType = Type.createNamedType(
                    processSimpleContentType(textBuff.toString(), options, xcForNamespaces), Type.SIMPLE_TYPE_SIMPLE_CONTENT);
                elemType.setExtensionType(extendedType);

                processAttributesInComplexType(elemType, attributes);
            }
            else
            {
                elemType.setContentType(Type.SIMPLE_TYPE_SIMPLE_CONTENT);
                elemType.setName(processSimpleContentType(textBuff.toString(), options, xcForNamespaces));

                // add enumeration value
                String enumValue = XmlString.type.getName().equals(elemType.getName()) ? textBuff.toString() : collapsedText;
                elemType.addEnumerationValue(enumValue, xcForNamespaces);
            }

            xcForNamespaces.dispose(); // end hack
        }

        checkIfReferenceToGlobalTypeIsNeeded( element, typeSystemHolder, options);

        return element;
    }

    protected void processElementsInComplexType(Type elemType, List children, String parentNamespace,
        TypeSystemHolder typeSystemHolder, Inst2XsdOptions options)
    {
        Map elemNamesToElements = new HashMap();
        Element currentElem = null;

        for (Iterator iterator = children.iterator(); iterator.hasNext();)
        {
            Element child = (Element) iterator.next();

            if (currentElem==null)
            {   // first element in this type
                checkIfElementReferenceIsNeeded(child, parentNamespace, typeSystemHolder, options);
                elemType.addElement(child);
                elemNamesToElements.put(child.getName(), child);
                currentElem = child;
                continue;
            }

            if (currentElem.getName()==child.getName())
            {   // same contiguos element
                combineTypes(currentElem.getType(), child.getType(), options); // unify types
                combineElementComments(currentElem, child);
                // minOcc=0 maxOcc=unbounded
                currentElem.setMinOccurs(0);
                currentElem.setMaxOccurs(Element.UNBOUNDED);
            }
            else
            {
                Element sameElem = (Element)elemNamesToElements.get(child.getName());
                if (sameElem==null)
                {   // new element name
                    checkIfElementReferenceIsNeeded(child, parentNamespace, typeSystemHolder, options);
                    elemType.addElement(child);
                    elemNamesToElements.put(child.getName(), child);
                }
                else
                {   //same non contiguos
                    combineTypes(currentElem.getType(), child.getType(), options);
                    combineElementComments(currentElem, child);
                    elemType.setTopParticleForComplexOrMixedContent(Type.PARTICLE_CHOICE_UNBOUNDED);
                }
                currentElem = child;
            }
        }
    }

    protected void checkIfElementReferenceIsNeeded(Element child, String parentNamespace,
        TypeSystemHolder typeSystemHolder, Inst2XsdOptions options)
    {
        if (!child.getName().getNamespaceURI().equals(parentNamespace))
        {
            Element referencedElem = new Element();
            referencedElem.setGlobal(true);
            referencedElem.setName(child.getName());
            referencedElem.setType(child.getType());

            if (child.isNillable())
            {
                referencedElem.setNillable(true);
                child.setNillable(false);
            }

            referencedElem = addGlobalElement(referencedElem, typeSystemHolder, options);

            child.setRef(referencedElem); // clears child's type
        }
    }

    protected void checkIfReferenceToGlobalTypeIsNeeded(Element elem, TypeSystemHolder typeSystemHolder,
        Inst2XsdOptions options)
    {
        // RussianDollDesign doesn't define global types
    }

    protected void processAttributesInComplexType(Type elemType, List attributes)
    {
        assert elemType.isComplexType();
        for (Iterator iterator = attributes.iterator(); iterator.hasNext();)
        {
            Attribute att = (Attribute) iterator.next();
            elemType.addAttribute(att);
        }
    }

    protected Attribute processAttribute(XmlCursor xc, Inst2XsdOptions options, String parentNamespace,
                                              TypeSystemHolder typeSystemHolder)
    {
        assert xc.isAttr() : "xc not on attribute";
        Attribute attribute = new Attribute();
        QName attName = xc.getName();

        attribute.setName(attName);

        XmlCursor parent = xc.newCursor();
        parent.toParent();

        Type simpleContentType = Type.createNamedType(
            processSimpleContentType(xc.getTextValue(), options, parent), Type.SIMPLE_TYPE_SIMPLE_CONTENT);

        parent.dispose();

        attribute.setType(simpleContentType);

        checkIfAttributeReferenceIsNeeded(attribute, parentNamespace, typeSystemHolder);

        return attribute;
    }

    protected void checkIfAttributeReferenceIsNeeded(Attribute attribute, String parentNamespace, TypeSystemHolder typeSystemHolder)
    {
        if (!attribute.getName().getNamespaceURI().equals("") &&
            !attribute.getName().getNamespaceURI().equals(parentNamespace))
        {
            // make attribute be a reference to a top level attribute in a different targetNamespace
            Attribute referencedAtt = new Attribute();
            referencedAtt.setGlobal(true);
            referencedAtt.setName(attribute.getName());
            referencedAtt.setType(attribute.getType());

            typeSystemHolder.addGlobalAttribute(referencedAtt);

            attribute.setRef(referencedAtt);
        }
    }

    protected class SCTValidationContext
        implements ValidationContext
    {
        protected boolean valid = true;

        public boolean isValid()
        {
            return valid;
        }

        public void resetToValid()
        {
            valid = true;
        }

        public void invalid(String message)
        {
            valid = false;
        }

        public void invalid(String code, Object[] args)
        {
            valid = false;
        }
    }

    private SCTValidationContext _validationContext = new SCTValidationContext();


    // List of precedence for smart simple primitive type determination
    // byte, short, int, long, integer, float, double, decimal,
    // boolean
    // date, dateTime, time, gDuration,
    // QName ?,
    // anyUri ? - triggered only for http:// or www. constructs,
    // list types ?
    // string
    protected QName processSimpleContentType(String lexicalValue, Inst2XsdOptions options, final XmlCursor xc)
    {
        // check options and return xsd:string or if smart is enabled, look for a better type
        if (options.getSimpleContentTypes()==Inst2XsdOptions.SIMPLE_CONTENT_TYPES_STRING)
            return XmlString.type.getName();

        if (options.getSimpleContentTypes()!=Inst2XsdOptions.SIMPLE_CONTENT_TYPES_SMART)
            throw new IllegalArgumentException("Unknown value for Inst2XsdOptions.getSimpleContentTypes() :" + options.getSimpleContentTypes());

        // Inst2XsdOptions.SIMPLE_CONTENT_TYPES_SMART case


        try
        {
            XsTypeConverter.lexByte(lexicalValue);
            return XmlByte.type.getName();
        }
        catch (Exception e) {}

        try
        {
            XsTypeConverter.lexShort(lexicalValue);
            return XmlShort.type.getName();
        }
        catch (Exception e) {}

        try
        {
            XsTypeConverter.lexInt(lexicalValue);
            return XmlInt.type.getName();
        }
        catch (Exception e) {}

        try
        {
            XsTypeConverter.lexLong(lexicalValue);
            return XmlLong.type.getName();
        }
        catch (Exception e) {}

        try
        {
            XsTypeConverter.lexInteger(lexicalValue);
            return XmlInteger.type.getName();
        }
        catch (Exception e) {}

        try
        {
            XsTypeConverter.lexFloat(lexicalValue);
            return XmlFloat.type.getName();
        }
        catch (Exception e) {}

//        // this not needed because it's lexical space is covered by float
//        try
//        {
//            XsTypeConverter.lexDouble(lexicalValue);
//            return XmlDouble.type.getName();
//        }
//        catch (Exception e) {}
//
//        try
//        {
//            XsTypeConverter.lexDecimal(lexicalValue);
//            return XmlDecimal.type.getName();
//        }
//        catch (Exception e) {}

        XmlDateImpl.validateLexical(lexicalValue, XmlDate.type, _validationContext);
        if (_validationContext.isValid())
            return XmlDate.type.getName();
        _validationContext.resetToValid();

        XmlDateTimeImpl.validateLexical(lexicalValue, XmlDateTime.type, _validationContext);
        if (_validationContext.isValid())
            return XmlDateTime.type.getName();
        _validationContext.resetToValid();

        XmlTimeImpl.validateLexical(lexicalValue, XmlTime.type, _validationContext);
        if (_validationContext.isValid())
            return XmlTime.type.getName();
        _validationContext.resetToValid();

        XmlDurationImpl.validateLexical(lexicalValue, XmlDuration.type, _validationContext);
        if (_validationContext.isValid())
            return XmlDuration.type.getName();
        _validationContext.resetToValid();

        // check for uri
        if (lexicalValue.startsWith("http://") || lexicalValue.startsWith("www."))
        {
            XmlAnyUriImpl.validateLexical(lexicalValue, _validationContext);
            if (_validationContext.isValid())
                return XmlAnyURI.type.getName();
            _validationContext.resetToValid();
        }

        // check for QName
        int idx = lexicalValue.indexOf(':');
        if (idx>=0 && idx==lexicalValue.lastIndexOf(':') && idx+1options.getUseEnumerations())
            {
                into.closeEnumeration();
            }
        }
    }

    protected QName combineToMoreGeneralSimpleType(QName t1, QName t2)
    {
        if (t1.equals(t2))
            return t1;

        if (t2.equals(XmlShort.type.getName()) && t1.equals(XmlByte.type.getName()))
            return t2;
        if (t1.equals(XmlShort.type.getName()) && t2.equals(XmlByte.type.getName()))
            return t1;

        if (t2.equals(XmlInt.type.getName()) &&
            (t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName())) )
            return t2;
        if (t1.equals(XmlInt.type.getName()) &&
            (t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName())) )
            return t1;

        if (t2.equals(XmlLong.type.getName()) &&
            (t1.equals(XmlInt.type.getName()) || t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName())) )
            return t2;
        if (t1.equals(XmlLong.type.getName()) &&
            (t2.equals(XmlInt.type.getName()) || t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName())) )
            return t1;

        if (t2.equals(XmlInteger.type.getName()) &&
            (t1.equals(XmlLong.type.getName()) || t1.equals(XmlInt.type.getName()) ||
            t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName())) )
            return t2;
        if (t1.equals(XmlInteger.type.getName()) &&
            (t2.equals(XmlLong.type.getName()) || t2.equals(XmlInt.type.getName()) ||
            t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName())) )
            return t1;

        if (t2.equals(XmlFloat.type.getName()) &&
            (t1.equals(XmlInteger.type.getName()) ||
            t1.equals(XmlLong.type.getName()) || t1.equals(XmlInt.type.getName()) ||
            t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName())) )
            return t2;
        if (t1.equals(XmlFloat.type.getName()) &&
            (t2.equals(XmlInteger.type.getName()) ||
            t2.equals(XmlLong.type.getName()) || t2.equals(XmlInt.type.getName()) ||
            t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName())) )
            return t1;

        //double, decimal will never get here since they don't get generated

        //the rest of the combinations are not compatible, so they will combine in xsd:string
        return XmlString.type.getName();
    }

    protected void combineAttributesOfTypes(Type into, Type from)
    {
        // loop through attributes: add fromAtt if they don't exist, combine them if they exist
        outterLoop:
        for (int i = 0; i < from.getAttributes().size(); i++)
        {
            Attribute fromAtt = (Attribute)from.getAttributes().get(i);
            for (int j = 0; j < into.getAttributes().size(); j++)
            {
                Attribute intoAtt = (Attribute)into.getAttributes().get(j);
                if (intoAtt.getName().equals(fromAtt.getName()))
                {
                    intoAtt.getType().setName(
                        combineToMoreGeneralSimpleType(intoAtt.getType().getName(), fromAtt.getType().getName()));
                    continue outterLoop;
                }
            }
            // fromAtt doesn't exist in into type, will add it right now
            into.addAttribute(fromAtt);
        }

        //optional attributes: if there are atts in into that are not in from, make them optional
        outterLoop:
        for (int i = 0; i < into.getAttributes().size(); i++)
        {
            Attribute intoAtt = (Attribute)into.getAttributes().get(i);
            for (int j = 0; j < from.getAttributes().size(); j++)
            {
                Attribute fromAtt = (Attribute)from.getAttributes().get(j);
                if (fromAtt.getName().equals(intoAtt.getName()))
                {
                    continue;
                }
            }
            // intoAtt doesn't exist in into type, will add it right now
            intoAtt.setOptional(true);
        }
    }

    protected void combineElementsOfTypes(Type into, Type from, boolean makeElementsOptional, Inst2XsdOptions options)
    {
        boolean needsUnboundedChoice = false;

        if (into.getTopParticleForComplexOrMixedContent()!=Type.PARTICLE_SEQUENCE ||
            from.getTopParticleForComplexOrMixedContent()!=Type.PARTICLE_SEQUENCE)
            needsUnboundedChoice = true;

        List res = new ArrayList();

        int fromStartingIndex = 0;
        int fromMatchedIndex = -1;
        int intoMatchedIndex = -1;

        // for each element in into
        for (int i = 0; !needsUnboundedChoice && i < into.getElements().size(); i++)
        {
            // try to find one with same name in from
            Element intoElement = (Element) into.getElements().get(i);
            for (int j = fromStartingIndex; j < from.getElements().size(); j++)
            {
                Element fromElement = (Element) from.getElements().get(j);
                if (intoElement.getName().equals(fromElement.getName()))
                {
                    fromMatchedIndex = j;
                    break;
                }
            }

            // if not found, it's safe to add this one to result 'res' (as optional) and continue
            if ( fromMatchedIndex < fromStartingIndex )
            {
                res.add(intoElement);
                intoElement.setMinOccurs(0);
                continue;
            }

            // else try out all from elemens between fromStartingIndex to fromMatchedIndex
            // to see if they match one of the into elements
            intoMatchingLoop:
            for (int j2 = fromStartingIndex; j2 < fromMatchedIndex; j2++)
            {
                Element fromCandidate = (Element) from.getElements().get(j2);

                for (int i2 = i+1; i2 < into.getElements().size(); i2++)
                {
                    Element intoCandidate = (Element) into.getElements().get(i2);
                    if (fromCandidate.getName().equals(intoCandidate.getName()))
                    {
                        intoMatchedIndex = i2;
                        break intoMatchingLoop;
                    }
                }
            }

            if (intoMatchedIndex0)
        {
            if (into.getComment()==null)
                into.setComment(with.getComment());
            else
                into.setComment(into.getComment() + with.getComment());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy