| /* Copyright 2004 The Apache Software Foundation |
| * |
| * Licensed 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.xmlbeans.impl.store; |
| |
| import javax.xml.namespace.QName; |
| |
| import org.apache.xmlbeans.SystemProperties; |
| import org.apache.xmlbeans.XmlDocumentProperties; |
| import org.apache.xmlbeans.XmlOptions; |
| import org.apache.xmlbeans.XmlOptionCharEscapeMap; |
| import org.apache.xmlbeans.xml.stream.*; |
| |
| import org.apache.xmlbeans.impl.common.*; |
| |
| import java.io.Writer; |
| import java.io.Reader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.UnsupportedEncodingException; |
| |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.ext.LexicalHandler; |
| import org.xml.sax.SAXException; |
| |
| import org.xml.sax.helpers.AttributesImpl; |
| |
| import java.util.Iterator; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.LinkedHashMap; |
| import java.util.ConcurrentModificationException; |
| |
| abstract class Saver |
| { |
| static final int ROOT = Cur.ROOT; |
| static final int ELEM = Cur.ELEM; |
| static final int ATTR = Cur.ATTR; |
| static final int COMMENT = Cur.COMMENT; |
| static final int PROCINST = Cur.PROCINST; |
| static final int TEXT = Cur.TEXT; |
| |
| protected abstract boolean emitElement ( SaveCur c, ArrayList attrNames, ArrayList attrValues ); |
| protected abstract void emitFinish ( SaveCur c ); |
| protected abstract void emitText ( SaveCur c ); |
| protected abstract void emitComment ( SaveCur c ); |
| protected abstract void emitProcinst ( SaveCur c ); |
| protected abstract void emitDocType ( String docTypeName, String publicId, String systemId ); |
| protected abstract void emitStartDoc ( SaveCur c ); |
| protected abstract void emitEndDoc ( SaveCur c ); |
| |
| protected void syntheticNamespace ( String prefix, String uri, boolean considerDefault ) { } |
| |
| Saver ( Cur c, XmlOptions options ) |
| { |
| assert c._locale.entered(); |
| |
| options = XmlOptions.maskNull( options ); |
| |
| _cur = createSaveCur( c, options ); |
| |
| _locale = c._locale; |
| _version = _locale.version(); |
| |
| _namespaceStack = new ArrayList(); |
| _uriMap = new HashMap(); |
| _prefixMap = new HashMap(); |
| |
| _attrNames = new ArrayList(); |
| _attrValues = new ArrayList (); |
| |
| // Define implicit xml prefixed namespace |
| |
| addMapping( "xml", Locale._xml1998Uri ); |
| |
| if (options.hasOption( XmlOptions.SAVE_IMPLICIT_NAMESPACES )) |
| { |
| Map m = (Map) options.get( XmlOptions.SAVE_IMPLICIT_NAMESPACES ); |
| |
| for ( Iterator i = m.keySet().iterator() ; i.hasNext() ; ) |
| { |
| String prefix = (String) i.next(); |
| addMapping( prefix, (String) m.get( prefix ) ); |
| } |
| } |
| |
| // define character map for escaped replacements |
| if (options.hasOption( XmlOptions.SAVE_SUBSTITUTE_CHARACTERS )) |
| { |
| _replaceChar = (XmlOptionCharEscapeMap) |
| options.get( XmlOptions.SAVE_SUBSTITUTE_CHARACTERS); |
| } |
| |
| // If the default prefix has not been mapped, do so now |
| |
| if (getNamespaceForPrefix( "" ) == null) |
| { |
| _initialDefaultUri = new String( "" ); |
| addMapping( "", _initialDefaultUri ); |
| } |
| |
| if (options.hasOption( XmlOptions.SAVE_AGGRESSIVE_NAMESPACES ) && |
| !(this instanceof SynthNamespaceSaver)) |
| { |
| SynthNamespaceSaver saver = new SynthNamespaceSaver( c, options ); |
| |
| while ( saver.process() ) |
| ; |
| |
| if (!saver._synthNamespaces.isEmpty()) |
| _preComputedNamespaces = saver._synthNamespaces; |
| } |
| |
| _useDefaultNamespace = |
| options.hasOption( XmlOptions.SAVE_USE_DEFAULT_NAMESPACE ); |
| |
| _saveNamespacesFirst = options.hasOption( XmlOptions.SAVE_NAMESPACES_FIRST ); |
| |
| if (options.hasOption( XmlOptions.SAVE_SUGGESTED_PREFIXES )) |
| _suggestedPrefixes = (Map) options.get( XmlOptions.SAVE_SUGGESTED_PREFIXES); |
| |
| _ancestorNamespaces = _cur.getAncestorNamespaces(); |
| } |
| |
| private static SaveCur createSaveCur ( Cur c, XmlOptions options ) |
| { |
| QName synthName = (QName) options.get( XmlOptions.SAVE_SYNTHETIC_DOCUMENT_ELEMENT ); |
| |
| QName fragName = synthName; |
| |
| if (fragName == null) |
| { |
| fragName = |
| options.hasOption( XmlOptions.SAVE_USE_OPEN_FRAGMENT ) |
| ? Locale._openuriFragment |
| : Locale._xmlFragment; |
| } |
| |
| boolean saveInner = |
| options.hasOption( XmlOptions.SAVE_INNER ) && |
| !options.hasOption( XmlOptions.SAVE_OUTER ); |
| |
| Cur start = c.tempCur(); |
| Cur end = c.tempCur(); |
| |
| SaveCur cur = null; |
| |
| int k = c.kind(); |
| |
| switch ( k ) |
| { |
| case ROOT : |
| { |
| positionToInner( c, start, end ); |
| |
| if (Locale.isFragment( start, end )) |
| cur = new FragSaveCur( start, end, fragName ); |
| else if (synthName != null) |
| cur = new FragSaveCur( start, end, synthName ); |
| else |
| cur = new DocSaveCur( c ); |
| |
| break; |
| } |
| |
| case ELEM : |
| { |
| if (saveInner) |
| { |
| positionToInner( c, start, end ); |
| |
| cur = |
| new FragSaveCur( |
| start, end, Locale.isFragment( start, end ) ? fragName : synthName ); |
| } |
| else if (synthName != null) |
| { |
| positionToInner( c, start, end ); |
| |
| cur = new FragSaveCur( start, end, synthName ); |
| } |
| else |
| { |
| start.moveToCur( c ); |
| end.moveToCur( c ); |
| end.skip(); |
| |
| cur = new FragSaveCur( start, end, null ); |
| } |
| |
| break; |
| } |
| } |
| |
| if (cur == null) |
| { |
| assert k < 0 || k == ATTR || k == COMMENT || k == PROCINST || k == TEXT; |
| |
| if (k < 0) |
| { |
| // Save out "" |
| start.moveToCur( c ); |
| end.moveToCur( c ); |
| } |
| else if (k == TEXT) |
| { |
| start.moveToCur( c ); |
| end.moveToCur( c ); |
| end.next(); |
| } |
| else if (saveInner) |
| { |
| start.moveToCur( c ); |
| start.next(); |
| |
| end.moveToCur( c ); |
| end.toEnd(); |
| } |
| else if (k == ATTR) |
| { |
| start.moveToCur( c ); |
| end.moveToCur( c ); |
| } |
| else |
| { |
| assert k == COMMENT || k == PROCINST; |
| |
| start.moveToCur( c ); |
| end.moveToCur( c ); |
| end.skip(); |
| } |
| |
| cur = new FragSaveCur( start, end, fragName ); |
| } |
| |
| String filterPI = (String) options.get( XmlOptions.SAVE_FILTER_PROCINST ); |
| |
| if (filterPI != null) |
| cur = new FilterPiSaveCur( cur, filterPI ); |
| |
| if (options.hasOption( XmlOptions.SAVE_PRETTY_PRINT )) |
| cur = new PrettySaveCur( cur, options ); |
| |
| start.release(); |
| end.release(); |
| |
| return cur; |
| } |
| |
| private static void positionToInner ( Cur c, Cur start, Cur end ) |
| { |
| assert c.isContainer(); |
| |
| start.moveToCur( c ); |
| |
| if (!start.toFirstAttr()) |
| start.next(); |
| |
| end.moveToCur( c ); |
| end.toEnd(); |
| } |
| |
| /** |
| * Test if a character is valid in xml character content. See |
| * http://www.w3.org/TR/REC-xml#NT-Char |
| */ |
| static boolean isBadChar ( char ch ) |
| { |
| return ! ( |
| Character.isHighSurrogate(ch) || |
| Character.isLowSurrogate(ch) || |
| (ch >= 0x20 && ch <= 0xD7FF ) || |
| (ch >= 0xE000 && ch <= 0xFFFD) || |
| (ch >= 0x10000 && ch <= 0x10FFFF) || |
| (ch == 0x9) || (ch == 0xA) || (ch == 0xD) |
| ); |
| } |
| |
| protected boolean saveNamespacesFirst ( ) |
| { |
| return _saveNamespacesFirst; |
| } |
| |
| protected void enterLocale() |
| { |
| _locale.enter(); |
| } |
| |
| protected void exitLocale() |
| { |
| _locale.exit(); |
| } |
| |
| protected final boolean process ( ) |
| { |
| assert _locale.entered(); |
| |
| if (_cur == null) |
| return false; |
| |
| if (_version != _locale.version()) |
| throw new ConcurrentModificationException( "Document changed during save" ); |
| |
| switch ( _cur.kind() ) |
| { |
| case ROOT : { processRoot(); break; } |
| case ELEM : { processElement(); break; } |
| case - ELEM : { processFinish (); break; } |
| case TEXT : { emitText ( _cur ); break; } |
| |
| case COMMENT : { emitComment ( _cur ); _cur.toEnd(); break; } |
| case PROCINST : { emitProcinst ( _cur ); _cur.toEnd(); break; } |
| |
| case - ROOT : |
| { |
| emitEndDoc(_cur); |
| _cur.release(); |
| _cur = null; |
| |
| return true; |
| } |
| |
| default : throw new RuntimeException( "Unexpected kind" ); |
| } |
| |
| _cur.next(); |
| |
| return true; |
| } |
| |
| private final void processFinish ( ) |
| { |
| emitFinish( _cur ); |
| popMappings(); |
| } |
| |
| private final void processRoot ( ) |
| { |
| assert _cur.isRoot(); |
| |
| XmlDocumentProperties props = _cur.getDocProps(); |
| String systemId = null; |
| String docTypeName = null; |
| if (props != null) |
| { |
| systemId = props.getDoctypeSystemId(); |
| docTypeName = props.getDoctypeName(); |
| } |
| |
| if (systemId != null || docTypeName != null) |
| { |
| if (docTypeName == null) |
| { |
| _cur.push(); |
| while (!_cur.isElem() && _cur.next()) |
| ; |
| if (_cur.isElem()) |
| docTypeName = _cur.getName().getLocalPart(); |
| _cur.pop(); |
| } |
| |
| String publicId = props.getDoctypePublicId(); |
| |
| if (docTypeName != null) |
| { |
| QName rootElemName = _cur.getName(); |
| |
| if ( rootElemName == null ) |
| { |
| _cur.push(); |
| while ( !_cur.isFinish() ) |
| { |
| if (_cur.isElem()) |
| { |
| rootElemName = _cur.getName(); |
| break; |
| } |
| _cur.next(); |
| } |
| _cur.pop(); |
| } |
| |
| if ( rootElemName!=null && docTypeName.equals(rootElemName.getLocalPart()) ) |
| { |
| emitDocType( docTypeName, publicId, systemId ); |
| return; |
| } |
| } |
| } |
| |
| emitStartDoc(_cur); |
| } |
| |
| private final void processElement ( ) |
| { |
| assert _cur.isElem() && _cur.getName() != null; |
| |
| QName name = _cur.getName(); |
| |
| // Add a new entry to the frontier. If this element has a name |
| // which has no namespace, then we must make sure that pushing |
| // the mappings causes the default namespace to be empty |
| |
| boolean ensureDefaultEmpty = name.getNamespaceURI().length() == 0; |
| |
| pushMappings( _cur, ensureDefaultEmpty ); |
| |
| // |
| // There are four things which use mappings: |
| // |
| // 1) The element name |
| // 2) The element value (qname based) |
| // 3) Attribute names |
| // 4) The attribute values (qname based) |
| // |
| |
| // 1) The element name (not for starts) |
| |
| ensureMapping( name.getNamespaceURI(), name.getPrefix(), !ensureDefaultEmpty, false ); |
| |
| // |
| // |
| // |
| |
| _attrNames.clear(); |
| _attrValues.clear(); |
| |
| _cur.push(); |
| |
| attrs: |
| for ( boolean A = _cur.toFirstAttr() ; A ; A = _cur.toNextAttr() ) |
| { |
| if (_cur.isNormalAttr()) |
| { |
| QName attrName = _cur.getName(); |
| |
| _attrNames.add( attrName ); |
| |
| for ( int i = _attrNames.size() - 2 ; i >= 0 ; i-- ) |
| { |
| if (_attrNames.get( i ).equals( attrName )) |
| { |
| _attrNames.remove( _attrNames.size() - 1 ); |
| continue attrs; |
| } |
| } |
| |
| _attrValues.add( _cur.getAttrValue() ); |
| |
| ensureMapping( attrName.getNamespaceURI(), attrName.getPrefix(), false, true ); |
| } |
| } |
| |
| _cur.pop(); |
| |
| // If I am doing aggressive namespaces and we're emitting a |
| // container which can contain content, add the namespaces |
| // we've computed. Basically, I'm making sure the pre-computed |
| // namespaces are mapped on the first container which has a name. |
| |
| if (_preComputedNamespaces != null) |
| { |
| for ( Iterator i = _preComputedNamespaces.keySet().iterator() ; i.hasNext() ; ) |
| { |
| String uri = (String) i.next(); |
| String prefix = (String) _preComputedNamespaces.get( uri ); |
| boolean considerDefault = prefix.length() == 0 && !ensureDefaultEmpty; |
| |
| ensureMapping( uri, prefix, considerDefault, false ); |
| } |
| |
| // Set to null so we do this once at the top |
| _preComputedNamespaces = null; |
| } |
| |
| if (emitElement( _cur, _attrNames, _attrValues )) |
| { |
| popMappings(); |
| _cur.toEnd(); |
| } |
| } |
| |
| // |
| // Layout of namespace stack: |
| // |
| // URI Undo |
| // URI Rename |
| // Prefix Undo |
| // Mapping |
| // |
| |
| boolean hasMappings ( ) |
| { |
| int i = _namespaceStack.size(); |
| |
| return i > 0 && _namespaceStack.get( i - 1 ) != null; |
| } |
| |
| void iterateMappings ( ) |
| { |
| _currentMapping = _namespaceStack.size(); |
| |
| while ( _currentMapping > 0 && _namespaceStack.get( _currentMapping - 1 ) != null ) |
| _currentMapping -= 8; |
| } |
| |
| boolean hasMapping ( ) |
| { |
| return _currentMapping < _namespaceStack.size(); |
| } |
| |
| void nextMapping ( ) |
| { |
| _currentMapping += 8; |
| } |
| |
| String mappingPrefix ( ) |
| { |
| assert hasMapping(); |
| return (String) _namespaceStack.get( _currentMapping + 6 ); |
| } |
| |
| String mappingUri ( ) |
| { |
| assert hasMapping(); |
| return (String) _namespaceStack.get( _currentMapping + 7 ); |
| } |
| |
| private final void pushMappings ( SaveCur c, boolean ensureDefaultEmpty ) |
| { |
| assert c.isContainer(); |
| |
| _namespaceStack.add( null ); |
| |
| c.push(); |
| |
| namespaces: |
| for ( boolean A = c.toFirstAttr() ; A ; A = c.toNextAttr() ) |
| if (c.isXmlns()) |
| addNewFrameMapping( c.getXmlnsPrefix(), c.getXmlnsUri(), ensureDefaultEmpty ); |
| |
| c.pop(); |
| |
| if (_ancestorNamespaces != null) |
| { |
| for ( int i = 0 ; i < _ancestorNamespaces.size() ; i += 2 ) |
| { |
| String prefix = (String) _ancestorNamespaces.get( i ); |
| String uri = (String) _ancestorNamespaces.get( i + 1 ); |
| |
| addNewFrameMapping( prefix, uri, ensureDefaultEmpty ); |
| } |
| |
| _ancestorNamespaces = null; |
| } |
| |
| if (ensureDefaultEmpty) |
| { |
| String defaultUri = (String) _prefixMap.get( "" ); |
| |
| // I map the default to "" at the very beginning |
| assert defaultUri != null; |
| |
| if (defaultUri.length() > 0) |
| addMapping( "", "" ); |
| } |
| } |
| |
| private final void addNewFrameMapping ( String prefix, String uri, boolean ensureDefaultEmpty ) |
| { |
| // If the prefix maps to "", this don't include this mapping 'cause it's not well formed. |
| // Also, if we want to make sure that the default namespace is always "", then check that |
| // here as well. |
| |
| if ((prefix.length() == 0 || uri.length() > 0) && |
| (!ensureDefaultEmpty || prefix.length() > 0 || uri.length() == 0)) |
| { |
| // Make sure the prefix is not already mapped in this frame |
| |
| for ( iterateMappings() ; hasMapping() ; nextMapping() ) |
| if (mappingPrefix().equals( prefix )) |
| return; |
| |
| // Also make sure that the prefix declaration is not redundant |
| // This has the side-effect of making it impossible to set a |
| // redundant prefix declaration, but seems that it's better |
| // to just never issue a duplicate prefix declaration. |
| if (uri.equals(getNamespaceForPrefix(prefix))) |
| return; |
| |
| addMapping( prefix, uri ); |
| } |
| } |
| |
| private final void addMapping ( String prefix, String uri ) |
| { |
| assert uri != null; |
| assert prefix != null; |
| |
| // If the prefix being mapped here is already mapped to a uri, |
| // that uri will either go out of scope or be mapped to another |
| // prefix. |
| |
| String renameUri = (String) _prefixMap.get( prefix ); |
| String renamePrefix = null; |
| |
| if (renameUri != null) |
| { |
| // See if this prefix is already mapped to this uri. If |
| // so, then add to the stack, but there is nothing to rename |
| |
| if (renameUri.equals( uri )) |
| renameUri = null; |
| else |
| { |
| int i = _namespaceStack.size(); |
| |
| while ( i > 0 ) |
| { |
| if (_namespaceStack.get( i - 1 ) == null) |
| { |
| i--; |
| continue; |
| } |
| |
| if (_namespaceStack.get( i - 7 ).equals( renameUri )) |
| { |
| renamePrefix = (String) _namespaceStack.get( i - 8 ); |
| |
| if (renamePrefix == null || !renamePrefix.equals( prefix )) |
| break; |
| } |
| |
| i -= 8; |
| } |
| |
| assert i > 0; |
| } |
| } |
| |
| _namespaceStack.add( _uriMap.get( uri ) ); |
| _namespaceStack.add( uri ); |
| |
| if (renameUri != null) |
| { |
| _namespaceStack.add( _uriMap.get( renameUri ) ); |
| _namespaceStack.add( renameUri ); |
| } |
| else |
| { |
| _namespaceStack.add( null ); |
| _namespaceStack.add( null ); |
| } |
| |
| _namespaceStack.add( prefix ); |
| _namespaceStack.add( _prefixMap.get( prefix ) ); |
| |
| _namespaceStack.add( prefix ); |
| _namespaceStack.add( uri ); |
| |
| _uriMap.put( uri, prefix ); |
| _prefixMap.put( prefix, uri ); |
| |
| if (renameUri != null) |
| _uriMap.put( renameUri, renamePrefix ); |
| } |
| |
| private final void popMappings ( ) |
| { |
| for ( ; ; ) |
| { |
| int i = _namespaceStack.size(); |
| |
| if (i == 0) |
| break; |
| |
| if (_namespaceStack.get( i - 1 ) == null) |
| { |
| _namespaceStack.remove( i - 1 ); |
| break; |
| } |
| |
| Object oldUri = _namespaceStack.get( i - 7 ); |
| Object oldPrefix = _namespaceStack.get( i - 8 ); |
| |
| if (oldPrefix == null) |
| _uriMap.remove( oldUri ); |
| else |
| _uriMap.put( oldUri, oldPrefix ); |
| |
| oldPrefix = _namespaceStack.get( i - 4 ); |
| oldUri = _namespaceStack.get( i - 3 ); |
| |
| if (oldUri == null) |
| _prefixMap.remove( oldPrefix ); |
| else |
| _prefixMap.put( oldPrefix, oldUri ); |
| |
| String uri = (String) _namespaceStack.get( i - 5 ); |
| |
| if (uri != null) |
| _uriMap.put( uri, _namespaceStack.get( i - 6 ) ); |
| |
| // Hahahahahaha -- :-( |
| _namespaceStack.remove( i - 1 ); |
| _namespaceStack.remove( i - 2 ); |
| _namespaceStack.remove( i - 3 ); |
| _namespaceStack.remove( i - 4 ); |
| _namespaceStack.remove( i - 5 ); |
| _namespaceStack.remove( i - 6 ); |
| _namespaceStack.remove( i - 7 ); |
| _namespaceStack.remove( i - 8 ); |
| } |
| } |
| |
| private final void dumpMappings ( ) |
| { |
| for ( int i = _namespaceStack.size() ; i > 0 ; ) |
| { |
| if (_namespaceStack.get( i - 1 ) == null) |
| { |
| System.out.println( "----------------" ); |
| i--; |
| continue; |
| } |
| |
| System.out.print( "Mapping: " ); |
| System.out.print( _namespaceStack.get( i - 2 ) ); |
| System.out.print( " -> " ); |
| System.out.print( _namespaceStack.get( i - 1 ) ); |
| System.out.println(); |
| |
| System.out.print( "Prefix Undo: " ); |
| System.out.print( _namespaceStack.get( i - 4 ) ); |
| System.out.print( " -> " ); |
| System.out.print( _namespaceStack.get( i - 3 ) ); |
| System.out.println(); |
| |
| System.out.print( "Uri Rename: " ); |
| System.out.print( _namespaceStack.get( i - 5 ) ); |
| System.out.print( " -> " ); |
| System.out.print( _namespaceStack.get( i - 6 ) ); |
| System.out.println(); |
| |
| System.out.print( "UriUndo: " ); |
| System.out.print( _namespaceStack.get( i - 7 ) ); |
| System.out.print( " -> " ); |
| System.out.print( _namespaceStack.get( i - 8 ) ); |
| System.out.println(); |
| |
| System.out.println(); |
| |
| i -= 8; |
| } |
| } |
| |
| private final String ensureMapping ( |
| String uri, String candidatePrefix, |
| boolean considerCreatingDefault, boolean mustHavePrefix ) |
| { |
| assert uri != null; |
| |
| // Can be called for no-namespaced things |
| |
| if (uri.length() == 0) |
| return null; |
| |
| String prefix = (String) _uriMap.get( uri ); |
| |
| if (prefix != null && (prefix.length() > 0 || !mustHavePrefix)) |
| return prefix; |
| |
| // |
| // I try prefixes from a number of places, in order: |
| // |
| // 1) What was passed in |
| // 2) The optional suggestions (for uri's) |
| // 3) The default mapping is allowed |
| // 4) ns#++ |
| // |
| |
| if (candidatePrefix != null && candidatePrefix.length() == 0) |
| candidatePrefix = null; |
| |
| if (candidatePrefix == null || !tryPrefix( candidatePrefix )) |
| { |
| if (_suggestedPrefixes != null && |
| _suggestedPrefixes.containsKey( uri ) && |
| tryPrefix( (String) _suggestedPrefixes.get( uri ) )) |
| { |
| candidatePrefix = (String) _suggestedPrefixes.get( uri ); |
| } |
| else if (considerCreatingDefault && _useDefaultNamespace && tryPrefix( "" )) |
| candidatePrefix = ""; |
| else |
| { |
| String basePrefix = QNameHelper.suggestPrefix( uri ); |
| candidatePrefix = basePrefix; |
| |
| for ( int i = 1 ; ; i++ ) |
| { |
| if (tryPrefix( candidatePrefix )) |
| break; |
| |
| candidatePrefix = basePrefix + i; |
| } |
| } |
| } |
| |
| assert candidatePrefix != null; |
| |
| syntheticNamespace( candidatePrefix, uri, considerCreatingDefault ); |
| |
| addMapping( candidatePrefix, uri ); |
| |
| return candidatePrefix; |
| } |
| |
| protected final String getUriMapping ( String uri ) |
| { |
| assert _uriMap.get( uri ) != null; |
| return (String) _uriMap.get( uri ); |
| } |
| |
| String getNonDefaultUriMapping ( String uri ) |
| { |
| String prefix = (String) _uriMap.get( uri ); |
| |
| if (prefix != null && prefix.length() > 0) |
| return prefix; |
| |
| for ( Iterator keys = _prefixMap.keySet().iterator() ; keys.hasNext() ; ) |
| { |
| prefix = (String) keys.next(); |
| |
| if (prefix.length() > 0 && _prefixMap.get( prefix ).equals( uri )) |
| return prefix; |
| } |
| |
| assert false : "Could not find non-default mapping"; |
| |
| return null; |
| } |
| |
| private final boolean tryPrefix ( String prefix ) |
| { |
| if (prefix == null || Locale.beginsWithXml( prefix )) |
| return false; |
| |
| String existingUri = (String) _prefixMap.get( prefix ); |
| |
| // If the prefix is currently mapped, then try another prefix. A |
| // special case is that of trying to map the default prefix (""). |
| // Here, there always exists a default mapping. If this is the |
| // mapping we found, then remap it anyways. I use != to compare |
| // strings because I want to test for the specific initial default |
| // uri I added when I initialized the saver. |
| |
| if (existingUri != null && (prefix.length() > 0 || existingUri != _initialDefaultUri)) |
| return false; |
| |
| return true; |
| } |
| |
| public final String getNamespaceForPrefix ( String prefix ) |
| { |
| assert !prefix.equals( "xml" ) || _prefixMap.get( prefix ).equals( Locale._xml1998Uri ); |
| |
| return (String) _prefixMap.get( prefix ); |
| } |
| |
| protected Map getPrefixMap() |
| { |
| return _prefixMap; |
| } |
| |
| // |
| // |
| // |
| |
| static final class SynthNamespaceSaver extends Saver |
| { |
| LinkedHashMap _synthNamespaces = new LinkedHashMap(); |
| |
| SynthNamespaceSaver ( Cur c, XmlOptions options ) |
| { |
| super( c, options ); |
| } |
| |
| protected void syntheticNamespace ( |
| String prefix, String uri, boolean considerCreatingDefault ) |
| { |
| _synthNamespaces.put( uri, considerCreatingDefault ? "" : prefix ); |
| } |
| |
| protected boolean emitElement ( |
| SaveCur c, ArrayList attrNames, ArrayList attrValues ) { return false; } |
| |
| protected void emitFinish ( SaveCur c ) { } |
| protected void emitText ( SaveCur c ) { } |
| protected void emitComment ( SaveCur c ) { } |
| protected void emitProcinst ( SaveCur c ) { } |
| protected void emitDocType ( String docTypeName, String publicId, String systemId ) { } |
| protected void emitStartDoc ( SaveCur c ) { } |
| protected void emitEndDoc ( SaveCur c ) { } |
| } |
| |
| // |
| // |
| // |
| |
| static final class TextSaver extends Saver |
| { |
| TextSaver ( Cur c, XmlOptions options, String encoding ) |
| { |
| super( c, options ); |
| |
| boolean noSaveDecl = |
| options != null && options.hasOption( XmlOptions.SAVE_NO_XML_DECL ); |
| |
| if (options != null && options.hasOption(XmlOptions.SAVE_CDATA_LENGTH_THRESHOLD)) |
| _cdataLengthThreshold = ((Integer)options.get(XmlOptions.SAVE_CDATA_LENGTH_THRESHOLD)).intValue(); |
| |
| if (options != null && options.hasOption(XmlOptions.SAVE_CDATA_ENTITY_COUNT_THRESHOLD)) |
| _cdataEntityCountThreshold = ((Integer)options.get(XmlOptions.SAVE_CDATA_ENTITY_COUNT_THRESHOLD)).intValue(); |
| |
| if (options != null && options.hasOption(XmlOptions.LOAD_SAVE_CDATA_BOOKMARKS) ) |
| _useCDataBookmarks = true; |
| |
| if (options != null && options.hasOption(XmlOptions.SAVE_PRETTY_PRINT) ) |
| _isPrettyPrint = true; |
| |
| _in = _out = 0; |
| _free = 0; |
| |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| if (encoding != null && !noSaveDecl) |
| { |
| XmlDocumentProperties props = Locale.getDocProps( c, false ); |
| |
| String version = props == null ? null : props.getVersion(); |
| |
| if (version == null) |
| version = "1.0"; |
| |
| emit( "<?xml version=\"" ); |
| emit( version ); |
| emit( "\" encoding=\"" + encoding + "\"?>" + _newLine ); |
| } |
| } |
| |
| protected boolean emitElement ( SaveCur c, ArrayList attrNames, ArrayList attrValues ) |
| { |
| assert c.isElem(); |
| |
| emit( '<' ); |
| emitName( c.getName(), false ); |
| |
| if (saveNamespacesFirst()) |
| emitNamespacesHelper(); |
| |
| for ( int i = 0 ; i < attrNames.size() ; i++ ) |
| emitAttrHelper( (QName) attrNames.get( i ), (String) attrValues.get( i ) ); |
| |
| if (!saveNamespacesFirst()) |
| emitNamespacesHelper(); |
| |
| if (!c.hasChildren() && !c.hasText()) |
| { |
| emit( '/', '>' ); |
| return true; |
| } |
| else |
| { |
| emit( '>' ); |
| return false; |
| } |
| } |
| |
| protected void emitFinish ( SaveCur c ) |
| { |
| emit( '<', '/' ); |
| emitName( c.getName(), false ); |
| emit( '>' ); |
| } |
| |
| protected void emitXmlns ( String prefix, String uri ) |
| { |
| assert prefix != null; |
| assert uri != null; |
| |
| emit( "xmlns" ); |
| |
| if (prefix.length() > 0) |
| { |
| emit( ':' ); |
| emit( prefix ); |
| } |
| |
| emit( '=', '\"' ); |
| |
| // TODO - must encode uri properly |
| |
| emit( uri ); |
| entitizeAttrValue(false); |
| |
| emit( '"' ); |
| } |
| |
| private void emitNamespacesHelper ( ) |
| { |
| for ( iterateMappings() ; hasMapping() ; nextMapping() ) |
| { |
| emit( ' ' ); |
| emitXmlns( mappingPrefix(), mappingUri() ); |
| } |
| } |
| |
| private void emitAttrHelper ( QName attrName, String attrValue ) |
| { |
| emit( ' ' ); |
| emitName( attrName, true ); |
| emit( '=', '\"' ); |
| emit( attrValue ); |
| entitizeAttrValue(true); |
| emit( '"' ); |
| } |
| |
| protected void emitText ( SaveCur c ) |
| { |
| assert c.isText(); |
| |
| // c.isTextCData() is expensive do it only if useCDataBookmarks option is enabled |
| boolean forceCData = _useCDataBookmarks && c.isTextCData(); |
| |
| emit( c ); |
| |
| entitizeContent( forceCData ); |
| } |
| |
| protected void emitComment ( SaveCur c ) |
| { |
| assert c.isComment(); |
| |
| emit( "<!--" ); |
| |
| c.push(); |
| c.next(); |
| |
| emit( c ); |
| |
| c.pop(); |
| |
| entitizeComment(); |
| emit( "-->" ); |
| } |
| |
| protected void emitProcinst ( SaveCur c ) |
| { |
| assert c.isProcinst(); |
| |
| emit( "<?" ); |
| |
| // TODO - encoding issues here? |
| emit( c.getName().getLocalPart() ); |
| |
| c.push(); |
| |
| c.next(); |
| |
| if (c.isText()) |
| { |
| emit( " " ); |
| emit( c ); |
| entitizeProcinst(); |
| } |
| |
| c.pop(); |
| |
| emit( "?>" ); |
| } |
| |
| private void emitLiteral ( String literal ) |
| { |
| // TODO: systemId production http://www.w3.org/TR/REC-xml/#NT-SystemLiteral |
| // TODO: publicId production http://www.w3.org/TR/REC-xml/#NT-PubidLiteral |
| if (literal.indexOf( "\"" ) < 0) |
| { |
| emit( '\"' ); |
| emit( literal ); |
| emit( '\"' ); |
| } |
| else |
| { |
| emit( '\'' ); |
| emit( literal ); |
| emit( '\'' ); |
| } |
| } |
| |
| protected void emitDocType ( String docTypeName, String publicId, String systemId ) |
| { |
| assert docTypeName != null; |
| |
| emit( "<!DOCTYPE " ); |
| emit( docTypeName ); |
| |
| if (publicId == null && systemId != null) |
| { |
| emit( " SYSTEM " ); |
| emitLiteral( systemId ); |
| } |
| else if (publicId != null) |
| { |
| emit( " PUBLIC " ); |
| emitLiteral( publicId ); |
| emit( " " ); |
| emitLiteral( systemId ); |
| } |
| |
| emit( ">" ); |
| emit( _newLine ); |
| } |
| |
| protected void emitStartDoc ( SaveCur c ) |
| { |
| } |
| |
| protected void emitEndDoc ( SaveCur c ) |
| { |
| } |
| |
| // |
| // |
| // |
| |
| private void emitName ( QName name, boolean needsPrefix ) |
| { |
| assert name != null; |
| |
| String uri = name.getNamespaceURI(); |
| |
| assert uri != null; |
| |
| if (uri.length() != 0) |
| { |
| String prefix = name.getPrefix(); |
| String mappedUri = getNamespaceForPrefix( prefix ); |
| |
| if (mappedUri == null || !mappedUri.equals( uri )) |
| prefix = getUriMapping( uri ); |
| |
| // Attrs need a prefix. If I have not found one, then there must be a default |
| // prefix obscuring the prefix needed for this attr. Find it manually. |
| |
| // NOTE - Consider keeping the currently mapped default URI separate fromn the |
| // _urpMap and _prefixMap. This way, I would not have to look it up manually |
| // here |
| |
| if (needsPrefix && prefix.length() == 0) |
| prefix = getNonDefaultUriMapping( uri ); |
| |
| if (prefix.length() > 0) |
| { |
| emit( prefix ); |
| emit( ':' ); |
| } |
| } |
| |
| assert name.getLocalPart().length() > 0; |
| |
| emit( name.getLocalPart() ); |
| } |
| |
| private void emit ( char ch ) |
| { |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| preEmit( 1 ); |
| |
| _buf[ _in ] = ch; |
| |
| _in = (_in + 1) % _buf.length; |
| |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| } |
| |
| private void emit ( char ch1, char ch2 ) |
| { |
| if( preEmit( 2 ) ) |
| return; |
| |
| _buf[ _in ] = ch1; |
| _in = (_in + 1) % _buf.length; |
| |
| _buf[ _in ] = ch2; |
| _in = (_in + 1) % _buf.length; |
| |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| } |
| |
| private void emit ( String s ) |
| { |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| int cch = s == null ? 0 : s.length(); |
| |
| if (preEmit( cch )) |
| return; |
| |
| int chunk; |
| |
| if (_in <= _out || cch < (chunk = _buf.length - _in)) |
| { |
| s.getChars( 0, cch, _buf, _in ); |
| _in += cch; |
| } |
| else |
| { |
| s.getChars( 0, chunk, _buf, _in ); |
| s.getChars( chunk, cch, _buf, 0 ); |
| _in = (_in + cch) % _buf.length; |
| } |
| |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| } |
| |
| private void emit ( SaveCur c ) |
| { |
| if (c.isText()) |
| { |
| Object src = c.getChars(); |
| int cch = c._cchSrc; |
| |
| if (preEmit( cch )) |
| return; |
| |
| int chunk; |
| |
| if (_in <= _out || cch < (chunk = _buf.length - _in)) |
| { |
| CharUtil.getChars( _buf, _in, src, c._offSrc, cch ); |
| _in += cch; |
| } |
| else |
| { |
| CharUtil.getChars( _buf, _in, src, c._offSrc, chunk ); |
| CharUtil.getChars( _buf, 0, src, c._offSrc + chunk, cch - chunk ); |
| _in = (_in + cch) % _buf.length; |
| } |
| } |
| else |
| preEmit( 0 ); |
| } |
| |
| private boolean preEmit ( int cch ) |
| { |
| assert cch >= 0; |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| _lastEmitCch = cch; |
| |
| if (cch == 0) |
| return true; |
| |
| if (_free <= cch) |
| resize( cch, -1 ); |
| |
| assert cch <= _free; |
| |
| int used = getAvailable(); |
| |
| // if we are about to emit and there is noting in the buffer, reset |
| // the buffer to be at the beginning so as to not grow it anymore |
| // than needed. |
| |
| if (used == 0) |
| { |
| assert _in == _out; |
| assert _free == _buf.length; |
| _in = _out = 0; |
| } |
| |
| _lastEmitIn = _in; |
| |
| _free -= cch; |
| |
| assert _free >= 0; |
| assert _buf==null || _free == (_in>=_out ? _buf.length - (_in - _out) : _out - _in ) - cch : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) - cch) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in - cch ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length - cch) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| return false; |
| } |
| |
| private void entitizeContent ( boolean forceCData ) |
| { |
| assert _free >=0; |
| |
| if (_lastEmitCch == 0) |
| return; |
| |
| int i = _lastEmitIn; |
| final int n = _buf.length; |
| |
| boolean hasCharToBeReplaced = false; |
| |
| int count = 0; |
| char prevChar = 0; |
| char prevPrevChar = 0; |
| for ( int cch = _lastEmitCch ; cch > 0 ; cch-- ) |
| { |
| char ch = _buf[ i ]; |
| |
| if (ch == '<' || ch == '&') |
| count++; |
| else if (prevPrevChar == ']' && prevChar == ']' && ch == '>' ) |
| hasCharToBeReplaced = true; |
| else if (isBadChar( ch ) || isEscapedChar( ch ) || (!_isPrettyPrint && ch == '\r') ) |
| hasCharToBeReplaced = true; |
| |
| if (++i == n) |
| i = 0; |
| |
| prevPrevChar = prevChar; |
| prevChar = ch; |
| } |
| |
| if (!forceCData && count == 0 && !hasCharToBeReplaced && count<_cdataEntityCountThreshold) |
| return; |
| |
| i = _lastEmitIn; |
| |
| // |
| // Heuristic for knowing when to save out stuff as a CDATA. |
| // |
| if (forceCData || (_lastEmitCch > _cdataLengthThreshold && count > _cdataEntityCountThreshold) ) |
| { |
| boolean lastWasBracket = _buf[ i ] == ']'; |
| |
| i = replace( i, "<![CDATA[" + _buf[ i ] ); |
| |
| boolean secondToLastWasBracket = lastWasBracket; |
| |
| lastWasBracket = _buf[ i ] == ']'; |
| |
| if (++i == _buf.length) |
| i = 0; |
| |
| for ( int cch = _lastEmitCch - 2 ; cch > 0 ; cch-- ) |
| { |
| char ch = _buf[ i ]; |
| |
| if (ch == '>' && secondToLastWasBracket && lastWasBracket) |
| i = replace( i, "]]>><![CDATA[" ); |
| else if (isBadChar( ch )) |
| i = replace( i, "?" ); |
| else |
| i++; |
| |
| secondToLastWasBracket = lastWasBracket; |
| lastWasBracket = ch == ']'; |
| |
| if (i == _buf.length) |
| i = 0; |
| } |
| |
| emit( "]]>" ); |
| } |
| else |
| { |
| char ch = 0, ch_1 = 0, ch_2; |
| for ( int cch = _lastEmitCch ; cch > 0 ; cch-- ) |
| { |
| ch_2 = ch_1; |
| ch_1 = ch; |
| ch = _buf[ i ]; |
| |
| if (ch == '<') |
| i = replace( i, "<" ); |
| else if (ch == '&') |
| i = replace( i, "&" ); |
| else if (ch == '>' && ch_1 == ']' && ch_2 == ']') |
| i = replace( i, ">" ); |
| else if (isBadChar( ch )) |
| i = replace( i, "?" ); |
| else if (!_isPrettyPrint && ch == '\r') |
| i = replace( i, " " ); |
| else if (isEscapedChar( ch )) |
| i = replace( i, _replaceChar.getEscapedString( ch ) ); |
| else |
| i++; |
| |
| if (i == _buf.length) |
| i = 0; |
| } |
| } |
| } |
| |
| private void entitizeAttrValue ( boolean replaceEscapedChar ) |
| { |
| if (_lastEmitCch == 0) |
| return; |
| |
| int i = _lastEmitIn; |
| |
| for ( int cch = _lastEmitCch ; cch > 0 ; cch-- ) |
| { |
| char ch = _buf[ i ]; |
| |
| if (ch == '<') |
| i = replace( i, "<" ); |
| else if (ch == '&') |
| i = replace( i, "&" ); |
| else if (ch == '"') |
| i = replace( i, """ ); |
| else if (isEscapedChar( ch )) |
| { |
| if (replaceEscapedChar) |
| i = replace( i, _replaceChar.getEscapedString( ch ) ); |
| } |
| else |
| i++; |
| |
| if (i == _buf.length) |
| i = 0; |
| } |
| } |
| |
| private void entitizeComment ( ) |
| { |
| if (_lastEmitCch == 0) |
| return; |
| |
| int i = _lastEmitIn; |
| |
| boolean lastWasDash = false; |
| |
| for ( int cch = _lastEmitCch ; cch > 0 ; cch-- ) |
| { |
| char ch = _buf[ i ]; |
| |
| if (isBadChar( ch )) |
| i = replace( i, "?" ); |
| else if (ch == '-') |
| { |
| if (lastWasDash) |
| { |
| // Replace "--" with "- " to make well formed |
| i = replace( i, " " ); |
| lastWasDash = false; |
| } |
| else |
| { |
| lastWasDash = true; |
| i++; |
| } |
| } |
| else |
| { |
| lastWasDash = false; |
| i++; |
| } |
| |
| if (i == _buf.length) |
| i = 0; |
| } |
| |
| // Because I have only replaced chars with single chars, |
| // _lastEmitIn will still be ok |
| |
| int offset = (_lastEmitIn + _lastEmitCch - 1) % _buf.length; |
| if (_buf[ offset ] == '-') |
| i = replace( offset, " " ); |
| } |
| |
| private void entitizeProcinst ( ) |
| { |
| if (_lastEmitCch == 0) |
| return; |
| |
| int i = _lastEmitIn; |
| |
| boolean lastWasQuestion = false; |
| |
| for ( int cch = _lastEmitCch ; cch > 0 ; cch-- ) |
| { |
| char ch = _buf[ i ]; |
| |
| if (isBadChar( ch )) |
| i = replace( i, "?" ); |
| |
| if (ch == '>') |
| { |
| // TODO - Had to convert to a space here ... imples not well formed XML |
| if (lastWasQuestion) |
| i = replace( i, " " ); |
| else |
| i++; |
| |
| lastWasQuestion = false; |
| } |
| else |
| { |
| lastWasQuestion = ch == '?'; |
| i++; |
| } |
| |
| if (i == _buf.length) |
| i = 0; |
| } |
| } |
| |
| /** |
| * Test if a character is to be replaced with an escaped value |
| */ |
| private boolean isEscapedChar ( char ch ) |
| { |
| return ( null != _replaceChar && _replaceChar.containsChar( ch ) ); |
| } |
| |
| private int replace ( int i, String replacement ) |
| { |
| assert replacement.length() > 0; |
| |
| int dCch = replacement.length() - 1; |
| |
| if (dCch == 0) |
| { |
| _buf[ i ] = replacement.charAt( 0 ); |
| return i + 1; |
| } |
| |
| assert _free >= 0; |
| |
| if (dCch > _free) |
| i = resize( dCch, i ); |
| |
| assert _free >= 0; |
| |
| assert _free >= dCch; |
| assert getAvailable() > 0; |
| |
| int charsToCopy = dCch + 1; |
| |
| if (_out > _in && i >= _out) |
| { |
| System.arraycopy( _buf, _out, _buf, _out - dCch, i - _out ); |
| _out -= dCch; |
| i -= dCch; |
| } |
| else |
| { |
| assert i < _in; |
| int availableEndChunk = _buf.length - _in; |
| if ( dCch <= availableEndChunk ) |
| { |
| System.arraycopy( _buf, i, _buf, i + dCch, _in - i ); |
| _in = ( _in + dCch) % _buf.length; |
| } |
| else if ( dCch <= availableEndChunk + _in - i - 1 ) |
| { |
| int numToCopyToStart = dCch - availableEndChunk; |
| System.arraycopy( _buf, _in-numToCopyToStart, _buf, 0, numToCopyToStart ); |
| System.arraycopy( _buf, i+1, _buf, i+1+dCch, _in-i-1-numToCopyToStart); |
| |
| _in = numToCopyToStart; |
| } |
| else |
| { |
| int numToCopyToStart = _in - i - 1; |
| charsToCopy = availableEndChunk + _in - i; |
| |
| System.arraycopy( _buf, _in-numToCopyToStart, _buf, dCch-charsToCopy+1, numToCopyToStart ); |
| replacement.getChars( charsToCopy, dCch + 1, _buf, 0); |
| |
| _in = numToCopyToStart + dCch - charsToCopy + 1; |
| } |
| } |
| |
| replacement.getChars( 0, charsToCopy, _buf, i ); |
| |
| _free -= dCch; |
| |
| assert _free >= 0; |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| return (i + dCch + 1) % _buf.length; |
| } |
| // |
| // |
| // |
| |
| private int ensure ( int cch ) |
| { |
| // Even if we're asked to ensure nothing, still try to ensure |
| // atleast one character so we can determine if we're at the |
| // end of the stream. |
| |
| if (cch <= 0) |
| cch = 1; |
| |
| int available = getAvailable(); |
| |
| for ( ; available < cch ; available = getAvailable() ) |
| if (!process()) |
| break; |
| |
| assert available == getAvailable(); |
| |
| // if (available == 0) |
| // return 0; |
| |
| return available; |
| } |
| |
| int getAvailable ( ) |
| { |
| return _buf == null ? 0 : _buf.length - _free; |
| } |
| |
| private int resize ( int cch, int i ) |
| { |
| assert _free >= 0; |
| assert cch > 0; |
| assert cch >= _free; |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| int newLen = _buf == null ? _initialBufSize : _buf.length * 2; |
| int used = getAvailable(); |
| |
| while ( newLen - used < cch ) |
| newLen *= 2; |
| |
| char[] newBuf = new char [ newLen ]; |
| |
| if (used > 0) |
| { |
| if (_in > _out) |
| { |
| assert i == -1 || (i >= _out && i < _in); |
| System.arraycopy( _buf, _out, newBuf, 0, used ); |
| i -= _out; |
| } |
| else |
| { |
| assert i == -1 || (i >= _out || i < _in); |
| System.arraycopy( _buf, _out, newBuf, 0, used - _in ); |
| System.arraycopy( _buf, 0, newBuf, used - _in, _in ); |
| i = i >= _out ? i - _out : i + _out; |
| } |
| |
| _out = 0; |
| _in = used; |
| _free += newBuf.length - _buf.length; |
| } |
| else |
| { |
| _free = newBuf.length; |
| assert _in == 0 && _out == 0; |
| assert i == -1; |
| } |
| |
| _buf = newBuf; |
| |
| assert _free >= 0; |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| return i; |
| } |
| |
| public int read ( ) |
| { |
| if (ensure( 1 ) == 0) |
| return -1; |
| |
| assert getAvailable() > 0; |
| |
| int ch = _buf[ _out ]; |
| |
| _out = (_out + 1) % _buf.length; |
| _free++; |
| |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| return ch; |
| } |
| |
| public int read ( char[] cbuf, int off, int len ) |
| { |
| // Check for end of stream even if there is no way to return |
| // characters because the Reader doc says to return -1 at end of |
| // stream. |
| |
| int n; |
| |
| if ((n = ensure( len )) == 0) |
| return -1; |
| |
| if (cbuf == null || len <= 0) |
| return 0; |
| |
| if (n < len) |
| len = n; |
| |
| if (_out < _in) |
| { |
| System.arraycopy( _buf, _out, cbuf, off, len ); |
| } |
| else |
| { |
| int chunk = _buf.length - _out; |
| |
| if (chunk >= len) |
| System.arraycopy( _buf, _out, cbuf, off, len ); |
| else |
| { |
| System.arraycopy( _buf, _out, cbuf, off, chunk ); |
| System.arraycopy( _buf, 0, cbuf, off + chunk, len - chunk ); |
| } |
| } |
| |
| _out = (_out + len) % _buf.length; |
| _free += len; |
| |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| assert _free >= 0; |
| |
| return len; |
| } |
| |
| public int write ( Writer writer, int cchMin ) |
| { |
| while ( getAvailable() < cchMin) |
| { |
| if (!process()) |
| break; |
| } |
| |
| int charsAvailable = getAvailable(); |
| |
| if (charsAvailable > 0) |
| { |
| // I don't want to deal with the circular cases |
| |
| assert _out == 0; |
| assert _in >= _out : "_in:" + _in + " < _out:" + _out; |
| assert _free == _buf.length - _in; |
| |
| try |
| { |
| //System.out.println("-------------\nWriting in corverter: TextSaver.write():1703 " + charsAvailable + " chars\n" + new String(_buf, 0, charsAvailable)); |
| writer.write( _buf, 0, charsAvailable ); |
| writer.flush(); |
| } |
| catch ( IOException e ) |
| { |
| throw new RuntimeException( e ); |
| } |
| |
| _free += charsAvailable; |
| |
| assert _free >= 0; |
| |
| _in = 0; |
| } |
| assert _buf==null || |
| (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges |
| (_out>_in && _free == _out - _in ) || // data on the edges, free in the middle |
| (_out==_in && _free == _buf.length) || // no data, all buffer free |
| (_out==_in && _free == 0) // buffer full |
| : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free; |
| |
| return charsAvailable; |
| } |
| |
| public String saveToString ( ) |
| { |
| // We're gonna build a string. Instead of using StringBuffer, may |
| // as well use my buffer here. Fill the whole sucker up and |
| // create a String! |
| |
| while ( process() ) |
| ; |
| |
| assert _out == 0; |
| |
| int available = getAvailable(); |
| |
| return available == 0 ? "" : new String( _buf, _out, available ); |
| } |
| |
| // |
| // |
| // |
| |
| private static final int _initialBufSize = 4096; |
| private int _cdataLengthThreshold = 32; |
| private int _cdataEntityCountThreshold = 5; |
| private boolean _useCDataBookmarks = false; |
| private boolean _isPrettyPrint = false; |
| |
| private int _lastEmitIn; |
| private int _lastEmitCch; |
| |
| private int _free; |
| private int _in; |
| private int _out; |
| private char[] _buf; |
| /* |
| _buf is a circular buffer, useful data is before _in up to _out, there are 2 posible configurations: |
| 1: _in<=_out |data|_in empty _out|data| |
| 2: _out<_in |empty _out|data|_in empty| |
| _free is used to keep around the remaining empty space in the bufer so assert _buf==null || _free == (_in>=_out ? _buf.length - (_in - _out) : _out - _in ) ; |
| */ |
| } |
| |
| static final class OptimizedForSpeedSaver |
| extends Saver |
| { |
| Writer _w; |
| private char[] _buf = new char[1024]; |
| |
| |
| static private class SaverIOException |
| extends RuntimeException |
| { |
| SaverIOException(IOException e) |
| { |
| super(e); |
| } |
| } |
| |
| |
| OptimizedForSpeedSaver(Cur cur, Writer writer) |
| { |
| super(cur, XmlOptions.maskNull(null)); |
| _w = writer; |
| } |
| |
| static void save(Cur cur, Writer writer) |
| throws IOException |
| { |
| try |
| { |
| Saver saver = new OptimizedForSpeedSaver(cur, writer); |
| while(saver.process()) |
| {} |
| } |
| catch (SaverIOException e) |
| { |
| throw (IOException)e.getCause(); |
| } |
| } |
| |
| private void emit(String s) |
| { |
| try |
| { |
| _w.write(s); |
| } |
| catch (IOException e) |
| { |
| throw new SaverIOException(e); |
| } |
| } |
| |
| private void emit(char c) |
| { |
| try |
| { |
| _buf[0] = c; |
| _w.write(_buf, 0, 1); |
| } |
| catch (IOException e) |
| { |
| throw new SaverIOException(e); |
| } |
| } |
| |
| private void emit(char c1, char c2) |
| { |
| try |
| { |
| _buf[0] = c1; |
| _buf[1] = c2; |
| _w.write(_buf, 0 , 2); |
| } |
| catch (IOException e) |
| { |
| throw new SaverIOException(e); |
| } |
| } |
| |
| private void emit(char[] buf, int start, int len) |
| { |
| try |
| { |
| _w.write(buf, start, len); |
| } |
| catch (IOException e) |
| { |
| throw new SaverIOException(e); |
| } |
| } |
| |
| protected boolean emitElement ( SaveCur c, ArrayList attrNames, ArrayList attrValues ) |
| { |
| assert c.isElem(); |
| |
| emit( '<' ); |
| emitName( c.getName(), false ); |
| |
| for ( int i = 0 ; i < attrNames.size() ; i++ ) |
| emitAttrHelper( (QName) attrNames.get( i ), (String) attrValues.get( i ) ); |
| |
| if (!saveNamespacesFirst()) |
| emitNamespacesHelper(); |
| |
| if (!c.hasChildren() && !c.hasText()) |
| { |
| emit( '/', '>' ); |
| return true; |
| } |
| else |
| { |
| emit( '>' ); |
| return false; |
| } |
| } |
| |
| protected void emitFinish ( SaveCur c ) |
| { |
| emit( '<', '/' ); |
| emitName( c.getName(), false ); |
| emit( '>' ); |
| } |
| |
| protected void emitXmlns ( String prefix, String uri ) |
| { |
| assert prefix != null; |
| assert uri != null; |
| |
| emit( "xmlns" ); |
| |
| if (prefix.length() > 0) |
| { |
| emit( ':' ); |
| emit( prefix ); |
| } |
| |
| emit( '=', '\"' ); |
| |
| // TODO - must encode uri properly |
| emitAttrValue(uri); |
| |
| emit( '"' ); |
| } |
| |
| private void emitNamespacesHelper ( ) |
| { |
| for ( iterateMappings() ; hasMapping() ; nextMapping() ) |
| { |
| emit( ' ' ); |
| emitXmlns( mappingPrefix(), mappingUri() ); |
| } |
| } |
| |
| private void emitAttrHelper ( QName attrName, String attrValue ) |
| { |
| emit( ' ' ); |
| emitName( attrName, true ); |
| emit( '=', '\"' ); |
| emitAttrValue(attrValue); |
| |
| emit( '"' ); |
| } |
| |
| protected void emitComment ( SaveCur c ) |
| { |
| assert c.isComment(); |
| |
| emit( "<!--" ); |
| |
| c.push(); |
| c.next(); |
| |
| emitCommentText( c ); |
| |
| c.pop(); |
| |
| emit( "-->" ); |
| } |
| |
| protected void emitProcinst ( SaveCur c ) |
| { |
| assert c.isProcinst(); |
| |
| emit( "<?" ); |
| |
| // TODO - encoding issues here? |
| emit( c.getName().getLocalPart() ); |
| |
| c.push(); |
| |
| c.next(); |
| |
| if (c.isText()) |
| { |
| emit( ' ' ); |
| emitPiText( c ); |
| } |
| |
| c.pop(); |
| |
| emit( "?>" ); |
| } |
| |
| protected void emitDocType ( String docTypeName, String publicId, String systemId ) |
| { |
| assert docTypeName != null; |
| |
| emit( "<!DOCTYPE " ); |
| emit( docTypeName ); |
| |
| if (publicId == null && systemId != null) |
| { |
| emit( " SYSTEM " ); |
| emitLiteral( systemId ); |
| } |
| else if (publicId != null) |
| { |
| emit( " PUBLIC " ); |
| emitLiteral( publicId ); |
| emit(' '); |
| emitLiteral( systemId ); |
| } |
| |
| emit( '>' ); |
| emit( _newLine ); |
| } |
| |
| protected void emitStartDoc ( SaveCur c ) |
| { |
| } |
| |
| protected void emitEndDoc ( SaveCur c ) |
| { |
| } |
| |
| // |
| // |
| // |
| |
| private void emitName ( QName name, boolean needsPrefix ) |
| { |
| assert name != null; |
| |
| String uri = name.getNamespaceURI(); |
| |
| assert uri != null; |
| |
| if (uri.length() != 0) |
| { |
| String prefix = name.getPrefix(); |
| String mappedUri = getNamespaceForPrefix( prefix ); |
| |
| if (mappedUri == null || !mappedUri.equals( uri )) |
| prefix = getUriMapping( uri ); |
| |
| // Attrs need a prefix. If I have not found one, then there must be a default |
| // prefix obscuring the prefix needed for this attr. Find it manually. |
| |
| // NOTE - Consider keeping the currently mapped default URI separate fromn the |
| // _urpMap and _prefixMap. This way, I would not have to look it up manually |
| // here |
| |
| if (needsPrefix && prefix.length() == 0) |
| prefix = getNonDefaultUriMapping( uri ); |
| |
| if (prefix.length() > 0) |
| { |
| emit( prefix ); |
| emit( ':' ); |
| } |
| } |
| |
| assert name.getLocalPart().length() > 0; |
| |
| emit( name.getLocalPart() ); |
| } |
| |
| private void emitAttrValue ( CharSequence attVal) |
| { |
| int len = attVal.length(); |
| |
| for ( int i = 0; i<len ; i++ ) |
| { |
| char ch = attVal.charAt(i); |
| |
| if (ch == '<') |
| emit( "<" ); |
| else if (ch == '&') |
| emit( "&" ); |
| else if (ch == '"') |
| emit( """ ); |
| else |
| emit(ch); |
| } |
| } |
| |
| private void emitLiteral ( String literal ) |
| { |
| // TODO: systemId production http://www.w3.org/TR/REC-xml/#NT-SystemLiteral |
| // TODO: publicId production http://www.w3.org/TR/REC-xml/#NT-PubidLiteral |
| if (literal.indexOf( "\"" ) < 0) |
| { |
| emit( '\"' ); |
| emit( literal ); |
| emit( '\"' ); |
| } |
| else |
| { |
| emit( '\'' ); |
| emit( literal ); |
| emit( '\'' ); |
| } |
| } |
| |
| protected void emitText ( SaveCur c ) |
| { |
| assert c.isText(); |
| |
| Object src = c.getChars(); |
| int cch = c._cchSrc; |
| int off = c._offSrc; |
| int index = 0; |
| int indexLimit = 0; |
| while( index<cch ) |
| { |
| indexLimit = index + 512 > cch ? cch : index + 512; |
| CharUtil.getChars( _buf, 0, src, off+index, indexLimit-index ); |
| entitizeAndWriteText(indexLimit-index); |
| index = indexLimit; |
| } |
| } |
| |
| protected void emitPiText ( SaveCur c ) |
| { |
| assert c.isText(); |
| |
| Object src = c.getChars(); |
| int cch = c._cchSrc; |
| int off = c._offSrc; |
| int index = 0; |
| int indexLimit = 0; |
| while( index<cch ) |
| { |
| indexLimit = index + 512 > cch ? cch : 512; |
| CharUtil.getChars( _buf, 0, src, off+index, indexLimit ); |
| entitizeAndWritePIText(indexLimit-index); |
| index = indexLimit; |
| } |
| } |
| |
| protected void emitCommentText ( SaveCur c ) |
| { |
| assert c.isText(); |
| |
| Object src = c.getChars(); |
| int cch = c._cchSrc; |
| int off = c._offSrc; |
| int index = 0; |
| int indexLimit = 0; |
| while( index<cch ) |
| { |
| indexLimit = index + 512 > cch ? cch : 512; |
| CharUtil.getChars( _buf, 0, src, off+index, indexLimit ); |
| entitizeAndWriteCommentText(indexLimit-index); |
| index = indexLimit; |
| } |
| } |
| |
| private void entitizeAndWriteText(int bufLimit) |
| { |
| int index = 0; |
| for (int i = 0; i < bufLimit; i++) |
| { |
| char c = _buf[i]; |
| switch(c) |
| { |
| case '<': |
| emit(_buf, index, i-index); |
| emit("<"); |
| index = i+1; |
| break; |
| case '&': |
| emit(_buf, index, i-index); |
| emit("&"); |
| index = i+1; |
| break; |
| } |
| } |
| emit(_buf, index, bufLimit-index); |
| } |
| |
| private void entitizeAndWriteCommentText ( int bufLimit ) |
| { |
| boolean lastWasDash = false; |
| |
| for ( int i=0 ; i<bufLimit ; i++ ) |
| { |
| char ch = _buf[ i ]; |
| |
| if (isBadChar( ch )) |
| _buf[i] = '?'; |
| else if (ch == '-') |
| { |
| if (lastWasDash) |
| { |
| // Replace "--" with "- " to make well formed |
| _buf[i] = ' '; |
| lastWasDash = false; |
| } |
| else |
| { |
| lastWasDash = true; |
| } |
| } |
| else |
| { |
| lastWasDash = false; |
| } |
| |
| if (i == _buf.length) |
| i = 0; |
| } |
| |
| if (_buf[ bufLimit-1 ] == '-') |
| _buf[ bufLimit-1 ] = ' '; |
| |
| emit(_buf, 0, bufLimit); |
| } |
| |
| private void entitizeAndWritePIText(int bufLimit) |
| { |
| boolean lastWasQuestion = false; |
| |
| for ( int i=0 ; i<bufLimit ; i++ ) |
| { |
| char ch = _buf[ i ]; |
| |
| if (isBadChar( ch )) |
| { |
| _buf[i] = '?'; |
| ch = '?'; |
| } |
| |
| if (ch == '>') |
| { |
| // Had to convert to a space here ... imples not well formed XML |
| if (lastWasQuestion) |
| _buf[i] = ' '; |
| |
| lastWasQuestion = false; |
| } |
| else |
| { |
| lastWasQuestion = ch == '?'; |
| } |
| } |
| emit(_buf, 0, bufLimit); |
| } |
| } |
| |
| static final class TextReader extends Reader |
| { |
| TextReader ( Cur c, XmlOptions options ) |
| { |
| _textSaver = new TextSaver( c, options, null ); |
| _locale = c._locale; |
| _closed = false; |
| } |
| |
| public void close ( ) throws IOException { _closed = true; } |
| |
| public boolean ready ( ) throws IOException { return !_closed; } |
| |
| public int read ( ) throws IOException |
| { |
| checkClosed(); |
| |
| if (_locale.noSync()) { _locale.enter(); try { return _textSaver.read(); } finally { _locale.exit(); } } |
| else synchronized ( _locale ) { _locale.enter(); try { return _textSaver.read(); } finally { _locale.exit(); } } |
| } |
| |
| public int read ( char[] cbuf ) throws IOException |
| { |
| checkClosed(); |
| |
| if (_locale.noSync()) { _locale.enter(); try { return _textSaver.read( cbuf, 0, cbuf == null ? 0 : cbuf.length ); } finally { _locale.exit(); } } |
| else synchronized ( _locale ) { _locale.enter(); try { return _textSaver.read( cbuf, 0, cbuf == null ? 0 : cbuf.length ); } finally { _locale.exit(); } } |
| } |
| |
| public int read ( char[] cbuf, int off, int len ) throws IOException |
| { |
| checkClosed(); |
| |
| if (_locale.noSync()) { _locale.enter(); try { return _textSaver.read( cbuf, off, len ); } finally { _locale.exit(); } } |
| else synchronized ( _locale ) { _locale.enter(); try { return _textSaver.read( cbuf, off, len ); } finally { _locale.exit(); } } |
| } |
| |
| private void checkClosed ( ) throws IOException |
| { |
| if (_closed) |
| throw new IOException( "Reader has been closed" ); |
| } |
| |
| private Locale _locale; |
| private TextSaver _textSaver; |
| private boolean _closed; |
| } |
| |
| static final class InputStreamSaver extends InputStream |
| { |
| InputStreamSaver ( Cur c, XmlOptions options ) |
| { |
| _locale = c._locale; |
| |
| _closed = false; |
| |
| assert _locale.entered(); |
| |
| options = XmlOptions.maskNull( options ); |
| |
| _outStreamImpl = new OutputStreamImpl(); |
| |
| String encoding = null; |
| |
| XmlDocumentProperties props = Locale.getDocProps( c, false ); |
| |
| if (props != null && props.getEncoding() != null) |
| encoding = EncodingMap.getIANA2JavaMapping( props.getEncoding() ); |
| |
| if (options.hasOption( XmlOptions.CHARACTER_ENCODING )) |
| encoding = (String) options.get( XmlOptions.CHARACTER_ENCODING ); |
| |
| if (encoding != null) |
| { |
| String ianaEncoding = EncodingMap.getJava2IANAMapping( encoding ); |
| |
| if (ianaEncoding != null) |
| encoding = ianaEncoding; |
| } |
| |
| if (encoding == null) |
| encoding = EncodingMap.getJava2IANAMapping( "UTF8" ); |
| |
| String javaEncoding = EncodingMap.getIANA2JavaMapping( encoding ); |
| |
| if (javaEncoding == null) |
| throw new IllegalStateException( "Unknown encoding: " + encoding ); |
| |
| try |
| { |
| _converter = new OutputStreamWriter( _outStreamImpl, javaEncoding ); |
| } |
| catch ( UnsupportedEncodingException e ) |
| { |
| throw new RuntimeException( e ); |
| } |
| |
| _textSaver = new TextSaver( c, options, encoding ); |
| } |
| |
| public void close ( ) throws IOException |
| { |
| _closed = true; |
| } |
| |
| private void checkClosed ( ) throws IOException |
| { |
| if (_closed) |
| throw new IOException( "Stream closed" ); |
| } |
| |
| // Having the gateway here is kinda slow for the single character case. It may be possible |
| // to only enter the gate when there are no chars in the buffer. |
| |
| public int read ( ) throws IOException |
| { |
| checkClosed(); |
| |
| if (_locale.noSync()) { _locale.enter(); try { return _outStreamImpl.read(); } finally { _locale.exit(); } } |
| else synchronized ( _locale ) { _locale.enter(); try { return _outStreamImpl.read(); } finally { _locale.exit(); } } |
| } |
| |
| public int read ( byte[] bbuf, int off, int len ) throws IOException |
| { |
| checkClosed(); |
| |
| if (bbuf == null) |
| throw new NullPointerException( "buf to read into is null" ); |
| |
| if (off < 0 || off > bbuf.length) |
| throw new IndexOutOfBoundsException( "Offset is not within buf" ); |
| |
| if (_locale.noSync()) { _locale.enter(); try { return _outStreamImpl.read( bbuf, off, len ); } finally { _locale.exit(); } } |
| else synchronized ( _locale ) { _locale.enter(); try { return _outStreamImpl.read( bbuf, off, len ); } finally { _locale.exit(); } } |
| } |
| |
| private int ensure ( int cbyte ) |
| { |
| // Even if we're asked to ensure nothing, still try to ensure |
| // atleast one byte so we can determine if we're at the |
| // end of the stream. |
| |
| if (cbyte <= 0) |
| cbyte = 1; |
| |
| int bytesAvailable = _outStreamImpl.getAvailable(); |
| |
| for ( ; bytesAvailable < cbyte ; |
| bytesAvailable = _outStreamImpl.getAvailable() ) |
| { |
| if (_textSaver.write( _converter, 2048 ) < 2048) |
| break; |
| } |
| |
| bytesAvailable = _outStreamImpl.getAvailable(); |
| |
| // if (bytesAvailable == 0) |
| // return 0; |
| |
| return bytesAvailable; |
| } |
| |
| public int available() |
| throws IOException |
| { |
| if (_locale.noSync()) |
| { _locale.enter(); try { |
| return ensure(1024); |
| } finally { _locale.exit(); } } |
| else |
| synchronized ( _locale ) |
| { _locale.enter(); try { return ensure(1024); } finally { _locale.exit(); } } |
| } |
| |
| private final class OutputStreamImpl extends OutputStream |
| { |
| int read ( ) |
| { |
| if (InputStreamSaver.this.ensure( 1 ) == 0) |
| return -1; |
| |
| assert getAvailable() > 0; |
| |
| int bite = _buf[ _out ]; |
| |
| _out = (_out + 1) % _buf.length; |
| _free++; |
| |
| return bite; |
| } |
| |
| int read ( byte[] bbuf, int off, int len ) |
| { |
| // Check for end of stream even if there is no way to return |
| // characters because the Reader doc says to return -1 at end of |
| // stream. |
| |
| int n; |
| |
| if ((n = ensure( len )) == 0) |
| return -1; |
| |
| if (bbuf == null || len <= 0) |
| return 0; |
| |
| if (n < len) |
| len = n; |
| |
| if (_out < _in) |
| { |
| System.arraycopy( _buf, _out, bbuf, off, len ); |
| } |
| else |
| { |
| int chunk = _buf.length - _out; |
| |
| if (chunk >= len) |
| System.arraycopy( _buf, _out, bbuf, off, len ); |
| else |
| { |
| System.arraycopy( _buf, _out, bbuf, off, chunk ); |
| |
| System.arraycopy( |
| _buf, 0, bbuf, off + chunk, len - chunk ); |
| } |
| } |
| _out = (_out + len) % _buf.length; |
| _free += len; |
| |
| //System.out.println("------------------------\nRead out of queue: Saver:2440 InputStreamSaver.read() bbuf " + len + " bytes :\n" + new String(bbuf, off, len)); |
| return len; |
| } |
| |
| int getAvailable ( ) |
| { |
| return _buf == null ? 0 : _buf.length - _free; |
| } |
| |
| public void write ( int bite ) |
| { |
| if (_free == 0) |
| resize( 1 ); |
| |
| assert _free > 0; |
| |
| _buf[ _in ] = (byte) bite; |
| |
| _in = (_in + 1) % _buf.length; |
| _free--; |
| } |
| |
| public void write ( byte[] buf, int off, int cbyte ) |
| { |
| assert cbyte >= 0; |
| //System.out.println("---------\nAfter converter, write in queue: OutputStreamImpl.write():Saver:2469 " + cbyte + " bytes \n" + new String(buf, off, cbyte)); |
| if (cbyte == 0) |
| return; |
| |
| if (_free < cbyte) |
| resize( cbyte ); |
| |
| if (_in == _out) |
| { |
| assert getAvailable() == 0; |
| assert _free == _buf.length - getAvailable(); |
| _in = _out = 0; |
| } |
| |
| int chunk = _buf.length - _in; |
| |
| if (_in <= _out || cbyte < chunk) |
| { |
| System.arraycopy( buf, off, _buf, _in, cbyte ); |
| _in += cbyte; |
| } |
| else |
| { |
| System.arraycopy( buf, off, _buf, _in, chunk ); |
| |
| System.arraycopy( |
| buf, off + chunk, _buf, 0, cbyte - chunk ); |
| |
| _in = (_in + cbyte) % _buf.length; |
| } |
| |
| _free -= cbyte; |
| } |
| |
| void resize ( int cbyte ) |
| { |
| assert cbyte > _free : cbyte + " !> " + _free; |
| |
| int newLen = _buf == null ? _initialBufSize : _buf.length * 2; |
| int used = getAvailable(); |
| |
| while ( newLen - used < cbyte ) |
| newLen *= 2; |
| |
| byte[] newBuf = new byte [ newLen ]; |
| |
| if (used > 0) |
| { |
| if (_in > _out) |
| System.arraycopy( _buf, _out, newBuf, 0, used ); |
| else |
| { |
| System.arraycopy( |
| _buf, _out, newBuf, 0, used - _in ); |
| |
| System.arraycopy( |
| _buf, 0, newBuf, used - _in, _in ); |
| } |
| |
| _out = 0; |
| _in = used; |
| _free += newBuf.length - _buf.length; |
| } |
| else |
| { |
| _free = newBuf.length; |
| assert _in == _out; |
| } |
| |
| _buf = newBuf; |
| } |
| |
| private static final int _initialBufSize = 4096; |
| |
| private int _free; |
| private int _in; |
| private int _out; |
| private byte[] _buf; |
| } |
| |
| private Locale _locale; |
| private boolean _closed; |
| private OutputStreamImpl _outStreamImpl; |
| private TextSaver _textSaver; |
| private OutputStreamWriter _converter; |
| } |
| |
| static final class XmlInputStreamSaver extends Saver |
| { |
| XmlInputStreamSaver ( Cur c, XmlOptions options ) |
| { |
| super( c, options ); |
| } |
| |
| protected boolean emitElement(SaveCur c, ArrayList attrNames, ArrayList attrValues) |
| { |
| assert c.isElem(); |
| |
| for ( iterateMappings() ; hasMapping() ; nextMapping() ) |
| { |
| enqueue( new StartPrefixMappingImpl( mappingPrefix(), mappingUri() ) ); |
| } |
| |
| StartElementImpl.AttributeImpl lastAttr = null; |
| StartElementImpl.AttributeImpl attributes = null; |
| StartElementImpl.AttributeImpl namespaces = null; |
| |
| for ( int i=0; i<attrNames.size(); i++ ) |
| { |
| XMLName attXMLName = computeName((QName)attrNames.get(i), this, true); |
| StartElementImpl.AttributeImpl attr = |
| new StartElementImpl.NormalAttributeImpl(attXMLName, (String)attrValues.get(i) ); |
| |
| if (attributes == null) |
| attributes = attr; |
| else |
| lastAttr._next = attr; |
| |
| lastAttr = attr; |
| } |
| |
| lastAttr = null; |
| |
| for ( iterateMappings() ; hasMapping() ; nextMapping() ) |
| { |
| String prefix = mappingPrefix(); |
| String uri = mappingUri(); |
| |
| StartElementImpl.AttributeImpl attr = |
| new StartElementImpl.XmlnsAttributeImpl(prefix, uri); |
| |
| if (namespaces == null) |
| namespaces = attr; |
| else |
| lastAttr._next = attr; |
| |
| lastAttr = attr; |
| } |
| |
| |
| QName name = c.getName(); |
| enqueue( new StartElementImpl( computeName(name, this, false), attributes, namespaces, getPrefixMap() ) ); |
| |
| return false; // still need to be called on end element |
| } |
| |
| protected void emitFinish(SaveCur c) |
| { |
| if (c.isRoot()) |
| enqueue( new EndDocumentImpl( ) ); |
| else |
| { |
| XMLName xmlName = computeName(c.getName(), this, false); |
| enqueue( new EndElementImpl( xmlName ) ); |
| } |
| |
| emitEndPrefixMappings(); |
| } |
| |
| protected void emitText(SaveCur c) |
| { |
| assert c.isText(); |
| Object src = c.getChars(); |
| int cch = c._cchSrc; |
| int off = c._offSrc; |
| |
| enqueue( new CharacterDataImpl( src, cch, off ) ); |
| } |
| |
| protected void emitComment(SaveCur c) |
| { |
| enqueue( new CommentImpl( c.getChars(), c._cchSrc, c._offSrc ) ); |
| } |
| |
| protected void emitProcinst(SaveCur c) |
| { |
| String target = null; |
| QName name = c.getName(); |
| |
| if (name!=null) |
| target = name.getLocalPart(); |
| |
| enqueue( new ProcessingInstructionImpl( target, c.getChars(), c._cchSrc, c._offSrc ) ); |
| } |
| |
| protected void emitDocType( String doctypeName, String publicID, String systemID ) |
| { |
| enqueue( new StartDocumentImpl( systemID, null, true, null ) ); //todo |
| } |
| |
| protected void emitStartDoc ( SaveCur c ) |
| { |
| emitDocType(null, null, null); |
| } |
| |
| protected void emitEndDoc ( SaveCur c ) |
| { |
| enqueue( new EndDocumentImpl()); |
| } |
| |
| XMLEvent dequeue ( ) |
| { |
| if (_out == null) |
| { |
| enterLocale(); |
| try |
| { |
| if(!process()) |
| return null; |
| } |
| finally |
| { |
| exitLocale(); |
| } |
| } |
| |
| if (_out == null) |
| return null; |
| |
| XmlEventImpl e = _out; |
| |
| if ((_out = _out._next) == null) |
| _in = null; |
| |
| return e; |
| } |
| |
| private void enqueue ( XmlEventImpl e ) |
| { |
| assert e._next == null; |
| |
| if (_in == null) |
| { |
| assert _out == null; |
| _out = _in = e; |
| } |
| else |
| { |
| _in._next = e; |
| _in = e; |
| } |
| } |
| |
| // |
| // |
| // |
| |
| protected void emitEndPrefixMappings ( ) |
| { |
| for ( iterateMappings() ; hasMapping() ; nextMapping() ) |
| { |
| String prevPrefixUri = null; // todo mappingPrevPrefixUri(); |
| String prefix = mappingPrefix(); |
| String uri = mappingUri(); |
| |
| if (prevPrefixUri == null) |
| enqueue( new EndPrefixMappingImpl( prefix ) ); |
| else |
| { |
| enqueue( new ChangePrefixMappingImpl( prefix, uri, prevPrefixUri ) ); |
| } |
| } |
| } |
| |
| // |
| // |
| // |
| |
| private static XMLName computeName ( QName name, Saver saver, boolean needsPrefix ) |
| { |
| String uri = name.getNamespaceURI(); |
| String local = name.getLocalPart(); |
| |
| assert uri != null; |
| assert local.length() > 0; |
| |
| String prefix = null; |
| |
| if (uri!=null && uri.length() != 0) |
| { |
| prefix = name.getPrefix(); |
| String mappedUri = saver.getNamespaceForPrefix( prefix ); |
| |
| if (mappedUri == null || !mappedUri.equals( uri )) |
| prefix = saver.getUriMapping( uri ); |
| |
| // Attrs need a prefix. If I have not found one, then there must be a default |
| // prefix obscuring the prefix needed for this attr. Find it manually. |
| |
| // NOTE - Consider keeping the currently mapped default URI separate fromn the |
| // _urpMap and _prefixMap. This way, I would not have to look it up manually |
| // here |
| |
| if (needsPrefix && prefix.length() == 0) |
| prefix = saver.getNonDefaultUriMapping( uri ); |
| |
| } |
| |
| return new XmlNameImpl( uri, local, prefix ); |
| } |
| |
| private static abstract class XmlEventImpl extends XmlEventBase |
| { |
| XmlEventImpl ( int type ) |
| { |
| super( type ); |
| } |
| |
| public XMLName getName ( ) |
| { |
| return null; |
| } |
| |
| public XMLName getSchemaType ( ) |
| { |
| throw new RuntimeException( "NYI" ); |
| } |
| |
| public boolean hasName ( ) |
| { |
| return false; |
| } |
| |
| public final Location getLocation ( ) |
| { |
| // (orig v1 comment)TODO - perhaps I can save a location goober sometimes? |
| return null; |
| } |
| |
| XmlEventImpl _next; |
| } |
| |
| private static class StartDocumentImpl |
| extends XmlEventImpl implements StartDocument |
| { |
| StartDocumentImpl ( String systemID, String encoding, boolean isStandAlone, String version ) |
| { |
| super( XMLEvent.START_DOCUMENT ); |
| _systemID = systemID; |
| _encoding = encoding; |
| _standAlone = isStandAlone; |
| _version = version; |
| } |
| |
| public String getSystemId ( ) |
| { |
| return _systemID; |
| } |
| |
| public String getCharacterEncodingScheme ( ) |
| { |
| return _encoding; |
| } |
| |
| public boolean isStandalone ( ) |
| { |
| return _standAlone; |
| } |
| |
| public String getVersion ( ) |
| { |
| return _version; |
| } |
| |
| String _systemID; |
| String _encoding; |
| boolean _standAlone; |
| String _version; |
| } |
| |
| private static class StartElementImpl |
| extends XmlEventImpl implements StartElement |
| { |
| StartElementImpl ( XMLName name, AttributeImpl attributes, AttributeImpl namespaces, Map prefixMap ) |
| { |
| super( XMLEvent.START_ELEMENT ); |
| |
| _name = name; |
| _attributes = attributes; |
| _namespaces = namespaces; |
| _prefixMap = prefixMap; |
| } |
| |
| public boolean hasName() |
| { |
| return true; |
| } |
| |
| public XMLName getName ( ) |
| { |
| return _name; |
| } |
| |
| public AttributeIterator getAttributes ( ) |
| { |
| return new AttributeIteratorImpl( _attributes, null ); |
| } |
| |
| public AttributeIterator getNamespaces ( ) |
| { |
| return new AttributeIteratorImpl( null, _namespaces ); |
| } |
| |
| public AttributeIterator getAttributesAndNamespaces ( ) |
| { |
| return new AttributeIteratorImpl( _attributes, _namespaces ); |
| } |
| |
| public Attribute getAttributeByName ( XMLName xmlName ) |
| { |
| for ( AttributeImpl a = _attributes ; a != null ; a = a._next ) |
| { |
| if (xmlName.equals( a.getName() )) |
| return a; |
| } |
| |
| return null; |
| } |
| |
| public String getNamespaceUri ( String prefix ) |
| { |
| return (String) _prefixMap.get( prefix == null ? "" : prefix ); |
| } |
| |
| public Map getNamespaceMap ( ) |
| { |
| return _prefixMap; |
| } |
| |
| private static class AttributeIteratorImpl |
| implements AttributeIterator |
| { |
| AttributeIteratorImpl( AttributeImpl attributes, AttributeImpl namespaces ) |
| { |
| _attributes = attributes; |
| _namespaces = namespaces; |
| } |
| |
| public Object monitor() |
| { |
| return this; |
| } |
| |
| public Attribute next ( ) |
| { |
| synchronized (monitor()) |
| { |
| checkVersion(); |
| |
| AttributeImpl attr = null; |
| |
| if (_attributes != null) |
| { |
| attr = _attributes; |
| _attributes = attr._next; |
| } |
| else if (_namespaces != null) |
| { |
| attr = _namespaces; |
| _namespaces = attr._next; |
| } |
| |
| return attr; |
| } |
| } |
| |
| public boolean hasNext ( ) |
| { |
| synchronized (monitor()) |
| { |
| checkVersion(); |
| |
| return _attributes != null || _namespaces != null; |
| } |
| } |
| |
| public Attribute peek ( ) |
| { |
| synchronized (monitor()) |
| { |
| checkVersion(); |
| |
| if (_attributes != null) |
| return _attributes; |
| else if (_namespaces != null) |
| return _namespaces; |
| |
| return null; |
| } |
| } |
| |
| public void skip ( ) |
| { |
| synchronized (monitor()) |
| { |
| checkVersion(); |
| |
| if (_attributes != null) |
| _attributes = _attributes._next; |
| else if (_namespaces != null) |
| _namespaces = _namespaces._next; |
| } |
| } |
| |
| private final void checkVersion ( ) |
| { |
| // if (_version != _root.getVersion()) |
| // throw new IllegalStateException( "Document changed" ); |
| } |
| |
| // private long _version; |
| private AttributeImpl _attributes; |
| private AttributeImpl _namespaces; |
| } |
| |
| private static abstract class AttributeImpl implements Attribute |
| { |
| /** |
| * Don't forget to set _name |
| */ |
| AttributeImpl () |
| { |
| } |
| |
| public XMLName getName ( ) |
| { |
| return _name; |
| } |
| |
| public String getType ( ) |
| { |
| // (from v1 impl) TODO - Make sure throwing away this DTD info is ok. |
| // (from v1 impl) Is there schema info which can return more useful info? |
| return "CDATA"; |
| } |
| |
| public XMLName getSchemaType ( ) |
| { |
| // (from v1 impl) TODO - Can I return something reasonable here? |
| return null; |
| } |
| |
| AttributeImpl _next; |
| |
| protected XMLName _name; |
| } |
| |
| private static class XmlnsAttributeImpl extends AttributeImpl |
| { |
| XmlnsAttributeImpl ( String prefix, String uri ) |
| { |
| super(); |
| _uri = uri; |
| |
| String local; |
| |
| if (prefix.length() == 0) |
| { |
| prefix = null; |
| local = "xmlns"; |
| } |
| else |
| { |
| local = prefix; |
| prefix = "xmlns"; |
| } |
| |
| _name = new XmlNameImpl( null, local, prefix ); |
| } |
| |
| public String getValue ( ) |
| { |
| return _uri; |
| } |
| |
| private String _uri; |
| } |
| |
| private static class NormalAttributeImpl extends AttributeImpl |
| { |
| NormalAttributeImpl (XMLName name, String value) |
| { |
| _name = name; |
| _value = value; |
| } |
| |
| public String getValue ( ) |
| { |
| return _value; |
| } |
| |
| private String _value; // If invalid in the store |
| } |
| |
| private XMLName _name; |
| private Map _prefixMap; |
| |
| private AttributeImpl _attributes; |
| private AttributeImpl _namespaces; |
| } |
| |
| private static class StartPrefixMappingImpl |
| extends XmlEventImpl implements StartPrefixMapping |
| { |
| StartPrefixMappingImpl ( String prefix, String uri ) |
| { |
| super( XMLEvent.START_PREFIX_MAPPING ); |
| |
| _prefix = prefix; |
| _uri = uri; |
| } |
| |
| public String getNamespaceUri ( ) |
| { |
| return _uri; |
| } |
| |
| public String getPrefix ( ) |
| { |
| return _prefix; |
| } |
| |
| private String _prefix, _uri; |
| } |
| |
| private static class ChangePrefixMappingImpl |
| extends XmlEventImpl implements ChangePrefixMapping |
| { |
| ChangePrefixMappingImpl ( String prefix, String oldUri, String newUri ) |
| { |
| super( XMLEvent.CHANGE_PREFIX_MAPPING ); |
| |
| _oldUri = oldUri; |
| _newUri = newUri; |
| _prefix = prefix; |
| } |
| |
| public String getOldNamespaceUri ( ) |
| { |
| return _oldUri; |
| } |
| |
| public String getNewNamespaceUri ( ) |
| { |
| return _newUri; |
| } |
| |
| public String getPrefix ( ) |
| { |
| return _prefix; |
| } |
| |
| private String _oldUri, _newUri, _prefix; |
| } |
| |
| private static class EndPrefixMappingImpl |
| extends XmlEventImpl implements EndPrefixMapping |
| { |
| EndPrefixMappingImpl ( String prefix ) |
| { |
| super( XMLEvent.END_PREFIX_MAPPING ); |
| _prefix = prefix; |
| } |
| |
| public String getPrefix ( ) |
| { |
| return _prefix; |
| } |
| |
| private String _prefix; |
| } |
| |
| private static class EndElementImpl |
| extends XmlEventImpl implements EndElement |
| { |
| EndElementImpl ( XMLName name ) |
| { |
| super( XMLEvent.END_ELEMENT ); |
| |
| _name = name; |
| } |
| |
| public boolean hasName ( ) |
| { |
| return true; |
| } |
| |
| public XMLName getName ( ) |
| { |
| return _name; |
| } |
| |
| private XMLName _name; |
| } |
| |
| private static class EndDocumentImpl |
| extends XmlEventImpl implements EndDocument |
| { |
| EndDocumentImpl ( ) |
| { |
| super( XMLEvent.END_DOCUMENT ); |
| } |
| } |
| |
| private static class TripletEventImpl |
| extends XmlEventImpl implements CharacterData |
| { |
| TripletEventImpl ( int eventType, Object obj, int cch, int off ) |
| { |
| super(eventType); |
| _obj = obj; |
| _cch = cch; |
| _off = off; |
| } |
| |
| public String getContent ( ) |
| { |
| return CharUtil.getString(_obj, _off, _cch); |
| } |
| |
| public boolean hasContent ( ) |
| { |
| return _cch > 0; |
| } |
| |
| private Object _obj; |
| private int _cch; |
| private int _off; |
| } |
| |
| private static class CharacterDataImpl |
| extends TripletEventImpl implements CharacterData |
| { |
| CharacterDataImpl ( Object obj, int cch, int off ) |
| { |
| super(XMLEvent.CHARACTER_DATA, obj, cch, off); |
| } |
| } |
| |
| private static class CommentImpl |
| extends TripletEventImpl implements Comment |
| { |
| CommentImpl ( Object obj, int cch, int off ) |
| { |
| super( XMLEvent.COMMENT, obj, cch, off); |
| } |
| } |
| |
| private static class ProcessingInstructionImpl |
| extends TripletEventImpl implements ProcessingInstruction |
| { |
| ProcessingInstructionImpl ( String target, Object obj, int cch, int off) |
| { |
| super( XMLEvent.PROCESSING_INSTRUCTION, obj, cch, off); |
| _target = target; |
| } |
| |
| public String getTarget ( ) |
| { |
| return _target; |
| } |
| |
| public String getData ( ) |
| { |
| return getContent(); |
| } |
| |
| private String _target; |
| } |
| |
| private XmlEventImpl _in, _out; |
| } |
| |
| static final class XmlInputStreamImpl extends GenericXmlInputStream |
| { |
| XmlInputStreamImpl ( Cur cur, XmlOptions options ) |
| { |
| _xmlInputStreamSaver = |
| new XmlInputStreamSaver( cur, options ); |
| |
| // Make the saver grind away just a bit to throw any exceptions |
| // related to the inability to create a stream on this xml |
| |
| _xmlInputStreamSaver.process(); |
| } |
| |
| protected XMLEvent nextEvent ( ) throws XMLStreamException |
| { |
| return _xmlInputStreamSaver.dequeue(); |
| } |
| |
| private XmlInputStreamSaver _xmlInputStreamSaver; |
| } |
| |
| static final class SaxSaver extends Saver |
| { |
| SaxSaver ( Cur c, XmlOptions options, ContentHandler ch, LexicalHandler lh ) |
| throws SAXException |
| { |
| super( c, options ); |
| |
| _contentHandler = ch; |
| _lexicalHandler = lh; |
| |
| _attributes = new AttributesImpl(); |
| _nsAsAttrs = !options.hasOption( XmlOptions.SAVE_SAX_NO_NSDECLS_IN_ATTRIBUTES ); |
| |
| _contentHandler.startDocument(); |
| |
| try |
| { |
| while ( process() ) |
| ; |
| } |
| catch ( SaverSAXException e ) |
| { |
| throw e._saxException; |
| } |
| |
| _contentHandler.endDocument(); |
| } |
| |
| private class SaverSAXException extends RuntimeException |
| { |
| SaverSAXException ( SAXException e ) |
| { |
| _saxException = e; |
| } |
| |
| SAXException _saxException; |
| } |
| |
| private String getPrefixedName ( QName name ) |
| { |
| String uri = name.getNamespaceURI(); |
| String local = name.getLocalPart(); |
| |
| if (uri.length() == 0) |
| return local; |
| |
| String prefix = getUriMapping( uri ); |
| |
| if (prefix.length() == 0) |
| return local; |
| |
| return prefix + ":" + local; |
| } |
| |
| private void emitNamespacesHelper ( ) |
| { |
| for ( iterateMappings() ; hasMapping() ; nextMapping() ) |
| { |
| String prefix = mappingPrefix(); |
| String uri = mappingUri(); |
| |
| try |
| { |
| _contentHandler.startPrefixMapping( prefix, uri ); |
| } |
| catch ( SAXException e ) |
| { |
| throw new SaverSAXException( e ); |
| } |
| |
| if (_nsAsAttrs) |
| if (prefix == null || prefix.length() == 0) |
| _attributes.addAttribute( "http://www.w3.org/2000/xmlns/", "xmlns", "xmlns", "CDATA", uri ); |
| else |
| _attributes.addAttribute( "http://www.w3.org/2000/xmlns/", prefix, "xmlns:" + prefix, "CDATA", uri ); |
| } |
| } |
| |
| protected boolean emitElement ( SaveCur c, ArrayList attrNames, ArrayList attrValues ) |
| { |
| _attributes.clear(); |
| |
| if (saveNamespacesFirst()) |
| emitNamespacesHelper(); |
| |
| for ( int i = 0 ; i < attrNames.size() ; i++ ) |
| { |
| QName name = (QName) attrNames.get( i ); |
| |
| _attributes.addAttribute( |
| name.getNamespaceURI(), name.getLocalPart(), getPrefixedName( name ), |
| "CDATA", (String) attrValues.get( i ) ); |
| } |
| |
| if (!saveNamespacesFirst()) |
| emitNamespacesHelper(); |
| |
| QName elemName = c.getName(); |
| |
| try |
| { |
| _contentHandler.startElement( |
| elemName.getNamespaceURI(), elemName.getLocalPart(), |
| getPrefixedName( elemName ), _attributes ); |
| } |
| catch ( SAXException e ) |
| { |
| throw new SaverSAXException( e ); |
| } |
| |
| return false; |
| } |
| |
| protected void emitFinish ( SaveCur c ) |
| { |
| QName name = c.getName(); |
| |
| try |
| { |
| _contentHandler.endElement( |
| name.getNamespaceURI(), name.getLocalPart(), getPrefixedName( name ) ); |
| |
| for ( iterateMappings() ; hasMapping() ; nextMapping() ) |
| _contentHandler.endPrefixMapping( mappingPrefix() ); |
| } |
| catch ( SAXException e ) |
| { |
| throw new SaverSAXException( e ); |
| } |
| } |
| |
| protected void emitText ( SaveCur c ) |
| { |
| assert c.isText(); |
| |
| Object src = c.getChars(); |
| |
| try |
| { |
| if (src instanceof char[]) |
| { |
| // Pray the user does not modify the buffer .... |
| _contentHandler.characters( (char[]) src, c._offSrc, c._cchSrc ); |
| } |
| else |
| { |
| if (_buf == null) |
| _buf = new char [ 1024 ]; |
| |
| while ( c._cchSrc > 0 ) |
| { |
| int cch = java.lang.Math.min( _buf.length, c._cchSrc ); |
| |
| CharUtil.getChars( _buf, 0, src, c._offSrc, cch ); |
| |
| _contentHandler.characters( _buf, 0, cch ); |
| |
| c._offSrc += cch; |
| c._cchSrc -= cch; |
| } |
| } |
| } |
| catch ( SAXException e ) |
| { |
| throw new SaverSAXException( e ); |
| } |
| } |
| |
| protected void emitComment ( SaveCur c ) |
| { |
| if (_lexicalHandler != null) |
| { |
| c.push(); |
| |
| c.next(); |
| |
| try |
| { |
| if (!c.isText()) |
| _lexicalHandler.comment( null, 0, 0 ); |
| else |
| { |
| Object src = c.getChars(); |
| |
| if (src instanceof char[]) |
| { |
| // Pray the user does not modify the buffer .... |
| _lexicalHandler.comment( (char[]) src, c._offSrc, c._cchSrc ); |
| } |
| else |
| { |
| if (_buf == null || _buf.length < c._cchSrc) |
| _buf = new char [ java.lang.Math.max( 1024, c._cchSrc ) ]; |
| |
| CharUtil.getChars( _buf, 0, src, c._offSrc, c._cchSrc ); |
| |
| _lexicalHandler.comment( _buf, 0, c._cchSrc ); |
| } |
| } |
| } |
| catch ( SAXException e ) |
| { |
| throw new SaverSAXException( e ); |
| } |
| |
| c.pop(); |
| } |
| } |
| |
| protected void emitProcinst ( SaveCur c ) |
| { |
| String target = c.getName().getLocalPart(); |
| |
| c.push(); |
| |
| c.next(); |
| |
| String value = CharUtil.getString( c.getChars(), c._offSrc, c._cchSrc ); |
| |
| c.pop(); |
| |
| try |
| { |
| _contentHandler.processingInstruction( c.getName().getLocalPart(), value ); |
| } |
| catch ( SAXException e ) |
| { |
| throw new SaverSAXException( e ); |
| } |
| } |
| |
| protected void emitDocType ( String docTypeName, String publicId, String systemId ) |
| { |
| if (_lexicalHandler != null) |
| { |
| try |
| { |
| _lexicalHandler.startDTD( docTypeName, publicId, systemId ); |
| _lexicalHandler.endDTD(); |
| } |
| catch ( SAXException e ) |
| { |
| throw new SaverSAXException( e ); |
| } |
| } |
| } |
| |
| protected void emitStartDoc ( SaveCur c ) |
| { |
| } |
| |
| protected void emitEndDoc ( SaveCur c ) |
| { |
| } |
| |
| private ContentHandler _contentHandler; |
| private LexicalHandler _lexicalHandler; |
| |
| private AttributesImpl _attributes; |
| |
| private char[] _buf; |
| private boolean _nsAsAttrs; |
| } |
| |
| // |
| // |
| // |
| |
| static abstract class SaveCur |
| { |
| final boolean isRoot ( ) { return kind() == ROOT; } |
| final boolean isElem ( ) { return kind() == ELEM; } |
| final boolean isAttr ( ) { return kind() == ATTR; } |
| final boolean isText ( ) { return kind() == TEXT; } |
| final boolean isComment ( ) { return kind() == COMMENT; } |
| final boolean isProcinst ( ) { return kind() == PROCINST; } |
| final boolean isFinish ( ) { return Cur.kindIsFinish( kind() ); } |
| final boolean isContainer ( ) { return Cur.kindIsContainer( kind() ); } |
| final boolean isNormalAttr ( ) { return kind() == ATTR && !isXmlns(); } |
| |
| final boolean skip ( ) { toEnd(); return next(); } |
| |
| abstract void release ( ); |
| |
| abstract int kind ( ); |
| |
| abstract QName getName ( ); |
| abstract String getXmlnsPrefix ( ); |
| abstract String getXmlnsUri ( ); |
| |
| abstract boolean isXmlns ( ); |
| |
| abstract boolean hasChildren ( ); |
| abstract boolean hasText ( ); |
| abstract boolean isTextCData ( ); |
| |
| abstract boolean toFirstAttr ( ); |
| abstract boolean toNextAttr ( ); |
| abstract String getAttrValue ( ); |
| |
| abstract boolean next ( ); |
| abstract void toEnd ( ); |
| |
| abstract void push ( ); |
| abstract void pop ( ); |
| |
| abstract Object getChars ( ); |
| abstract List getAncestorNamespaces ( ); |
| abstract XmlDocumentProperties getDocProps ( ); |
| |
| int _offSrc; |
| int _cchSrc; |
| } |
| |
| // TODO - saving a fragment need to take namesapces from root and |
| // reflect them on the document element |
| |
| private static final class DocSaveCur extends SaveCur |
| { |
| DocSaveCur ( Cur c ) |
| { |
| assert c.isRoot(); |
| _cur = c.weakCur( this ); |
| } |
| |
| void release ( ) |
| { |
| _cur.release(); |
| _cur = null; |
| } |
| |
| int kind ( ) { return _cur.kind(); } |
| |
| QName getName ( ) { return _cur.getName(); } |
| String getXmlnsPrefix ( ) { return _cur.getXmlnsPrefix(); } |
| String getXmlnsUri ( ) { return _cur.getXmlnsUri(); } |
| |
| boolean isXmlns ( ) { return _cur.isXmlns(); } |
| |
| boolean hasChildren ( ) { return _cur.hasChildren(); } |
| boolean hasText ( ) { return _cur.hasText(); } |
| boolean isTextCData ( ) { return _cur.isTextCData(); } |
| |
| boolean toFirstAttr ( ) { return _cur.toFirstAttr(); } |
| boolean toNextAttr ( ) { return _cur.toNextAttr(); } |
| String getAttrValue ( ) { assert _cur.isAttr(); return _cur.getValueAsString(); } |
| |
| void toEnd ( ) { _cur.toEnd(); } |
| boolean next ( ) { return _cur.next(); } |
| |
| void push ( ) { _cur.push(); } |
| void pop ( ) { _cur.pop(); } |
| |
| List getAncestorNamespaces ( ) { return null; } |
| |
| Object getChars ( ) |
| { |
| Object o = _cur.getChars( -1 ); |
| |
| _offSrc = _cur._offSrc; |
| _cchSrc = _cur._cchSrc; |
| |
| return o; |
| } |
| |
| XmlDocumentProperties getDocProps ( ) { return Locale.getDocProps(_cur, false); } |
| |
| private Cur _cur; |
| } |
| |
| private static abstract class FilterSaveCur extends SaveCur |
| { |
| FilterSaveCur ( SaveCur c ) |
| { |
| assert c.isRoot(); |
| _cur = c; |
| } |
| |
| // Can filter anything by root and attributes and text |
| protected abstract boolean filter ( ); |
| |
| void release ( ) |
| { |
| _cur.release(); |
| _cur = null; |
| } |
| |
| int kind ( ) { return _cur.kind(); } |
| |
| QName getName ( ) { return _cur.getName(); } |
| String getXmlnsPrefix ( ) { return _cur.getXmlnsPrefix(); } |
| String getXmlnsUri ( ) { return _cur.getXmlnsUri(); } |
| |
| boolean isXmlns ( ) { return _cur.isXmlns(); } |
| |
| boolean hasChildren ( ) { return _cur.hasChildren(); } |
| boolean hasText ( ) { return _cur.hasText(); } |
| boolean isTextCData ( ) { return _cur.isTextCData(); } |
| |
| boolean toFirstAttr ( ) { return _cur.toFirstAttr(); } |
| boolean toNextAttr ( ) { return _cur.toNextAttr(); } |
| String getAttrValue ( ) { return _cur.getAttrValue(); } |
| |
| void toEnd ( ) { _cur.toEnd(); } |
| |
| boolean next ( ) |
| { |
| if (!_cur.next()) |
| return false; |
| |
| if (!filter()) |
| return true; |
| |
| assert !isRoot() && !isText() && !isAttr(); |
| |
| toEnd(); |
| |
| return next(); |
| } |
| |
| void push ( ) { _cur.push(); } |
| void pop ( ) { _cur.pop(); } |
| |
| List getAncestorNamespaces ( ) { return _cur.getAncestorNamespaces(); } |
| |
| Object getChars ( ) |
| { |
| Object o = _cur.getChars(); |
| |
| _offSrc = _cur._offSrc; |
| _cchSrc = _cur._cchSrc; |
| |
| return o; |
| } |
| |
| XmlDocumentProperties getDocProps ( ) { return _cur.getDocProps(); } |
| |
| private SaveCur _cur; |
| } |
| |
| private static final class FilterPiSaveCur extends FilterSaveCur |
| { |
| FilterPiSaveCur ( SaveCur c, String target ) |
| { |
| super( c ); |
| |
| _piTarget = target; |
| } |
| |
| protected boolean filter ( ) |
| { |
| return kind() == PROCINST && getName().getLocalPart().equals( _piTarget ); |
| } |
| |
| private String _piTarget; |
| } |
| |
| private static final class FragSaveCur extends SaveCur |
| { |
| FragSaveCur ( Cur start, Cur end, QName synthElem ) |
| { |
| _saveAttr = start.isAttr() && start.isSamePos( end ); |
| |
| _cur = start.weakCur( this ); |
| _end = end.weakCur( this ); |
| |
| _elem = synthElem; |
| |
| _state = ROOT_START; |
| |
| _stateStack = new int [ 8 ]; |
| |
| start.push(); |
| computeAncestorNamespaces( start ); |
| start.pop(); |
| } |
| |
| List getAncestorNamespaces ( ) |
| { |
| return _ancestorNamespaces; |
| } |
| |
| private void computeAncestorNamespaces ( Cur c ) |
| { |
| _ancestorNamespaces = new ArrayList(); |
| |
| while ( c.toParentRaw() ) |
| { |
| if (c.toFirstAttr()) |
| { |
| do |
| { |
| if (c.isXmlns()) |
| { |
| String prefix = c.getXmlnsPrefix(); |
| String uri = c.getXmlnsUri(); |
| |
| // Don't let xmlns:foo="" get used |
| |
| if (uri.length() > 0 || prefix.length() == 0) |
| { |
| _ancestorNamespaces.add( c.getXmlnsPrefix() ); |
| _ancestorNamespaces.add( c.getXmlnsUri() ); |
| } |
| } |
| } |
| while ( c.toNextAttr() ); |
| |
| c.toParent(); |
| } |
| } |
| } |
| |
| // |
| // |
| // |
| |
| void release ( ) |
| { |
| _cur.release(); |
| _cur = null; |
| |
| _end.release(); |
| _end = null; |
| } |
| |
| int kind ( ) |
| { |
| switch ( _state ) |
| { |
| case ROOT_START : return ROOT; |
| case ELEM_START : return ELEM; |
| case ELEM_END : return -ELEM; |
| case ROOT_END : return -ROOT; |
| } |
| |
| assert _state == CUR; |
| |
| return _cur.kind(); |
| } |
| |
| QName getName ( ) |
| { |
| switch ( _state ) |
| { |
| case ROOT_START : |
| case ROOT_END : return null; |
| case ELEM_START : |
| case ELEM_END : return _elem; |
| } |
| |
| assert _state == CUR; |
| |
| return _cur.getName(); |
| } |
| |
| String getXmlnsPrefix ( ) |
| { |
| assert _state == CUR && _cur.isAttr(); |
| return _cur.getXmlnsPrefix(); |
| } |
| |
| String getXmlnsUri ( ) |
| { |
| assert _state == CUR && _cur.isAttr(); |
| return _cur.getXmlnsUri(); |
| } |
| |
| boolean isXmlns ( ) |
| { |
| assert _state == CUR && _cur.isAttr(); |
| return _cur.isXmlns(); |
| } |
| |
| boolean hasChildren ( ) |
| { |
| boolean hasChildren = false; |
| |
| if (isContainer()) |
| { // is there a faster way to do this? |
| push(); |
| next(); |
| |
| if (!isText() && !isFinish()) |
| hasChildren = true; |
| |
| pop(); |
| } |
| |
| return hasChildren; |
| } |
| |
| boolean hasText ( ) |
| { |
| boolean hasText = false; |
| |
| if (isContainer()) |
| { |
| push(); |
| next(); |
| |
| if (isText()) |
| hasText = true; |
| |
| pop(); |
| } |
| |
| return hasText; |
| } |
| |
| boolean isTextCData ( ) |
| { |
| return _cur.isTextCData(); |
| } |
| |
| Object getChars ( ) |
| { |
| assert _state == CUR && _cur.isText(); |
| |
| Object src = _cur.getChars( -1 ); |
| |
| _offSrc = _cur._offSrc; |
| _cchSrc = _cur._cchSrc; |
| |
| return src; |
| } |
| |
| boolean next ( ) |
| { |
| switch ( _state ) |
| { |
| case ROOT_START : |
| { |
| _state = _elem == null ? CUR : ELEM_START; |
| break; |
| } |
| |
| case ELEM_START : |
| { |
| if (_saveAttr) |
| _state = ELEM_END; |
| else |
| { |
| if (_cur.isAttr()) |
| { |
| _cur.toParent(); |
| _cur.next(); |
| } |
| |
| if (_cur.isSamePos( _end )) |
| _state = ELEM_END; |
| else |
| _state = CUR; |
| } |
| |
| break; |
| } |
| |
| case CUR : |
| { |
| assert !_cur.isAttr(); |
| |
| _cur.next(); |
| |
| if (_cur.isSamePos( _end )) |
| _state = _elem == null ? ROOT_END : ELEM_END; |
| |
| break; |
| } |
| |
| case ELEM_END : |
| { |
| _state = ROOT_END; |
| break; |
| } |
| case ROOT_END : |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void toEnd ( ) |
| { |
| switch ( _state ) |
| { |
| case ROOT_START : _state = ROOT_END; return; |
| case ELEM_START : _state = ELEM_END; return; |
| case ROOT_END : |
| case ELEM_END : return; |
| } |
| |
| assert _state == CUR && !_cur.isAttr() && !_cur.isText(); |
| |
| _cur.toEnd(); |
| } |
| |
| boolean toFirstAttr ( ) |
| { |
| switch ( _state ) |
| { |
| case ROOT_END : |
| case ELEM_END : |
| case ROOT_START : return false; |
| case CUR : return _cur.toFirstAttr(); |
| } |
| |
| assert _state == ELEM_START; |
| |
| if (!_cur.isAttr()) |
| return false; |
| |
| _state = CUR; |
| |
| return true; |
| } |
| |
| boolean toNextAttr ( ) |
| { |
| assert _state == CUR; |
| return !_saveAttr && _cur.toNextAttr(); |
| } |
| |
| String getAttrValue ( ) |
| { |
| assert _state == CUR && _cur.isAttr(); |
| return _cur.getValueAsString(); |
| } |
| |
| void push ( ) |
| { |
| if (_stateStackSize == _stateStack.length) |
| { |
| int[] newStateStack = new int [ _stateStackSize * 2 ]; |
| System.arraycopy( _stateStack, 0, newStateStack, 0, _stateStackSize ); |
| _stateStack = newStateStack; |
| } |
| |
| _stateStack [ _stateStackSize++ ] = _state; |
| _cur.push(); |
| } |
| |
| void pop () |
| { |
| _cur.pop(); |
| _state = _stateStack [ --_stateStackSize ]; |
| } |
| |
| XmlDocumentProperties getDocProps ( ) { return Locale.getDocProps(_cur, false); } |
| |
| // |
| // |
| // |
| |
| private Cur _cur; |
| private Cur _end; |
| |
| private ArrayList _ancestorNamespaces; |
| |
| private QName _elem; |
| |
| private boolean _saveAttr; |
| |
| private static final int ROOT_START = 1; |
| private static final int ELEM_START = 2; |
| private static final int ROOT_END = 3; |
| private static final int ELEM_END = 4; |
| private static final int CUR = 5; |
| |
| private int _state; |
| |
| private int[] _stateStack; |
| private int _stateStackSize; |
| } |
| |
| private static final class PrettySaveCur extends SaveCur |
| { |
| PrettySaveCur ( SaveCur c, XmlOptions options ) |
| { |
| _sb = new StringBuffer(); |
| _stack = new ArrayList(); |
| |
| _cur = c; |
| |
| assert options != null; |
| |
| _prettyIndent = 2; |
| |
| if (options.hasOption( XmlOptions.SAVE_PRETTY_PRINT_INDENT )) |
| { |
| _prettyIndent = |
| ((Integer) options.get( XmlOptions.SAVE_PRETTY_PRINT_INDENT )).intValue(); |
| } |
| |
| if (options.hasOption( XmlOptions.SAVE_PRETTY_PRINT_OFFSET )) |
| { |
| _prettyOffset = |
| ((Integer) options.get( XmlOptions.SAVE_PRETTY_PRINT_OFFSET )).intValue(); |
| } |
| |
| if (options.hasOption( XmlOptions.LOAD_SAVE_CDATA_BOOKMARKS )) |
| { |
| _useCDataBookmarks = true; |
| } |
| } |
| |
| List getAncestorNamespaces ( ) { return _cur.getAncestorNamespaces(); } |
| |
| void release ( ) { _cur.release(); } |
| |
| int kind ( ) { return _txt == null ? _cur.kind() : TEXT; } |
| |
| QName getName ( ) { assert _txt == null; return _cur.getName(); } |
| String getXmlnsPrefix ( ) { assert _txt == null; return _cur.getXmlnsPrefix(); } |
| String getXmlnsUri ( ) { assert _txt == null; return _cur.getXmlnsUri(); } |
| |
| boolean isXmlns ( ) { return _txt == null ? _cur.isXmlns() : false; } |
| |
| boolean hasChildren ( ) { return _txt == null ? _cur.hasChildren() : false; } |
| boolean hasText ( ) { return _txt == null ? _cur.hasText() : false; } |
| |
| // _cur.isTextCData() is expensive do it only if useCDataBookmarks option is enabled |
| boolean isTextCData ( ) { return _txt == null ? (_useCDataBookmarks && _cur.isTextCData()) |
| : _isTextCData; } |
| |
| boolean toFirstAttr ( ) { assert _txt == null; return _cur.toFirstAttr(); } |
| boolean toNextAttr ( ) { assert _txt == null; return _cur.toNextAttr(); } |
| String getAttrValue ( ) { assert _txt == null; return _cur.getAttrValue(); } |
| |
| void toEnd ( ) |
| { |
| assert _txt == null; |
| _cur.toEnd(); |
| |
| if (_cur.kind() == -ELEM) |
| _depth--; |
| } |
| |
| boolean next ( ) |
| { |
| int k; |
| |
| if (_txt != null) |
| { |
| assert _txt.length() > 0; |
| assert !_cur.isText(); |
| _txt = null; |
| _isTextCData = false; |
| k = _cur.kind(); |
| } |
| else |
| { |
| int prevKind = k = _cur.kind(); |
| |
| if (!_cur.next()) |
| return false; |
| |
| _sb.delete( 0, _sb.length() ); |
| |
| assert _txt == null; |
| |
| // place any text encountered in the buffer |
| if (_cur.isText()) |
| { |
| // _cur.isTextCData() is expensive do it only if useCDataBookmarks option is enabled |
| _isTextCData = _useCDataBookmarks && _cur.isTextCData(); |
| CharUtil.getString( _sb, _cur.getChars(), _cur._offSrc, _cur._cchSrc ); |
| _cur.next(); |
| trim( _sb ); |
| } |
| |
| k = _cur.kind(); |
| |
| // Check for non leaf, _prettyIndent < 0 means that the save is all on one line |
| |
| if (_prettyIndent >= 0 && |
| prevKind != COMMENT && prevKind != PROCINST && (prevKind != ELEM || k != -ELEM)) |
| // if (prevKind != COMMENT && prevKind != PROCINST && (prevKind != ELEM || k != -ELEM)) |
| { |
| if (_sb.length() > 0) |
| { |
| _sb.insert( 0, _newLine ); |
| spaces( _sb, _newLine.length(), _prettyOffset + _prettyIndent * _depth ); |
| } |
| |
| if (k != -ROOT) |
| { |
| if (prevKind != ROOT) |
| _sb.append( _newLine ); |
| |
| int d = k < 0 ? _depth - 1 : _depth; |
| spaces( _sb, _sb.length(), _prettyOffset + _prettyIndent * d ); |
| } |
| } |
| |
| if (_sb.length() > 0) |
| { |
| _txt = _sb.toString(); |
| k = TEXT; |
| } |
| } |
| |
| if (k == ELEM) |
| _depth++; |
| else if (k == -ELEM) |
| _depth--; |
| |
| return true; |
| } |
| |
| void push ( ) |
| { |
| _cur.push(); |
| _stack.add( _txt ); |
| _stack.add( new Integer( _depth ) ); |
| _isTextCData = false; |
| } |
| |
| void pop ( ) |
| { |
| _cur.pop(); |
| _depth = ((Integer) _stack.remove( _stack.size() - 1 )).intValue(); |
| _txt = (String) _stack.remove( _stack.size() - 1 ); |
| _isTextCData = false; |
| } |
| |
| Object getChars ( ) |
| { |
| if (_txt != null) |
| { |
| _offSrc = 0; |
| _cchSrc = _txt.length(); |
| return _txt; |
| } |
| |
| Object o = _cur.getChars(); |
| |
| _offSrc = _cur._offSrc; |
| _cchSrc = _cur._cchSrc; |
| |
| return o; |
| } |
| |
| XmlDocumentProperties getDocProps ( ) { return _cur.getDocProps(); } |
| |
| static void spaces ( StringBuffer sb, int offset, int count ) |
| { |
| while ( count-- > 0 ) |
| sb.insert( offset, ' ' ); |
| } |
| |
| static void trim ( StringBuffer sb ) |
| { |
| int i; |
| |
| for ( i = 0 ; i < sb.length() ; i++ ) |
| if (!CharUtil.isWhiteSpace( sb.charAt( i ) )) |
| break; |
| |
| sb.delete( 0, i ); |
| |
| for ( i = sb.length() ; i > 0 ; i-- ) |
| if (!CharUtil.isWhiteSpace( sb.charAt( i - 1 ) )) |
| break; |
| |
| sb.delete( i, sb.length() ); |
| } |
| |
| private SaveCur _cur; |
| |
| private int _prettyIndent; |
| private int _prettyOffset; |
| |
| private String _txt; |
| private StringBuffer _sb; |
| |
| private int _depth; |
| |
| private ArrayList _stack; |
| private boolean _isTextCData = false; |
| private boolean _useCDataBookmarks = false; |
| } |
| |
| |
| // |
| // |
| // |
| |
| private final Locale _locale; |
| private final long _version; |
| |
| private SaveCur _cur; |
| |
| private List _ancestorNamespaces; |
| private Map _suggestedPrefixes; |
| protected XmlOptionCharEscapeMap _replaceChar; |
| private boolean _useDefaultNamespace; |
| private Map _preComputedNamespaces; |
| private boolean _saveNamespacesFirst; |
| |
| private ArrayList _attrNames; |
| private ArrayList _attrValues; |
| |
| private ArrayList _namespaceStack; |
| private int _currentMapping; |
| private HashMap _uriMap; |
| private HashMap _prefixMap; |
| private String _initialDefaultUri; |
| |
| static final String _newLine = |
| SystemProperties.getProperty( "line.separator" ) == null |
| ? "\n" |
| : SystemProperties.getProperty( "line.separator" ); |
| } |