package org.xmlsh.types; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.util.List; import net.sf.saxon.om.Item; import net.sf.saxon.om.Sequence; import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.XdmAtomicValue; import net.sf.saxon.s9api.XdmItem; import net.sf.saxon.s9api.XdmNode; import net.sf.saxon.s9api.XdmValue; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.xmlsh.core.CoreException; import org.xmlsh.core.InvalidArgumentException; import org.xmlsh.core.UnexpectedException; import org.xmlsh.core.UnimplementedException; import org.xmlsh.core.XValue; import org.xmlsh.sh.shell.SerializeOpts; import org.xmlsh.types.xtypes.XValueSequence; import org.xmlsh.util.JavaUtils; import org.xmlsh.util.S9Util; import org.xmlsh.util.Util; import org.xmlsh.util.XMLUtils; public final class XDMTypeFamily extends AbstractTypeFamily implements ITypeFamily { private static final XDMTypeFamily _instance = new XDMTypeFamily(); private static final Object _nullValue = null; private static Logger mLogger = LogManager.getLogger(); @Override public boolean isClassOfFamily(Class<?> cls) { assert( cls != null ); if( cls == null ) return false; return XdmItem.class.isAssignableFrom(cls) || Sequence.class.isAssignableFrom(cls) || net.sf.saxon.s9api.QName.class.isAssignableFrom(cls) ; } @Override public boolean isInstanceOfFamily(Object obj) { if( obj == null ) return true ; if( obj instanceof XdmItem || obj instanceof Sequence || obj instanceof net.sf.saxon.s9api.QName || obj instanceof URI ) return true ; return false ; } @Override public TypeFamily typeFamily() { return TypeFamily.XDM; } @Override public XValue append(Object obj, XValue xvalue) throws InvalidArgumentException { assert( obj != null ); assert (obj instanceof XdmItem ); if(xvalue == null) return XValue.newXValue( TypeFamily.XDM , obj); return XValue.newXValue(new XValueSequence( getXValue(obj), xvalue )); } @Override public String asString(Object obj) { if(obj == null) return ""; assert( obj != null ); assert (obj instanceof XdmItem ); if(obj instanceof XdmItem) { try { SerializeOpts shellLocalOpts = SerializeOpts.getShellLocalOpts(); return new String(XMLUtils.toByteArray((XdmItem) obj, shellLocalOpts), shellLocalOpts.getOutput_xml_encoding()); } catch (SaxonApiException | IOException e) { mLogger.warn("Exception serializing XDM value", e); return ""; } } else return obj.toString(); } @Override public int getSize(Object obj) { assert( obj != null ); assert (obj instanceof XdmItem); return ((XdmItem)obj).size(); } // Named index XValue @Override public XValue getXValue(Object obj, String ind) throws CoreException { if( obj == null ) return nullXValue(); assert( obj != null ); assert( obj instanceof XdmItem ); if( Util.isEmpty(ind)) return getXValue(obj); if( Util.isInt(ind, false)) return getXValue( obj , Util.parseInt(ind, 0)-1); XValue xv = getXValue(obj); return xv.xpath( null , ind ); } @Override public void serialize(Object obj, OutputStream out, SerializeOpts opts) throws IOException { assert( obj != null ); assert( obj instanceof XdmItem ); if(obj instanceof XdmItem) { XdmItem xv = (XdmItem) obj; if(!XMLUtils.isAtomic(xv)) { try { XMLUtils.serialize(xv, out, opts); } catch (SaxonApiException e) { Util.wrapIOException(e); } return; } out.write(xv.toString().getBytes(opts.getOutput_text_encoding())); return ; } assert(false); } /* * (non-Javadoc) * * @see org.xmlsh.types.AbstractMethods#simpleTypeName(java.lang.Object) */ @Override public String simpleTypeName(Object obj) { if( obj == null ) return "null"; assert( obj instanceof XdmItem ); if( obj instanceof XdmItem ) return XMLUtils.simpleTypeName((XdmItem) obj ); else return JavaUtils.simpleTypeName(obj); } @Override public boolean isEmpty(Object obj) { assert( obj != null ); assert( obj instanceof XdmItem ); // Non-XdmValues not considered atomic. if(!(obj instanceof XdmItem)) return false; return ((XdmItem) obj).size() == 0; } @Override public XValue setXValue(XValue xobj, String ind, XValue value) throws CoreException { throw new UnimplementedException("Set indexed value not supported on XDM types"); } @Override public List<XValue> getXValues(Object obj) { assert( obj != null ); assert( obj instanceof XdmItem ); if(obj == null) return XValue.emptyList(); XdmItem item = (XdmItem) obj; return XMLUtils.toXValueList( item ); } @Override public boolean isAtomic(Object value) { assert( value != null ); if(value == null) return true; if(!(value instanceof XdmItem)) return false; return XMLUtils.isAtomic((XdmItem) value); } @Override public XValue getXValue(Object obj) throws InvalidArgumentException { if( obj == null ) return XTypeFamily.getInstance().nullXValue(); if( obj instanceof XdmValue && ! (obj instanceof XdmItem) ) { XdmValue v = (XdmValue) obj ; v = XMLUtils.simplify(v); switch( v.size() ){ case 0: return XValue.empytSequence(); case 1: obj = v.itemAt(0); default: return XValue.newXValue(XMLUtils.toXValueList( v )); } } if( obj instanceof XdmItem ) assert( ( (XdmItem) obj ).size() <= 1 ); else if( obj instanceof net.sf.saxon.s9api.QName ) obj = new XdmAtomicValue( (net.sf.saxon.s9api.QName) obj ); else if( obj instanceof String ) obj = new XdmAtomicValue( (String) obj ); else if( obj instanceof URI ) obj = new XdmAtomicValue( (URI) obj ); else if( obj instanceof Item ) obj = S9Util.wrapItem((Item)obj); else return XValue.newXValue(obj); return XValue.newXValue(this,obj,false); } @Override public XValue getXValue(Object obj, int index) throws CoreException { if(obj == null) return nullXValue(); assert (obj instanceof XdmValue); XdmValue v = (XdmValue) obj; if(index < 0 || index >= v.size()) throw new InvalidArgumentException("Invalid index for sequence: " + index); return XValue.newXValue(this,v.itemAt(index),false); } @Override public XValue setXValue(XValue obj, int index, XValue value) throws UnimplementedException { throw new UnimplementedException("Invalid operation: setXValue by index"); } @Override public Object nullValue() { return _nullValue; } @Override public XValue nullXValue() { try { return XValue.newXValue(TypeFamily.XDM, _nullValue); } catch (InvalidArgumentException e) { throw new IllegalArgumentException(e); } } public static XDMTypeFamily getInstance() { return _instance; } @Override public boolean hasKey(Object obj, String key) throws InvalidArgumentException { if( XMLUtils.isXdmElement(obj)){ XdmNode element = XMLUtils.asXdmNode(obj); if( element.getAttributeValue( new net.sf.saxon.s9api.QName(key) ) != null ) return true ; } return false ; } @Override public boolean isContainer(Object obj) { return ( obj instanceof XValueSequence) ; } }