Package dk.brics.xact.analysis.transformations

Source Code of dk.brics.xact.analysis.transformations.Visitor

package dk.brics.xact.analysis.transformations;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

import dk.brics.misc.Origin;
import dk.brics.relaxng.converter.ParseException;
import dk.brics.xact.XMLException;
import dk.brics.xact.analysis.config.Configuration;
import dk.brics.xact.analysis.flowgraph.FlowGraph;
import dk.brics.xact.analysis.flowgraph.SchemaType;
import dk.brics.xact.analysis.flowgraph.Statement;
import dk.brics.xact.analysis.flowgraph.TemplateConstant;
import dk.brics.xact.analysis.flowgraph.statements.AnalyzeStm;
import dk.brics.xact.analysis.flowgraph.statements.BasicStatementVisitor;
import dk.brics.xact.analysis.flowgraph.statements.ConstStm;
import dk.brics.xact.analysis.flowgraph.statements.GapifyStm;
import dk.brics.xact.analysis.flowgraph.statements.ValidateStm;
import dk.brics.xact.analysis.xmlgraph.XMLSchemaDatatypes;
import dk.brics.xact.operations.XMLGraphConverter;
import dk.brics.xact.operations.XMLValidator;
import dk.brics.xmlgraph.AttributeNode;
import dk.brics.xmlgraph.ChoiceNode;
import dk.brics.xmlgraph.ElementNode;
import dk.brics.xmlgraph.MultiContentNode;
import dk.brics.xmlgraph.Node;
import dk.brics.xmlgraph.OneOrMoreNode;
import dk.brics.xmlgraph.SequenceNode;
import dk.brics.xmlgraph.SingleContentNode;
import dk.brics.xmlgraph.TextNode;
import dk.brics.xmlgraph.XMLGraph;
import dk.brics.xmlgraph.XMLGraphFragment;

/**
* Loads all XML schemas used by the program and then builds an XML graph containing:
* <ul>
* <li>All types defined in loaded XML schemas.
* <li>The necessary types from the XMLSchema-datatypes namespace.
* <li>The necessary quantified versions of those types (suffixed ?,+, or * accordingly)
* </ul>
* Additionally, only <tt>ChoiceNode</tt>s can have element and attribute nodes as content,
* and the contents of each element node will be a <tt>ChoiceNode</tt>.
* <p/>
* A <em>quantified</em> type is the name of a type suffixed with ?, + or *, corresponding
* to the zero-or-one, one-or-more and zero-or-more operators. Quantifiers can appear both
* inside and outside of gap annotations.
* For example, these are all types that use quantifiers:
* <pre>
*  foo+
*  foo[bar* X]
*  foo*[bar+ Z]
*  foo[bar? X, baz? Y]
* </pre>
*/
public class SchemaTypeLinking {
   
    enum Quantifier {
        One,
        OneOrMore,
        ZeroOrOne,
        ZeroOrMore,
    }
   
    public void run(final FlowGraph g, Configuration config) {
        final XMLGraph xg = new XMLGraph();
        g.setXMLGraph(xg);
       
        // load XML Schemas
        for (Map.Entry<String,Origin> entry : g.getSchemas().entrySet()) {
            try {
                Map<String,SequenceNode> map = XMLValidator.loadXMLSchema(new URL(entry.getKey()), xg);
                g.getTypemap().putAll(map);
            } catch (MalformedURLException ex) {
                throw new XMLException("Schema URL is malformed", ex, entry.getValue());
            } catch (ParseException ex) {
                throw new XMLException("Could not parse schema: " + ex.getMessage(), ex, entry.getValue());
            }
        }
        for (URL url : config.getAdditionalSchemas()) {
            try {
                Map<String, SequenceNode> map = XMLValidator.loadXMLSchema(url, xg);
                g.getTypemap().putAll(map);
            } catch (ParseException ex) {
                throw new XMLException("Could not parse schema: " + ex.getMessage(), ex);
            }
        }
       
        // link SchemaTypes to their nodes
        final class Visitor extends BasicStatementVisitor {
            Origin origin;
            @Override
            public void visitAnalyzeStm(AnalyzeStm s) {
              if (s.getKind() != AnalyzeStm.Kind.HOTSPOT) {
                linkSchemaType(s, s.getSchema());
              }
            }
            @Override
            public void visitValidateStm(ValidateStm s) {
                linkSchemaType(s, s.getSchema());
            }
            @Override
            public void visitGapifyStm(GapifyStm s) {
                if (s.getSchema() != null)
                    linkSchemaType(s, s.getSchema());
            }
            @Override
            public void visitConstStm(ConstStm s) {
                TemplateConstant t = s.getConst();
                if (t.getFragment() != null)
                    return;
                // handle empty XML specially to avoid "" text nodes
                if (t.getXML().isText() && t.getXML().asText().getString().isEmpty()) {
                    SequenceNode n = new SequenceNode(Collections.<Integer>emptyList(), s.getOrigin());
                    xg.addNode(n);
                    XMLGraphFragment f = new XMLGraphFragment(n, null, null, null);
                    t.setFragment(f);
                } else {
                    XMLGraphFragment f = XMLGraphConverter.extend(xg, t.getXML(), XMLGraphConverter.GapConversion.CLOSED, true);
                    for (Map.Entry<String,String> en : f.getGapTypeMap().entrySet()) {
                        String gaptype = en.getValue();
                        // add gap types to the xml graph, and ensure that they are valid
                        resolveTypename(gaptype);
                    }
                    t.setFragment(f);
                }
            }
            void linkSchemaType(Statement s, SchemaType type) {
                type.setTypeNode(resolveTypename(type.getType()));
                for (Map.Entry<String,String> gap : type.getGapTypes().entrySet()) {
                    String gapname = gap.getKey();
                    String gaptype = gap.getValue();
                    type.getGapTypeNodes().put(gapname, resolveTypename(gaptype));
                }
            }
            Node resolveTypename(String name) {
                if (g.getTypemap().containsKey(name)) {
                    return g.getTypemap().get(name);
                }
               
                String schemaName;
                Quantifier quantifier;
                if (name.endsWith("?")) {
                    schemaName = name.substring(0, name.length() - 1);
                    quantifier = Quantifier.ZeroOrOne;
                } else if (name.endsWith("+")) {
                    schemaName = name.substring(0, name.length() - 1);
                    quantifier = Quantifier.OneOrMore;
                } else if (name.endsWith("*")) {
                    schemaName = name.substring(0, name.length() - 1);
                    quantifier = Quantifier.ZeroOrMore;
                } else {
                    schemaName = name;
                    quantifier = Quantifier.One;
                }
               
                Node base = g.getTypemap().get(schemaName);
               
                // add datatypes on-the-fly when needed
                if (base == null) {
                    if (XMLSchemaDatatypes.isDatatype(schemaName)) {
                        base = new TextNode(XMLSchemaDatatypes.getDatatype(schemaName, origin), new Origin("", -1, -1));
                        xg.addNode(base);
                        g.getTypemap().put(schemaName, base);
                    }
                }
               
                // if still not found, it does not exist
                if (base == null) {
                    throw new XMLException("Could not find schema for type " + schemaName + " referred to at " + origin, origin);
                }
               
                Node result;
                OneOrMoreNode on;
                SequenceNode empty;
                switch (quantifier) {
                case One:
                    result = base;
                    break;
                case OneOrMore:
                    result = new OneOrMoreNode(base.getIndex(), origin);
                    xg.addNode(result);
                    break;
                case ZeroOrMore:
                    empty = new SequenceNode(Collections.<Integer>emptyList(), origin);
                    on = new OneOrMoreNode(base.getIndex(), origin);
                    xg.addNode(empty);
                    xg.addNode(on);
                    result = new ChoiceNode(set(empty.getIndex(), on.getIndex()), origin);
                    xg.addNode(result);
                    break;
                case ZeroOrOne:
                default:
                    empty = new SequenceNode(Collections.<Integer>emptyList(), origin);
                    xg.addNode(empty);
                    result = new ChoiceNode(set(base.getIndex(), empty.getIndex()), origin);
                    xg.addNode(result);
                    break;
                }
               
                g.getTypemap().put(name, result);
               
                return result;
            }
        }
        Visitor visitor = new Visitor();
        for (Statement stm : g.getNodes()) {
            visitor.origin = stm.getOrigin();
            stm.visitBy(visitor);
        }
        wrapNodesInChoices(xg);
    }
   
    private Set<Integer> set(Integer ... contents) {
        return new LinkedHashSet<Integer>(Arrays.asList(contents));
    }
   
    /** Puts a choice node around each element node, and before each attribute node */
    void wrapNodesInChoices(XMLGraph xg) {
        Map<Node, ChoiceNode> pred = new HashMap<Node, ChoiceNode>();
        Map<Node, ChoiceNode> succ = new HashMap<Node, ChoiceNode>();
        for (Node node : new ArrayList<Node>(xg.getNodes())) {
            if (node instanceof ElementNode) {
                ElementNode en = (ElementNode)node;
                LinkedHashSet<Integer> cs1 = new LinkedHashSet<Integer>();
                cs1.add(en.getIndex());
                ChoiceNode ch1 = new ChoiceNode(cs1, node.getOrigin());
                pred.put(en, ch1);
                xg.addNode(ch1);
               
                LinkedHashSet<Integer> cs2 = new LinkedHashSet<Integer>();
                cs2.add(en.getContent());
                ChoiceNode ch2 = new ChoiceNode(cs2, node.getOrigin());
                succ.put(en, ch2);
                xg.addNode(ch2);
               
                en.setContent(ch2.getIndex());
            }
            else if (node instanceof AttributeNode) {
                AttributeNode an = (AttributeNode)node;
                LinkedHashSet<Integer> cs = new LinkedHashSet<Integer>();
                cs.add(an.getIndex());
                ChoiceNode ch = new ChoiceNode(cs, node.getOrigin());
                pred.put(an, ch);
                xg.addNode(ch);
            }
        }
        // update ingoing edges
        for (Node node : xg.getNodes()) {
            if (node instanceof SingleContentNode) {
                SingleContentNode sc = (SingleContentNode)node;
                Node content = xg.getNode(sc.getContent());
                if (content instanceof ElementNode || content instanceof AttributeNode) {
                    sc.setContent(pred.get(content).getIndex());
                }
            }
            else if (node instanceof MultiContentNode) {
                MultiContentNode mc = (MultiContentNode)node;
                LinkedList<Integer> newcontent = new LinkedList<Integer>();
                LinkedList<Integer> removedcontent = new LinkedList<Integer>();
                for (int child : mc.getContents()) {
                    Node content = xg.getNode(child);
                    if (content instanceof ElementNode || content instanceof AttributeNode) {
                        Node p = pred.get(content);
                        if (p != mc) { // predecessor might be this node -- don't change that
                            newcontent.add(p.getIndex());
                            removedcontent.add(child);
                        }
                    }
                }
                mc.getContents().removeAll(removedcontent);
                mc.getContents().addAll(newcontent);
            }
        }
    }

}
TOP

Related Classes of dk.brics.xact.analysis.transformations.Visitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.