/** * $Id: $ * $Date: $ * */ package org.xmlsh.internal.commands; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Stack; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventFactory; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import org.xmlsh.core.Options; import org.xmlsh.core.XCommand; import org.xmlsh.core.XValue; import org.xmlsh.sh.shell.SerializeOpts; import org.xmlsh.util.Util; import javanet.staxutils.XMLStreamUtils; public class xaddbase extends XCommand { private XMLEventFactory mFactory = XMLEventFactory.newInstance(); private static QName mBaseQName = new QName( "http://www.w3.org/XML/1998/namespace", "base", "xml"); @Override public int run(List<XValue> args) throws Exception { boolean opt_all = false; boolean opt_relative = false; Options opts = new Options("a=all,r=relative", SerializeOpts.getOptionDefs()); opts.parse(args); SerializeOpts serializeOpts = getSerializeOpts(opts); XMLEventReader reader = getStdin().asXMLEventReader(serializeOpts); XMLEventWriter writer = getStdout().asXMLEventWriter(serializeOpts); opt_all = opts.hasOpt("all"); opt_relative = opts.hasOpt("relative"); add_base(reader, writer, opt_all, opt_relative, getStdin().getSystemId()); return 0; } private XMLEvent add_base_attr(StartElement start, String base) { Attribute attr = mFactory.createAttribute(mBaseQName, base); return XMLStreamUtils.mergeAttributes(start, Collections.singletonList(attr).iterator(), mFactory); } private String resolve(String parentBaseURI, String baseURI, boolean opt_relative) throws URISyntaxException { if(!opt_relative) return baseURI; if(Util.isBlank(parentBaseURI)) return baseURI; URI u = new URI(parentBaseURI); // Remove final path component from parent String path = u.getPath(); int slash = path.lastIndexOf('/'); if(slash >= 0) path = path.substring(0, slash); URI parent_URI = new URI(u.getScheme(), u.getUserInfo(), u.getHost(), u.getPort(), path, u.getQuery(), u.getFragment()); URI base_URI = new URI(baseURI); URI relative = parent_URI.relativize(base_URI); return relative.toString(); } private void add_base(XMLEventReader reader, XMLEventWriter writer, boolean opt_all, boolean opt_relative, String parentURI) throws URISyntaxException, XMLStreamException { Stack<String> mURIs = new Stack<String>(); mURIs.push(parentURI); while(reader.hasNext()) { XMLEvent event = reader.nextEvent(); if(event.getEventType() == XMLStreamConstants.START_ELEMENT) { boolean addBase = false; StartElement start = event.asStartElement(); String baseURI = getBaseURI(start); parentURI = mURIs.lastElement(); boolean bIsRoot = mURIs.size() == 1; if(bIsRoot) { // document root // First element - always set the xml:base attribute addBase = true; } else if(opt_all) addBase = true; else if(baseURI.equals(parentURI)) addBase = false; else if(!Util.isEqual(parentURI, baseURI)) addBase = true; if(addBase) { String uri = resolve(parentURI, baseURI, bIsRoot ? false : opt_relative); event = add_base_attr(start, uri); } else event = removeBase(start); mURIs.push(baseURI); } else if(event.getEventType() == XMLStreamConstants.END_ELEMENT) mURIs.pop(); writer.add(event); } } private String getBaseURI(StartElement start) { Attribute baseAttr = start.getAttributeByName(mBaseQName); if(baseAttr != null) return baseAttr.getValue(); String systemID = start.getLocation().getSystemId(); if(Util.isBlank(systemID)) return ""; return systemID; } @SuppressWarnings("unchecked") private XMLEvent removeBase(StartElement start) { if(start.getAttributeByName(mBaseQName) != null) { Iterator iter = start.getAttributes(); while(iter.hasNext()) { Attribute a = (Attribute) iter.next(); if(a.getName().equals(mBaseQName)) iter.remove(); } } return start; } } // // // 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. //