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

jlibs.xml.xsd.XSInstance Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
/**
 * Copyright 2015 Santhosh Kumar Tekuri
 *
 * The JLibs authors license 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 jlibs.xml.xsd;

import jlibs.core.graph.*;
import jlibs.core.graph.navigators.FilteredTreeNavigator;
import jlibs.core.graph.sequences.DuplicateSequence;
import jlibs.core.graph.sequences.EmptySequence;
import jlibs.core.graph.sequences.IterableSequence;
import jlibs.core.graph.sequences.RepeatSequence;
import jlibs.core.graph.visitors.ReflectionVisitor;
import jlibs.core.graph.walkers.PreorderWalker;
import jlibs.core.io.IOUtil;
import jlibs.core.lang.ImpossibleException;
import jlibs.core.lang.OS;
import jlibs.core.net.URLUtil;
import jlibs.core.util.CollectionUtil;
import jlibs.core.util.RandomUtil;
import jlibs.xml.Namespaces;
import jlibs.xml.XMLUtil;
import jlibs.xml.sax.XMLDocument;
import jlibs.xml.xsd.display.XSDisplayFilter;
import org.apache.xerces.xs.*;
import org.xml.sax.SAXException;

import javax.xml.bind.DatatypeConverter;
import javax.xml.namespace.QName;
import javax.xml.transform.stream.StreamResult;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author Santhosh Kumar T
 */
@SuppressWarnings({"unchecked"})
public class XSInstance{
    public int minimumElementsGenerated = 2;
    public int maximumElementsGenerated = 2;
    public int minimumListItemsGenerated = 2;
    public int maximumListItemsGenerated = 2;
    public int maximumRecursionDepth = 1;

    // TRUE=always, FALSE=never, null=random
    public Boolean generateOptionalElements = Boolean.TRUE;
    public Boolean generateOptionalAttributes = Boolean.TRUE;
    public Boolean generateFixedAttributes = Boolean.TRUE;
    public Boolean generateDefaultAttributes = Boolean.TRUE;
    public Boolean generateDefaultElementValues = Boolean.TRUE;

    public boolean generateAllChoices = false;

    // TRUE=always, FALSE=never, null=when_appropriate
    public Boolean showContentModel = null;

    private int generateRepeatCount(int minOccurs, int maxOccurs){
        if(minOccurs==0 && maxOccurs==1) //optional case
            return RandomUtil.randomBoolean(generateOptionalElements) ? 1 : 0;

        if(maxOccurs==-1)
            maxOccurs = Math.max(minOccurs, maximumElementsGenerated);

        int min, max;
        if(minimumElementsGenerated>maxOccurs || maximumElementsGenerated process(XSParticle particle){
            XSTerm term = particle.getTerm();
            if(term instanceof XSModelGroup){
                XSModelGroup group = (XSModelGroup)term;
                if(group.getCompositor()==XSModelGroup.COMPOSITOR_CHOICE){
                    XSObjectList particles = group.getParticles();
                    int count = particles.getLength();
                    if(!generateAllChoices && !particle.getMaxOccursUnbounded())
                        count = Math.min(count, particle.getMaxOccurs());
                    List list = new ArrayList(particles.getLength());
                    for(int i=0; i(super.process(particle), repeatCount);
        }

        protected Sequence process(XSModelGroup modelGroup){
            switch(modelGroup.getCompositor()){
                case XSModelGroup.COMPOSITOR_ALL :
                    XSObjectList particles = modelGroup.getParticles();
                    List list = new ArrayList(particles.getLength());
                    for(int i=0; i(list);
                default:
                    return super.process(modelGroup);
            }
        }

        protected Sequence process(XSElementDeclaration elem){
            if(elem.getAbstract()){
                XSObjectList substitutionGroup = xsModel.getSubstitutionGroup(elem);
                if(substitutionGroup.getLength()==0)
                    return EmptySequence.getInstance();
                int rand = RandomUtil.random(0, substitutionGroup.getLength() - 1);
                return new DuplicateSequence(substitutionGroup.item(rand));
            }
            if(elem.getTypeDefinition() instanceof XSComplexTypeDefinition){
                XSComplexTypeDefinition complexType = (XSComplexTypeDefinition)elem.getTypeDefinition();
                if(complexType.getAbstract()){
                    XSTypeDefinition subType = null;
                    if(sampleValueGenerator!=null)
                        subType = sampleValueGenerator.selectSubType(elem);
                    if(subType==null){
                        List subTypes = XSUtil.getSubTypes(xsModel, complexType);
                        if(subTypes.isEmpty())
                            return EmptySequence.getInstance();
                        int rand = RandomUtil.random(0, subTypes.size() - 1);
                        subType = subTypes.get(rand);
                    }
                    return new DuplicateSequence(subType);
                }
            }
            return new DuplicateSequence(elem.getTypeDefinition());
        }
    }

    private class XSSampleVisitor extends ReflectionVisitor>{
        private XMLDocument doc;
        private String xsiSchemaLocation;
        private String xsiNoNamespaceSchemaLocation;

        private XSSampleVisitor(XMLDocument doc, String xsiSchemaLocation, String xsiNoNamespaceSchemaLocation){
            this.doc = doc;
            this.xsiSchemaLocation = xsiSchemaLocation;
            this.xsiNoNamespaceSchemaLocation = xsiNoNamespaceSchemaLocation;
        }

        private void addXSILocations() throws SAXException{
            if(doc.getDepth()==1){
                if(xsiSchemaLocation!=null)
                    doc.addAttribute(Namespaces.URI_XSI, "schemaLocation", xsiSchemaLocation);
                if(xsiNoNamespaceSchemaLocation!=null)
                    doc.addAttribute(Namespaces.URI_XSI, "noNamespaceSchemaLocation", xsiNoNamespaceSchemaLocation);
            }
        }

        @Override
        protected Processor getDefault(Object elem){
            return null;
        }

        protected Processor process(XSElementDeclaration elem){
            return elemProcessor;
        }

        protected Processor process(XSWildcard wildcard){
            return wildcardProcessor;
        }

        protected Processor process(XSComplexTypeDefinition complexType){
            return complexTypeProcessor;
        }

        protected Processor process(XSAttributeUse attr){
            return attrProcessor;
        }

        private Processor elemProcessor = new Processor(){
            private boolean isRecursionDepthCrossed(XSElementDeclaration elem, Path path){
                if(path.getRecursionDepth()>maximumRecursionDepth)
                    return true;

                int typeRecursionDepth = -1;
                while(path!=null){
                    if(path.getElement()==elem.getTypeDefinition())
                        typeRecursionDepth++;
                    path = path.getParentPath();
                }

                return typeRecursionDepth>maximumRecursionDepth;
            };


            @Override
            public boolean preProcess(XSElementDeclaration elem, Path path){
                if(isRecursionDepthCrossed(elem, path))
                    return false;
                try{
                    if(!Boolean.FALSE.equals(showContentModel) && elem.getTypeDefinition() instanceof XSComplexTypeDefinition){
                        XSComplexTypeDefinition complexType = (XSComplexTypeDefinition)elem.getTypeDefinition();

                        StringBuilder contentModel = new StringBuilder();

                        XSObjectList attributeUses = complexType.getAttributeUses();
                        if(!attributeUses.isEmpty()){
                            contentModel.append("@(");
                            for(int i=0; i0)
                                    contentModel.append(", ");
                                contentModel.append(XMLUtil.getQName(XSUtil.getQName(attrUse, doc.getNamespaceSupport())));
                                if(!attrUse.getRequired())
                                    contentModel.append('?');
                            }
                            contentModel.append(")");
                        }

                        switch(complexType.getContentType()){
                            case XSComplexTypeDefinition.CONTENTTYPE_ELEMENT:
                            case XSComplexTypeDefinition.CONTENTTYPE_MIXED:
                                if(contentModel.length()>0)
                                    contentModel.append(" ");
                                contentModel.append(new XSContentModel().toString(complexType, doc));
                        }

                        if(contentModel.length()>0){
                            boolean addComment = false;
                            if(Boolean.TRUE.equals(showContentModel))
                                addComment = true;
                            else{
                                for(char ch: "?*+|;[".toCharArray()){
                                    if(contentModel.indexOf(String.valueOf(ch))!=-1){
                                        addComment = true;
                                        break;
                                    }
                                }
                            }
                            if(addComment)
                                addComment(path, contentModel.toString());
                        }
                    }
                    doc.startElement(elem.getNamespace(), elem.getName());
                    addXSILocations();
                    return true;
                }catch(SAXException ex){
                    throw new ImpossibleException(ex);
                }
            }

            private void addComment(Path path, String comment) throws SAXException{
                int depth = 0;
                while(true){
                    path = path.getParentPath(XSElementDeclaration.class);
                    if(path!=null)
                        depth++;
                    else
                        break;
                }
                doc.addText("\n");
                for(int i=depth; i>0; i--)
                    doc.addText("   ");
                doc.addComment(comment);
                doc.addText("\n");
                for(int i=depth; i>0; i--)
                    doc.addText("   ");
            }

            @Override
            public void postProcess(XSElementDeclaration elem, Path path){
                if(isRecursionDepthCrossed(elem, path))
                    return;
                try{
                    switch(elem.getConstraintType()){
                        case XSConstants.VC_FIXED:
                            doc.addText(elem.getValueConstraintValue().getNormalizedValue());
                            break;
                        case XSConstants.VC_DEFAULT:
                            if(RandomUtil.randomBoolean(generateDefaultElementValues)){
                                doc.addText(elem.getValueConstraintValue().getNormalizedValue());
                                break;
                            }
                        default:
                            XSSimpleTypeDefinition simpleType = null;
                            if(elem.getTypeDefinition().getTypeCategory()==XSTypeDefinition.SIMPLE_TYPE)
                                simpleType = (XSSimpleTypeDefinition)elem.getTypeDefinition();
                            else{
                                XSComplexTypeDefinition complexType = (XSComplexTypeDefinition)elem.getTypeDefinition();
                                if(complexType.getContentType()==XSComplexTypeDefinition.CONTENTTYPE_SIMPLE)
                                    simpleType = complexType.getSimpleType();
                            }
                            if(simpleType!=null){
                                String sampleValue = null;
                                if(sampleValueGenerator!=null)
                                    sampleValue = sampleValueGenerator.generateSampleValue(elem, simpleType);
                                if(sampleValue==null)
                                    sampleValue = generateSampleValue(simpleType, elem.getName());
                                doc.addText(sampleValue);
                            }
                    }
                    doc.endElement();
                }catch(SAXException ex){
                    throw new ImpossibleException(ex);
                }
            }
        };

        private Processor attrProcessor = new Processor(){
            @Override
            public boolean preProcess(XSAttributeUse attr, Path path){
                try{
                    XSAttributeDeclaration decl = attr.getAttrDeclaration();

                    String sampleValue = null;
                    switch(attr.getConstraintType()){
                        case XSConstants.VC_FIXED:
                            if(RandomUtil.randomBoolean(generateFixedAttributes))
                                sampleValue = attr.getValueConstraintValue().getNormalizedValue();
                            break;
                        case XSConstants.VC_DEFAULT:
                            if(RandomUtil.randomBoolean(generateDefaultAttributes))
                                sampleValue = attr.getValueConstraintValue().getNormalizedValue();
                            break;
                        default:
                            if(attr.getRequired() || RandomUtil.randomBoolean(generateOptionalAttributes)){
                                if(sampleValueGenerator!=null)
                                    sampleValue = sampleValueGenerator.generateSampleValue(decl, decl.getTypeDefinition());
                                if(sampleValue==null)
                                    sampleValue = generateSampleValue(decl.getTypeDefinition(), decl.getName());
                            }
                    }
                    if(sampleValue!=null)
                        doc.addAttribute(decl.getNamespace(), decl.getName(), sampleValue);
                    return false;
                }catch(SAXException ex){
                    throw new ImpossibleException(ex);
                }
            }

            @Override
            public void postProcess(XSAttributeUse elem, Path path){}
        };

        private Processor complexTypeProcessor = new Processor(){
            @Override
            public boolean preProcess(XSComplexTypeDefinition complexType, Path path){
                try{
                    XSElementDeclaration elem = (XSElementDeclaration)path.getParentPath().getElement();
                    XSComplexTypeDefinition elemType = (XSComplexTypeDefinition)elem.getTypeDefinition();
                    if(elemType.getAbstract())
                        doc.addAttribute(Namespaces.URI_XSI, "type", doc.toQName(complexType.getNamespace(), complexType.getName()));
                    return true;
                }catch(SAXException ex){
                    throw new ImpossibleException(ex);
                }
            }

            @Override
            public void postProcess(XSComplexTypeDefinition complexType, Path path){}
        };

        private Processor wildcardProcessor = new Processor(){
            @Override
            public boolean preProcess(XSWildcard wildcard, Path path){
                if(wildcard.getProcessContents()==XSWildcard.PC_STRICT)
                    return true;
                try{
                    String uri;
                    switch(wildcard.getConstraintType()){
                        case XSWildcard.NSCONSTRAINT_ANY:
                            uri = "anyNS";
                            break;
                        case XSWildcard.NSCONSTRAINT_LIST:
                            StringList list = wildcard.getNsConstraintList();
                            int rand = RandomUtil.random(0, list.getLength()-1);
                            uri = list.item(rand);
                            if(uri==null)
                                uri = ""; //  returns nsConstraintList with null
                            break;
                        case XSWildcard.NSCONSTRAINT_NOT:
                            list = wildcard.getNsConstraintList();
                            List namespaces = new ArrayList();
                            for(int i=0; i counters = new HashMap();

        private static final String XSD_DATE_FORMAT = "yyyy-MM-dd";
        private static final String XSD_TIME_FORMAT = "HH:mm:ss";

        private String generateSampleValue(XSSimpleTypeDefinition simpleType, String hint){
            if(simpleType.getBuiltInKind()==XSConstants.LIST_DT){
                XSSimpleTypeDefinition itemType = simpleType.getItemType();

                int len;
                XSFacet facet = getFacet(itemType, XSSimpleTypeDefinition.FACET_LENGTH);
                if(facet!=null)
                    len = Integer.parseInt(facet.getLexicalFacetValue());
                else{
                    int minOccurs = 0;
                    facet = getFacet(itemType, XSSimpleTypeDefinition.FACET_MINLENGTH);
                    if(facet!=null)
                        minOccurs = Integer.parseInt(facet.getLexicalFacetValue());
                    int maxOccurs = -1;
                    facet = getFacet(itemType, XSSimpleTypeDefinition.FACET_MAXLENGTH);
                    if(facet!=null)
                        maxOccurs = Integer.parseInt(facet.getLexicalFacetValue());

                    if(maxOccurs==-1)
                        maxOccurs = Math.max(minOccurs, maximumListItemsGenerated);

                    int min, max;
                    if(minimumListItemsGenerated>maxOccurs || maximumListItemsGenerated enums = XSUtil.getEnumeratedValues(itemType);
                if(enums.isEmpty()){
                    StringBuilder buff = new StringBuilder();
                    while(len>0){
                        buff.append(" ");
                        buff.append(generateSampleValue(itemType, hint));
                        len--;
                    }
                    return buff.toString().trim();
                }else{
                    while(enums.size()(enums));
                    Collections.shuffle(enums);

                    StringBuilder buff = new StringBuilder();
                    while(len>0){
                        buff.append(" ");
                        buff.append(enums.remove(0));
                        len--;
                    }
                    return buff.toString().trim();
                }
            }else if(simpleType.getMemberTypes().getLength()>0){
                XSObjectList members = simpleType.getMemberTypes();
                int rand = RandomUtil.random(0, members.getLength()-1);
                return generateSampleValue((XSSimpleTypeDefinition)members.item(rand), hint);
            }

            List enums = XSUtil.getEnumeratedValues(simpleType);
            if(!enums.isEmpty())
                return enums.get(RandomUtil.random(0, enums.size()-1));

            XSSimpleTypeDefinition builtInType = simpleType;
            while(!Namespaces.URI_XSD.equals(builtInType.getNamespace()))
                builtInType = (XSSimpleTypeDefinition)builtInType.getBaseType();


            String name = builtInType.getName().toLowerCase();
            if("boolean".equals(name))
                return RandomUtil.randomBoolean() ? "true" : "false";

            if("double".equals(name)
                    || "decimal".equals(name)
                    || "float".equals(name)
                    || name.endsWith("integer")
                    || name.endsWith("int")
                    || name.endsWith("long")
                    || name.endsWith("short")
                    || name.endsWith("byte"))
                return randomNumber(simpleType, name);

            if("date".equals(name))
                return new SimpleDateFormat(XSD_DATE_FORMAT).format(new Date());
            if("time".equals(name))
                return new SimpleDateFormat(XSD_TIME_FORMAT).format(new Date());
            if("datetime".equals(name)){
                Date date = new Date();
                return new SimpleDateFormat(XSD_DATE_FORMAT).format(date)+'T'+new SimpleDateFormat(XSD_TIME_FORMAT).format(date);
            }else{
                Integer count = counters.get(hint);
                count = count==null ? 1 : ++count;
                counters.put(hint, count);
                String countStr = count.toString();

                XSFacet lengthFacet = getFacet(simpleType, XSSimpleTypeDefinition.FACET_LENGTH);

                XSFacet facet = getFacet(simpleType, XSSimpleTypeDefinition.FACET_MINLENGTH);
                if(facet==null)
                    facet = lengthFacet;
                if(facet!=null){
                    int len = Integer.parseInt(facet.getLexicalFacetValue());
                    len -= hint.length();
                    len -= countStr.length();
                    if(len>0){
                        char ch[] = new char[len];
                        Arrays.fill(ch, '_');
                        hint += new String(ch);
                    }
                }
                facet = getFacet(simpleType, XSSimpleTypeDefinition.FACET_MAXLENGTH);
                if(facet==null)
                    facet = lengthFacet;
                if(facet!=null){
                    int maxLen = Integer.parseInt(facet.getLexicalFacetValue());
                    int len = maxLen;
                    len = hint.length() + countStr.length() - len;
                    if(len>0){
                        if(hint.length()>len)
                            hint = hint.substring(0, hint.length()-len);
                        else{
                            hint = hint.substring(0, maxLen);
                            countStr = "";
                        }
                    }
                }
                String value = hint+countStr;

                if("base64binary".equals(name))
                    return DatatypeConverter.printBase64Binary(value.getBytes(IOUtil.UTF_8));
                else
                    return value;
            }
        }

        private XSFacet getFacet(XSSimpleTypeDefinition simpleType, int kind){
            XSObjectList facets = simpleType.getFacets();
            for(int i=0; i=0?fractionDigits:3)+"f", (Double)randomNumber);
            else
                str = String.valueOf(randomNumber);
            String number, fraction;
            int dot = str.indexOf(".");
            if(dot==-1){
                number = str;
                fraction = "";
            }else{
                number = str.substring(0, dot);
                fraction = str.substring(dot+1);
            }
            boolean negative = false;
            if(number.startsWith("-")){
                negative = true;
                number = number.substring(1);
            }
            if(totalDigits>=0){
                if(number.length()>totalDigits)
                    number = number.substring(0, totalDigits);
            }
            if(fractionDigits>=0){
                if(fraction.length()>fractionDigits)
                    fraction = fraction.substring(0, fractionDigits);
            }

            str = negative ? "-" : "";
            str += number;
            if(fraction.length()>0)
                str += '.' + fraction;
            return str;
        }
    }

    public void loadOptions(Properties options){
        String value = options.getProperty("minimumElementsGenerated");
        if(value!=null)
            minimumElementsGenerated = Integer.parseInt(value);
        value = options.getProperty("maximumElementsGenerated");
        if(value!=null)
            maximumElementsGenerated = Integer.parseInt(value);
        value = options.getProperty("minimumListItemsGenerated");
        if(value!=null)
            minimumListItemsGenerated = Integer.parseInt(value);
        value = options.getProperty("maximumListItemsGenerated");
        if(value!=null)
            maximumListItemsGenerated = Integer.parseInt(value);
        value = options.getProperty("maximumRecursionDepth");
        if(value!=null)
            maximumRecursionDepth = Integer.parseInt(value);

        value = options.getProperty("generateOptionalElements");
        if(value!=null)
            generateOptionalElements = "always".equals(value) ? Boolean.TRUE : ("never".equals(value) ? Boolean.FALSE : null);
        value = options.getProperty("generateOptionalAttributes");
        if(value!=null)
            generateOptionalAttributes = "always".equals(value) ? Boolean.TRUE : ("never".equals(value) ? Boolean.FALSE : null);
        value = options.getProperty("generateFixedAttributes");
        if(value!=null)
            generateFixedAttributes = "always".equals(value) ? Boolean.TRUE : ("never".equals(value) ? Boolean.FALSE : null);
        value = options.getProperty("generateDefaultAttributes");
        if(value!=null)
            generateDefaultAttributes = "always".equals(value) ? Boolean.TRUE : ("never".equals(value) ? Boolean.FALSE : null);
        value = options.getProperty("generateDefaultElementValues");
        if(value!=null)
            generateDefaultElementValues = "always".equals(value) ? Boolean.TRUE : ("never".equals(value) ? Boolean.FALSE : null);
        value = options.getProperty("generateAllChoices");
        if(value!=null)
            generateAllChoices = Boolean.parseBoolean(value);
        value = options.getProperty("showContentModel");
        if(value!=null)
            showContentModel = "always".equals(value) ? Boolean.TRUE : ("never".equals(value) ? Boolean.FALSE : null);
    }

    public SampleValueGenerator sampleValueGenerator;

    public static interface SampleValueGenerator{
        public String generateSampleValue(XSElementDeclaration element, XSSimpleTypeDefinition simpleType);
        public String generateSampleValue(XSAttributeDeclaration attribute, XSSimpleTypeDefinition simpleType);
        public XSTypeDefinition selectSubType(XSElementDeclaration element);
    }

    public static void main(String[] args) throws Exception{
        if(args.length==0){
            System.err.println("Usage:");
            System.err.println("\txsd-instance."+(OS.get().isWindows()?"bat":"sh")+"  [root-element]");
            System.err.println("Example:");
            System.err.println("\txsd-instance."+(OS.get().isWindows()?"bat":"sh")+" purchase-order.xsd {http://jlibs.org}PurchaseOrder");
            System.exit(1);
        }

        XSModel xsModel = new XSParser().parse(args[0]);
        QName rootElement = null;
        if(args.length>1)
            rootElement = QName.valueOf(args[1]);
        else{
            List elements = XSUtil.guessRootElements(xsModel);
            if(elements.size()==0){
                System.err.println("no elements found in given xml schema");
                System.exit(1);
            }else if(elements.size()==1){
                XSElementDeclaration elem = elements.get(0);
                rootElement = XSUtil.getQName(elem);
            }else{
                int i = 1;
                for(XSElementDeclaration elem: elements)
                    System.err.println(i++ +": "+XSUtil.getQName(elem));
                System.err.print("Select Root Element: ");
                String line = new BufferedReader(new InputStreamReader(System.in)).readLine();
                XSElementDeclaration elem = elements.get(Integer.parseInt(line)-1);
                rootElement = XSUtil.getQName(elem);
            }
        }

        XSInstance xsInstance = new XSInstance();
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("xsd-instance.properties");
        if(is!=null)
            xsInstance.loadOptions(CollectionUtil.readProperties(is, null));
        XMLDocument xml = new XMLDocument(new StreamResult(System.out), true, 4, null);
        XSUtil.suggestNamespacePrefixes(URLUtil.toURL(args[0]), xml);
        xsInstance.generate(xsModel, rootElement, xml);
        System.out.println();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy