/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jena.rdfxml.xmlinput.states; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import org.apache.jena.rdfxml.xmlinput.impl.AbsXMLContext ; import org.apache.jena.rdfxml.xmlinput.impl.XMLHandler ; import org.xml.sax.Attributes; import org.xml.sax.SAXParseException; public abstract class AbsXMLLiteral extends Frame { boolean checkComposingChar = true; static Map<String, String> xmlNameSpace = new TreeMap<>(); static { xmlNameSpace.put("xml", xmlns); xmlNameSpace.put("", ""); } @Override String suggestParsetypeLiteral() { // shouldn't be called. return ""; } final protected StringBuffer rslt; public final Map<String, String> namespaces; private static String prefix(String qname) { int colon = qname.indexOf(':'); return colon == -1?"":qname.substring(0,colon); } protected void append(String s) { rslt.append(s); } private void append(char ch[], int s, int l) { rslt.append(ch,s,l); } protected void append(char s) { rslt.append(s); } public AbsXMLLiteral(FrameI p, AbsXMLContext x, StringBuffer r) { super(p, x); rslt = r; namespaces = xmlNameSpace; } public AbsXMLLiteral(AbsXMLLiteral p, Map<String, String> ns) { super(p, p.xml); rslt = p.rslt; namespaces = ns; } public AbsXMLLiteral(XMLHandler h,AbsXMLContext x) { super(h, x); rslt = new StringBuffer(); namespaces = xmlNameSpace; } private void useNameSpace(String prefix, String uri, Map<String, String> ns) { if (!uri.equals(namespaces.get(prefix))) ns.put(prefix, uri); } @Override abstract public void endElement() throws SAXParseException; void startLitElement(String uri, String rawName, Map<String, String> ns) { append('<'); append(rawName); useNameSpace(prefix(rawName),uri, ns); } private void appendAttrValue(String s) { String replace; char ch; for (int i = 0; i < s.length(); i++) { ch = s.charAt(i); switch (ch) { case '&' : replace = "&"; break; case '<' : replace = "<"; break; case '"' : replace = """; break; case 9 : replace = " "; break; case 0xA : replace = " "; break; case 0xD : replace = " "; break; default : replace = null; } if (replace != null) { append(replace); } else { append(ch); } } } /** except all ampersands are replaced by &, all open angle brackets () are replaced by <, all closing angle brackets (>) are replaced by >, and all #xD characters are replaced by . * @throws SAXParseException */ @Override public void characters(char[] chrs, int start, int length) throws SAXParseException { if (checkComposingChar) checkComposingChar(taint,chrs, start, length); checkComposingChar = false; String replace; char ch; for (int i = 0; i < length; i++) { ch = chrs[start+i]; switch (ch) { case '&' : replace = "&"; break; case '<' : replace = "<"; break; case '>' : replace = ">"; break; case 0xD : replace = " "; break; default : replace = null; } if (replace != null) { append(replace); } else { append(ch); } } } @Override public void comment(char[] ch, int start, int length) throws SAXParseException { append("<!--"); append(ch,start,length); append("-->"); checkComposingChar = true; } @Override public void processingInstruction(String target, String data) { append("<?"); append(target); append(' '); append(data); append("?>"); checkComposingChar = true; } @Override public FrameI startElement(String uri, String localName, String rawName, Attributes atts) { checkComposingChar = true; Map<String, String> attrMap = new TreeMap<>(); Map<String, String> childNameSpaces = new TreeMap<>(); startLitElement( uri, rawName, childNameSpaces); for (int i = atts.getLength()-1;i>=0;i--) { String ns = atts.getURI(i); String qname = atts.getQName(i); String prefix = prefix(qname); if (!prefix.equals("")) useNameSpace(prefix,ns, childNameSpaces); attrMap.put(qname,atts.getValue(i)); } // At this stage, childNameSpaces contains the new visibly used // namespaces (i.e. those not in this). // attrMap contains the attributes // Both are sorted correctly, so we just read them off, // namespaces first. Iterator<Map.Entry<String, String>> it = childNameSpaces.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> pair = it.next(); append(" xmlns"); String prefix = pair.getKey(); if (!"".equals(prefix)) { append(':'); append(prefix); } append("=\""); appendAttrValue(pair.getValue()); append('"'); } it = attrMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> pair = it.next(); append(' '); append(pair.getKey()); append("=\""); appendAttrValue(pair.getValue()); append('"'); } append('>'); // Now sort out our namespaces, so that // child can see all of them. if (childNameSpaces.isEmpty()) { childNameSpaces = namespaces; } else { it = namespaces.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> pair = it.next(); String prefix = pair.getKey(); if (!childNameSpaces.containsKey(prefix)) childNameSpaces.put(prefix,pair.getValue()); // else prefix was overwritten with different value } } return new InnerXMLLiteral(this, rawName, childNameSpaces); } }