Package org.exist.xquery.modules

Source Code of org.exist.xquery.modules.ModuleUtils

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2001-08 The eXist Project
*  http://exist-db.org
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*
*  This program 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.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*
* $Id$
*/
package org.exist.xquery.modules;

import java.io.InputStream;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;

import org.apache.log4j.Logger;
import org.exist.memtree.DocumentBuilderReceiver;
import org.exist.memtree.DocumentImpl;
import org.exist.memtree.MemTreeBuilder;
import org.exist.memtree.SAXAdapter;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.value.NodeValue;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
* Utility Functions for XQuery Extension Modules
*
* @author Adam Retter <adam@exist-db.org>
* @serial 200805202059
* @version 1.1
*/
public class ModuleUtils {
  protected final static Logger LOG = Logger.getLogger(ModuleUtils.class);

  /**
   * Takes a String of XML and Creates an XML Node from it using SAX in the
   * context of the query
   *
   * @param context
   *            The Context of the calling XQuery
   * @param str
   *            The String of XML
   *
   * @return The NodeValue of XML
   */
  public static NodeValue stringToXML(XQueryContext context, String str) throws SAXException, IOException {
            final Reader reader = new StringReader(str);
            try {
                return inputSourceToXML(context, new InputSource(reader));
            } finally {
                reader.close();
            }
  }
 
 
  /**
   * Takes an InputStream of XML and Creates an XML Node from it using SAX in the
   * context of the query
   *
   * @param context
   *            The Context of the calling XQuery
   * @param is
   *            The InputStream of XML
   *
   * @return The NodeValue of XML
   */
  public static NodeValue streamToXML(XQueryContext context, InputStream is) throws SAXException, IOException {
            return inputSourceToXML(context, new InputSource(is));
  }
       
        /**
   * Takes a Source of XML and Creates an XML Node from it using SAX in the
   * context of the query
   *
   * @param context
   *            The Context of the calling XQuery
   * @param src
   *            The Source of XML
   *
   * @return The NodeValue of XML
   */
        public static NodeValue sourceToXML(XQueryContext context, Source src) throws SAXException, IOException {
            if(src instanceof SAXSource && ((SAXSource)src).getXMLReader() != null) {
                //Handles the case where a SAXSource may already have an
                //XMLReader allocated, for example EXPath httpclient
                //where it wants to tidy html using TagSoup
                return inputSourceToXML(context, (SAXSource)src);
            } else {
                final InputSource inputSource = SAXSource.sourceToInputSource(src);
                if(inputSource == null){
                    throw new IOException(src.getClass().getName() + " is unsupported.");
                }

                return inputSourceToXML(context, inputSource);
            }
        }
 

        /**
   * Takes a InputSource of XML and Creates an XML Node from it using SAX in the
   * context of the query
   *
   * @param context
   *            The Context of the calling XQuery
   * @param inputSource
   *            The InputSource of XML
   *
   * @return The NodeValue of XML
   */
  public static NodeValue inputSourceToXML(XQueryContext context, InputSource inputSource) throws SAXException, IOException  {
            context.pushDocumentContext();

            XMLReader reader = null;
            try {
                // try and construct xml document from input stream, we use eXist's
                // in-memory DOM implementation
                reader = context.getBroker().getBrokerPool().getParserPool().borrowXMLReader();
                LOG.debug( "Parsing XML response ..." );

                // TODO : we should be able to cope with context.getBaseURI()
                final MemTreeBuilder builder = context.getDocumentBuilder();
                final DocumentBuilderReceiver receiver = new DocumentBuilderReceiver( builder, true );
                reader.setContentHandler(receiver);
                reader.setProperty("http://xml.org/sax/properties/lexical-handler", receiver);
                reader.parse(inputSource);
                final Document doc = receiver.getDocument();
                // return (NodeValue)doc.getDocumentElement();
                return((NodeValue)doc);
            finally {
                context.popDocumentContext();

                if(reader != null){
                    context.getBroker().getBrokerPool().getParserPool().returnXMLReader(reader);
                }
            }
  }
       
        /**
   * Takes a InputSource of XML and Creates an XML Node from it using SAX in the
   * context of the query
   *
   * @param context
   *            The Context of the calling XQuery
   * @param xml
   *            The InputSource of XML
   *
   * @return The NodeValue of XML
   */
  private static NodeValue inputSourceToXML(XQueryContext context, SAXSource src) throws SAXException, IOException  {
            if(src.getXMLReader() == null) {
                throw new SAXException("No XML Reader specified.");
            }
            final XMLReader reader = src.getXMLReader();
           
            context.pushDocumentContext();

            try {
                // try and construct xml document from input stream, we use eXist's
                // in-memory DOM implementation

                // TODO : we should be able to cope with context.getBaseURI()
                final MemTreeBuilder builder = context.getDocumentBuilder();
                final DocumentBuilderReceiver receiver = new DocumentBuilderReceiver(builder, true);
                reader.setContentHandler(receiver);
                reader.parse(src.getInputSource());
                final Document doc = receiver.getDocument();
                return((NodeValue)doc);
            finally {
                context.popDocumentContext();
            }
  }
       
        /**
   * Takes a HTML InputSource and creates an XML representation of the HTML by
   * tidying it (uses NekoHTML)
   *
   * @param context
   *            The Context of the calling XQuery
   * @param srcHtml
   *            The Source for the HTML
         * @param parserFeatures
         *            The features to set on the Parser
         * @param parserProperties
         *            The properties to set on the Parser
   *
   * @return An in-memory Document representing the XML'ised HTML
   */
  public static DocumentImpl htmlToXHtml(XQueryContext context, String url, Source srcHtml, Map<String, Boolean> parserFeatures, Map<String, String>parserProperties) throws IOException, SAXException {
   
            final InputSource inputSource = SAXSource.sourceToInputSource(srcHtml);
       
            if(inputSource == null){
                throw new IOException(srcHtml.getClass().getName() + " is unsupported.");
            }
           
            return htmlToXHtml(context, url, inputSource, parserFeatures, parserProperties);
  }
       
  /**
   * Takes a HTML InputSource and creates an XML representation of the HTML by
   * tidying it (uses NekoHTML)
   *
   * @param context
   *            The Context of the calling XQuery
   * @param srcHtml
   *            The InputSource for the HTML
         * @param parserFeatures
         *            The features to set on the Parser
         * @param parserProperties
         *            The properties to set on the Parser
   *
   * @return An in-memory Document representing the XML'ised HTML
   */
  public static DocumentImpl htmlToXHtml(XQueryContext context, String url, InputSource srcHtml, Map<String, Boolean> parserFeatures, Map<String, String>parserProperties) throws IOException, SAXException {
            // we use eXist's in-memory DOM implementation
            org.exist.memtree.DocumentImpl memtreeDoc = null;

            // use Neko to parse the HTML content to XML
            XMLReader reader = null;
            try {
                LOG.debug("Converting HTML to XML using NekoHTML parser for: " + url);
                reader = (XMLReader) Class.forName("org.cyberneko.html.parsers.SAXParser").newInstance();
               
                if(parserFeatures != null) {
                    for(final Entry<String, Boolean> parserFeature : parserFeatures.entrySet()) {
                        reader.setFeature(parserFeature.getKey(), parserFeature.getValue());
                    }
                }

                if(parserProperties == null) {
                    //default: do not modify the case of elements and attributes
                    reader.setProperty("http://cyberneko.org/html/properties/names/elems","match");
                    reader.setProperty("http://cyberneko.org/html/properties/names/attrs","no-change");
                } else {
                    for(final Entry<String, String> parserProperty : parserProperties.entrySet()) {
                        reader.setProperty(parserProperty.getKey(), parserProperty.getValue());
                    }
                }
            } catch(final Exception e) {
                    final String errorMsg = "Error while invoking NekoHTML parser. ("
                                    + e.getMessage()
                                    + "). If you want to parse non-wellformed HTML files, put "
                                    + "nekohtml.jar into directory 'lib/user'.";
                    LOG.error(errorMsg, e);

                    throw new IOException(errorMsg, e);
            }

            final SAXAdapter adapter = new SAXAdapter();

            // allow multiple attributes of the same name attached to the same element
            // to enhance resilience against bad HTML. The last attribute value wins.
            adapter.setReplaceAttributeFlag(true);

            reader.setContentHandler(adapter);
            reader.parse(srcHtml);
            final Document doc = adapter.getDocument();
            memtreeDoc = (DocumentImpl) doc;
            memtreeDoc.setContext(context);
           
            return memtreeDoc;
  }

  /**
   * Parses a structure like <parameters><param name="a" value="1"/><param
   * name="b" value="2"/></parameters> into a set of Properties
   *
   * @param nParameters
   *            The parameters Node
   * @return a set of name value properties for representing the XML
   *         parameters
   */
  public static Properties parseParameters(Node nParameters){
            return parseProperties(nParameters, "param");
  }

  /**
   * Parses a structure like <properties><property name="a" value="1"/><property
   * name="b" value="2"/></properties> into a set of Properties
   *
   * @param nProperties
   *            The properties Node
   * @return a set of name value properties for representing the XML
   *         properties
   */
  public static Properties parseProperties(Node nProperties) {
            return parseProperties(nProperties, "property");
  }

  /**
   * Parses a structure like <properties><property name="a" value="1"/><property
   * name="b" value="2"/></properties> into a set of Properties
   *
   * @param container
   *            The container of the properties
   * @param elementName
   *            The name of the property element
   * @return a set of name value properties for representing the XML
   *         properties
   */
  private static Properties parseProperties(Node container, String elementName) {
            final Properties properties = new Properties();

            if(container != null && container.getNodeType() == Node.ELEMENT_NODE) {
                final NodeList params = ((Element) container).getElementsByTagName(elementName);
                for(int i = 0; i < params.getLength(); i++) {
                    final Element param = ((Element) params.item(i));

                    final String name = param.getAttribute("name");
                    final String value = param.getAttribute("value");

                    if(name != null && value != null) {
                        properties.setProperty(name, value);
                    } else {
                        LOG.warn("Name or value attribute missing for " + elementName);
                    }
                }
            }
            return properties;
  }
   
       
    private static class ContextMapLocks {
        private final Map<String, ReentrantReadWriteLock> locks = new HashMap<String, ReentrantReadWriteLock>();
       
        private synchronized ReentrantReadWriteLock getLock(String contextMapName) {
            ReentrantReadWriteLock lock = locks.get(contextMapName);
            if(lock == null) {
                lock = new ReentrantReadWriteLock();
                locks.put(contextMapName, lock);
            }
            return lock;
        }
       
        public ReadLock getReadLock(String contextMapName) {
            return getLock(contextMapName).readLock();
        }

        public WriteLock getWriteLock(String contextMapName) {
            return getLock(contextMapName).writeLock();
        }
    }   
   
    private final static ContextMapLocks contextMapLocks = new ContextMapLocks();
   
    /**
     * Retrieves a previously stored Object from the Context of an XQuery.
     *
     * @param   context         The Context of the XQuery containing the Object
     * @param   contextMapName  DOCUMENT ME!
     * @param   objectUID       The UID of the Object to retrieve from the Context of the XQuery
     *
     * @return  DOCUMENT ME!
     */       
    public static <T> T retrieveObjectFromContextMap(XQueryContext context, String contextMapName, long objectUID) {
       
        contextMapLocks.getReadLock(contextMapName).lock();
        try{
            // get the existing object map from the context
            final Map<Long, T> map = (HashMap<Long, T>)context.getXQueryContextVar(contextMapName);

            if(map == null) {
                return null;
            }

            // get the connection
            return map.get(objectUID);
        } finally {
            contextMapLocks.getReadLock(contextMapName).unlock();
        }
    }
   
    public static <T> void modifyContextMap(XQueryContext context, String contextMapName, ContextMapModifier<T> modifier) {
        contextMapLocks.getWriteLock(contextMapName).lock();
        try {
            // get the existing map from the context
            Map<Long, T> map = (Map<Long, T>)context.getXQueryContextVar(contextMapName);
            if(map == null) {
                //create a new map if it doesnt exist
                map = new HashMap<Long, T>();
                context.setXQueryContextVar(contextMapName, map);
            }
           
            //modify the map
            modifier.modify(map);
           
        } finally {
            contextMapLocks.getWriteLock(contextMapName).unlock();
        }
    }
   
    public interface ContextMapModifier<T> {
        public void modify(Map<Long, T> map);
    }
   
    public static abstract class ContextMapEntryModifier<T> implements ContextMapModifier<T> {
       
        @Override
        public void modify(Map<Long, T> map) {
            for(final Entry<Long, T> entry : map.entrySet()) {
                modify(entry);
            }
        }
       
        public abstract void modify(Entry<Long, T> entry);
    }

    /**
     * Stores an Object in the Context of an XQuery.
     *
     * @param   context         The Context of the XQuery to store the Object in
     * @param   contextMapName  The name of the context map
     * @param   o               The Object to store
     *
     * @return  A unique ID representing the Object
     */
    public static <T> long storeObjectInContextMap(XQueryContext context, String contextMapName, T o) {
       
        contextMapLocks.getWriteLock(contextMapName).lock();
        try{

            // get the existing map from the context
            Map<Long, T> map = (Map<Long, T>)context.getXQueryContextVar(contextMapName);

            if(map == null) {
                // if there is no map, create a new one
                map = new HashMap<Long, T>();
            }

            // get an id for the map
            long uid = 0;
            while(uid == 0 || map.keySet().contains(uid)) {
                uid = getUID();
            }

            // place the object in the map
            map.put(uid, o);

            // store the map back in the context
            context.setXQueryContextVar(contextMapName, map);

            return (uid);
        } finally {
            contextMapLocks.getWriteLock(contextMapName).unlock();
        }
    }
   
    private final static Random random = new Random();
   
    private static long getUID() {
        final BigInteger bi = new BigInteger(64, random);
        return bi.longValue();
    }
}
TOP

Related Classes of org.exist.xquery.modules.ModuleUtils

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.