Package dk.brics.xact.operations

Source Code of dk.brics.xact.operations.XMLValidator$ConvertedSchema

package dk.brics.xact.operations;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import dk.brics.misc.Origin;
import dk.brics.relaxng.Grammar;
import dk.brics.relaxng.converter.ParseException;
import dk.brics.relaxng.converter.RNGParser;
import dk.brics.relaxng.converter.RestrRelaxNG2XMLGraph;
import dk.brics.relaxng.converter.StandardDatatypes;
import dk.brics.relaxng.converter.dtd.DTD2RestrRelaxNG;
import dk.brics.relaxng.converter.xmlschema.XMLSchema2RestrRelaxNG;
import dk.brics.xact.XML;
import dk.brics.xact.XMLValidationException;
import dk.brics.xmlgraph.ElementNode;
import dk.brics.xmlgraph.SequenceNode;
import dk.brics.xmlgraph.XMLGraph;
import dk.brics.xmlgraph.validator.ValidationErrorHandler;
import dk.brics.xmlgraph.validator.Validator;

/**
* Schema validation for XML templates.
* Supported schema languages: DTD, XML Schema, and Restricted RELAX NG.
*/
public class XMLValidator { // TODO: runtime validation is slow!
 
    private final static Collection<ConvertedSchema> schemas = new ArrayList<ConvertedSchema>();;

  private final static StandardDatatypes datatypes = new StandardDatatypes();

  private XMLValidator() {}

  /**
   * Checks that the given template is valid according to the given schema type.
   * @throws XMLValidationException if the template is invalid
   */
  public static void validate(XML x, String type, Origin origin) throws XMLValidationException {
    XMLGraph xg = XMLGraphConverter.convert(x, XMLGraphConverter.GapConversion.IGNORE, false); // TODO: make variant of validate that treats gaps optimistically instead of just ignoring them
    //new dk.brics.xmlgraph.converter.XMLGraph2Dot(System.out).print(xg);
    XMLGraph t = null;
    int i = 0;
    String expanded = expandQName(type, origin);
    for (ConvertedSchema s : schemas) {
      SequenceNode n = s.types.get(expanded);
      if (n != null) {
        t = s.xg;
        i = n.getIndex();
        break;
      }
    }
    if (t == null)
      throw new XMLValidationException("No schema found for type " + type, origin);
    LocalValidationErrorHandler handler = new LocalValidationErrorHandler();
    new Validator(handler).validate(xg, t, i);
    if (!handler.errors.isEmpty()) {
      XMLValidationException ex = null;
      while (!handler.errors.isEmpty()) {
        XMLValidationException e = handler.errors.pop();
        e.setNext(ex);
        ex = e;
      }
      throw ex;
    }
  }
 
  private static String expandQName(String type, Origin origin) {
    return expandQName(type, XML.getThreadNamespaceMap(), XML.getNamespaceMap(), origin);
  }

  /**
   * Converts a QName into an expanded name using the given namespace maps.
   * @throws XMLValidationException if the prefix is not declared in any of the given maps
   */
  public static String expandQName(String type, Map<String,String> map1, Map<String,String> map2, Origin origin) {
    if (type.startsWith("{")) // already expanded
      return type;
    String prefix;
    String localname;
    int i = type.indexOf(':');
    if (i == -1) {
      prefix = "";
      localname = type;
    } else {
      prefix = type.substring(0, i);
      localname = type.substring(i + 1);
    }
    String ns = null;
    if (map1 != null)
      ns = map1.get(prefix);
    if (ns == null && map2 != null)
      ns = map2.get(prefix);
    if (ns == null && prefix.length() == 0)
      ns = "";
    if (ns == null)
      throw new XMLValidationException("Undeclared namespace prefix " + prefix, origin);
    if (ns.length() == 0)
      return localname;
    else
      return "{" + ns + "}" + localname;
  }

  /**
   * Loads an XML schema.
   * @throws ParseException if a parse error occurs
   */
  public static void loadXMLSchema(URL url) throws ParseException {
    loadXMLSchema(url, null);
  }
 
  /**
   * Loads an XML schema into an existing XML graph.
   * @throws ParseException if a parse error occurs
   */
  public static Map<String,SequenceNode> loadXMLSchema(URL url, XMLGraph xg) throws ParseException {
    boolean extend = xg != null;
    String u = url.toString();
    Map<String,SequenceNode> types = new HashMap<String,SequenceNode>();
    RNGParser rngparser = new RNGParser();
    RestrRelaxNG2XMLGraph rrng2xg = new RestrRelaxNG2XMLGraph(xg, datatypes);
    if (u.endsWith(".xsd")) {
      XMLSchema2RestrRelaxNG xsd2rrng = new XMLSchema2RestrRelaxNG(datatypes);
      if (extend)
        rrng2xg.extend(rngparser.parse(xsd2rrng.convert(url), url));
      else
        xg = rrng2xg.convert(rngparser.parse(xsd2rrng.convert(url), url));
      Map<String,String> m1 = xsd2rrng.getNameMap();
      Map<String,String> m2 = rngparser.getTopLevelNewNames();
      Map<String,SequenceNode> m3 = rrng2xg.getDefineNodes();
      for (Map.Entry<String,String> e : m1.entrySet())
        types.put(e.getKey(), m3.get(m2.get(e.getValue())));
    } else if (u.endsWith(".dtd")) {
      DTD2RestrRelaxNG dtd2rrng = new DTD2RestrRelaxNG();
      if (extend)
        rrng2xg.extend(rngparser.parse(dtd2rrng.convert(url), url));
      else
        xg = rrng2xg.convert(rngparser.parse(dtd2rrng.convert(url), url));
      Map<String,String> m1 = dtd2rrng.getNameMap();
      Map<String,String> m2 = rngparser.getTopLevelNewNames();
      Map<String,SequenceNode> m3 = rrng2xg.getDefineNodes();
      for (Map.Entry<String,String> e : m1.entrySet())
        types.put(e.getKey(), m3.get(m2.get(e.getValue())));
    } else if (u.endsWith(".rrng") || u.endsWith(".rng")) {
      Grammar rrng = rngparser.parse(url);
      if (!rrng.check(System.err))
        throw new ParseException("Schema is not Restricted RELAX NG " + url);
      if (extend)
        rrng2xg.extend(rrng);
      else
        xg = rrng2xg.convert(rrng);
      Map<String,String> m1 = rngparser.getTopLevelNewNames();
      Map<String,SequenceNode> m2 = rrng2xg.getDefineNodes();
      for (Map.Entry<String,String> e : m1.entrySet())
        types.put(e.getKey(), m2.get(e.getValue()));
    } else
      throw new ParseException("Unrecognized schema type " + url);
    if (extend)
      return types;
    else {
      schemas.add(new ConvertedSchema(xg, types));
      return null;
    }
  }
 
  private static class ConvertedSchema {
   
    final XMLGraph xg; 

    final Map<String,SequenceNode> types;

    ConvertedSchema(XMLGraph xg, Map<String,SequenceNode> types) {
      this.xg = xg;
      this.types = types;
    }
  }
 
  private static class LocalValidationErrorHandler implements ValidationErrorHandler {
   
    final Stack<XMLValidationException> errors = new Stack<XMLValidationException>();
   
    public boolean error(ElementNode n, Origin origin, String msg, String example, Origin schema) {
      StringBuilder b = new StringBuilder(msg);
      Origin or;
      if (n != null) {
        String name = n.getName().getShortestExample(true);
        int i1 = name.indexOf('@');
        name = (i1 < 0) ? name : name.substring(0, i1);
        int i2 = name.indexOf('%');
        name = (i2 < 0) ? name : name.substring(0, i2);
        b.append(" at element " + name);
        or = n.getOrigin();
      } else {
        b.append(" at root");
        or = origin;
      }
      if (example != null)
        b.append(": ").append(example.length() > 0 ? example : "[empty contents]");
      errors.push(new XMLValidationException(b.toString(), or));
      return true;
    }
  }
}
TOP

Related Classes of dk.brics.xact.operations.XMLValidator$ConvertedSchema

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.
m/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');