package org.xmlsh.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.List;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import net.sf.saxon.expr.StaticProperty;
import net.sf.saxon.om.EmptyAtomicSequence;
import net.sf.saxon.om.FunctionItem;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.om.SequenceTool;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.SaxonApiUncheckedException;
import net.sf.saxon.s9api.Serializer;
import net.sf.saxon.s9api.XPathCompiler;
import net.sf.saxon.s9api.XPathExecutable;
import net.sf.saxon.s9api.XPathSelector;
import net.sf.saxon.s9api.XdmEmptySequence;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XdmNodeKind;
import net.sf.saxon.s9api.XdmValue;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.AtomicValue;
import net.sf.saxon.value.EmptySequence;
import net.sf.saxon.value.ObjectValue;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.xmlsh.core.InvalidArgumentException;
import org.xmlsh.core.Namespaces;
import org.xmlsh.core.XValue;
import org.xmlsh.sh.shell.SerializeOpts;
import org.xmlsh.sh.shell.Shell;
import org.xmlsh.sh.shell.ShellConstants;
import org.xmlsh.types.ITypeConverter;
import org.xmlsh.types.TypeFamily;
public class XMLUtils
{
private static Logger mLogger = LogManager.getLogger();
public static byte[] toByteArray(XdmValue xdm , SerializeOpts opts ) throws SaxonApiException, IOException
{
if( xdm != null ){
if( isAtomic(xdm) )
return xdm.toString().getBytes(opts.getOutput_text_encoding());
else
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
serialize( xdm , out , opts );
return out.toByteArray();
}
}
return null;
}
public static boolean isAtomic(XdmValue value) {
if( value == null ) // strange but null is atomic for this
return true ;
@SuppressWarnings("rawtypes")
Sequence item = value.getUnderlyingValue();
boolean isAtom = ( item instanceof AtomicValue ) ||
( item instanceof NodeInfo && ((NodeInfo)item).getNodeKind() == net.sf.saxon.type.Type.TEXT ) ;
return isAtom;
}
public static XdmItem asXdmItem(XdmValue value)
{
if( value == null)
return null ;
try {
return value.itemAt(0);
} catch (IndexOutOfBoundsException e) {
return null;
} catch (SaxonApiUncheckedException e) {
return null;
}
}
public static XdmValue asXdmValue(Iterable<XdmItem> list) {
return simplify( new XdmValue(list) );
}
public static XdmValue asXdmValue(Object obj) throws InvalidArgumentException
{
if( ! (obj instanceof XdmValue) )
return null ;
return (XdmValue) obj ;
}
public static XdmNode asXdmNode(Object obj) throws InvalidArgumentException
{
if( ! (obj instanceof XdmValue) )
return null ;
XdmItem item = asXdmItem((XdmValue)obj );
if( item instanceof XdmNode )
return (XdmNode) item ;
else
throw new InvalidArgumentException("Value is not a Node");
}
public static void serialize( XdmValue value , OutputStream out, SerializeOpts opt) throws SaxonApiException, IOException
{
Serializer ser = Util.getSerializer(opt);
ser.setOutputStream( out );
Util.writeXdmValue( value , ser );
ser.close();;
out.flush();
}
public static XdmValue emptySequence() {
return XdmEmptySequence.getInstance();
}
public static XdmValue toXdmValue( Object obj ) throws InvalidArgumentException {
return JavaUtils.convert(obj, XdmValue.class);
}
public static XdmItem toXdmItem( Object obj ) throws InvalidArgumentException {
return JavaUtils.convert(obj, XdmItem.class);
}
public static String cardinalityString( int card) {
switch( card & StaticProperty.CARDINALITY_MASK ) {
case StaticProperty.ALLOWS_ONE_OR_MORE : return "+" ;
case StaticProperty.ALLOWS_ZERO_OR_MORE : return "*" ;
case StaticProperty.ALLOWS_ZERO_OR_ONE : return "?" ;
default :
return "" ;
}
}
public static int getCardinality( XdmValue value ) {
Sequence v = value.getUnderlyingValue();
return SequenceTool.getCardinality(v);
}
public static String simpleTypeName( XdmItem v )
{
Sequence s = v.getUnderlyingValue();
if(s instanceof EmptySequence || s instanceof EmptyAtomicSequence || SequenceTool.getCardinality(s) == StaticProperty.ALLOWS_ZERO )
return "empty-sequence()";
try {
Item item = SequenceTool.asItem(s);
/*
return Type.displayTypeName( item );
if (item instanceof NodeInfo) {
return ((NodeInfo)item).getDisplayName() + cs ;
} else if (item instanceof AtomicValue) {
return ((AtomicValue)item).getPrimitiveType().getName() + cs ;
} else if (item instanceof FunctionItem) {
return ((FunctionItem<?>)item).getFunctionName().toString() + cs ;
}
*/
if (item instanceof NodeInfo) {
NodeInfo node = (NodeInfo) item;
switch (node.getNodeKind()) {
case Type.DOCUMENT:
return "document-node()";
case Type.ELEMENT:
return "element()";
case Type.ATTRIBUTE:
return "attribute()";
case Type.TEXT:
return "text()";
case Type.COMMENT:
return "comment()";
case Type.PROCESSING_INSTRUCTION:
return "processing-instruction()";
case Type.NAMESPACE:
return "namespace()";
default:
return "";
}
} else if (item instanceof ObjectValue) {
return ((ObjectValue<?>) item).getObject().getClass().toString();
} else if (item instanceof AtomicValue) {
return ((AtomicValue) item).getItemType().toString();
} else if (item instanceof FunctionItem) {
return "function(*)";
} else {
return item.getClass().toString();
}
} catch (net.sf.saxon.trans.XPathException e) {
mLogger.info("Excpetion getting simple type",e);
}
return JavaUtils.simpleTypeName( v );
}
public static String simpleTypeName( XdmValue value ) {
if (value == null)
return null;
if( value.size() == 0 )
return "empty-sequence()" ;
String cs = cardinalityString(getCardinality(value));
if( value.size() == 1 )
return simpleTypeName( value.itemAt(0) );
else
{
String prev = null ;
for( XdmItem item : value ) {
String st = simpleTypeName( item );
if( prev == null ) {
prev = st ;
}
else
if( ! prev.equals(st ) )
return "item()+" ;
}
if( prev == null )
return "empty-sequence()";
return prev + cs ;
}
}
public static Iterator<XdmItem> asXdmItemIter(XdmValue value) {
assert( value != null );
if( value == null )
return null ;
return value.iterator() ;
}
public static XdmValue simplify( XdmValue value ) {
assert( value != null );
if( value == null )
return null ;
int n = value.size();
if (n == 0) {
return XdmEmptySequence.getInstance();
} else if (n == 1) {
return value.itemAt(0);
} else {
return value;
}
}
@SuppressWarnings("rawtypes")
public static Sequence asSequence(XdmValue value )
{
if( value == null )
return null ;
Sequence s = value.getUnderlyingValue();
return s ;
}
@SuppressWarnings("rawtypes")
public static SequenceIterator asSequenceIterator(XdmValue value ) throws XPathException
{
if( value == null )
return null ;
Sequence s = value.getUnderlyingValue();
return s.iterate();
}
public static XdmValue toXdmValue( Iterator<XValue> iter )
{
return new XdmValue(
Util.toConvertingIterable(iter,
new XValueToXdmItemConverter()));
}
public static XdmValue toXdmValue( Iterable<XValue> iter )
{
return toXdmValue(iter.iterator() );
}
public static XdmValue toXdmValue( List<XValue> list )
{
if( list == null || list.size() == 0 )
return emptySequence();
return new XdmValue(
Util.toConvertingIterable(list.iterator(),
new XValueToXdmItemConverter()));
}
public static List<XValue> toXValueList(XdmValue item )
{
return Util.toList(
Util.toConvertingIterater(item.iterator(),
new XMLUtils.XdmToXValueConverter()));
}
public static List<XValue> toXValueList(XdmItem item )
{
return toXValueList((XdmValue) item );
}
/*
public static SequenceIterator asSequenceIterator( Iterable<XValue> iter )
{
List<Item> items = new ArrayList<Item>();
for( XValue v : iter ) {
items.add( (Item) v.toXdmItem().getUnderlyingValue());
}
return new SequenceExtent<Item>(items).asIterator()
}
public static SequenceIterator asSequenceIterator(Iterable<Item> iitem)
{
List<Item> items = new ArrayList<Item>();
for (Item item : iitem) {
items.add(item);
}
return new SequenceExtent<Item>(items);
}
*/
final static class XValueToItemConverter
implements ITypeConverter<XValue, Item >
{
@SuppressWarnings("rawtypes")
@Override
public Item convert(XValue xvalue) throws XPathException, InvalidArgumentException
{
Sequence s = xvalue.toXdmItem().getUnderlyingValue();
if( SequenceTool.getLength(s) == 1 )
return s.head();
return null;
}
}
final static class XValueToXdmItemConverter
implements ITypeConverter<XValue,XdmItem >
{
@Override
public XdmItem convert(XValue xvalue) throws InvalidArgumentException
{
return xvalue.toXdmItem();
}
}
public static final class XdmToXValueConverter implements ITypeConverter<XdmItem, XValue>
{
@Override
public XValue convert(XdmItem value) throws InvalidArgumentException
{
return XValue.newXValue(TypeFamily.XDM , value);
}
}
public static boolean isXdmValue(Object obj) {
return obj instanceof XdmValue ;
}
public static boolean isXdmElement(Object obj) {
if( obj instanceof XdmNode ){
XdmNode node = ((XdmNode)obj);
return node.getNodeKind() == XdmNodeKind.ELEMENT;
}
return false ;
}
public static XMLEventReader createEventReader( String s ) throws UnsupportedEncodingException, FactoryConfigurationError, XMLStreamException
{
return createEventReader(s.getBytes(ShellConstants.kENCODING_UTF_8) );
}
public static XMLEventReader createEventReader(byte[] bytes)
throws FactoryConfigurationError, XMLStreamException {
ByteArrayInputStream iss = new ByteArrayInputStream( bytes );
XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.valueOf(false)); // Dont try to reference http://java.sun.com/dtd/properties.dtd !!!
XMLEventReader reader = factory.createXMLEventReader( null , iss);
return reader;
}
public static XPathSelector evalXPath(XdmValue value, String expr, Shell shell) throws IOException, SaxonApiException {
Processor processor = Shell.getProcessor();
XPathCompiler compiler = processor.newXPathCompiler();
XPathExecutable exec = compiler.compile(expr);
if( shell != null ){
Namespaces ns = shell.getEnv().getNamespaces();
if(ns != null) {
for (String prefix : ns.keySet()) {
String uri = ns.get(prefix);
compiler.declareNamespace(prefix, uri);
}
}
}
XPathSelector eval = exec.load();
eval.setContextItem(value.itemAt(0));
return eval;
}
};