Package com.caucho.relaxng

Source Code of com.caucho.relaxng.VerifierHandlerImpl$EndElementKey

/*
* Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT.  See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
*   Free Software Foundation, Inc.
*   59 Temple Place, Suite 330
*   Boston, MA 02111-1307  USA
*
* @author Scott Ferguson
*/

package com.caucho.relaxng;

import com.caucho.relaxng.program.EmptyItem;
import com.caucho.relaxng.program.Item;
import com.caucho.util.CharBuffer;
import com.caucho.util.L10N;
import com.caucho.util.LruCache;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.Vfs;
import com.caucho.xml.QName;

import org.xml.sax.Attributes;
import org.xml.sax.ErrorHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* JARV verifier implementation
*/
public class VerifierHandlerImpl extends DefaultHandler
  implements VerifierHandler
{
  private static final L10N L = new L10N(VerifierHandlerImpl.class);
  protected static final Logger log
    = Logger.getLogger(VerifierHandlerImpl.class.getName());

  // very verbose logging
  private static final boolean _isDebug = false;

  private SchemaImpl _schema;
  private VerifierImpl _verifier;
  private boolean _hasProgram;

  private boolean _isValid = true;

  private LruCache<Object,Item> _programCache;

  private QName _name;
  private ArrayList<QName> _nameStack = new ArrayList<QName>();
  private String _eltLocation;
  private ArrayList<String> _eltLocationStack = new ArrayList<String>();

  private Item _item;
  private ArrayList<Item> _itemStack = new ArrayList<Item>();

  private Locator _locator;
 
  private boolean _isLogFinest;

  private CharBuffer _text = new CharBuffer();
  private boolean _hasText;
 
  private StartKey _startKey = new StartKey();
  private EndElementKey _endElementKey = new EndElementKey();

  /**
   * Creates the Verifier Handler.
   */
  public VerifierHandlerImpl(SchemaImpl schema, VerifierImpl verifier)
  {
    _schema = schema;
    _programCache = _schema.getProgramCache();
    _verifier = verifier;
  }

  /**
   * Creates the Verifier Handler.
   */
  public VerifierHandlerImpl(SchemaImpl schema)
  {
    this(schema, (VerifierImpl) schema.newVerifier());
  }

  /**
   * Sets the locator.
   */
  public void setDocumentLocator(Locator locator)
  {
    _locator = locator;
  }
 
  /**
   * Sets the error handler
   */
  public void setErrorHandler(ErrorHandler errorHandler)
    throws SAXException
  {
    _verifier.setErrorHandler(errorHandler);
  }

  private String getFileName()
  {
    if (_locator != null)
      return _locator.getSystemId();
    else
      return null;
  }

  private int getLine()
  {
    if (_locator != null)
      return _locator.getLineNumber();
    else
      return -1;
  }

  /**
   * Called when the document starts.
   */
  public void startDocument()
    throws SAXException
  {
    try {
      _nameStack.clear();
      _itemStack.clear();
      _eltLocationStack.clear();

      _name = new QName("#top", "");
      _item = _schema.getStartItem();

      _itemStack.add(_item);

      _eltLocation = getLocation();

      _isLogFinest = _isDebug && log.isLoggable(Level.FINEST);
      _hasText = false;
      _text.clear();
    } catch (Exception e) {
      error(e);
    }
  }

  /**
   * Called when an element starts.
   */
  public void startElement(String uri, String localName,
                           String qName, Attributes attrs)
    throws SAXException
  {
    if (! _isValid)
      return;

    if (_hasText)
      sendText();
   
    if (_isLogFinest)
      log.finest("element start: " + qName);

    try {
      QName parent = _name;
      _nameStack.add(parent);

      String parentLocation = _eltLocation;
      _eltLocationStack.add(parentLocation);
     
      QName name = new QName(qName, uri);
      _name = name;
      _eltLocation = getLocation();

      Item newItem = getStartElement(_item, name);

      if (newItem == null) {
  Item parentItem = _itemStack.get(_itemStack.size() - 1);
 
        if (parent.getName().equals("#top"))
          throw new RelaxException(L.l("<{0}> is an unexpected top-level tag.{1}",
                                       errorNodeName(name, _item, parentItem),
               errorMessageDetail(_item, parentItem, null, name)));
        else
          throw new RelaxException(L.l("<{0}> is an unexpected tag (parent <{1}> starts at {2}).{3}",
                                       errorNodeName(name, _item, parentItem),
               parent.getName(), parentLocation,
                                       errorMessageDetail(_item, parentItem, parent.getName(), name)));
      }

      _item = newItem;
      _itemStack.add(newItem);

      Item parentItem = newItem;

      int len = attrs.getLength();
      for (int i = 0; i < len; i++) {
        String attrUri = attrs.getURI(i);
        String attrQName = attrs.getQName(i);
        String value = attrs.getValue(i);
       
        if (_isLogFinest)
          log.finest("attribute: " + attrQName + "=\"" + value + "\"");
       
        name = new QName(attrQName, attrUri);

        if (attrQName.startsWith("xml:")) {
        }
        else if (! _item.allowAttribute(name, value)) {
          throw new RelaxException(L.l("{0}=\"{1}\" is an unexpected attribute in <{2}>.{3}",
                                       attrQName, value, qName,
               attributeMessageDetail(_item,
                    parentItem,
                    qName, null)));
  }
        else
          _item = _item.setAttribute(name, value);

        if (_item == null)
          _item = EmptyItem.create();
      }

      newItem = _item.attributeEnd();
      if (newItem == null) {
        throw new RelaxException(L.l("<{0}> expects more attributes.{1}",
                                     qName,
             attributeMessageDetail(_item,
                  parentItem,
                  qName, null)));
      }
     
      _item = newItem;
    } catch (Exception e) {
      error(e);
    }
  }

  private Item getStartElement(Item item, QName name)
    throws RelaxException
  {
    _startKey.init(item, name);

    Item newItem = null;//_programCache.get(_startKey);

    if (newItem != null) {
      return newItem;
    }
   
    newItem = _item.startElement(name);

    /*
    if (newItem != null)
      _programCache.put(new StartKey(item, name), newItem);
    */

    return newItem;
  }
 
  public void characters(char ch[], int start, int length)
    throws SAXException
  {
    _hasText = true;
    _text.append(ch, start, length);
  }

  public void sendText()
    throws SAXException
  {
    if (! _hasText)
      return;

    _hasText = false;

    try {
      Item newItem = _item.text(_text);
     
      if (newItem == null) {
        String string = _text.toString();

  Item parentItem = _itemStack.get(_itemStack.size() - 1);
 
        throw new RelaxException(L.l("The following text is not allowed in this context.\n{0}\n{1}", string,
             errorMessageDetail(_item, parentItem,
              _name.getName(), null)));
      }

      _text.clear();
      _item = newItem;                 
    } catch (Exception e) {
      _text.clear();
      error(e);
    }
  }

  /**
   * Called when an element ends.
   */
  public void endElement(String uri, String localName, String qName)
    throws SAXException
  {
    if (_hasText)
      sendText();
   
    if (! _isValid)
      return;

    if (_isLogFinest)
      log.finest("element end: " + qName);

    QName name = _name;
    QName parent = _nameStack.remove(_nameStack.size() - 1);
    _name = parent;

    Item parentItem = _itemStack.remove(_itemStack.size() - 1);

    String eltOpen = _eltLocation;
    _eltLocation = _eltLocationStack.remove(_eltLocationStack.size() - 1);
   
    try {
      Item nextItem = getEndElement(_item);
     
      if (nextItem == null)
        throw new RelaxException(L.l("<{0}> closed while expecting more elements (open at {1}).{2}",
                                     qName, eltOpen,
             requiredMessageDetail(_item, parentItem,
              qName, null)));
     
      _item = nextItem;
    } catch (Exception e) {
      error(e);
    }
  }

  private Item getEndElement(Item item)
    throws RelaxException
  {
    _endElementKey.init(item);

    Item newItem = null;//_programCache.get(_endElementKey);

    if (newItem != null) {
      return newItem;
    }
   
    newItem = _item.endElement();

    /*
    if (newItem != null)
      _programCache.put(new EndElementKey(item), newItem);
    */

    return newItem;
  }

  /**
   * Called for errors.
   */
  private void error(SAXException e)
    throws SAXException
  {
    _isValid = false;

    _verifier.error(new SAXParseException(e.getMessage(), _locator));
  }

  /**
   * Called for errors.
   */
  private void error(Exception e)
    throws SAXException
  {
    if (e instanceof RuntimeException)
      throw (RuntimeException) e;
    else if (e instanceof SAXException)
      error((SAXException) e);
    else
      error(new SAXException(e.getMessage(), e));
  }

  /**
   * Returns a string containing the allowed values.
   */
  private String errorNodeName(QName name, Item item, Item parentItem)
  {
    Item currentItem = item;
   
    if (currentItem == null)
      currentItem = parentItem;

    if (currentItem == null)
      return name.toString();

    HashSet<QName> values = new LinkedHashSet<QName>();
    currentItem.firstSet(values);

    for (QName value : values) {
      if (! name.getLocalName().equals(value.getLocalName())) {
      }
      else if (name.getPrefix() == null || name.getPrefix().equals("")) {
  return name.getName() + " xmlns=\"" + name.getNamespaceURI() + "\"";
      }
      else {
  return name.getName() + " xmlns:" + name.getPrefix() + "=\"" + name.getNamespaceURI() + "\"";
      }
    }

    return name.getName();
  }

  /**
   * Returns a string containing the allowed values.
   */
  private String errorMessageDetail(Item item, Item parentItem,
            String parentName, QName qName)
  {
    Item currentItem = item;
   
    if (currentItem == null)
      currentItem = parentItem;

    HashSet<QName> values = new LinkedHashSet<QName>();
    currentItem.firstSet(values);

    String expected = null;
    if (values.size() <= 5)
      expected = namesToString(values, parentName, qName,
             currentItem.allowEmpty());
   
    return (getLineContext(getFileName(), getLine())
      + syntaxMessage(item, parentItem, parentName, qName, expected));
  }

  /**
   * Returns a string containing the allowed values.
   */
  private String requiredMessageDetail(Item item, Item parentItem,
               String parentName, QName qName)
  {
    Item currentItem = item;

    if (currentItem == null)
      currentItem = parentItem;

    HashSet<QName> values = new LinkedHashSet<QName>();
    currentItem.requiredFirstSet(values);
     
    String expected = null;
   
    if (values.size() <= 5) {
      expected = namesToString(values, parentName, qName,
             currentItem.allowEmpty());
    }
   
    return (getLineContext(getFileName(), getLine())
      + syntaxMessage(item, parentItem, parentName, qName, expected));
  }

  /**
   * Returns a string containing the allowed values.
   */
  private String attributeMessageDetail(Item item, Item parentItem,
          String parentName, QName qName)
  {
    Item currentItem = item;

    if (currentItem == null)
      currentItem = parentItem;
   
    String allowed = allowedAttributes(currentItem, qName);
   
    return (getLineContext(getFileName(), getLine())
      + syntaxMessage(item, parentItem, parentName, qName, allowed));
  }

  /**
   * Returns a string containing the allowed values.
   */
  private String syntaxMessage(Item item, Item parentItem,
             String parentName, QName qName,
             String expected)
  {
    String syntaxPrefix;

    if (parentName == null || parentName.equals("#top"))
      syntaxPrefix = "Syntax: ";
    else
      syntaxPrefix = "<" + parentName + "> syntax: ";
   
    String msg = "";

    Item topParent = null;
    for (Item parent = item;
   parent != null;
   parent = null) { // parent.getParent()) {
      if (qName != null && parent.allowsElement(qName)) {
  msg = "\n Check for duplicate and out-of-order tags.";

  if (expected != null)
    msg += expected + "\n";
 
  msg += "\n";

  String prefix = "Syntax: ";
  if (parent == parentItem)
    prefix = syntaxPrefix;
 
  msg += prefix + parent.toSyntaxDescription(prefix.length());
  break;
      }
     
      // topParent = parent;
    }

    if (topParent == null || topParent instanceof EmptyItem) {
      topParent = parentItem;

      if (qName != null && topParent.allowsElement(qName)) {
  msg = "\n Check for duplicate and out-of-order tags.";

  if (expected != null)
    msg += expected + "\n";
 
  msg += "\n";

  String prefix = syntaxPrefix;
  msg += prefix + topParent.toSyntaxDescription(prefix.length());
      }
    }

    if (msg.equals("")) {
      msg = "";

      if (expected != null)
  msg += expected + "\n";
     
      msg += "\n";

      String prefix = syntaxPrefix;
      msg += prefix + topParent.toSyntaxDescription(prefix.length());
    }

    return msg;
  }

  /**
   * Returns a string containing the allowed values.
   */
  private String requiredValues(Item item, String parentName, QName qName)
  {
    if (item == null)
      return "";

    HashSet<QName> values = new LinkedHashSet<QName>();
    item.requiredFirstSet(values);

    return namesToString(values, parentName, qName, item.allowEmpty());
  }

  private String namesToString(HashSet<QName> values,
             String parentName,
             QName qName,
             boolean allowEmpty)
  {
    CharBuffer cb = new CharBuffer();
    if (values.size() > 0) {
      ArrayList<QName> sortedValues = new ArrayList<QName>(values);
      Collections.sort(sortedValues);
     
      for (int i = 0; i < sortedValues.size(); i++) {
        QName name = sortedValues.get(i);

        if (i == 0)
          cb.append("\n\n");
        else if (i == sortedValues.size() - 1)
          cb.append(" or\n");
        else
          cb.append(",\n");

  if (name.getName().equals("#text")) {
    cb.append("text");
  }
        else if (name.getNamespaceURI() == null || qName == null)
          cb.append("<" + name.getName() + ">");
        else if (qName.getNamespaceURI() != name.getNamespaceURI()) {
          if (name.getPrefix() != null)
            cb.append("<" + name.getName() + " xmlns:" + name.getPrefix() + "=\"" + name.getNamespaceURI() + "\">");
          else
            cb.append("<" + name.getName() + " xmlns=\"" + name.getNamespaceURI() + "\">");
        }
        else
          cb.append("<" + name.getName() + ">");
      }

      if (values.size() == 1)
        cb.append(" is expected");
      else
        cb.append(" are expected");

      if (allowEmpty) {
        if (parentName == null || parentName.equals("#top"))
          cb.append(",\nor the document may end.");
        else
          cb.append(",\nor </" + parentName + "> may close.");
      }
      else
        cb.append(".");
    }
    else if (allowEmpty) {
      if (parentName == null || parentName.equals("#top"))
        cb.append("\n\nThe document is expected to end.");
      else
        cb.append("\n\n</" + parentName + "> is expected to close.");
    }

    return cb.toString();
  }     

  /**
   * Returns a string containing the allowed values.
   */
  private String allowedAttributes(Item item, QName qName)
  {
    if (item == null)
      return "";

    HashSet<QName> values = new LinkedHashSet<QName>();
    item.attributeSet(values);

    CharBuffer cb = new CharBuffer();
    if (values.size() > 0) {
      ArrayList<QName> sortedValues = new ArrayList<QName>(values);
      Collections.sort(sortedValues);
     
      for (int i = 0; i < sortedValues.size(); i++) {
        QName name = sortedValues.get(i);

        if (i == 0)
          cb.append("\n\n");
        else if (i == sortedValues.size() - 1)
          cb.append(" or ");
        else
          cb.append(", ");

  String uri = name.getNamespaceURI();
        if (uri == null || uri.equals(""))
          cb.append("'" + name.getName() + "'");
        else if (qName == null || qName.getName().equals(name.getName()))
          cb.append("'" + name.getCanonicalName() + "'");
        else
          cb.append("'" + name.getName() + "'");
      }

      if (values.size() == 1)
        cb.append(" is expected.");
      else
        cb.append(" are expected.");
    }

    return cb.toString();
  }     

  /**
   * Returns the current location.
   */
  private String getLocation()
  {
    if (_locator == null)
      return "";
    else
      return "" + _locator.getLineNumber();
  }
 
  /**
   * Checks if the document was valid.
   *
   * <p>
   * This method can be only called after this handler receives
   * the <code>endDocument</code> event.
   *
   * @return
   *    <b>true</b> if the document was valid,
   *    <b>false</b> if not.
   *
   * @exception IllegalStateException
   *    If this method is called before the endDocument event is dispatched.
   */
  public boolean isValid() throws IllegalStateException
  {
    return _isValid;
  }

  private String getLineContext(String filename, int errorLine)
  {
    if (filename == null || errorLine <= 0)
      return "";
   
    ReadStream is = null;
    try {
      Path path = Vfs.lookup().lookup(filename);

      StringBuilder sb = new StringBuilder("\n\n");

      is = path.openRead();
      int line = 0;
      String text;
      while ((text = is.readLine()) != null) {
  line++;

  if (errorLine - 2 <= line && line <= errorLine + 2) {
    sb.append(line);
    sb.append(": ");
    sb.append(text);
    sb.append("\n");
  }
      }

      return sb.toString();
    } catch (IOException e) {
      log.log(Level.FINEST, e.toString(), e);

      return "";
    } finally {
      if (is != null)
  is.close();
    }
  }

  static class StartKey {
    private Item _item;
    private QName _name;
   
    public StartKey(Item item, QName name)
    {
      _item = item;
      _name = name;
    }
   
    public StartKey()
    {
    }

    public void init(Item item, QName name)
    {
      _item = item;
      _name = name;
    }

    public int hashCode()
    {
      return _name.hashCode() + 137 * System.identityHashCode(_item);
    }

    public boolean equals(Object o)
    {
      if (o == this)
        return true;

      if (o.getClass() != StartKey.class)
        return false;

      StartKey key = (StartKey) o;

      return _name.equals(key._name) && _item == key._item;
    }
  }

  static class EndElementKey {
    private Item _item;
   
    public EndElementKey(Item item)
    {
      _item = item;
    }
   
    public EndElementKey()
    {
    }

    public void init(Item item)
    {
      _item = item;
    }

    public int hashCode()
    {
      return 137 + _item.hashCode();
    }

    public boolean equals(Object o)
    {
      if (o == this)
        return true;

      if (o.getClass() != EndElementKey.class)
        return false;

      EndElementKey key = (EndElementKey) o;

      return _item.equals(key._item);
    }
  }
}
TOP

Related Classes of com.caucho.relaxng.VerifierHandlerImpl$EndElementKey

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.