/** * $Id: $ * $Date: $ * */ package org.xmlsh.internal.commands; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.xml.transform.Source; import org.xmlsh.core.InputPort; import org.xmlsh.core.InvalidArgumentException; import org.xmlsh.core.Namespaces; import org.xmlsh.core.Options; import org.xmlsh.core.Options.OptionValue; import org.xmlsh.core.XCommand; import org.xmlsh.core.XValue; import org.xmlsh.core.io.OutputPort; import org.xmlsh.sh.shell.SerializeOpts; import org.xmlsh.sh.shell.Shell; import org.xmlsh.types.TypeFamily; import org.xmlsh.util.Util; import net.sf.saxon.event.Builder; import net.sf.saxon.om.CodedName; import net.sf.saxon.om.DocumentInfo; import net.sf.saxon.om.Item; import net.sf.saxon.om.MutableNodeInfo; import net.sf.saxon.om.NamePool; import net.sf.saxon.om.NodeInfo; import net.sf.saxon.om.TreeModel; import net.sf.saxon.s9api.Axis; import net.sf.saxon.s9api.DocumentBuilder; import net.sf.saxon.s9api.Processor; import net.sf.saxon.s9api.QName; import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.SaxonApiUncheckedException; import net.sf.saxon.s9api.XPathCompiler; import net.sf.saxon.s9api.XPathExecutable; import net.sf.saxon.s9api.XPathSelector; import net.sf.saxon.s9api.XdmItem; import net.sf.saxon.s9api.XdmNode; import net.sf.saxon.s9api.XdmNodeKind; import net.sf.saxon.s9api.XdmSequenceIterator; import net.sf.saxon.s9api.XdmValue; import net.sf.saxon.trans.XPathException; import net.sf.saxon.tree.iter.AxisIterator; import net.sf.saxon.tree.linked.DocumentImpl; import net.sf.saxon.type.BuiltInAtomicType; import net.sf.saxon.type.Type; public class xed extends XCommand { private DocumentBuilder mBuilder; private XPathCompiler mCompiler; private Processor mProcessor; private void setupBuilders() throws IOException { /* * mProcessor = new Processor(false); * mProcessor.setConfigurationProperty(FeatureKeys.TREE_MODEL, * net.sf.saxon.event.Builder.LINKED_TREE); */ mProcessor = Shell.getProcessor(); mCompiler = mProcessor.newXPathCompiler(); mBuilder = mProcessor.newDocumentBuilder(); mBuilder.setTreeModel(TreeModel.LINKED_TREE); Namespaces ns = getEnv().getNamespaces(); if(ns != null) { for(String prefix : ns.keySet()) { String uri = ns.get(prefix); mCompiler.declareNamespace(prefix, uri); } } } @Override public int run(List<XValue> args) throws Exception { boolean opt_delete = false; XValue opt_add = null; XValue opt_replace = null; String opt_matches = null; String opt_xpath = null; String opt_replacex = null; String opt_rename = null; // backwards compatible -r == -r/text Options opts = new Options( "i=input:,e=xpath:,n,v,r=replace:,a=add:,d=delete,m=matches:,rx=replacex:,ren=rename:", SerializeOpts.getOptionDefs()); opts.parse(args); setupBuilders(); XdmNode context = null; // boolean bReadStdin = false ; SerializeOpts serializeOpts = getSerializeOpts(opts); if(!opts.hasOpt("n")) { // Has XML data input OptionValue ov = opts.getOpt("i"); // If -i argument is an XML expression take the first node as the context if(ov != null && ov.getValue().isXdmItem()) { XdmItem item = ov.getValue().asXdmItem(); if(item instanceof XdmNode) // context = (XdmNode) item ; // // builder.build(((XdmNode)item).asSource()); // context = (XdmNode) ov.getValue().toXdmValue(); context = importNode((XdmNode) item); } if(context == null) { Source src = null; InputPort insrc = null; if(ov != null && !ov.getValue().toString().equals("-")) insrc = getInput(ov.getValue()); else { insrc = getStdin(); } context = build(insrc.asSource(serializeOpts)); } } List<XValue> xvargs = opts.getRemainingArgs(); if(opts.hasOpt("v")) { // Read pairs from args to set for(int i = 0; i < xvargs.size() / 2; i++) { String name = xvargs.get(i * 2).toString(); mCompiler.declareVariable(new QName(name)); } } opt_add = opts.getOptValue("a"); opt_replace = opts.getOptValue("r"); opt_delete = opts.hasOpt("d"); opt_replacex = opts.getOptString("rx", null); opt_rename = opts.getOptString("ren", null); opt_matches = opts.getOptString("matches", null); opt_xpath = opts.getOptString("xpath", null); if(opt_matches == null && opt_xpath == null) throw new InvalidArgumentException( "option xpath or matches must be specified"); XPathExecutable expr; if(opt_matches == null) expr = mCompiler.compile(opt_xpath); else expr = mCompiler.compilePattern(opt_matches); XPathSelector eval = expr.load(); if(opts.hasOpt("v")) { // Read pairs from args to set for(int i = 0; i < xvargs.size() / 2; i++) { String name = xvargs.get(i * 2).toString(); XValue value = xvargs.get(i * 2 + 1); eval.setVariable(new QName(name), value.toXdmValue()); } } XPathSelector replacex = null; if(opt_replacex != null) { XPathExecutable xe = mCompiler.compile(opt_replacex); replacex = xe.load(); } if(opt_replace != null || opt_delete || opt_add != null || opt_replacex != null || opt_rename != null) { Iterable<XdmItem> results = getResults(eval, context, opt_matches != null); for(XdmItem item : results) { Object obj = item.getUnderlyingValue(); if(obj instanceof MutableNodeInfo) { MutableNodeInfo node = (MutableNodeInfo) obj; if(opt_replace != null) replace(node, opt_replace, true); if(replacex != null) replace(item, node, replacex); if(opt_add != null) add(node, opt_add); if(opt_delete) delete(node); if(opt_rename != null) rename(node, opt_rename); } } } OutputPort stdout = getStdout(); Util.writeXdmValue(context, stdout.asDestination(serializeOpts)); stdout.writeSequenceTerminator(serializeOpts); return 0; } private Iterable<XdmItem> getResults(XPathSelector eval, XdmNode root, boolean opt_matches) throws SaxonApiException { if(!opt_matches) { if(root != null) eval.setContextItem(root); return eval; } ArrayList<XdmItem> results = new ArrayList<XdmItem>(); if(root == null) return results; XdmSequenceIterator iter = root.axisIterator(Axis.DESCENDANT_OR_SELF); while(iter.hasNext()) { XdmItem item = iter.next(); eval.setContextItem(item); if(eval.effectiveBooleanValue()) results.add(item); if(item instanceof XdmNode) { XdmSequenceIterator aiter = ((XdmNode) item) .axisIterator(Axis.ATTRIBUTE); while(aiter.hasNext()) { XdmItem item2 = aiter.next(); eval.setContextItem(item2); if(eval.effectiveBooleanValue()) results.add(item2); } } } return results; } private void delete(MutableNodeInfo node) { node.delete(); } private void add(MutableNodeInfo node, XValue add) throws IndexOutOfBoundsException, SaxonApiUncheckedException, SaxonApiException, InvalidArgumentException { if(!add.isAtomic()) { XdmNode xnode = add.asXdmNode(); if(xnode.getNodeKind() == XdmNodeKind.ATTRIBUTE) { NodeInfo anode = xnode.getUnderlyingNode(); addAttribute(node, anode.getPrefix(), anode.getURI(), anode.getLocalPart(), anode.getStringValue()); } else { node.insertChildren(new NodeInfo[] { getNodeInfo(xnode) }, true, true); } } else node.replaceStringValue(node.getStringValue() + add.toString()); } private void addAttribute(MutableNodeInfo node, String prefix, String uri, String local, String value) { NamePool pool = node.getNamePool(); int nameCode = pool.allocate(prefix, uri, local); CodedName name = new CodedName(nameCode, pool); node.addAttribute(name, BuiltInAtomicType.UNTYPED_ATOMIC, value, 0); if(!Util.isEmpty(prefix)) { node.addNamespace(name.getNamespaceBinding(), false); } } private void replace(MutableNodeInfo node, XValue replace, boolean compat_mode) throws IndexOutOfBoundsException, SaxonApiUncheckedException, SaxonApiException, XPathException, InvalidArgumentException { if(!replace.isAtomic()) { XdmNode xnode = replace.asXdmNode(); if(xnode.getNodeKind() == XdmNodeKind.ATTRIBUTE) { NodeInfo anode = xnode.getUnderlyingNode(); NodeInfo existsAttr = findAttribute(node, anode); if(existsAttr != null) node.removeAttribute(existsAttr); addAttribute(node, anode.getPrefix(), anode.getURI(), anode.getLocalPart(), anode.getStringValue()); } else node.replace(new NodeInfo[] { getNodeInfo(xnode) }, true); } else if(compat_mode || node.getNodeKind() == Type.ATTRIBUTE) node.replaceStringValue(replace.toString()); else node.replace( new NodeInfo[] { createTextNode(node, replace.toXdmValue()) }, true); } private void rename(MutableNodeInfo node, String opt_rename) throws IndexOutOfBoundsException, SaxonApiUncheckedException, SaxonApiException { NamePool pool = node.getNamePool(); int newNameCode = pool.allocateClarkName(opt_rename); CodedName name = new CodedName(newNameCode, pool); node.rename(name); } private void replace(XdmItem item, MutableNodeInfo node, XPathSelector replacex) throws IndexOutOfBoundsException, SaxonApiUncheckedException, SaxonApiException, XPathException, InvalidArgumentException { replacex.setContextItem(item); // Convert to string and turn into an XdmItem XValue xreplace = XValue.newXValue(TypeFamily.XDM, replacex.evaluate()); replace(node, xreplace, false); } private NodeInfo createTextNode(MutableNodeInfo parent, XdmValue value) throws XPathException { /* * net.sf.saxon.om.Orphan textNode = new * net.sf.saxon.om.Orphan(mProcessor.getUnderlyingConfiguration()); * textNode.setNodeKind( Type.TEXT ); * textNode.setStringValue( replace ); */ /* * net.sf.saxon.tree.TextImpl textNode = new net.sf.saxon.tree.TextImpl( * null , replace); */ /* * try { * Class<?> cls = Class.forName("net.sf.saxon.tree.TextImpl"); * Class<?> parentClass = Class.forName("net.sf.saxon.tree.ParentNodeImpl"); * Constructor<?> cons = cls.getConstructor(parentClass , String.class ); * NodeInfo text = (NodeInfo) cons.newInstance(null , replace ); * return text ; * * * * } catch( Exception e ) * { * this.printErr("Exception loading textImpl", e); * return null; * } */ // return SaxonUtil.createTextNode(replace); /* * TOTAL HACK BECAUSE WE CANT CREATE A TEXT NODE !!!! */ // Make the children a text node Builder builder = parent.newBuilder(); builder.open(); builder.startDocument(0); builder.characters(value.toString(), 0, 0); builder.endDocument(); builder.close(); return builder.getCurrentRoot().iterateAxis(net.sf.saxon.om.AxisInfo.CHILD) .next(); /* * parent.replaceStringValue(replace); * * Item item = parent.iterateAxis( net.sf.saxon.om.Axis.CHILD ).next(); * return (NodeInfo) item ; * */ } /* * Import the node using the builder into this object model */ private XdmNode importNode(XdmNode node) throws SaxonApiException { Source src = node.asSource(); return build(src); } private NodeInfo getNodeInfo(XdmNode node) throws IndexOutOfBoundsException, SaxonApiUncheckedException, SaxonApiException { XdmNode xnode = importNode(node); return ((DocumentImpl) xnode.getUnderlyingNode().getDocumentRoot()) .getDocumentElement(); } /* * Creates/Builds a Tree (LINKED_TREE) type node from any source */ private XdmNode build(Source src) throws SaxonApiException { // @TODO: To get over a bug in Saxon's build() have to use the root element // instead of a document node to force building of a linked tree model // Otherwise the source is just returned unchnaged if(src instanceof DocumentInfo) src = (((DocumentInfo) src).iterateAxis(net.sf.saxon.om.AxisInfo.CHILD) .next()); return mBuilder.build(src); } /* * Find a matching attribute to a passed in one * Compare URI and local part */ private NodeInfo findAttribute(NodeInfo node, NodeInfo attr) { // Write attributes AxisIterator iter = node.iterateAxis(net.sf.saxon.om.AxisInfo.ATTRIBUTE); Item item; while((item = iter.next()) != null) { NodeInfo a = (NodeInfo) item; if(a.getURI().equals(attr.getURI()) && a.getLocalPart().equals(attr.getLocalPart())) return a; } return null; } } // // // Copyright (C) 2008-2014 David A. Lee. // // The contents of this file are subject to the "Simplified BSD License" (the // "License"); // you may not use this file except in compliance with the License. You may // obtain a copy of the // License at http://www.opensource.org/licenses/bsd-license.php // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. // See the License for the specific language governing rights and limitations // under the License. // // The Original Code is: all this file. // // The Initial Developer of the Original Code is David A. Lee // // Portions created by (your name) are Copyright (C) (your legal entity). All // Rights Reserved. // // Contributor(s): none. //