Save This Page
Home » xmlbeans-2.5.0-src » org.apache.xmlbeans.impl » store » [javadoc | source]
    1   /*   Copyright 2004 The Apache Software Foundation
    2    *
    3    *   Licensed under the Apache License, Version 2.0 (the "License");
    4    *   you may not use this file except in compliance with the License.
    5    *   You may obtain a copy of the License at
    6    *
    7    *       http://www.apache.org/licenses/LICENSE-2.0
    8    *
    9    *   Unless required by applicable law or agreed to in writing, software
   10    *   distributed under the License is distributed on an "AS IS" BASIS,
   11    *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   12    *   See the License for the specific language governing permissions and
   13    *  limitations under the License.
   14    */
   15   
   16   package org.apache.xmlbeans.impl.store;
   17   
   18   import javax.xml.namespace.QName;
   19   
   20   import org.apache.xmlbeans.SystemProperties;
   21   import org.apache.xmlbeans.XmlDocumentProperties;
   22   import org.apache.xmlbeans.XmlOptions;
   23   import org.apache.xmlbeans.XmlOptionCharEscapeMap;
   24   import org.apache.xmlbeans.xml.stream;
   25   
   26   import org.apache.xmlbeans.impl.common;
   27   
   28   import java.io.Writer;
   29   import java.io.Reader;
   30   import java.io.IOException;
   31   import java.io.InputStream;
   32   import java.io.OutputStream;
   33   import java.io.OutputStreamWriter;
   34   import java.io.UnsupportedEncodingException;
   35   
   36   import org.xml.sax.ContentHandler;
   37   import org.xml.sax.ext.LexicalHandler;
   38   import org.xml.sax.SAXException;
   39   
   40   import org.xml.sax.helpers.AttributesImpl;
   41   
   42   import java.util.Iterator;
   43   import java.util.ArrayList;
   44   import java.util.List;
   45   import java.util.HashMap;
   46   import java.util.Map;
   47   import java.util.LinkedHashMap;
   48   import java.util.ConcurrentModificationException;
   49   
   50   abstract class Saver
   51   {
   52       static final int ROOT     = Cur.ROOT;
   53       static final int ELEM     = Cur.ELEM;
   54       static final int ATTR     = Cur.ATTR;
   55       static final int COMMENT  = Cur.COMMENT;
   56       static final int PROCINST = Cur.PROCINST;
   57       static final int TEXT     = Cur.TEXT;
   58   
   59       protected abstract boolean emitElement ( SaveCur c, ArrayList attrNames, ArrayList attrValues );
   60       protected abstract void emitFinish     ( SaveCur c );
   61       protected abstract void emitText       ( SaveCur c );
   62       protected abstract void emitComment    ( SaveCur c );
   63       protected abstract void emitProcinst   ( SaveCur c );
   64       protected abstract void emitDocType    ( String docTypeName, String publicId, String systemId );
   65       protected abstract void emitStartDoc   ( SaveCur c );
   66       protected abstract void emitEndDoc     ( SaveCur c );
   67   
   68       protected void syntheticNamespace ( String prefix, String uri, boolean considerDefault ) { }
   69   
   70       Saver ( Cur c, XmlOptions options )
   71       {
   72           assert c._locale.entered();
   73   
   74           options = XmlOptions.maskNull( options );
   75   
   76           _cur = createSaveCur( c, options );
   77   
   78           _locale = c._locale;
   79           _version = _locale.version();
   80   
   81           _namespaceStack = new ArrayList();
   82           _uriMap = new HashMap();
   83           _prefixMap = new HashMap();
   84   
   85           _attrNames = new ArrayList();
   86           _attrValues = new ArrayList ();
   87   
   88           // Define implicit xml prefixed namespace
   89   
   90           addMapping( "xml", Locale._xml1998Uri );
   91   
   92           if (options.hasOption( XmlOptions.SAVE_IMPLICIT_NAMESPACES ))
   93           {
   94               Map m = (Map) options.get( XmlOptions.SAVE_IMPLICIT_NAMESPACES );
   95   
   96               for ( Iterator i = m.keySet().iterator() ; i.hasNext() ; )
   97               {
   98                   String prefix = (String) i.next();
   99                   addMapping( prefix, (String) m.get( prefix ) );
  100               }
  101           }
  102   
  103           // define character map for escaped replacements
  104           if (options.hasOption( XmlOptions.SAVE_SUBSTITUTE_CHARACTERS ))
  105           {
  106               _replaceChar = (XmlOptionCharEscapeMap)
  107                   options.get( XmlOptions.SAVE_SUBSTITUTE_CHARACTERS);
  108           }
  109   
  110           // If the default prefix has not been mapped, do so now
  111   
  112           if (getNamespaceForPrefix( "" ) == null)
  113           {
  114               _initialDefaultUri = new String( "" );
  115               addMapping( "", _initialDefaultUri );
  116           }
  117   
  118           if (options.hasOption( XmlOptions.SAVE_AGGRESSIVE_NAMESPACES ) &&
  119                   !(this instanceof SynthNamespaceSaver))
  120           {
  121               SynthNamespaceSaver saver = new SynthNamespaceSaver( c, options );
  122   
  123               while ( saver.process() )
  124                   ;
  125   
  126               if (!saver._synthNamespaces.isEmpty())
  127                   _preComputedNamespaces = saver._synthNamespaces;
  128           }
  129   
  130           _useDefaultNamespace =
  131               options.hasOption( XmlOptions.SAVE_USE_DEFAULT_NAMESPACE );
  132   
  133           _saveNamespacesFirst = options.hasOption( XmlOptions.SAVE_NAMESPACES_FIRST );
  134   
  135           if (options.hasOption( XmlOptions.SAVE_SUGGESTED_PREFIXES ))
  136               _suggestedPrefixes = (Map) options.get( XmlOptions.SAVE_SUGGESTED_PREFIXES);
  137   
  138           _ancestorNamespaces = _cur.getAncestorNamespaces();
  139       }
  140   
  141       private static SaveCur createSaveCur ( Cur c, XmlOptions options )
  142       {
  143           QName synthName = (QName) options.get( XmlOptions.SAVE_SYNTHETIC_DOCUMENT_ELEMENT );
  144   
  145           QName fragName = synthName;
  146   
  147           if (fragName == null)
  148           {
  149               fragName =
  150                   options.hasOption( XmlOptions.SAVE_USE_OPEN_FRAGMENT )
  151                       ? Locale._openuriFragment
  152                       : Locale._xmlFragment;
  153           }
  154   
  155           boolean saveInner =
  156               options.hasOption( XmlOptions.SAVE_INNER ) &&
  157                   !options.hasOption( XmlOptions.SAVE_OUTER );
  158   
  159           Cur start = c.tempCur();
  160           Cur end   = c.tempCur();
  161   
  162           SaveCur cur = null;
  163   
  164           int k = c.kind();
  165   
  166           switch ( k )
  167           {
  168           case ROOT :
  169           {
  170               positionToInner( c, start, end );
  171   
  172               if (Locale.isFragment( start, end ))
  173                   cur = new FragSaveCur( start, end, fragName );
  174               else if (synthName != null)
  175                   cur = new FragSaveCur( start, end, synthName );
  176               else
  177                   cur = new DocSaveCur( c );
  178   
  179               break;
  180           }
  181   
  182           case ELEM :
  183           {
  184               if (saveInner)
  185               {
  186                   positionToInner( c, start, end );
  187   
  188                   cur =
  189                       new FragSaveCur(
  190                           start, end, Locale.isFragment( start, end ) ? fragName : synthName );
  191               }
  192               else if (synthName != null)
  193               {
  194                   positionToInner( c, start, end );
  195   
  196                   cur = new FragSaveCur( start, end, synthName );
  197               }
  198               else
  199               {
  200                   start.moveToCur( c );
  201                   end.moveToCur( c );
  202                   end.skip();
  203   
  204                   cur = new FragSaveCur( start, end, null );
  205               }
  206   
  207               break;
  208           }
  209           }
  210   
  211           if (cur == null)
  212           {
  213               assert k < 0 || k == ATTR || k == COMMENT || k == PROCINST || k == TEXT;
  214   
  215               if (k < 0)
  216               {
  217                   // Save out ""
  218                   start.moveToCur( c );
  219                   end.moveToCur( c );
  220               }
  221               else if (k == TEXT)
  222               {
  223                   start.moveToCur( c );
  224                   end.moveToCur( c );
  225                   end.next();
  226               }
  227               else if (saveInner)
  228               {
  229                   start.moveToCur( c );
  230                   start.next();
  231   
  232                   end.moveToCur( c );
  233                   end.toEnd();
  234               }
  235               else if (k == ATTR)
  236               {
  237                   start.moveToCur( c );
  238                   end.moveToCur( c );
  239               }
  240               else
  241               {
  242                   assert k == COMMENT || k == PROCINST;
  243   
  244                   start.moveToCur( c );
  245                   end.moveToCur( c );
  246                   end.skip();
  247               }
  248   
  249               cur = new FragSaveCur( start, end, fragName );
  250           }
  251   
  252           String filterPI = (String) options.get( XmlOptions.SAVE_FILTER_PROCINST );
  253   
  254           if (filterPI != null)
  255               cur = new FilterPiSaveCur( cur, filterPI );
  256   
  257           if (options.hasOption( XmlOptions.SAVE_PRETTY_PRINT ))
  258               cur = new PrettySaveCur( cur, options );
  259   
  260           start.release();
  261           end.release();
  262   
  263           return cur;
  264       }
  265   
  266       private static void positionToInner ( Cur c, Cur start, Cur end )
  267       {
  268           assert c.isContainer();
  269   
  270           start.moveToCur( c );
  271   
  272           if (!start.toFirstAttr())
  273               start.next();
  274   
  275           end.moveToCur( c );
  276           end.toEnd();
  277       }
  278   
  279       protected boolean saveNamespacesFirst ( )
  280       {
  281           return _saveNamespacesFirst;
  282       }
  283   
  284       protected void enterLocale()
  285       {
  286           _locale.enter();
  287       }
  288   
  289       protected void exitLocale()
  290       {
  291           _locale.exit();
  292       }
  293   
  294       protected final boolean process ( )
  295       {
  296           assert _locale.entered();
  297   
  298           if (_cur == null)
  299               return false;
  300   
  301           if (_version != _locale.version())
  302               throw new ConcurrentModificationException( "Document changed during save" );
  303   
  304           switch ( _cur.kind() )
  305           {
  306               case   ROOT     : { processRoot();                        break; }
  307               case   ELEM     : { processElement();                     break; }
  308               case - ELEM     : { processFinish ();                     break; }
  309               case   TEXT     : { emitText      ( _cur );               break; }
  310   
  311               case   COMMENT  : { emitComment   ( _cur ); _cur.toEnd(); break; }
  312               case   PROCINST : { emitProcinst  ( _cur ); _cur.toEnd(); break; }
  313   
  314               case - ROOT :
  315               {
  316                   emitEndDoc(_cur);
  317                   _cur.release();
  318                   _cur = null;
  319   
  320                   return true;
  321               }
  322   
  323               default : throw new RuntimeException( "Unexpected kind" );
  324           }
  325   
  326           _cur.next();
  327   
  328           return true;
  329       }
  330   
  331       private final void processFinish ( )
  332       {
  333           emitFinish( _cur );
  334           popMappings();
  335       }
  336   
  337       private final void processRoot ( )
  338       {
  339           assert _cur.isRoot();
  340   
  341           XmlDocumentProperties props = _cur.getDocProps();
  342           String systemId = null;
  343           String docTypeName = null;
  344           if (props != null)
  345           {
  346               systemId = props.getDoctypeSystemId();
  347               docTypeName = props.getDoctypeName();
  348           }
  349   
  350           if (systemId != null || docTypeName != null)
  351           {
  352               if (docTypeName == null)
  353               {
  354                   _cur.push();
  355                   while (!_cur.isElem() && _cur.next())
  356                       ;
  357                   if (_cur.isElem())
  358                       docTypeName = _cur.getName().getLocalPart();
  359                   _cur.pop();
  360               }
  361   
  362               String publicId = props.getDoctypePublicId();
  363   
  364               if (docTypeName != null)
  365               {
  366                   QName rootElemName = _cur.getName();
  367   
  368                   if ( rootElemName == null )
  369                   {
  370                       _cur.push();
  371                       while ( !_cur.isFinish() )
  372                       {
  373                           if (_cur.isElem())
  374                           {
  375                               rootElemName = _cur.getName();
  376                               break;
  377                           }
  378                           _cur.next();
  379                       }
  380                       _cur.pop();
  381                   }
  382   
  383                   if ( rootElemName!=null && docTypeName.equals(rootElemName.getLocalPart()) )
  384                   {
  385                       emitDocType( docTypeName, publicId, systemId );
  386                       return;
  387                   }
  388               }
  389           }
  390   
  391           emitStartDoc(_cur);
  392       }
  393   
  394       private final void processElement ( )
  395       {
  396           assert _cur.isElem() && _cur.getName() != null;
  397   
  398           QName name = _cur.getName();
  399   
  400           // Add a new entry to the frontier.  If this element has a name
  401           // which has no namespace, then we must make sure that pushing
  402           // the mappings causes the default namespace to be empty
  403   
  404           boolean ensureDefaultEmpty = name.getNamespaceURI().length() == 0;
  405   
  406           pushMappings( _cur, ensureDefaultEmpty );
  407   
  408           //
  409           // There are four things which use mappings:
  410           //
  411           //   1) The element name
  412           //   2) The element value (qname based)
  413           //   3) Attribute names
  414           //   4) The attribute values (qname based)
  415           //
  416   
  417           // 1) The element name (not for starts)
  418   
  419           ensureMapping( name.getNamespaceURI(), name.getPrefix(), !ensureDefaultEmpty, false );
  420   
  421           //
  422           //
  423           //
  424   
  425           _attrNames.clear();
  426           _attrValues.clear();
  427   
  428           _cur.push();
  429   
  430           attrs:
  431           for ( boolean A = _cur.toFirstAttr() ; A ; A = _cur.toNextAttr() )
  432           {
  433               if (_cur.isNormalAttr())
  434               {
  435                   QName attrName = _cur.getName();
  436   
  437                   _attrNames.add( attrName );
  438   
  439                   for ( int i = _attrNames.size() - 2 ; i >= 0 ; i-- )
  440                   {
  441                       if (_attrNames.get( i ).equals( attrName ))
  442                       {
  443                           _attrNames.remove( _attrNames.size() - 1 );
  444                           continue attrs;
  445                       }
  446                   }
  447   
  448                   _attrValues.add( _cur.getAttrValue() );
  449   
  450                   ensureMapping( attrName.getNamespaceURI(), attrName.getPrefix(), false, true );
  451               }
  452           }
  453   
  454           _cur.pop();
  455   
  456           // If I am doing aggressive namespaces and we're emitting a
  457           // container which can contain content, add the namespaces
  458           // we've computed.  Basically, I'm making sure the pre-computed
  459           // namespaces are mapped on the first container which has a name.
  460   
  461           if (_preComputedNamespaces != null)
  462           {
  463               for ( Iterator i = _preComputedNamespaces.keySet().iterator() ; i.hasNext() ; )
  464               {
  465                   String uri = (String) i.next();
  466                   String prefix = (String) _preComputedNamespaces.get( uri );
  467                   boolean considerDefault = prefix.length() == 0 && !ensureDefaultEmpty;
  468   
  469                   ensureMapping( uri, prefix, considerDefault, false );
  470               }
  471   
  472               // Set to null so we do this once at the top
  473               _preComputedNamespaces = null;
  474           }
  475   
  476           if (emitElement( _cur, _attrNames, _attrValues ))
  477           {
  478               popMappings();
  479               _cur.toEnd();
  480           }
  481       }
  482   
  483       //
  484       // Layout of namespace stack:
  485       //
  486       //    URI Undo
  487       //    URI Rename
  488       //    Prefix Undo
  489       //    Mapping
  490       //
  491   
  492       boolean hasMappings ( )
  493       {
  494           int i = _namespaceStack.size();
  495   
  496           return i > 0 && _namespaceStack.get( i - 1 ) != null;
  497       }
  498   
  499       void iterateMappings ( )
  500       {
  501           _currentMapping = _namespaceStack.size();
  502   
  503           while ( _currentMapping > 0 && _namespaceStack.get( _currentMapping - 1 ) != null )
  504               _currentMapping -= 8;
  505       }
  506   
  507       boolean hasMapping ( )
  508       {
  509           return _currentMapping < _namespaceStack.size();
  510       }
  511   
  512       void nextMapping ( )
  513       {
  514           _currentMapping += 8;
  515       }
  516   
  517       String mappingPrefix ( )
  518       {
  519           assert hasMapping();
  520           return (String) _namespaceStack.get( _currentMapping + 6 );
  521       }
  522   
  523       String mappingUri ( )
  524       {
  525           assert hasMapping();
  526           return (String) _namespaceStack.get( _currentMapping + 7 );
  527       }
  528   
  529       private final void pushMappings ( SaveCur c, boolean ensureDefaultEmpty )
  530       {
  531           assert c.isContainer();
  532   
  533           _namespaceStack.add( null );
  534   
  535           c.push();
  536   
  537           namespaces:
  538           for ( boolean A = c.toFirstAttr() ; A ; A = c.toNextAttr() )
  539               if (c.isXmlns())
  540                   addNewFrameMapping( c.getXmlnsPrefix(), c.getXmlnsUri(), ensureDefaultEmpty );
  541   
  542           c.pop();
  543   
  544           if (_ancestorNamespaces != null)
  545           {
  546               for ( int i = 0 ; i < _ancestorNamespaces.size() ; i += 2 )
  547               {
  548                   String prefix = (String) _ancestorNamespaces.get( i );
  549                   String uri    = (String) _ancestorNamespaces.get( i + 1 );
  550   
  551                   addNewFrameMapping( prefix, uri, ensureDefaultEmpty );
  552               }
  553   
  554               _ancestorNamespaces = null;
  555           }
  556   
  557           if (ensureDefaultEmpty)
  558           {
  559               String defaultUri = (String) _prefixMap.get( "" );
  560   
  561               // I map the default to "" at the very beginning
  562               assert defaultUri != null;
  563   
  564               if (defaultUri.length() > 0)
  565                   addMapping( "", "" );
  566           }
  567       }
  568   
  569       private final void addNewFrameMapping ( String prefix, String uri, boolean ensureDefaultEmpty )
  570       {
  571           // If the prefix maps to "", this don't include this mapping 'cause it's not well formed.
  572           // Also, if we want to make sure that the default namespace is always "", then check that
  573           // here as well.
  574   
  575           if ((prefix.length() == 0 || uri.length() > 0) &&
  576                   (!ensureDefaultEmpty || prefix.length() > 0 || uri.length() == 0))
  577           {
  578               // Make sure the prefix is not already mapped in this frame
  579   
  580               for ( iterateMappings() ; hasMapping() ; nextMapping() )
  581                   if (mappingPrefix().equals( prefix ))
  582                       return;
  583   
  584               // Also make sure that the prefix declaration is not redundant
  585               // This has the side-effect of making it impossible to set a
  586               // redundant prefix declaration, but seems that it's better
  587               // to just never issue a duplicate prefix declaration.
  588               if (uri.equals(getNamespaceForPrefix(prefix)))
  589                   return;
  590   
  591               addMapping( prefix, uri );
  592           }
  593       }
  594   
  595       private final void addMapping ( String prefix, String uri )
  596       {
  597           assert uri != null;
  598           assert prefix != null;
  599   
  600           // If the prefix being mapped here is already mapped to a uri,
  601           // that uri will either go out of scope or be mapped to another
  602           // prefix.
  603   
  604           String renameUri = (String) _prefixMap.get( prefix );
  605           String renamePrefix = null;
  606   
  607           if (renameUri != null)
  608           {
  609               // See if this prefix is already mapped to this uri.  If
  610               // so, then add to the stack, but there is nothing to rename
  611   
  612               if (renameUri.equals( uri ))
  613                   renameUri = null;
  614               else
  615               {
  616                   int i = _namespaceStack.size();
  617   
  618                   while ( i > 0 )
  619                   {
  620                       if (_namespaceStack.get( i - 1 ) == null)
  621                       {
  622                           i--;
  623                           continue;
  624                       }
  625   
  626                       if (_namespaceStack.get( i - 7 ).equals( renameUri ))
  627                       {
  628                           renamePrefix = (String) _namespaceStack.get( i - 8 );
  629   
  630                           if (renamePrefix == null || !renamePrefix.equals( prefix ))
  631                               break;
  632                       }
  633   
  634                       i -= 8;
  635                   }
  636   
  637                   assert i > 0;
  638               }
  639           }
  640   
  641           _namespaceStack.add( _uriMap.get( uri ) );
  642           _namespaceStack.add( uri );
  643   
  644           if (renameUri != null)
  645           {
  646               _namespaceStack.add( _uriMap.get( renameUri ) );
  647               _namespaceStack.add( renameUri );
  648           }
  649           else
  650           {
  651               _namespaceStack.add( null );
  652               _namespaceStack.add( null );
  653           }
  654   
  655           _namespaceStack.add( prefix );
  656           _namespaceStack.add( _prefixMap.get( prefix ) );
  657   
  658           _namespaceStack.add( prefix );
  659           _namespaceStack.add( uri );
  660   
  661           _uriMap.put( uri, prefix );
  662           _prefixMap.put( prefix, uri );
  663   
  664           if (renameUri != null)
  665               _uriMap.put( renameUri, renamePrefix );
  666       }
  667   
  668       private final void popMappings ( )
  669       {
  670           for ( ; ; )
  671           {
  672               int i = _namespaceStack.size();
  673   
  674               if (i == 0)
  675                   break;
  676   
  677               if (_namespaceStack.get( i - 1 ) == null)
  678               {
  679                   _namespaceStack.remove( i - 1 );
  680                   break;
  681               }
  682   
  683               Object oldUri = _namespaceStack.get( i - 7 );
  684               Object oldPrefix = _namespaceStack.get( i - 8 );
  685   
  686               if (oldPrefix == null)
  687                   _uriMap.remove( oldUri );
  688               else
  689                   _uriMap.put( oldUri, oldPrefix );
  690   
  691               oldPrefix = _namespaceStack.get( i - 4 );
  692               oldUri = _namespaceStack.get( i - 3 );
  693   
  694               if (oldUri == null)
  695                   _prefixMap.remove( oldPrefix );
  696               else
  697                   _prefixMap.put( oldPrefix, oldUri );
  698   
  699               String uri = (String) _namespaceStack.get( i - 5 );
  700   
  701               if (uri != null)
  702                   _uriMap.put( uri, _namespaceStack.get( i - 6 ) );
  703   
  704               // Hahahahahaha -- :-(
  705               _namespaceStack.remove( i - 1 );
  706               _namespaceStack.remove( i - 2 );
  707               _namespaceStack.remove( i - 3 );
  708               _namespaceStack.remove( i - 4 );
  709               _namespaceStack.remove( i - 5 );
  710               _namespaceStack.remove( i - 6 );
  711               _namespaceStack.remove( i - 7 );
  712               _namespaceStack.remove( i - 8 );
  713           }
  714       }
  715   
  716       private final void dumpMappings ( )
  717       {
  718           for ( int i = _namespaceStack.size() ; i > 0 ; )
  719           {
  720               if (_namespaceStack.get( i - 1 ) == null)
  721               {
  722                   System.out.println( "----------------" );
  723                   i--;
  724                   continue;
  725               }
  726   
  727               System.out.print( "Mapping: " );
  728               System.out.print( _namespaceStack.get( i - 2 ) );
  729               System.out.print( " -> " );
  730               System.out.print( _namespaceStack.get( i - 1 ) );
  731               System.out.println();
  732   
  733               System.out.print( "Prefix Undo: " );
  734               System.out.print( _namespaceStack.get( i - 4 ) );
  735               System.out.print( " -> " );
  736               System.out.print( _namespaceStack.get( i - 3 ) );
  737               System.out.println();
  738   
  739               System.out.print( "Uri Rename: " );
  740               System.out.print( _namespaceStack.get( i - 5 ) );
  741               System.out.print( " -> " );
  742               System.out.print( _namespaceStack.get( i - 6 ) );
  743               System.out.println();
  744   
  745               System.out.print( "UriUndo: " );
  746               System.out.print( _namespaceStack.get( i - 7 ) );
  747               System.out.print( " -> " );
  748               System.out.print( _namespaceStack.get( i - 8 ) );
  749               System.out.println();
  750   
  751               System.out.println();
  752   
  753               i -= 8;
  754           }
  755       }
  756   
  757       private final String ensureMapping (
  758           String uri, String candidatePrefix,
  759           boolean considerCreatingDefault, boolean mustHavePrefix )
  760       {
  761           assert uri != null;
  762   
  763           // Can be called for no-namespaced things
  764   
  765           if (uri.length() == 0)
  766               return null;
  767   
  768           String prefix = (String) _uriMap.get( uri );
  769   
  770           if (prefix != null && (prefix.length() > 0 || !mustHavePrefix))
  771               return prefix;
  772   
  773           //
  774           // I try prefixes from a number of places, in order:
  775           //
  776           //  1) What was passed in
  777           //  2) The optional suggestions (for uri's)
  778           //  3) The default mapping is allowed
  779           //  4) ns#++
  780           //
  781   
  782           if (candidatePrefix != null && candidatePrefix.length() == 0)
  783               candidatePrefix = null;
  784   
  785           if (candidatePrefix == null || !tryPrefix( candidatePrefix ))
  786           {
  787               if (_suggestedPrefixes != null &&
  788                       _suggestedPrefixes.containsKey( uri ) &&
  789                           tryPrefix( (String) _suggestedPrefixes.get( uri ) ))
  790               {
  791                   candidatePrefix = (String) _suggestedPrefixes.get( uri );
  792               }
  793               else if (considerCreatingDefault && _useDefaultNamespace && tryPrefix( "" ))
  794                   candidatePrefix = "";
  795               else
  796               {
  797                   String basePrefix = QNameHelper.suggestPrefix( uri );
  798                   candidatePrefix = basePrefix;
  799   
  800                   for ( int i = 1 ; ; i++ )
  801                   {
  802                       if (tryPrefix( candidatePrefix ))
  803                           break;
  804   
  805                       candidatePrefix = basePrefix + i;
  806                   }
  807               }
  808           }
  809   
  810           assert candidatePrefix != null;
  811   
  812           syntheticNamespace( candidatePrefix, uri, considerCreatingDefault );
  813   
  814           addMapping( candidatePrefix, uri );
  815   
  816           return candidatePrefix;
  817       }
  818   
  819       protected final String getUriMapping ( String uri )
  820       {
  821           assert _uriMap.get( uri ) != null;
  822           return (String) _uriMap.get( uri );
  823       }
  824   
  825       String getNonDefaultUriMapping ( String uri )
  826       {
  827           String prefix = (String) _uriMap.get( uri );
  828   
  829           if (prefix != null && prefix.length() > 0)
  830               return prefix;
  831   
  832           for ( Iterator keys = _prefixMap.keySet().iterator() ; keys.hasNext() ; )
  833           {
  834               prefix = (String) keys.next();
  835   
  836               if (prefix.length() > 0 && _prefixMap.get( prefix ).equals( uri ))
  837                   return prefix;
  838           }
  839   
  840           assert false : "Could not find non-default mapping";
  841   
  842           return null;
  843       }
  844   
  845       private final boolean tryPrefix ( String prefix )
  846       {
  847           if (prefix == null || Locale.beginsWithXml( prefix ))
  848               return false;
  849   
  850           String existingUri = (String) _prefixMap.get( prefix );
  851   
  852           // If the prefix is currently mapped, then try another prefix.  A
  853           // special case is that of trying to map the default prefix ("").
  854           // Here, there always exists a default mapping.  If this is the
  855           // mapping we found, then remap it anyways. I use != to compare
  856           // strings because I want to test for the specific initial default
  857           // uri I added when I initialized the saver.
  858   
  859           if (existingUri != null && (prefix.length() > 0 || existingUri != _initialDefaultUri))
  860               return false;
  861   
  862           return true;
  863       }
  864   
  865       public final String getNamespaceForPrefix ( String prefix )
  866       {
  867           assert !prefix.equals( "xml" ) || _prefixMap.get( prefix ).equals( Locale._xml1998Uri );
  868   
  869           return (String) _prefixMap.get( prefix );
  870       }
  871   
  872       protected Map getPrefixMap()
  873       {
  874           return _prefixMap;
  875       }
  876   
  877       //
  878       //
  879       //
  880   
  881       static final class SynthNamespaceSaver extends Saver
  882       {
  883           LinkedHashMap _synthNamespaces = new LinkedHashMap();
  884   
  885           SynthNamespaceSaver ( Cur c, XmlOptions options )
  886           {
  887               super( c, options );
  888           }
  889   
  890           protected void syntheticNamespace (
  891               String prefix, String uri, boolean considerCreatingDefault )
  892           {
  893               _synthNamespaces.put( uri, considerCreatingDefault ? "" : prefix );
  894           }
  895   
  896           protected boolean emitElement (
  897               SaveCur c, ArrayList attrNames, ArrayList attrValues ) { return false; }
  898   
  899           protected void emitFinish    ( SaveCur c ) { }
  900           protected void emitText      ( SaveCur c ) { }
  901           protected void emitComment   ( SaveCur c ) { }
  902           protected void emitProcinst  ( SaveCur c ) { }
  903           protected void emitDocType   ( String docTypeName, String publicId, String systemId ) { }
  904           protected void emitStartDoc  ( SaveCur c ) { }
  905           protected void emitEndDoc    ( SaveCur c ) { }
  906       }
  907   
  908       //
  909       //
  910       //
  911   
  912       static final class TextSaver extends Saver
  913       {
  914           TextSaver ( Cur c, XmlOptions options, String encoding )
  915           {
  916               super( c, options );
  917   
  918               boolean noSaveDecl =
  919                   options != null && options.hasOption( XmlOptions.SAVE_NO_XML_DECL );
  920   
  921               if (options != null && options.hasOption(XmlOptions.SAVE_CDATA_LENGTH_THRESHOLD))
  922                   _cdataLengthThreshold = ((Integer)options.get(XmlOptions.SAVE_CDATA_LENGTH_THRESHOLD)).intValue();
  923   
  924               if (options != null && options.hasOption(XmlOptions.SAVE_CDATA_ENTITY_COUNT_THRESHOLD))
  925                   _cdataEntityCountThreshold = ((Integer)options.get(XmlOptions.SAVE_CDATA_ENTITY_COUNT_THRESHOLD)).intValue();
  926   
  927               if (options != null && options.hasOption(XmlOptions.LOAD_SAVE_CDATA_BOOKMARKS) )
  928                   _useCDataBookmarks = true;
  929   
  930               _in = _out = 0;
  931               _free = 0;
  932   
  933               assert _buf==null ||
  934                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
  935                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
  936                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
  937                   (_out==_in && _free == 0)                               // buffer full
  938                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
  939   
  940               if (encoding != null && !noSaveDecl)
  941               {
  942                   XmlDocumentProperties props = Locale.getDocProps( c, false );
  943   
  944                   String version = props == null ? null : props.getVersion();
  945   
  946                   if (version == null)
  947                       version = "1.0";
  948   
  949                   emit( "<?xml version=\"" );
  950                   emit( version );
  951                   emit( "\" encoding=\"" + encoding + "\"?>" + _newLine );
  952               }
  953           }
  954   
  955           protected boolean emitElement ( SaveCur c, ArrayList attrNames, ArrayList attrValues )
  956           {
  957               assert c.isElem();
  958   
  959               emit( '<' );
  960               emitName( c.getName(), false );
  961   
  962               if (saveNamespacesFirst())
  963                   emitNamespacesHelper();
  964   
  965               for ( int i = 0 ; i < attrNames.size() ; i++ )
  966                   emitAttrHelper( (QName) attrNames.get( i ), (String) attrValues.get( i ) );
  967   
  968               if (!saveNamespacesFirst())
  969                   emitNamespacesHelper();
  970   
  971               if (!c.hasChildren() && !c.hasText())
  972               {
  973                   emit( '/', '>' );
  974                   return true;
  975               }
  976               else
  977               {
  978                   emit( '>' );
  979                   return false;
  980               }
  981           }
  982   
  983           protected void emitFinish ( SaveCur c )
  984           {
  985               emit( '<', '/' );
  986               emitName( c.getName(), false );
  987               emit( '>' );
  988           }
  989   
  990           protected void emitXmlns ( String prefix, String uri )
  991           {
  992               assert prefix != null;
  993               assert uri != null;
  994   
  995               emit( "xmlns" );
  996   
  997               if (prefix.length() > 0)
  998               {
  999                   emit( ':' );
 1000                   emit( prefix );
 1001               }
 1002   
 1003               emit( '=', '\"' );
 1004   
 1005               // TODO - must encode uri properly
 1006   
 1007               emit( uri );
 1008               entitizeAttrValue(false);
 1009   
 1010               emit( '"' );
 1011           }
 1012   
 1013           private void emitNamespacesHelper ( )
 1014           {
 1015               for ( iterateMappings() ; hasMapping() ; nextMapping() )
 1016               {
 1017                   emit( ' ' );
 1018                   emitXmlns( mappingPrefix(), mappingUri() );
 1019               }
 1020           }
 1021   
 1022           private void emitAttrHelper ( QName attrName, String attrValue )
 1023           {
 1024               emit( ' ' );
 1025               emitName( attrName, true );
 1026               emit( '=', '\"' );
 1027               emit( attrValue );
 1028               entitizeAttrValue(true);
 1029               emit( '"' );
 1030           }
 1031   
 1032           protected void emitText ( SaveCur c )
 1033           {
 1034               assert c.isText();
 1035   
 1036               // c.isTextCData() is expensive do it only if useCDataBookmarks option is enabled
 1037               boolean forceCData = _useCDataBookmarks && c.isTextCData();
 1038   
 1039               emit( c );
 1040   
 1041               entitizeContent( forceCData );
 1042           }
 1043   
 1044           protected void emitComment ( SaveCur c )
 1045           {
 1046               assert c.isComment();
 1047   
 1048               emit( "<!--" );
 1049   
 1050               c.push();
 1051               c.next();
 1052   
 1053               emit( c );
 1054   
 1055               c.pop();
 1056   
 1057               entitizeComment();
 1058               emit( "-->" );
 1059           }
 1060   
 1061           protected void emitProcinst ( SaveCur c )
 1062           {
 1063               assert c.isProcinst();
 1064   
 1065               emit( "<?" );
 1066   
 1067               // TODO - encoding issues here?
 1068               emit( c.getName().getLocalPart() );
 1069   
 1070               c.push();
 1071   
 1072               c.next();
 1073   
 1074               if (c.isText())
 1075               {
 1076                   emit( " " );
 1077                   emit( c );
 1078                   entitizeProcinst();
 1079               }
 1080   
 1081               c.pop();
 1082   
 1083               emit( "?>" );
 1084           }
 1085   
 1086           private void emitLiteral ( String literal )
 1087           {
 1088               // TODO: systemId production http://www.w3.org/TR/REC-xml/#NT-SystemLiteral
 1089               // TODO: publicId production http://www.w3.org/TR/REC-xml/#NT-PubidLiteral
 1090               if (literal.indexOf( "\"" ) < 0)
 1091               {
 1092                   emit( '\"' );
 1093                   emit( literal );
 1094                   emit( '\"' );
 1095               }
 1096               else
 1097               {
 1098                   emit( '\'' );
 1099                   emit( literal );
 1100                   emit( '\'' );
 1101               }
 1102           }
 1103   
 1104           protected void emitDocType ( String docTypeName, String publicId, String systemId )
 1105           {
 1106               assert docTypeName != null;
 1107   
 1108               emit( "<!DOCTYPE " );
 1109               emit( docTypeName );
 1110   
 1111               if (publicId == null && systemId != null)
 1112               {
 1113                   emit( " SYSTEM " );
 1114                   emitLiteral( systemId );
 1115               }
 1116               else if (publicId != null)
 1117               {
 1118                   emit( " PUBLIC " );
 1119                   emitLiteral( publicId );
 1120                   emit( " " );
 1121                   emitLiteral( systemId );
 1122               }
 1123   
 1124               emit( ">" );
 1125               emit( _newLine );
 1126           }
 1127   
 1128           protected void emitStartDoc ( SaveCur c )
 1129           {
 1130           }
 1131   
 1132           protected void emitEndDoc ( SaveCur c )
 1133           {
 1134           }
 1135   
 1136           //
 1137           //
 1138           //
 1139   
 1140           private void emitName ( QName name, boolean needsPrefix )
 1141           {
 1142               assert name != null;
 1143   
 1144               String uri = name.getNamespaceURI();
 1145   
 1146               assert uri != null;
 1147   
 1148               if (uri.length() != 0)
 1149               {
 1150                   String prefix = name.getPrefix();
 1151                   String mappedUri = getNamespaceForPrefix( prefix );
 1152   
 1153                   if (mappedUri == null || !mappedUri.equals( uri ))
 1154                       prefix = getUriMapping( uri );
 1155   
 1156                   // Attrs need a prefix.  If I have not found one, then there must be a default
 1157                   // prefix obscuring the prefix needed for this attr.  Find it manually.
 1158   
 1159                   // NOTE - Consider keeping the currently mapped default URI separate fromn the
 1160                   // _urpMap and _prefixMap.  This way, I would not have to look it up manually
 1161                   // here
 1162   
 1163                   if (needsPrefix && prefix.length() == 0)
 1164                       prefix = getNonDefaultUriMapping( uri );
 1165   
 1166                   if (prefix.length() > 0)
 1167                   {
 1168                       emit( prefix );
 1169                       emit( ':' );
 1170                   }
 1171               }
 1172   
 1173               assert name.getLocalPart().length() > 0;
 1174   
 1175               emit( name.getLocalPart() );
 1176           }
 1177   
 1178           private void emit ( char ch )
 1179           {
 1180               assert _buf==null ||
 1181                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1182                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1183                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1184                   (_out==_in && _free == 0)                               // buffer full
 1185                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1186   
 1187               preEmit( 1 );
 1188   
 1189               _buf[ _in ] = ch;
 1190   
 1191               _in = (_in + 1) % _buf.length;
 1192   
 1193               assert _buf==null ||
 1194                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1195                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1196                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1197                   (_out==_in && _free == 0)                               // buffer full
 1198                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1199           }
 1200   
 1201           private void emit ( char ch1, char ch2 )
 1202           {
 1203               if( preEmit( 2 ) )
 1204                   return;
 1205   
 1206               _buf[ _in ] = ch1;
 1207               _in = (_in + 1) % _buf.length;
 1208   
 1209               _buf[ _in ] = ch2;
 1210               _in = (_in + 1) % _buf.length;
 1211   
 1212               assert _buf==null ||
 1213                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1214                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1215                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1216                   (_out==_in && _free == 0)                               // buffer full
 1217                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;            
 1218           }
 1219   
 1220           private void emit ( String s )
 1221           {
 1222               assert _buf==null ||
 1223                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1224                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1225                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1226                   (_out==_in && _free == 0)                               // buffer full
 1227                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1228   
 1229               int cch = s == null ? 0 : s.length();
 1230   
 1231               if (preEmit( cch ))
 1232                   return;
 1233   
 1234               int chunk;
 1235   
 1236               if (_in <= _out || cch < (chunk = _buf.length - _in))
 1237               {
 1238                   s.getChars( 0, cch, _buf, _in );
 1239                   _in += cch;
 1240               }
 1241               else
 1242               {
 1243                   s.getChars( 0, chunk, _buf, _in );
 1244                   s.getChars( chunk, cch, _buf, 0 );
 1245                   _in = (_in + cch) % _buf.length;
 1246               }
 1247   
 1248               assert _buf==null ||
 1249                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1250                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1251                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1252                   (_out==_in && _free == 0)                               // buffer full
 1253                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1254           }
 1255   
 1256           private void emit ( SaveCur c )
 1257           {
 1258               if (c.isText())
 1259               {
 1260                   Object src = c.getChars();
 1261                   int cch = c._cchSrc;
 1262   
 1263                   if (preEmit( cch ))
 1264                       return;
 1265   
 1266                   int chunk;
 1267   
 1268                   if (_in <= _out || cch < (chunk = _buf.length - _in))
 1269                   {
 1270                       CharUtil.getChars( _buf, _in, src, c._offSrc, cch );
 1271                       _in += cch;
 1272                   }
 1273                   else
 1274                   {
 1275                       CharUtil.getChars( _buf, _in, src, c._offSrc, chunk );
 1276                       CharUtil.getChars( _buf, 0, src, c._offSrc + chunk, cch - chunk );
 1277                       _in = (_in + cch) % _buf.length;
 1278                   }
 1279               }
 1280               else
 1281                   preEmit( 0 );
 1282           }
 1283   
 1284           private boolean preEmit ( int cch )
 1285           {
 1286               assert cch >= 0;
 1287               assert _buf==null ||
 1288                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1289                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1290                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1291                   (_out==_in && _free == 0)                               // buffer full
 1292                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1293   
 1294               _lastEmitCch = cch;
 1295   
 1296               if (cch == 0)
 1297                   return true;
 1298   
 1299               if (_free <= cch)
 1300                   resize( cch, -1 );
 1301   
 1302               assert cch <= _free;
 1303   
 1304               int used = getAvailable();
 1305   
 1306               // if we are about to emit and there is noting in the buffer, reset
 1307               // the buffer to be at the beginning so as to not grow it anymore
 1308               // than needed.
 1309   
 1310               if (used == 0)
 1311               {
 1312                   assert _in == _out;
 1313                   assert _free == _buf.length;
 1314                   _in = _out = 0;
 1315               }
 1316   
 1317               _lastEmitIn = _in;
 1318   
 1319               _free -= cch;
 1320   
 1321               assert _free >= 0;
 1322               assert _buf==null || _free == (_in>=_out ? _buf.length - (_in - _out) : _out - _in ) - cch : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1323               assert _buf==null ||
 1324                   (_out<_in && _free == _buf.length - ( _in - _out ) - cch) || // data in the middle, free on the edges
 1325                   (_out>_in && _free == _out - _in - cch ) ||                  // data on the edges, free in the middle
 1326                   (_out==_in && _free == _buf.length - cch) ||                 // no data, all buffer free
 1327                   (_out==_in && _free == 0)                                    // buffer full
 1328                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1329   
 1330               return false;
 1331           }
 1332   
 1333           private void entitizeContent ( boolean forceCData )
 1334           {
 1335               assert _free >=0;
 1336               
 1337               if (_lastEmitCch == 0)
 1338                   return;
 1339   
 1340               int i = _lastEmitIn;
 1341               final int n = _buf.length;
 1342   
 1343               boolean hasCharToBeReplaced = false;
 1344   
 1345               int count = 0;
 1346               char prevChar = 0;
 1347               char prevPrevChar = 0;
 1348               for ( int cch = _lastEmitCch ; cch > 0 ; cch-- )
 1349               {
 1350                   char ch = _buf[ i ];
 1351   
 1352                   if (ch == '<' || ch == '&')
 1353                       count++;
 1354                   else if (prevPrevChar == ']' && prevChar == ']' && ch == '>' )
 1355                       hasCharToBeReplaced = true;
 1356                   else if (isBadChar( ch ) || isEscapedChar( ch ))
 1357                       hasCharToBeReplaced = true;
 1358   
 1359                   if (++i == n)
 1360                       i = 0;
 1361   
 1362                   prevPrevChar = prevChar;
 1363                   prevChar = ch;
 1364               }
 1365   
 1366               if (!forceCData && count == 0 && !hasCharToBeReplaced && count<_cdataEntityCountThreshold)
 1367                   return;
 1368   
 1369               i = _lastEmitIn;
 1370   
 1371               //
 1372               // Heuristic for knowing when to save out stuff as a CDATA.
 1373               //
 1374               if (forceCData || (_lastEmitCch > _cdataLengthThreshold && count > _cdataEntityCountThreshold) )
 1375               {
 1376                   boolean lastWasBracket = _buf[ i ] == ']';
 1377   
 1378                   i = replace( i, "<![CDATA[" + _buf[ i ] );
 1379   
 1380                   boolean secondToLastWasBracket = lastWasBracket;
 1381   
 1382                   lastWasBracket = _buf[ i ] == ']';
 1383   
 1384                   if (++i == _buf.length)
 1385                       i = 0;
 1386   
 1387                   for ( int cch = _lastEmitCch ; cch > 0 ; cch-- )
 1388                   {
 1389                       char ch = _buf[ i ];
 1390   
 1391                       if (ch == '>' && secondToLastWasBracket && lastWasBracket)
 1392                           i = replace( i, "]]>><![CDATA[" );
 1393                       else if (isBadChar( ch ))
 1394                           i = replace( i, "?" );
 1395                       else
 1396                           i++;
 1397   
 1398                       secondToLastWasBracket = lastWasBracket;
 1399                       lastWasBracket = ch == ']';
 1400   
 1401                       if (i == _buf.length)
 1402                           i = 0;
 1403                   }
 1404   
 1405                   emit( "]]>" );
 1406               }
 1407               else
 1408               {
 1409                   char ch = 0, ch_1 = 0, ch_2;
 1410                   for ( int cch = _lastEmitCch ; cch > 0 ; cch-- )
 1411                   {
 1412                       ch_2 = ch_1;
 1413                       ch_1 = ch;
 1414                       ch = _buf[ i ];
 1415   
 1416                       if (ch == '<')
 1417                           i = replace( i, "&lt;" );
 1418                       else if (ch == '&')
 1419                           i = replace( i, "&amp;" );
 1420                       else if (ch == '>' && ch_1 == ']' && ch_2 == ']')
 1421                           i = replace( i, "&gt;" );
 1422                       else if (isBadChar( ch ))
 1423                           i = replace( i, "?" );
 1424                       else if (isEscapedChar( ch ))
 1425                           i = replace( i, _replaceChar.getEscapedString( ch ) );
 1426                       else
 1427                           i++;
 1428   
 1429                       if (i == _buf.length)
 1430                           i = 0;
 1431                   }
 1432               }
 1433           }
 1434   
 1435           private void entitizeAttrValue ( boolean replaceEscapedChar )
 1436           {
 1437               if (_lastEmitCch == 0)
 1438                   return;
 1439   
 1440               int i = _lastEmitIn;
 1441   
 1442               for ( int cch = _lastEmitCch ; cch > 0 ; cch-- )
 1443               {
 1444                   char ch = _buf[ i ];
 1445   
 1446                   if (ch == '<')
 1447                       i = replace( i, "&lt;" );
 1448                   else if (ch == '&')
 1449                       i = replace( i, "&amp;" );
 1450                   else if (ch == '"')
 1451                       i = replace( i, "&quot;" );
 1452                   else if (isEscapedChar( ch ))
 1453                   {
 1454                       if (replaceEscapedChar)
 1455                           i = replace( i, _replaceChar.getEscapedString( ch ) );
 1456                   }
 1457                   else
 1458                       i++;
 1459   
 1460                   if (i == _buf.length)
 1461                       i = 0;
 1462               }
 1463           }
 1464   
 1465           private void entitizeComment ( )
 1466           {
 1467               if (_lastEmitCch == 0)
 1468                   return;
 1469   
 1470               int i = _lastEmitIn;
 1471   
 1472               boolean lastWasDash = false;
 1473   
 1474               for ( int cch = _lastEmitCch ; cch > 0 ; cch-- )
 1475               {
 1476                   char ch = _buf[ i ];
 1477   
 1478                   if (isBadChar( ch ))
 1479                       i = replace( i, "?" );
 1480                   else if (ch == '-')
 1481                   {
 1482                       if (lastWasDash)
 1483                       {
 1484                           // Replace "--" with "- " to make well formed
 1485                           i = replace( i, " " );
 1486                           lastWasDash = false;
 1487                       }
 1488                       else
 1489                       {
 1490                           lastWasDash = true;
 1491                           i++;
 1492                       }
 1493                   }
 1494                   else
 1495                   {
 1496                       lastWasDash = false;
 1497                       i++;
 1498                   }
 1499   
 1500                   if (i == _buf.length)
 1501                       i = 0;
 1502               }
 1503   
 1504               // Because I have only replaced chars with single chars,
 1505               // _lastEmitIn will still be ok
 1506   
 1507               int offset = (_lastEmitIn + _lastEmitCch - 1) % _buf.length;
 1508               if (_buf[ offset ] == '-')
 1509                   i = replace( offset, " " );
 1510           }
 1511   
 1512           private void entitizeProcinst ( )
 1513           {
 1514               if (_lastEmitCch == 0)
 1515                   return;
 1516   
 1517               int i = _lastEmitIn;
 1518   
 1519               boolean lastWasQuestion = false;
 1520   
 1521               for ( int cch = _lastEmitCch ; cch > 0 ; cch-- )
 1522               {
 1523                   char ch = _buf[ i ];
 1524   
 1525                   if (isBadChar( ch ))
 1526                       i = replace( i, "?" );
 1527   
 1528                   if (ch == '>')
 1529                   {
 1530       // TODO - Had to convert to a space here ... imples not well formed XML
 1531                       if (lastWasQuestion)
 1532                           i = replace( i, " " );
 1533                       else
 1534                           i++;
 1535   
 1536                       lastWasQuestion = false;
 1537                   }
 1538                   else
 1539                   {
 1540                       lastWasQuestion = ch == '?';
 1541                       i++;
 1542                   }
 1543   
 1544                   if (i == _buf.length)
 1545                       i = 0;
 1546               }
 1547           }
 1548   
 1549           /**
 1550            * Test if a character is valid in xml character content. See
 1551            * http://www.w3.org/TR/REC-xml#NT-Char
 1552            */
 1553   
 1554           private boolean isBadChar ( char ch )
 1555           {
 1556               return ! (
 1557                   (ch >= 0x20 && ch <= 0xD7FF ) ||
 1558                   (ch >= 0xE000 && ch <= 0xFFFD) ||
 1559                   (ch >= 0x10000 && ch <= 0x10FFFF) ||
 1560                   (ch == 0x9) || (ch == 0xA) || (ch == 0xD)
 1561                   );
 1562           }
 1563   
 1564           /**
 1565            * Test if a character is to be replaced with an escaped value
 1566            */
 1567           private boolean isEscapedChar ( char ch )
 1568           {
 1569               return ( null != _replaceChar && _replaceChar.containsChar( ch ) );
 1570           }
 1571   
 1572           private int replace ( int i, String replacement )
 1573           {
 1574               assert replacement.length() > 0;
 1575   
 1576               int dCch = replacement.length() - 1;
 1577   
 1578               if (dCch == 0)
 1579               {
 1580                   _buf[ i ] = replacement.charAt( 0 );
 1581                   return i + 1;
 1582               }
 1583   
 1584               assert _free >= 0;
 1585   
 1586               if (dCch > _free)
 1587                   i = resize( dCch, i );
 1588   
 1589               assert _free >= 0;
 1590   
 1591               assert _free >= dCch;
 1592               assert getAvailable() > 0;
 1593   
 1594               int charsToCopy = dCch + 1;
 1595   
 1596               if (_out > _in && i >= _out)
 1597               {
 1598                   System.arraycopy( _buf, _out, _buf, _out - dCch, i - _out );
 1599                   _out -= dCch;
 1600                   i -= dCch;
 1601               }
 1602               else
 1603               {
 1604                   assert i < _in;
 1605                   int availableEndChunk = _buf.length - _in;
 1606                   if ( dCch <= availableEndChunk )
 1607                   {
 1608                       System.arraycopy( _buf, i, _buf, i + dCch, _in - i );
 1609                       _in = ( _in + dCch) % _buf.length;
 1610                   }
 1611                   else if ( dCch <= availableEndChunk + _in - i - 1 )
 1612                   {
 1613                       int numToCopyToStart = dCch - availableEndChunk;
 1614                       System.arraycopy( _buf, _in-numToCopyToStart, _buf, 0, numToCopyToStart );
 1615                       System.arraycopy( _buf, i+1, _buf, i+1+dCch, _in-i-1-numToCopyToStart);
 1616   
 1617                       _in = numToCopyToStart;
 1618                   }
 1619                   else
 1620                   {
 1621                       int numToCopyToStart = _in - i - 1;
 1622                       charsToCopy = availableEndChunk + _in - i;
 1623   
 1624                       System.arraycopy( _buf, _in-numToCopyToStart, _buf, dCch-charsToCopy+1, numToCopyToStart );
 1625                       replacement.getChars( charsToCopy, dCch + 1, _buf, 0);
 1626   
 1627                       _in = numToCopyToStart + dCch - charsToCopy + 1;
 1628                   }
 1629               }
 1630   
 1631               replacement.getChars( 0, charsToCopy, _buf, i );
 1632   
 1633               _free -= dCch;
 1634   
 1635               assert _free >= 0;
 1636               assert _buf==null ||
 1637                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1638                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1639                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1640                   (_out==_in && _free == 0)                               // buffer full
 1641                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1642   
 1643               return (i + dCch + 1) % _buf.length;
 1644           }
 1645           //
 1646           //
 1647           //
 1648   
 1649           private int ensure ( int cch )
 1650           {
 1651               // Even if we're asked to ensure nothing, still try to ensure
 1652               // atleast one character so we can determine if we're at the
 1653               // end of the stream.
 1654   
 1655               if (cch <= 0)
 1656                   cch = 1;
 1657   
 1658               int available = getAvailable();
 1659   
 1660               for ( ; available < cch ; available = getAvailable() )
 1661                   if (!process())
 1662                       break;
 1663   
 1664               assert available == getAvailable();
 1665   
 1666   //            if (available == 0)
 1667   //                return 0;
 1668   
 1669               return available;
 1670           }
 1671   
 1672           int getAvailable ( )
 1673           {
 1674               return _buf == null ? 0 : _buf.length - _free;
 1675           }
 1676   
 1677           private int resize ( int cch, int i )
 1678           {
 1679               assert _free >= 0;
 1680               assert cch > 0;
 1681               assert cch >= _free;
 1682               assert _buf==null ||
 1683                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1684                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1685                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1686                   (_out==_in && _free == 0)                               // buffer full
 1687                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1688   
 1689               int newLen = _buf == null ? _initialBufSize : _buf.length * 2;
 1690               int used = getAvailable();
 1691   
 1692               while ( newLen - used < cch )
 1693                   newLen *= 2;
 1694   
 1695               char[] newBuf = new char [ newLen ];
 1696   
 1697               if (used > 0)
 1698               {
 1699                   if (_in > _out)
 1700                   {
 1701                       assert i == -1 || (i >= _out && i < _in);
 1702                       System.arraycopy( _buf, _out, newBuf, 0, used );
 1703                       i -= _out;
 1704                   }
 1705                   else
 1706                   {
 1707                       assert i == -1 || (i >= _out || i < _in);
 1708                       System.arraycopy( _buf, _out, newBuf, 0, used - _in );
 1709                       System.arraycopy( _buf, 0, newBuf, used - _in, _in );
 1710                       i = i >= _out ? i - _out : i + _out;
 1711                   }
 1712   
 1713                   _out = 0;
 1714                   _in = used;
 1715                   _free += newBuf.length - _buf.length;
 1716               }
 1717               else
 1718               {
 1719                   _free = newBuf.length;
 1720                   assert _in == 0 && _out == 0;
 1721                   assert i == -1;
 1722               }
 1723   
 1724               _buf = newBuf;
 1725   
 1726               assert _free >= 0;
 1727               assert _buf==null ||
 1728                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1729                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1730                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1731                   (_out==_in && _free == 0)                               // buffer full
 1732                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1733   
 1734               return i;
 1735           }
 1736   
 1737           public int read ( )
 1738           {
 1739               if (ensure( 1 ) == 0)
 1740                   return -1;
 1741   
 1742               assert getAvailable() > 0;
 1743   
 1744               int ch = _buf[ _out ];
 1745   
 1746               _out = (_out + 1) % _buf.length;
 1747               _free++;
 1748   
 1749               assert _buf==null ||
 1750                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1751                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1752                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1753                   (_out==_in && _free == 0)                               // buffer full
 1754                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1755   
 1756               return ch;
 1757           }
 1758   
 1759           public int read ( char[] cbuf, int off, int len )
 1760           {
 1761               // Check for end of stream even if there is no way to return
 1762               // characters because the Reader doc says to return -1 at end of
 1763               // stream.
 1764   
 1765               int n;
 1766   
 1767               if ((n = ensure( len )) == 0)
 1768                   return -1;
 1769   
 1770               if (cbuf == null || len <= 0)
 1771                   return 0;
 1772   
 1773               if (n < len)
 1774                   len = n;
 1775   
 1776               if (_out < _in)
 1777               {
 1778                   System.arraycopy( _buf, _out, cbuf, off, len );
 1779               }
 1780               else
 1781               {
 1782                   int chunk = _buf.length - _out;
 1783   
 1784                   if (chunk >= len)
 1785                       System.arraycopy( _buf, _out, cbuf, off, len );
 1786                   else
 1787                   {
 1788                       System.arraycopy( _buf, _out, cbuf, off, chunk );
 1789                       System.arraycopy( _buf, 0, cbuf, off + chunk, len - chunk );
 1790                   }
 1791               }
 1792   
 1793               _out = (_out + len) % _buf.length;
 1794               _free += len;
 1795   
 1796               assert _buf==null ||
 1797                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1798                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1799                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1800                   (_out==_in && _free == 0)                               // buffer full
 1801                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1802   
 1803               assert _free >= 0;
 1804   
 1805               return len;
 1806           }
 1807   
 1808           public int write ( Writer writer, int cchMin )
 1809           {
 1810               while ( getAvailable() < cchMin)
 1811               {
 1812                   if (!process())
 1813                       break;
 1814               }
 1815   
 1816               int charsAvailable = getAvailable();
 1817   
 1818               if (charsAvailable > 0)
 1819               {
 1820                   // I don't want to deal with the circular cases
 1821   
 1822                   assert _out == 0;
 1823                   assert _in >= _out : "_in:" + _in + " < _out:" + _out;
 1824                   assert _free == _buf.length - _in;
 1825   
 1826                   try
 1827                   {
 1828   //System.out.println("-------------\nWriting in corverter: TextSaver.write():1703  " + charsAvailable + " chars\n" + new String(_buf, 0, charsAvailable));
 1829                       writer.write( _buf, 0, charsAvailable );
 1830                       writer.flush();
 1831                   }
 1832                   catch ( IOException e )
 1833                   {
 1834                       throw new RuntimeException( e );
 1835                   }
 1836   
 1837                   _free += charsAvailable;
 1838   
 1839                   assert _free >= 0;
 1840   
 1841                   _in = 0;
 1842               }
 1843               assert _buf==null ||
 1844                   (_out<_in && _free == _buf.length - ( _in - _out ) ) || // data in the middle, free on the edges
 1845                   (_out>_in && _free == _out - _in ) ||                   // data on the edges, free in the middle
 1846                   (_out==_in && _free == _buf.length) ||                  // no data, all buffer free
 1847                   (_out==_in && _free == 0)                               // buffer full
 1848                   : "_buf.length:" + _buf.length + " _in:" + _in + " _out:" + _out + " _free:" + _free;
 1849   
 1850               return charsAvailable;
 1851           }
 1852   
 1853           public String saveToString ( )
 1854           {
 1855               // We're gonna build a string.  Instead of using StringBuffer, may
 1856               // as well use my buffer here.  Fill the whole sucker up and
 1857               // create a String!
 1858   
 1859               while ( process() )
 1860                   ;
 1861   
 1862               assert _out == 0;
 1863   
 1864               int available = getAvailable();
 1865   
 1866               return available == 0 ? "" : new String( _buf, _out, available );
 1867           }
 1868   
 1869           //
 1870           //
 1871           //
 1872   
 1873           private static final int _initialBufSize = 4096;
 1874           private int _cdataLengthThreshold = 32;
 1875           private int _cdataEntityCountThreshold = 5;
 1876           private boolean _useCDataBookmarks = false;
 1877   
 1878           private int _lastEmitIn;
 1879           private int _lastEmitCch;
 1880   
 1881           private int    _free;
 1882           private int    _in;
 1883           private int    _out;
 1884           private char[] _buf;
 1885           /*
 1886           _buf is a circular buffer, useful data is before _in up to _out, there are 2 posible configurations:
 1887           1: _in<=_out  |data|_in  empty  _out|data|
 1888           2: _out<_in   |empty _out|data|_in  empty|
 1889           _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 ) ;
 1890            */
 1891       }
 1892   
 1893       static final class OptimizedForSpeedSaver
 1894           extends Saver
 1895       {
 1896           Writer _w;
 1897           private char[] _buf = new char[1024];
 1898   
 1899   
 1900           static private class SaverIOException
 1901               extends RuntimeException
 1902           {
 1903               SaverIOException(IOException e)
 1904               {
 1905                   super(e);
 1906               }
 1907           }
 1908   
 1909   
 1910           OptimizedForSpeedSaver(Cur cur, Writer writer)
 1911           {
 1912               super(cur, XmlOptions.maskNull(null));
 1913               _w = writer;
 1914           }
 1915   
 1916           static void save(Cur cur, Writer writer)
 1917               throws IOException
 1918           {
 1919               try
 1920               {
 1921                   Saver saver = new OptimizedForSpeedSaver(cur, writer);
 1922                   while(saver.process())
 1923                   {}
 1924               }
 1925               catch (SaverIOException e)
 1926               {
 1927                   throw (IOException)e.getCause();
 1928               }
 1929           }
 1930   
 1931           private void emit(String s)
 1932           {
 1933               try
 1934               {
 1935                   _w.write(s);
 1936               }
 1937               catch (IOException e)
 1938               {
 1939                   throw new SaverIOException(e);
 1940               }
 1941           }
 1942   
 1943           private void emit(char c)
 1944           {
 1945               try
 1946               {
 1947                   _buf[0] = c;
 1948                   _w.write(_buf, 0, 1);
 1949               }
 1950               catch (IOException e)
 1951               {
 1952                   throw new SaverIOException(e);
 1953               }
 1954           }
 1955   
 1956           private void emit(char c1, char c2)
 1957           {
 1958               try
 1959               {
 1960                   _buf[0] = c1;
 1961                   _buf[1] = c2;
 1962                   _w.write(_buf, 0 , 2);
 1963               }
 1964               catch (IOException e)
 1965               {
 1966                   throw new SaverIOException(e);
 1967               }
 1968           }
 1969   
 1970           private void emit(char[] buf, int start, int len)
 1971           {
 1972               try
 1973               {
 1974                   _w.write(buf, start, len);
 1975               }
 1976               catch (IOException e)
 1977               {
 1978                   throw new SaverIOException(e);
 1979               }
 1980           }
 1981   
 1982           protected boolean emitElement ( SaveCur c, ArrayList attrNames, ArrayList attrValues )
 1983           {
 1984               assert c.isElem();
 1985   
 1986               emit( '<' );
 1987               emitName( c.getName(), false );
 1988   
 1989               for ( int i = 0 ; i < attrNames.size() ; i++ )
 1990                   emitAttrHelper( (QName) attrNames.get( i ), (String) attrValues.get( i ) );
 1991   
 1992               if (!saveNamespacesFirst())
 1993                   emitNamespacesHelper();
 1994   
 1995               if (!c.hasChildren() && !c.hasText())
 1996               {
 1997                   emit( '/', '>' );
 1998                   return true;
 1999               }
 2000               else
 2001               {
 2002                   emit( '>' );
 2003                   return false;
 2004               }
 2005           }
 2006   
 2007           protected void emitFinish ( SaveCur c )
 2008           {
 2009               emit( '<', '/' );
 2010               emitName( c.getName(), false );
 2011               emit( '>' );
 2012           }
 2013   
 2014           protected void emitXmlns ( String prefix, String uri )
 2015           {
 2016               assert prefix != null;
 2017               assert uri != null;
 2018   
 2019               emit( "xmlns" );
 2020   
 2021               if (prefix.length() > 0)
 2022               {
 2023                   emit( ':' );
 2024                   emit( prefix );
 2025               }
 2026   
 2027               emit( '=', '\"' );
 2028   
 2029               // TODO - must encode uri properly
 2030               emitAttrValue(uri);
 2031   
 2032               emit( '"' );
 2033           }
 2034   
 2035           private void emitNamespacesHelper ( )
 2036           {
 2037               for ( iterateMappings() ; hasMapping() ; nextMapping() )
 2038               {
 2039                   emit( ' ' );
 2040                   emitXmlns( mappingPrefix(), mappingUri() );
 2041               }
 2042           }
 2043   
 2044           private void emitAttrHelper ( QName attrName, String attrValue )
 2045           {
 2046               emit( ' ' );
 2047               emitName( attrName, true );
 2048               emit( '=', '\"' );
 2049               emitAttrValue(attrValue);
 2050   
 2051               emit( '"' );
 2052           }
 2053   
 2054           protected void emitComment ( SaveCur c )
 2055           {
 2056               assert c.isComment();
 2057   
 2058               emit( "<!--" );
 2059   
 2060               c.push();
 2061               c.next();
 2062   
 2063               emitCommentText( c );
 2064   
 2065               c.pop();
 2066   
 2067               emit( "-->" );
 2068           }
 2069   
 2070           protected void emitProcinst ( SaveCur c )
 2071           {
 2072               assert c.isProcinst();
 2073   
 2074               emit( "<?" );
 2075   
 2076               // TODO - encoding issues here?
 2077               emit( c.getName().getLocalPart() );
 2078   
 2079               c.push();
 2080   
 2081               c.next();
 2082   
 2083               if (c.isText())
 2084               {
 2085                   emit( ' ' );
 2086                   emitPiText( c );
 2087               }
 2088   
 2089               c.pop();
 2090   
 2091               emit( "?>" );
 2092           }
 2093   
 2094           protected void emitDocType ( String docTypeName, String publicId, String systemId )
 2095           {
 2096               assert docTypeName != null;
 2097   
 2098               emit( "<!DOCTYPE " );
 2099               emit( docTypeName );
 2100   
 2101               if (publicId == null && systemId != null)
 2102               {
 2103                   emit( " SYSTEM " );
 2104                   emitLiteral( systemId );
 2105               }
 2106               else if (publicId != null)
 2107               {
 2108                   emit( " PUBLIC " );
 2109                   emitLiteral( publicId );
 2110                   emit(' ');
 2111                   emitLiteral( systemId );
 2112               }
 2113   
 2114               emit( '>' );
 2115               emit( _newLine );
 2116           }
 2117   
 2118           protected void emitStartDoc ( SaveCur c )
 2119           {
 2120           }
 2121   
 2122           protected void emitEndDoc ( SaveCur c )
 2123           {
 2124           }
 2125   
 2126           //
 2127           //
 2128           //
 2129   
 2130           private void emitName ( QName name, boolean needsPrefix )
 2131           {
 2132               assert name != null;
 2133   
 2134               String uri = name.getNamespaceURI();
 2135   
 2136               assert uri != null;
 2137   
 2138               if (uri.length() != 0)
 2139               {
 2140                   String prefix = name.getPrefix();
 2141                   String mappedUri = getNamespaceForPrefix( prefix );
 2142   
 2143                   if (mappedUri == null || !mappedUri.equals( uri ))
 2144                       prefix = getUriMapping( uri );
 2145   
 2146                   // Attrs need a prefix.  If I have not found one, then there must be a default
 2147                   // prefix obscuring the prefix needed for this attr.  Find it manually.
 2148   
 2149                   // NOTE - Consider keeping the currently mapped default URI separate fromn the
 2150                   // _urpMap and _prefixMap.  This way, I would not have to look it up manually
 2151                   // here
 2152   
 2153                   if (needsPrefix && prefix.length() == 0)
 2154                       prefix = getNonDefaultUriMapping( uri );
 2155   
 2156                   if (prefix.length() > 0)
 2157                   {
 2158                       emit( prefix );
 2159                       emit( ':' );
 2160                   }
 2161               }
 2162   
 2163               assert name.getLocalPart().length() > 0;
 2164   
 2165               emit( name.getLocalPart() );
 2166           }
 2167   
 2168           private void emitAttrValue ( CharSequence attVal)
 2169           {
 2170               int len = attVal.length();
 2171   
 2172               for ( int i = 0; i<len ; i++ )
 2173               {
 2174                   char ch = attVal.charAt(i);
 2175   
 2176                   if (ch == '<')
 2177                       emit( "&lt;" );
 2178                   else if (ch == '&')
 2179                       emit( "&amp;" );
 2180                   else if (ch == '"')
 2181                       emit( "&quot;" );
 2182                   else
 2183                       emit(ch);
 2184               }
 2185           }
 2186   
 2187           /**
 2188            * Test if a character is valid in xml character content. See
 2189            * http://www.w3.org/TR/REC-xml#NT-Char
 2190            */
 2191           private boolean isBadChar ( char ch )
 2192           {
 2193               return ! (
 2194                   (ch >= 0x20 && ch <= 0xD7FF ) ||
 2195                   (ch >= 0xE000 && ch <= 0xFFFD) ||
 2196                   (ch >= 0x10000 && ch <= 0x10FFFF) ||
 2197                   (ch == 0x9) || (ch == 0xA) || (ch == 0xD)
 2198                   );
 2199           }
 2200   
 2201           private void emitLiteral ( String literal )
 2202           {
 2203               // TODO: systemId production http://www.w3.org/TR/REC-xml/#NT-SystemLiteral
 2204               // TODO: publicId production http://www.w3.org/TR/REC-xml/#NT-PubidLiteral
 2205               if (literal.indexOf( "\"" ) < 0)
 2206               {
 2207                   emit( '\"' );
 2208                   emit( literal );
 2209                   emit( '\"' );
 2210               }
 2211               else
 2212               {
 2213                   emit( '\'' );
 2214                   emit( literal );
 2215                   emit( '\'' );
 2216               }
 2217           }
 2218   
 2219           protected void emitText ( SaveCur c )
 2220           {
 2221               assert c.isText();
 2222   
 2223               Object src = c.getChars();
 2224               int cch = c._cchSrc;
 2225               int off = c._offSrc;
 2226               int index = 0;
 2227               int indexLimit = 0;
 2228               while( index<cch )
 2229               {
 2230                   indexLimit = index + 512 > cch ? cch : index + 512;
 2231                   CharUtil.getChars( _buf, 0, src, off+index, indexLimit-index );
 2232                   entitizeAndWriteText(indexLimit-index);
 2233                   index = indexLimit;
 2234               }
 2235           }
 2236   
 2237           protected void emitPiText ( SaveCur c )
 2238           {
 2239               assert c.isText();
 2240   
 2241               Object src = c.getChars();
 2242               int cch = c._cchSrc;
 2243               int off = c._offSrc;
 2244               int index = 0;
 2245               int indexLimit = 0;
 2246               while( index<cch )
 2247               {
 2248                   indexLimit = index + 512 > cch ? cch : 512;
 2249                   CharUtil.getChars( _buf, 0, src, off+index, indexLimit );
 2250                   entitizeAndWritePIText(indexLimit-index);
 2251                   index = indexLimit;
 2252               }
 2253           }
 2254   
 2255           protected void emitCommentText ( SaveCur c )
 2256           {
 2257               assert c.isText();
 2258   
 2259               Object src = c.getChars();
 2260               int cch = c._cchSrc;
 2261               int off = c._offSrc;
 2262               int index = 0;
 2263               int indexLimit = 0;
 2264               while( index<cch )
 2265               {
 2266                   indexLimit = index + 512 > cch ? cch : 512;
 2267                   CharUtil.getChars( _buf, 0, src, off+index, indexLimit );
 2268                   entitizeAndWriteCommentText(indexLimit-index);
 2269                   index = indexLimit;
 2270               }
 2271           }
 2272   
 2273           private void entitizeAndWriteText(int bufLimit)
 2274           {
 2275               int index = 0;
 2276               for (int i = 0; i < bufLimit; i++)
 2277               {
 2278                   char c = _buf[i];
 2279                   switch(c)
 2280                   {
 2281                   case '<':
 2282                       emit(_buf, index, i-index);
 2283                       emit("&lt;");
 2284                       index = i+1;
 2285                       break;
 2286                   case '&':
 2287                       emit(_buf, index, i-index);
 2288                       emit("&amp;");
 2289                       index = i+1;
 2290                       break;
 2291                   }
 2292               }
 2293               emit(_buf, index, bufLimit-index);
 2294           }
 2295   
 2296           private void entitizeAndWriteCommentText ( int bufLimit )
 2297           {
 2298               boolean lastWasDash = false;
 2299   
 2300               for ( int i=0 ; i<bufLimit ; i++ )
 2301               {
 2302                   char ch = _buf[ i ];
 2303   
 2304                   if (isBadChar( ch ))
 2305                       _buf[i] = '?';
 2306                   else if (ch == '-')
 2307                   {
 2308                       if (lastWasDash)
 2309                       {
 2310                           // Replace "--" with "- " to make well formed
 2311                           _buf[i] = ' ';
 2312                           lastWasDash = false;
 2313                       }
 2314                       else
 2315                       {
 2316                           lastWasDash = true;
 2317                       }
 2318                   }
 2319                   else
 2320                   {
 2321                       lastWasDash = false;
 2322                   }
 2323   
 2324                   if (i == _buf.length)
 2325                       i = 0;
 2326               }
 2327   
 2328               if (_buf[ bufLimit-1 ] == '-')
 2329                   _buf[ bufLimit-1 ] = ' ';
 2330   
 2331               emit(_buf, 0, bufLimit);
 2332           }
 2333   
 2334           private void entitizeAndWritePIText(int bufLimit)
 2335           {
 2336               boolean lastWasQuestion = false;
 2337   
 2338               for ( int i=0 ; i<bufLimit ; i++ )
 2339               {
 2340                   char ch = _buf[ i ];
 2341   
 2342                   if (isBadChar( ch ))
 2343                   {
 2344                       _buf[i] = '?';
 2345                       ch = '?';
 2346                   }
 2347   
 2348                   if (ch == '>')
 2349                   {
 2350                       // Had to convert to a space here ... imples not well formed XML
 2351                       if (lastWasQuestion)
 2352                           _buf[i] = ' ';
 2353   
 2354                       lastWasQuestion = false;
 2355                   }
 2356                   else
 2357                   {
 2358                       lastWasQuestion = ch == '?';
 2359                   }
 2360               }
 2361               emit(_buf, 0, bufLimit);
 2362           }
 2363       }
 2364   
 2365       static final class TextReader extends Reader
 2366       {
 2367           TextReader ( Cur c, XmlOptions options )
 2368           {
 2369               _textSaver = new TextSaver( c, options, null );
 2370               _locale = c._locale;
 2371               _closed = false;
 2372           }
 2373   
 2374           public void close ( ) throws IOException { _closed = true; }
 2375   
 2376           public boolean ready ( ) throws IOException { return !_closed; }
 2377   
 2378           public int read ( ) throws IOException
 2379           {
 2380               checkClosed();
 2381   
 2382               if (_locale.noSync())         { _locale.enter(); try { return _textSaver.read(); } finally { _locale.exit(); } }
 2383               else synchronized ( _locale ) { _locale.enter(); try { return _textSaver.read(); } finally { _locale.exit(); } }
 2384           }
 2385   
 2386           public int read ( char[] cbuf ) throws IOException
 2387           {
 2388               checkClosed();
 2389   
 2390               if (_locale.noSync())         { _locale.enter(); try { return _textSaver.read( cbuf, 0, cbuf == null ? 0 : cbuf.length ); } finally { _locale.exit(); } }
 2391               else synchronized ( _locale ) { _locale.enter(); try { return _textSaver.read( cbuf, 0, cbuf == null ? 0 : cbuf.length ); } finally { _locale.exit(); } }
 2392           }
 2393   
 2394           public int read ( char[] cbuf, int off, int len ) throws IOException
 2395           {
 2396               checkClosed();
 2397   
 2398               if (_locale.noSync())         { _locale.enter(); try { return _textSaver.read( cbuf, off, len ); } finally { _locale.exit(); } }
 2399               else synchronized ( _locale ) { _locale.enter(); try { return _textSaver.read( cbuf, off, len ); } finally { _locale.exit(); } }
 2400           }
 2401   
 2402           private void checkClosed ( ) throws IOException
 2403           {
 2404               if (_closed)
 2405                   throw new IOException( "Reader has been closed" );
 2406           }
 2407   
 2408           private Locale    _locale;
 2409           private TextSaver _textSaver;
 2410           private boolean   _closed;
 2411       }
 2412   
 2413       static final class InputStreamSaver extends InputStream
 2414       {
 2415           InputStreamSaver ( Cur c, XmlOptions options )
 2416           {
 2417               _locale = c._locale;
 2418   
 2419               _closed = false;
 2420   
 2421               assert _locale.entered();
 2422   
 2423               options = XmlOptions.maskNull( options );
 2424   
 2425               _outStreamImpl = new OutputStreamImpl();
 2426   
 2427               String encoding = null;
 2428   
 2429               XmlDocumentProperties props = Locale.getDocProps( c, false );
 2430   
 2431               if (props != null && props.getEncoding() != null)
 2432                   encoding = EncodingMap.getIANA2JavaMapping( props.getEncoding() );
 2433   
 2434               if (options.hasOption( XmlOptions.CHARACTER_ENCODING ))
 2435                   encoding = (String) options.get( XmlOptions.CHARACTER_ENCODING );
 2436   
 2437               if (encoding != null)
 2438               {
 2439                   String ianaEncoding = EncodingMap.getJava2IANAMapping( encoding );
 2440   
 2441                   if (ianaEncoding != null)
 2442                       encoding = ianaEncoding;
 2443               }
 2444   
 2445               if (encoding == null)
 2446                   encoding = EncodingMap.getJava2IANAMapping( "UTF8" );
 2447   
 2448               String javaEncoding = EncodingMap.getIANA2JavaMapping( encoding );
 2449   
 2450               if (javaEncoding == null)
 2451                   throw new IllegalStateException( "Unknown encoding: " + encoding );
 2452   
 2453               try
 2454               {
 2455                   _converter = new OutputStreamWriter( _outStreamImpl, javaEncoding );
 2456               }
 2457               catch ( UnsupportedEncodingException e )
 2458               {
 2459                   throw new RuntimeException( e );
 2460               }
 2461   
 2462               _textSaver = new TextSaver( c, options, encoding );
 2463           }
 2464   
 2465           public void close ( ) throws IOException
 2466           {
 2467               _closed = true;
 2468           }
 2469   
 2470           private void checkClosed ( ) throws IOException
 2471           {
 2472               if (_closed)
 2473                   throw new IOException( "Stream closed" );
 2474           }
 2475   
 2476           // Having the gateway here is kinda slow for the single character case.  It may be possible
 2477           // to only enter the gate when there are no chars in the buffer.
 2478   
 2479           public int read ( ) throws IOException
 2480           {
 2481               checkClosed();
 2482   
 2483               if (_locale.noSync())         { _locale.enter(); try { return _outStreamImpl.read(); } finally { _locale.exit(); } }
 2484               else synchronized ( _locale ) { _locale.enter(); try { return _outStreamImpl.read(); } finally { _locale.exit(); } }
 2485           }
 2486   
 2487           public int read ( byte[] bbuf, int off, int len ) throws IOException
 2488           {
 2489               checkClosed();
 2490   
 2491               if (bbuf == null)
 2492                   throw new NullPointerException( "buf to read into is null" );
 2493   
 2494               if (off < 0 || off > bbuf.length)
 2495                   throw new IndexOutOfBoundsException( "Offset is not within buf" );
 2496   
 2497               if (_locale.noSync())         { _locale.enter(); try { return _outStreamImpl.read( bbuf, off, len ); } finally { _locale.exit(); } }
 2498               else synchronized ( _locale ) { _locale.enter(); try { return _outStreamImpl.read( bbuf, off, len ); } finally { _locale.exit(); } }
 2499           }
 2500   
 2501           private int ensure ( int cbyte )
 2502           {
 2503               // Even if we're asked to ensure nothing, still try to ensure
 2504               // atleast one byte so we can determine if we're at the
 2505               // end of the stream.
 2506   
 2507               if (cbyte <= 0)
 2508                   cbyte = 1;
 2509   
 2510               int bytesAvailable = _outStreamImpl.getAvailable();
 2511   
 2512               for ( ; bytesAvailable < cbyte ;
 2513                     bytesAvailable = _outStreamImpl.getAvailable() )
 2514               {
 2515                   if (_textSaver.write( _converter, 2048 ) < 2048)
 2516                       break;
 2517               }
 2518   
 2519               bytesAvailable = _outStreamImpl.getAvailable();
 2520   
 2521   //            if (bytesAvailable == 0)
 2522   //                return 0;
 2523   
 2524               return bytesAvailable;
 2525           }
 2526   
 2527           public int available()
 2528               throws IOException
 2529           {
 2530               if (_locale.noSync())
 2531                   { _locale.enter(); try {
 2532                       return ensure(1024);
 2533                   } finally { _locale.exit(); } }
 2534               else
 2535                   synchronized ( _locale )
 2536                   { _locale.enter(); try { return ensure(1024); } finally { _locale.exit(); } }
 2537           }
 2538   
 2539           private final class OutputStreamImpl extends OutputStream
 2540           {
 2541               int read ( )
 2542               {
 2543                   if (InputStreamSaver.this.ensure( 1 ) == 0)
 2544                       return -1;
 2545   
 2546                   assert getAvailable() > 0;
 2547   
 2548                   int bite = _buf[ _out ];
 2549   
 2550                   _out = (_out + 1) % _buf.length;
 2551                   _free++;
 2552   
 2553                   return bite;
 2554               }
 2555   
 2556               int read ( byte[] bbuf, int off, int len )
 2557               {
 2558                   // Check for end of stream even if there is no way to return
 2559                   // characters because the Reader doc says to return -1 at end of
 2560                   // stream.
 2561   
 2562                   int n;
 2563   
 2564                   if ((n = ensure( len )) == 0)
 2565                       return -1;
 2566   
 2567                   if (bbuf == null || len <= 0)
 2568                       return 0;
 2569   
 2570                   if (n < len)
 2571                       len = n;
 2572   
 2573                   if (_out < _in)
 2574                   {
 2575                       System.arraycopy( _buf, _out, bbuf, off, len );
 2576                   }
 2577                   else
 2578                   {
 2579                       int chunk = _buf.length - _out;
 2580   
 2581                       if (chunk >= len)
 2582                           System.arraycopy( _buf, _out, bbuf, off, len );
 2583                       else
 2584                       {
 2585                           System.arraycopy( _buf, _out, bbuf, off, chunk );
 2586   
 2587                           System.arraycopy(
 2588                               _buf, 0, bbuf, off + chunk, len - chunk );
 2589                       }
 2590                   }
 2591   //System.out.println("------------------------\nRead out of queue: Saver:2440 InputStreamSaver.read() bbuf   " + len + " bytes :\n" + new String(bbuf, off, len));
 2592                   _out = (_out + len) % _buf.length;
 2593                   _free += len;
 2594   
 2595                   return len;
 2596               }
 2597   
 2598               int getAvailable ( )
 2599               {
 2600                   return _buf == null ? 0 : _buf.length - _free;
 2601               }
 2602   
 2603               public void write ( int bite )
 2604               {
 2605                   if (_free == 0)
 2606                       resize( 1 );
 2607   
 2608                   assert _free > 0;
 2609   
 2610                   _buf[ _in ] = (byte) bite;
 2611   
 2612                   _in = (_in + 1) % _buf.length;
 2613                   _free--;
 2614               }
 2615   
 2616               public void write ( byte[] buf, int off, int cbyte )
 2617               {
 2618                   assert cbyte >= 0;
 2619   //System.out.println("---------\nAfter converter, write in queue: OutputStreamImpl.write():Saver:2469  " + cbyte + " bytes \n" + new String(buf, off, cbyte));
 2620                   if (cbyte == 0)
 2621                       return;
 2622   
 2623                   if (_free < cbyte)
 2624                       resize( cbyte );
 2625   
 2626                   if (_in == _out)
 2627                   {
 2628                       assert getAvailable() == 0;
 2629                       assert _free == _buf.length - getAvailable();
 2630                       _in = _out = 0;
 2631                   }
 2632   
 2633                   int chunk;
 2634   
 2635                   if (_in <= _out || cbyte < (chunk = _buf.length - _in))
 2636                   {
 2637                       System.arraycopy( buf, off, _buf, _in, cbyte );
 2638                       _in += cbyte;
 2639                   }
 2640                   else
 2641                   {
 2642                       System.arraycopy( buf, off, _buf, _in, chunk );
 2643   
 2644                       System.arraycopy(
 2645                           buf, off + chunk, _buf, 0, cbyte - chunk );
 2646   
 2647                       _in = (_in + cbyte) % _buf.length;
 2648                   }
 2649   
 2650                   _free -= cbyte;
 2651               }
 2652   
 2653               void resize ( int cbyte )
 2654               {
 2655                   assert cbyte > _free;
 2656   
 2657                   int newLen = _buf == null ? _initialBufSize : _buf.length * 2;
 2658                   int used = getAvailable();
 2659   
 2660                   while ( newLen - used < cbyte )
 2661                       newLen *= 2;
 2662   
 2663                   byte[] newBuf = new byte [ newLen ];
 2664   
 2665                   if (used > 0)
 2666                   {
 2667                       if (_out == _in)
 2668                           System.arraycopy( _buf, 0, newBuf, 0, used );
 2669                       else if (_in > _out)
 2670                           System.arraycopy( _buf, _out, newBuf, 0, used );
 2671                       else
 2672                       {
 2673                           System.arraycopy(
 2674                               _buf, _out, newBuf, 0, used - _in );
 2675   
 2676                           System.arraycopy(
 2677                               _buf, 0, newBuf, used - _in, _in );
 2678                       }
 2679   
 2680                       _out = 0;
 2681                       _in = used;
 2682                       _free += newBuf.length - _buf.length;
 2683                   }
 2684                   else
 2685                   {
 2686                       _free = newBuf.length;
 2687                       assert _in == _out;
 2688                   }
 2689   
 2690                   _buf = newBuf;
 2691               }
 2692   
 2693               private static final int _initialBufSize = 4096;
 2694   
 2695               int    _free;
 2696               int    _in;
 2697               int    _out;
 2698               byte[] _buf;
 2699           }
 2700   
 2701           private Locale             _locale;
 2702           private boolean            _closed;
 2703           private OutputStreamImpl   _outStreamImpl;
 2704           private TextSaver          _textSaver;
 2705           private OutputStreamWriter _converter;
 2706       }
 2707   
 2708       static final class XmlInputStreamSaver extends Saver
 2709       {
 2710           XmlInputStreamSaver ( Cur c, XmlOptions options )
 2711           {
 2712               super( c, options );
 2713           }
 2714   
 2715           protected boolean emitElement(SaveCur c, ArrayList attrNames, ArrayList attrValues)
 2716           {
 2717               assert c.isElem();
 2718   
 2719               for ( iterateMappings() ; hasMapping() ; nextMapping() )
 2720               {
 2721                   enqueue( new StartPrefixMappingImpl( mappingPrefix(), mappingUri() ) );
 2722               }
 2723   
 2724               StartElementImpl.AttributeImpl lastAttr = null;
 2725               StartElementImpl.AttributeImpl attributes = null;
 2726               StartElementImpl.AttributeImpl namespaces = null;
 2727   
 2728               for ( int i=0; i<attrNames.size(); i++ )
 2729               {
 2730                   XMLName attXMLName = computeName((QName)attrNames.get(i), this, true);
 2731                   StartElementImpl.AttributeImpl attr =
 2732                       new StartElementImpl.NormalAttributeImpl(attXMLName, (String)attrValues.get(i) );
 2733   
 2734                   if (attributes == null)
 2735                       attributes = attr;
 2736                   else
 2737                       lastAttr._next = attr;
 2738   
 2739                   lastAttr = attr;
 2740               }
 2741   
 2742               lastAttr = null;
 2743   
 2744               for ( iterateMappings() ; hasMapping() ; nextMapping() )
 2745               {
 2746                   String prefix = mappingPrefix();
 2747                   String uri = mappingUri();
 2748   
 2749                   StartElementImpl.AttributeImpl attr =
 2750                       new StartElementImpl.XmlnsAttributeImpl(prefix, uri);
 2751   
 2752                   if (namespaces == null)
 2753                       namespaces = attr;
 2754                   else
 2755                       lastAttr._next = attr;
 2756   
 2757                   lastAttr = attr;
 2758               }
 2759   
 2760   
 2761               QName name = c.getName();
 2762               enqueue( new StartElementImpl( computeName(name, this, false), attributes, namespaces, getPrefixMap() ) );
 2763   
 2764               return false;  // still need to be called on end element
 2765           }
 2766   
 2767           protected void emitFinish(SaveCur c)
 2768           {
 2769               if (c.isRoot())
 2770                   enqueue( new EndDocumentImpl(  ) );
 2771               else
 2772               {
 2773                   XMLName xmlName = computeName(c.getName(), this, false);
 2774                   enqueue( new EndElementImpl( xmlName ) );
 2775               }
 2776   
 2777               emitEndPrefixMappings();
 2778           }
 2779   
 2780           protected void emitText(SaveCur c)
 2781           {
 2782               assert c.isText();
 2783               Object src = c.getChars();
 2784               int cch = c._cchSrc;
 2785               int off = c._offSrc;
 2786   
 2787               enqueue( new CharacterDataImpl( src, cch, off ) );
 2788           }
 2789   
 2790           protected void emitComment(SaveCur c)
 2791           {
 2792               enqueue( new CommentImpl( c.getChars(), c._cchSrc, c._offSrc ) );
 2793           }
 2794   
 2795           protected void emitProcinst(SaveCur c)
 2796           {
 2797               String target = null;
 2798               QName name = c.getName();
 2799   
 2800               if (name!=null)
 2801                   target = name.getLocalPart();
 2802   
 2803               enqueue( new ProcessingInstructionImpl( target, c.getChars(), c._cchSrc, c._offSrc ) );
 2804           }
 2805   
 2806           protected void emitDocType( String doctypeName, String publicID, String systemID )
 2807           {
 2808               enqueue( new StartDocumentImpl( systemID, null, true, null ) ); //todo
 2809           }
 2810   
 2811           protected void emitStartDoc ( SaveCur c )
 2812           {
 2813               emitDocType(null, null, null);
 2814           }
 2815   
 2816           protected void emitEndDoc ( SaveCur c )
 2817           {
 2818               enqueue( new EndDocumentImpl());
 2819           }
 2820   
 2821           XMLEvent dequeue ( )
 2822           {
 2823               if (_out == null)
 2824               {
 2825                   enterLocale();
 2826                   try
 2827                   {
 2828                       if(!process())
 2829                           return null;
 2830                   }
 2831                   finally
 2832                   {
 2833                       exitLocale();
 2834                   }
 2835               }
 2836   
 2837               if (_out == null)
 2838                   return null;
 2839   
 2840               XmlEventImpl e = _out;
 2841   
 2842               if ((_out = _out._next) == null)
 2843                   _in = null;
 2844   
 2845               return e;
 2846           }
 2847   
 2848           private void enqueue ( XmlEventImpl e )
 2849           {
 2850               assert e._next == null;
 2851   
 2852               if (_in == null)
 2853               {
 2854                   assert _out == null;
 2855                   _out = _in = e;
 2856               }
 2857               else
 2858               {
 2859                   _in._next = e;
 2860                   _in = e;
 2861               }
 2862           }
 2863   
 2864           //
 2865           //
 2866           //
 2867   
 2868           protected void emitEndPrefixMappings ( )
 2869           {
 2870               for ( iterateMappings() ; hasMapping() ; nextMapping() )
 2871               {
 2872                   String prevPrefixUri = null; // todo mappingPrevPrefixUri();
 2873                   String prefix = mappingPrefix();
 2874                   String uri = mappingUri();
 2875   
 2876                   if (prevPrefixUri == null)
 2877                       enqueue( new EndPrefixMappingImpl( prefix ) );
 2878                   else
 2879                   {
 2880                       enqueue( new ChangePrefixMappingImpl( prefix, uri, prevPrefixUri ) );
 2881                   }
 2882               }
 2883           }
 2884   
 2885           //
 2886           //
 2887           //
 2888   
 2889           private static XMLName computeName ( QName name, Saver saver, boolean needsPrefix )
 2890           {
 2891               String uri = name.getNamespaceURI();
 2892               String local = name.getLocalPart();
 2893   
 2894               assert uri != null;
 2895               assert local.length() > 0;
 2896   
 2897               String prefix = null;
 2898   
 2899               if (uri!=null && uri.length() != 0)
 2900               {
 2901                   prefix = name.getPrefix();
 2902                   String mappedUri = saver.getNamespaceForPrefix( prefix );
 2903   
 2904                   if (mappedUri == null || !mappedUri.equals( uri ))
 2905                       prefix = saver.getUriMapping( uri );
 2906   
 2907                   // Attrs need a prefix.  If I have not found one, then there must be a default
 2908                   // prefix obscuring the prefix needed for this attr.  Find it manually.
 2909   
 2910                   // NOTE - Consider keeping the currently mapped default URI separate fromn the
 2911                   // _urpMap and _prefixMap.  This way, I would not have to look it up manually
 2912                   // here
 2913   
 2914                   if (needsPrefix && prefix.length() == 0)
 2915                       prefix = saver.getNonDefaultUriMapping( uri );
 2916   
 2917               }
 2918   
 2919               return new XmlNameImpl( uri, local, prefix );
 2920           }
 2921   
 2922           private static abstract class XmlEventImpl extends XmlEventBase
 2923           {
 2924               XmlEventImpl ( int type )
 2925               {
 2926                   super( type );
 2927               }
 2928   
 2929               public XMLName getName ( )
 2930               {
 2931                   return null;
 2932               }
 2933   
 2934               public XMLName getSchemaType ( )
 2935               {
 2936                   throw new RuntimeException( "NYI" );
 2937               }
 2938   
 2939               public boolean hasName ( )
 2940               {
 2941                   return false;
 2942               }
 2943   
 2944               public final Location getLocation ( )
 2945               {
 2946                   // (orig v1 comment)TODO - perhaps I can save a location goober sometimes?
 2947                   return null;
 2948               }
 2949   
 2950               XmlEventImpl _next;
 2951           }
 2952   
 2953           private static class StartDocumentImpl
 2954               extends XmlEventImpl implements StartDocument
 2955           {
 2956               StartDocumentImpl ( String systemID, String encoding, boolean isStandAlone, String version )
 2957               {
 2958                   super( XMLEvent.START_DOCUMENT );
 2959                   _systemID = systemID;
 2960                   _encoding = encoding;
 2961                   _standAlone = isStandAlone;
 2962                   _version = version;
 2963               }
 2964   
 2965               public String getSystemId ( )
 2966               {
 2967                   return _systemID;
 2968               }
 2969   
 2970               public String getCharacterEncodingScheme ( )
 2971               {
 2972                   return _encoding;
 2973               }
 2974   
 2975               public boolean isStandalone ( )
 2976               {
 2977                   return _standAlone;
 2978               }
 2979   
 2980               public String getVersion ( )
 2981               {
 2982                   return _version;
 2983               }
 2984   
 2985               String _systemID;
 2986               String _encoding;
 2987               boolean _standAlone;
 2988               String _version;
 2989           }
 2990   
 2991           private static class StartElementImpl
 2992               extends XmlEventImpl implements StartElement
 2993           {
 2994               StartElementImpl ( XMLName name, AttributeImpl attributes, AttributeImpl namespaces, Map prefixMap )
 2995               {
 2996                   super( XMLEvent.START_ELEMENT );
 2997   
 2998                   _name = name;
 2999                   _attributes = attributes;
 3000                   _namespaces = namespaces;
 3001                   _prefixMap = prefixMap;
 3002               }
 3003   
 3004               public boolean hasName()
 3005               {
 3006                   return true;
 3007               }
 3008   
 3009               public XMLName getName ( )
 3010               {
 3011                   return _name;
 3012               }
 3013   
 3014               public AttributeIterator getAttributes ( )
 3015               {
 3016                   return new AttributeIteratorImpl( _attributes, null );
 3017               }
 3018   
 3019               public AttributeIterator getNamespaces ( )
 3020               {
 3021                   return new AttributeIteratorImpl( null, _namespaces );
 3022               }
 3023   
 3024               public AttributeIterator getAttributesAndNamespaces ( )
 3025               {
 3026                   return  new AttributeIteratorImpl( _attributes, _namespaces );
 3027               }
 3028   
 3029               public Attribute getAttributeByName ( XMLName xmlName )
 3030               {
 3031                   for ( AttributeImpl a = _attributes ; a != null ; a = a._next )
 3032                   {
 3033                       if (xmlName.equals( a.getName() ))
 3034                           return a;
 3035                   }
 3036   
 3037                   return null;
 3038               }
 3039   
 3040               public String getNamespaceUri ( String prefix )
 3041               {
 3042                   return (String) _prefixMap.get( prefix == null ? "" : prefix );
 3043               }
 3044   
 3045               public Map getNamespaceMap ( )
 3046               {
 3047                   return _prefixMap;
 3048               }
 3049   
 3050               private static class AttributeIteratorImpl
 3051                   implements AttributeIterator
 3052               {
 3053                   AttributeIteratorImpl( AttributeImpl attributes, AttributeImpl namespaces )
 3054                   {
 3055                       _attributes = attributes;
 3056                       _namespaces = namespaces;
 3057                   }
 3058   
 3059                   public Object monitor()
 3060                   {
 3061                       return this;
 3062                   }
 3063   
 3064                   public Attribute next ( )
 3065                   {
 3066                       synchronized (monitor())
 3067                       {
 3068                           checkVersion();
 3069   
 3070                           AttributeImpl attr = null;
 3071   
 3072                           if (_attributes != null)
 3073                           {
 3074                               attr = _attributes;
 3075                               _attributes = attr._next;
 3076                           }
 3077                           else if (_namespaces != null)
 3078                           {
 3079                               attr = _namespaces;
 3080                               _namespaces = attr._next;
 3081                           }
 3082   
 3083                           return attr;
 3084                       }
 3085                   }
 3086   
 3087                   public boolean hasNext ( )
 3088                   {
 3089                       synchronized (monitor())
 3090                       {
 3091                           checkVersion();
 3092   
 3093                           return _attributes != null || _namespaces != null;
 3094                       }
 3095                   }
 3096   
 3097                   public Attribute peek ( )
 3098                   {
 3099                       synchronized (monitor())
 3100                       {
 3101                           checkVersion();
 3102   
 3103                           if (_attributes != null)
 3104                               return _attributes;
 3105                           else if (_namespaces != null)
 3106                               return _namespaces;
 3107   
 3108                           return null;
 3109                       }
 3110                   }
 3111   
 3112                   public void skip ( )
 3113                   {
 3114                       synchronized (monitor())
 3115                       {
 3116                           checkVersion();
 3117   
 3118                           if (_attributes != null)
 3119                               _attributes = _attributes._next;
 3120                           else if (_namespaces != null)
 3121                               _namespaces = _namespaces._next;
 3122                       }
 3123                   }
 3124   
 3125                   private final void checkVersion ( )
 3126                   {
 3127   //                    if (_version != _root.getVersion())
 3128   //                        throw new IllegalStateException( "Document changed" );
 3129                   }
 3130   
 3131   //                private long          _version;
 3132                   private AttributeImpl _attributes;
 3133                   private AttributeImpl _namespaces;
 3134               }
 3135   
 3136               private static abstract class AttributeImpl implements Attribute
 3137               {
 3138                   /**
 3139                    * Don't forget to set _name
 3140                    */
 3141                   AttributeImpl ()
 3142                   {
 3143                   }
 3144   
 3145                   public XMLName getName ( )
 3146                   {
 3147                       return _name;
 3148                   }
 3149   
 3150                   public String getType ( )
 3151                   {
 3152                       // (from v1 impl) TODO - Make sure throwing away this DTD info is ok.
 3153                       // (from v1 impl) Is there schema info which can return more useful info?
 3154                       return "CDATA";
 3155                   }
 3156   
 3157                   public XMLName getSchemaType ( )
 3158                   {
 3159                       // (from v1 impl) TODO - Can I return something reasonable here?
 3160                       return null;
 3161                   }
 3162   
 3163                   AttributeImpl _next;
 3164   
 3165                   protected XMLName _name;
 3166               }
 3167   
 3168               private static class XmlnsAttributeImpl extends AttributeImpl
 3169               {
 3170                   XmlnsAttributeImpl ( String prefix, String uri )
 3171                   {
 3172                       super();
 3173                       _uri = uri;
 3174   
 3175                       String local;
 3176   
 3177                       if (prefix.length() == 0)
 3178                       {
 3179                           prefix = null;
 3180                           local = "xmlns";
 3181                       }
 3182                       else
 3183                       {
 3184                           local = prefix;
 3185                           prefix = "xmlns";
 3186                       }
 3187   
 3188                       _name = new XmlNameImpl( null, local, prefix );
 3189                   }
 3190   
 3191                   public String getValue ( )
 3192                   {
 3193                       return _uri;
 3194                   }
 3195   
 3196                   private String _uri;
 3197               }
 3198   
 3199               private static class NormalAttributeImpl extends AttributeImpl
 3200               {
 3201                   NormalAttributeImpl (XMLName name, String value)
 3202                   {
 3203                       _name = name;
 3204                       _value = value;
 3205                   }
 3206   
 3207                   public String getValue ( )
 3208                   {
 3209                       return _value;
 3210                   }
 3211   
 3212                   private String _value; // If invalid in the store
 3213               }
 3214   
 3215               private XMLName _name;
 3216               private Map     _prefixMap;
 3217   
 3218               private AttributeImpl _attributes;
 3219               private AttributeImpl _namespaces;
 3220           }
 3221   
 3222           private static class StartPrefixMappingImpl
 3223               extends XmlEventImpl implements StartPrefixMapping
 3224           {
 3225               StartPrefixMappingImpl ( String prefix, String uri )
 3226               {
 3227                   super( XMLEvent.START_PREFIX_MAPPING );
 3228   
 3229                   _prefix = prefix;
 3230                   _uri = uri;
 3231               }
 3232   
 3233               public String getNamespaceUri ( )
 3234               {
 3235                   return _uri;
 3236               }
 3237   
 3238               public String getPrefix ( )
 3239               {
 3240                   return _prefix;
 3241               }
 3242   
 3243               private String _prefix, _uri;
 3244           }
 3245   
 3246           private static class ChangePrefixMappingImpl
 3247               extends XmlEventImpl implements ChangePrefixMapping
 3248           {
 3249               ChangePrefixMappingImpl ( String prefix, String oldUri, String newUri )
 3250               {
 3251                   super( XMLEvent.CHANGE_PREFIX_MAPPING );
 3252   
 3253                   _oldUri = oldUri;
 3254                   _newUri = newUri;
 3255                   _prefix = prefix;
 3256               }
 3257   
 3258               public String getOldNamespaceUri ( )
 3259               {
 3260                   return _oldUri;
 3261               }
 3262   
 3263               public String getNewNamespaceUri ( )
 3264               {
 3265                   return _newUri;
 3266               }
 3267   
 3268               public String getPrefix ( )
 3269               {
 3270                   return _prefix;
 3271               }
 3272   
 3273               private String _oldUri, _newUri, _prefix;
 3274           }
 3275   
 3276           private static class EndPrefixMappingImpl
 3277               extends XmlEventImpl implements EndPrefixMapping
 3278           {
 3279               EndPrefixMappingImpl ( String prefix )
 3280               {
 3281                   super( XMLEvent.END_PREFIX_MAPPING );
 3282                   _prefix = prefix;
 3283               }
 3284   
 3285               public String getPrefix ( )
 3286               {
 3287                   return _prefix;
 3288               }
 3289   
 3290               private String _prefix;
 3291           }
 3292   
 3293           private static class EndElementImpl
 3294               extends XmlEventImpl implements EndElement
 3295           {
 3296               EndElementImpl ( XMLName name )
 3297               {
 3298                   super( XMLEvent.END_ELEMENT );
 3299   
 3300                   _name = name;
 3301               }
 3302   
 3303               public boolean hasName ( )
 3304               {
 3305                   return true;
 3306               }
 3307   
 3308               public XMLName getName ( )
 3309               {
 3310                   return _name;
 3311               }
 3312   
 3313               private XMLName _name;
 3314           }
 3315   
 3316           private static class EndDocumentImpl
 3317               extends XmlEventImpl implements EndDocument
 3318           {
 3319               EndDocumentImpl ( )
 3320               {
 3321                   super( XMLEvent.END_DOCUMENT );
 3322               }
 3323           }
 3324   
 3325           private static class TripletEventImpl
 3326               extends XmlEventImpl implements CharacterData
 3327           {
 3328               TripletEventImpl ( int eventType, Object obj, int cch, int off )
 3329               {
 3330                   super(eventType);
 3331                   _obj = obj;
 3332                   _cch = cch;
 3333                   _off = off;
 3334               }
 3335   
 3336               public String getContent ( )
 3337               {
 3338                   return CharUtil.getString(_obj, _off, _cch);
 3339               }
 3340   
 3341               public boolean hasContent ( )
 3342               {
 3343                   return _cch > 0;
 3344               }
 3345   
 3346               private Object _obj;
 3347               private int    _cch;
 3348               private int    _off;
 3349           }
 3350   
 3351           private static class CharacterDataImpl
 3352               extends TripletEventImpl implements CharacterData
 3353           {
 3354               CharacterDataImpl ( Object obj, int cch, int off )
 3355               {
 3356                   super(XMLEvent.CHARACTER_DATA, obj, cch, off);
 3357               }
 3358           }
 3359   
 3360           private static class CommentImpl
 3361               extends TripletEventImpl implements Comment
 3362           {
 3363               CommentImpl ( Object obj, int cch, int off )
 3364               {
 3365                   super( XMLEvent.COMMENT, obj, cch, off);
 3366               }
 3367           }
 3368   
 3369           private static class ProcessingInstructionImpl
 3370               extends TripletEventImpl implements ProcessingInstruction
 3371           {
 3372               ProcessingInstructionImpl ( String target, Object obj, int cch, int off)
 3373               {
 3374                   super( XMLEvent.PROCESSING_INSTRUCTION, obj, cch, off);
 3375                   _target = target;
 3376               }
 3377   
 3378               public String getTarget ( )
 3379               {
 3380                   return _target;
 3381               }
 3382   
 3383               public String getData ( )
 3384               {
 3385                   return getContent();
 3386               }
 3387   
 3388               private String _target;
 3389           }
 3390   
 3391           private XmlEventImpl _in, _out;
 3392       }
 3393   
 3394       static final class XmlInputStreamImpl extends GenericXmlInputStream
 3395       {
 3396           XmlInputStreamImpl ( Cur cur, XmlOptions options )
 3397           {
 3398               _xmlInputStreamSaver =
 3399                   new XmlInputStreamSaver( cur, options );
 3400   
 3401               // Make the saver grind away just a bit to throw any exceptions
 3402               // related to the inability to create a stream on this xml
 3403   
 3404               _xmlInputStreamSaver.process();
 3405           }
 3406   
 3407           protected XMLEvent nextEvent ( ) throws XMLStreamException
 3408           {
 3409               return _xmlInputStreamSaver.dequeue();
 3410           }
 3411   
 3412           private XmlInputStreamSaver _xmlInputStreamSaver;
 3413       }
 3414   
 3415       static final class SaxSaver extends Saver
 3416       {
 3417           SaxSaver ( Cur c, XmlOptions options, ContentHandler ch, LexicalHandler lh )
 3418               throws SAXException
 3419           {
 3420               super( c, options );
 3421   
 3422               _contentHandler = ch;
 3423               _lexicalHandler = lh;
 3424   
 3425               _attributes = new AttributesImpl();
 3426               _nsAsAttrs = !options.hasOption( XmlOptions.SAVE_SAX_NO_NSDECLS_IN_ATTRIBUTES );
 3427   
 3428               _contentHandler.startDocument();
 3429   
 3430               try
 3431               {
 3432                   while ( process() )
 3433                       ;
 3434               }
 3435               catch ( SaverSAXException e )
 3436               {
 3437                   throw e._saxException;
 3438               }
 3439   
 3440               _contentHandler.endDocument();
 3441           }
 3442   
 3443           private class SaverSAXException extends RuntimeException
 3444           {
 3445               SaverSAXException ( SAXException e )
 3446               {
 3447                   _saxException = e;
 3448               }
 3449   
 3450               SAXException _saxException;
 3451           }
 3452   
 3453           private String getPrefixedName ( QName name )
 3454           {
 3455               String uri = name.getNamespaceURI();
 3456               String local = name.getLocalPart();
 3457   
 3458               if (uri.length() == 0)
 3459                   return local;
 3460   
 3461               String prefix = getUriMapping( uri );
 3462   
 3463               if (prefix.length() == 0)
 3464                   return local;
 3465   
 3466               return prefix + ":" + local;
 3467           }
 3468   
 3469           private void emitNamespacesHelper ( )
 3470           {
 3471               for ( iterateMappings() ; hasMapping() ; nextMapping() )
 3472               {
 3473                   String prefix = mappingPrefix();
 3474                   String uri = mappingUri();
 3475   
 3476                   try
 3477                   {
 3478                       _contentHandler.startPrefixMapping( prefix, uri );
 3479                   }
 3480                   catch ( SAXException e )
 3481                   {
 3482                       throw new SaverSAXException( e );
 3483                   }
 3484   
 3485                   if (_nsAsAttrs)
 3486                       if (prefix == null || prefix.length() == 0)
 3487                           _attributes.addAttribute( "http://www.w3.org/2000/xmlns/", "xmlns", "xmlns", "CDATA", uri );
 3488                       else
 3489                           _attributes.addAttribute( "http://www.w3.org/2000/xmlns/", prefix, "xmlns:" + prefix, "CDATA", uri );
 3490               }
 3491           }
 3492   
 3493           protected boolean emitElement ( SaveCur c, ArrayList attrNames, ArrayList attrValues )
 3494           {
 3495               _attributes.clear();
 3496   
 3497               if (saveNamespacesFirst())
 3498                   emitNamespacesHelper();
 3499   
 3500               for ( int i = 0 ; i < attrNames.size() ; i++ )
 3501               {
 3502                   QName name = (QName) attrNames.get( i );
 3503   
 3504                   _attributes.addAttribute(
 3505                       name.getNamespaceURI(), name.getLocalPart(), getPrefixedName( name ),
 3506                       "CDATA", (String) attrValues.get( i ) );
 3507               }
 3508   
 3509               if (!saveNamespacesFirst())
 3510                   emitNamespacesHelper();
 3511   
 3512               QName elemName = c.getName();
 3513   
 3514               try
 3515               {
 3516                   _contentHandler.startElement(
 3517                       elemName.getNamespaceURI(), elemName.getLocalPart(),
 3518                       getPrefixedName( elemName ), _attributes );
 3519               }
 3520               catch ( SAXException e )
 3521               {
 3522                   throw new SaverSAXException( e );
 3523               }
 3524   
 3525               return false;
 3526           }
 3527   
 3528           protected void emitFinish ( SaveCur c )
 3529           {
 3530               QName name = c.getName();
 3531   
 3532               try
 3533               {
 3534                   _contentHandler.endElement(
 3535                       name.getNamespaceURI(), name.getLocalPart(), getPrefixedName( name ) );
 3536   
 3537                   for ( iterateMappings() ; hasMapping() ; nextMapping() )
 3538                       _contentHandler.endPrefixMapping( mappingPrefix() );
 3539               }
 3540               catch ( SAXException e )
 3541               {
 3542                   throw new SaverSAXException( e );
 3543               }
 3544           }
 3545   
 3546           protected void emitText ( SaveCur c )
 3547           {
 3548               assert c.isText();
 3549   
 3550               Object src = c.getChars();
 3551   
 3552               try
 3553               {
 3554                   if (src instanceof char[])
 3555                   {
 3556                       // Pray the user does not modify the buffer ....
 3557                       _contentHandler.characters( (char[]) src, c._offSrc, c._cchSrc );
 3558                   }
 3559                   else
 3560                   {
 3561                       if (_buf == null)
 3562                           _buf = new char [ 1024 ];
 3563   
 3564                       while ( c._cchSrc > 0 )
 3565                       {
 3566                           int cch = java.lang.Math.min( _buf.length, c._cchSrc );
 3567   
 3568                           CharUtil.getChars( _buf, 0, src, c._offSrc, cch );
 3569   
 3570                           _contentHandler.characters( _buf, 0, cch );
 3571   
 3572                           c._offSrc += cch;
 3573                           c._cchSrc -= cch;
 3574                       }
 3575                   }
 3576               }
 3577               catch ( SAXException e )
 3578               {
 3579                   throw new SaverSAXException( e );
 3580               }
 3581           }
 3582   
 3583           protected void emitComment ( SaveCur c )
 3584           {
 3585               if (_lexicalHandler != null)
 3586               {
 3587                   c.push();
 3588   
 3589                   c.next();
 3590   
 3591                   try
 3592                   {
 3593                       if (!c.isText())
 3594                           _lexicalHandler.comment( null, 0, 0 );
 3595                       else
 3596                       {
 3597                           Object src = c.getChars();
 3598   
 3599                           if (src instanceof char[])
 3600                           {
 3601                               // Pray the user does not modify the buffer ....
 3602                               _lexicalHandler.comment( (char[]) src, c._offSrc, c._cchSrc );
 3603                           }
 3604                           else
 3605                           {
 3606                               if (_buf == null || _buf.length < c._cchSrc)
 3607                                   _buf = new char [ java.lang.Math.max( 1024, c._cchSrc ) ];
 3608   
 3609                               CharUtil.getChars( _buf, 0, src, c._offSrc, c._cchSrc );
 3610   
 3611                               _lexicalHandler.comment( _buf, 0, c._cchSrc );
 3612                           }
 3613                       }
 3614                   }
 3615                   catch ( SAXException e )
 3616                   {
 3617                       throw new SaverSAXException( e );
 3618                   }
 3619   
 3620                   c.pop();
 3621               }
 3622           }
 3623   
 3624           protected void emitProcinst ( SaveCur c )
 3625           {
 3626               String target = c.getName().getLocalPart();
 3627   
 3628               c.push();
 3629   
 3630               c.next();
 3631   
 3632               String value = CharUtil.getString( c.getChars(), c._offSrc, c._cchSrc );
 3633   
 3634               c.pop();
 3635   
 3636               try
 3637               {
 3638                   _contentHandler.processingInstruction( c.getName().getLocalPart(), value );
 3639               }
 3640               catch ( SAXException e )
 3641               {
 3642                   throw new SaverSAXException( e );
 3643               }
 3644           }
 3645   
 3646           protected void emitDocType ( String docTypeName, String publicId, String systemId )
 3647           {
 3648               if (_lexicalHandler != null)
 3649               {
 3650                   try
 3651                   {
 3652                       _lexicalHandler.startDTD( docTypeName, publicId, systemId );
 3653                       _lexicalHandler.endDTD();
 3654                   }
 3655                   catch ( SAXException e )
 3656                   {
 3657                       throw new SaverSAXException( e );
 3658                   }
 3659               }
 3660           }
 3661   
 3662           protected void emitStartDoc ( SaveCur c )
 3663           {
 3664           }
 3665   
 3666           protected void emitEndDoc ( SaveCur c )
 3667           {
 3668           }
 3669   
 3670           private ContentHandler _contentHandler;
 3671           private LexicalHandler _lexicalHandler;
 3672   
 3673           private AttributesImpl _attributes;
 3674   
 3675           private char[] _buf;
 3676           private boolean _nsAsAttrs;
 3677       }
 3678   
 3679       //
 3680       //
 3681       //
 3682   
 3683       static abstract class SaveCur
 3684       {
 3685           final boolean isRoot       ( ) { return kind() == ROOT;     }
 3686           final boolean isElem       ( ) { return kind() == ELEM;     }
 3687           final boolean isAttr       ( ) { return kind() == ATTR;     }
 3688           final boolean isText       ( ) { return kind() == TEXT;     }
 3689           final boolean isComment    ( ) { return kind() == COMMENT;  }
 3690           final boolean isProcinst   ( ) { return kind() == PROCINST; }
 3691           final boolean isFinish     ( ) { return Cur.kindIsFinish( kind() ); }
 3692           final boolean isContainer  ( ) { return Cur.kindIsContainer( kind() ); }
 3693           final boolean isNormalAttr ( ) { return kind() == ATTR && !isXmlns(); }
 3694   
 3695           final boolean skip ( ) { toEnd(); return next(); }
 3696   
 3697           abstract void release ( );
 3698   
 3699           abstract int kind ( );
 3700   
 3701           abstract QName  getName ( );
 3702           abstract String getXmlnsPrefix ( );
 3703           abstract String getXmlnsUri ( );
 3704   
 3705           abstract boolean isXmlns ( );
 3706   
 3707           abstract boolean hasChildren  ( );
 3708           abstract boolean hasText      ( );
 3709           abstract boolean isTextCData  ( );
 3710   
 3711           abstract boolean toFirstAttr ( );
 3712           abstract boolean toNextAttr ( );
 3713           abstract String  getAttrValue ( );
 3714   
 3715           abstract boolean next  ( );
 3716           abstract void    toEnd ( );
 3717   
 3718           abstract void push ( );
 3719           abstract void pop ( );
 3720   
 3721           abstract Object getChars ( );
 3722           abstract List  getAncestorNamespaces ( );
 3723           abstract XmlDocumentProperties getDocProps ( );
 3724   
 3725           int _offSrc;
 3726           int _cchSrc;
 3727       }
 3728   
 3729       // TODO - saving a fragment need to take namesapces from root and
 3730       // reflect them on the document element
 3731   
 3732       private static final class DocSaveCur extends SaveCur
 3733       {
 3734           DocSaveCur ( Cur c )
 3735           {
 3736               assert c.isRoot();
 3737               _cur = c.weakCur( this );
 3738           }
 3739   
 3740           void release ( )
 3741           {
 3742               _cur.release();
 3743               _cur = null;
 3744           }
 3745   
 3746           int kind ( ) { return _cur.kind(); }
 3747   
 3748           QName  getName        ( ) { return _cur.getName(); }
 3749           String getXmlnsPrefix ( ) { return _cur.getXmlnsPrefix(); }
 3750           String getXmlnsUri    ( ) { return _cur.getXmlnsUri(); }
 3751   
 3752           boolean isXmlns       ( ) { return _cur.isXmlns();     }
 3753   
 3754           boolean hasChildren   ( ) { return _cur.hasChildren(); }
 3755           boolean hasText       ( ) { return _cur.hasText();     }
 3756           boolean isTextCData   ( ) { return _cur.isTextCData(); }
 3757   
 3758           boolean toFirstAttr   ( ) { return _cur.toFirstAttr(); }
 3759           boolean toNextAttr    ( ) { return _cur.toNextAttr();  }
 3760           String  getAttrValue  ( ) { assert _cur.isAttr(); return _cur.getValueAsString(); }
 3761   
 3762           void    toEnd         ( ) { _cur.toEnd();              }
 3763           boolean next          ( ) { return _cur.next();        }
 3764   
 3765           void push ( )         { _cur.push(); }
 3766           void pop  ( )         { _cur.pop(); }
 3767   
 3768           List getAncestorNamespaces ( ) { return null; }
 3769   
 3770           Object getChars ( )
 3771           {
 3772               Object o = _cur.getChars( -1 );
 3773   
 3774               _offSrc = _cur._offSrc;
 3775               _cchSrc = _cur._cchSrc;
 3776   
 3777               return o;
 3778           }
 3779   
 3780           XmlDocumentProperties getDocProps ( ) { return Locale.getDocProps(_cur, false); }
 3781   
 3782           private Cur _cur;
 3783       }
 3784   
 3785       private static abstract class FilterSaveCur extends SaveCur
 3786       {
 3787           FilterSaveCur ( SaveCur c )
 3788           {
 3789               assert c.isRoot();
 3790               _cur = c;
 3791           }
 3792   
 3793           // Can filter anything by root and attributes and text
 3794           protected abstract boolean filter ( );
 3795   
 3796           void release ( )
 3797           {
 3798               _cur.release();
 3799               _cur = null;
 3800           }
 3801   
 3802           int kind ( ) { return _cur.kind(); }
 3803   
 3804           QName  getName        ( ) { return _cur.getName();        }
 3805           String getXmlnsPrefix ( ) { return _cur.getXmlnsPrefix(); }
 3806           String getXmlnsUri    ( ) { return _cur.getXmlnsUri();    }
 3807   
 3808           boolean isXmlns       ( ) { return _cur.isXmlns();      }
 3809   
 3810           boolean hasChildren   ( ) { return _cur.hasChildren();  }
 3811           boolean hasText       ( ) { return _cur.hasText();      }
 3812           boolean isTextCData   ( ) { return _cur.isTextCData(); }
 3813   
 3814           boolean toFirstAttr   ( ) { return _cur.toFirstAttr();  }
 3815           boolean toNextAttr    ( ) { return _cur.toNextAttr();   }
 3816           String  getAttrValue  ( ) { return _cur.getAttrValue(); }
 3817   
 3818           void toEnd ( ) { _cur.toEnd(); }
 3819   
 3820           boolean next ( )
 3821           {
 3822               if (!_cur.next())
 3823                   return false;
 3824   
 3825               if (!filter())
 3826                   return true;
 3827   
 3828               assert !isRoot() && !isText() && !isAttr();
 3829   
 3830               toEnd();
 3831   
 3832               return next();
 3833           }
 3834   
 3835           void push ( ) { _cur.push(); }
 3836           void pop  ( ) { _cur.pop(); }
 3837   
 3838           List getAncestorNamespaces ( ) { return _cur.getAncestorNamespaces(); }
 3839   
 3840           Object getChars ( )
 3841           {
 3842               Object o = _cur.getChars();
 3843   
 3844               _offSrc = _cur._offSrc;
 3845               _cchSrc = _cur._cchSrc;
 3846   
 3847               return o;
 3848           }
 3849   
 3850           XmlDocumentProperties getDocProps ( ) { return _cur.getDocProps(); }
 3851   
 3852           private SaveCur _cur;
 3853       }
 3854   
 3855       private static final class FilterPiSaveCur extends FilterSaveCur
 3856       {
 3857           FilterPiSaveCur ( SaveCur c, String target )
 3858           {
 3859               super( c );
 3860   
 3861               _piTarget = target;
 3862           }
 3863   
 3864           protected boolean filter ( )
 3865           {
 3866               return kind() == PROCINST && getName().getLocalPart().equals( _piTarget );
 3867           }
 3868   
 3869           private String _piTarget;
 3870       }
 3871   
 3872       private static final class FragSaveCur extends SaveCur
 3873       {
 3874           FragSaveCur ( Cur start, Cur end, QName synthElem )
 3875           {
 3876               _saveAttr = start.isAttr() && start.isSamePos( end );
 3877   
 3878               _cur = start.weakCur( this );
 3879               _end = end.weakCur( this );
 3880   
 3881               _elem = synthElem;
 3882   
 3883               _state = ROOT_START;
 3884   
 3885               _stateStack = new int [ 8 ];
 3886   
 3887               start.push();
 3888               computeAncestorNamespaces( start );
 3889               start.pop();
 3890           }
 3891   
 3892           List getAncestorNamespaces ( )
 3893           {
 3894               return _ancestorNamespaces;
 3895           }
 3896   
 3897           private void computeAncestorNamespaces ( Cur c )
 3898           {
 3899               _ancestorNamespaces = new ArrayList();
 3900   
 3901               while ( c.toParentRaw() )
 3902               {
 3903                   if (c.toFirstAttr())
 3904                   {
 3905                       do
 3906                       {
 3907                           if (c.isXmlns())
 3908                           {
 3909                               String prefix = c.getXmlnsPrefix();
 3910                               String uri = c.getXmlnsUri();
 3911   
 3912                               // Don't let xmlns:foo="" get used
 3913   
 3914                               if (uri.length() > 0 || prefix.length() == 0)
 3915                               {
 3916                                   _ancestorNamespaces.add( c.getXmlnsPrefix() );
 3917                                   _ancestorNamespaces.add( c.getXmlnsUri() );
 3918                               }
 3919                           }
 3920                       }
 3921                       while ( c.toNextAttr() );
 3922   
 3923                       c.toParent();
 3924                   }
 3925               }
 3926           }
 3927   
 3928           //
 3929           //
 3930           //
 3931   
 3932           void release ( )
 3933           {
 3934               _cur.release();
 3935               _cur = null;
 3936   
 3937               _end.release();
 3938               _end = null;
 3939           }
 3940   
 3941           int kind ( )
 3942           {
 3943               switch ( _state )
 3944               {
 3945               case ROOT_START : return  ROOT;
 3946               case ELEM_START : return  ELEM;
 3947               case ELEM_END   : return -ELEM;
 3948               case ROOT_END   : return -ROOT;
 3949               }
 3950   
 3951               assert _state == CUR;
 3952   
 3953               return _cur.kind();
 3954           }
 3955   
 3956           QName getName ( )
 3957           {
 3958               switch ( _state )
 3959               {
 3960               case ROOT_START :
 3961               case ROOT_END   : return null;
 3962               case ELEM_START :
 3963               case ELEM_END   : return _elem;
 3964               }
 3965   
 3966               assert _state == CUR;
 3967   
 3968               return _cur.getName();
 3969           }
 3970   
 3971           String getXmlnsPrefix ( )
 3972           {
 3973               assert _state == CUR && _cur.isAttr();
 3974               return _cur.getXmlnsPrefix();
 3975           }
 3976   
 3977           String getXmlnsUri ( )
 3978           {
 3979               assert _state == CUR && _cur.isAttr();
 3980               return _cur.getXmlnsUri();
 3981           }
 3982   
 3983           boolean isXmlns ( )
 3984           {
 3985               assert _state == CUR && _cur.isAttr();
 3986               return _cur.isXmlns();
 3987           }
 3988   
 3989           boolean hasChildren ( )
 3990           {
 3991               boolean hasChildren = false;
 3992   
 3993               if (isContainer())
 3994               {   // is there a faster way to do this?
 3995                   push();
 3996                   next();
 3997   
 3998                   if (!isText() && !isFinish())
 3999                       hasChildren = true;
 4000   
 4001                   pop();
 4002               }
 4003   
 4004               return hasChildren;
 4005           }
 4006   
 4007           boolean hasText ( )
 4008           {
 4009               boolean hasText = false;
 4010   
 4011               if (isContainer())
 4012               {
 4013                   push();
 4014                   next();
 4015   
 4016                   if (isText())
 4017                       hasText = true;
 4018   
 4019                   pop();
 4020               }
 4021   
 4022               return hasText;
 4023           }
 4024   
 4025           boolean isTextCData ( )
 4026           {
 4027               return _cur.isTextCData();
 4028           }
 4029   
 4030           Object getChars ( )
 4031           {
 4032               assert _state == CUR && _cur.isText();
 4033   
 4034               Object src = _cur.getChars( -1 );
 4035   
 4036               _offSrc = _cur._offSrc;
 4037               _cchSrc = _cur._cchSrc;
 4038   
 4039               return src;
 4040           }
 4041   
 4042           boolean next ( )
 4043           {
 4044               switch ( _state )
 4045               {
 4046               case ROOT_START :
 4047               {
 4048                   _state = _elem == null ? CUR : ELEM_START;
 4049                   break;
 4050               }
 4051   
 4052               case ELEM_START :
 4053               {
 4054                   if (_saveAttr)
 4055                       _state = ELEM_END;
 4056                   else
 4057                   {
 4058                       if (_cur.isAttr())
 4059                       {
 4060                           _cur.toParent();
 4061                           _cur.next();
 4062                       }
 4063   
 4064                       if (_cur.isSamePos( _end ))
 4065                           _state = ELEM_END;
 4066                       else
 4067                           _state = CUR;
 4068                   }
 4069   
 4070                   break;
 4071               }
 4072   
 4073               case CUR :
 4074               {
 4075                   assert !_cur.isAttr();
 4076   
 4077                   _cur.next();
 4078   
 4079                   if (_cur.isSamePos( _end ))
 4080                       _state = _elem == null ? ROOT_END : ELEM_END;
 4081   
 4082                   break;
 4083               }
 4084   
 4085               case ELEM_END :
 4086               {
 4087                   _state = ROOT_END;
 4088                   break;
 4089               }
 4090               case ROOT_END :
 4091                   return false;
 4092               }
 4093   
 4094               return true;
 4095           }
 4096   
 4097           void toEnd ( )
 4098           {
 4099               switch ( _state )
 4100               {
 4101               case ROOT_START : _state = ROOT_END; return;
 4102               case ELEM_START : _state = ELEM_END; return;
 4103               case ROOT_END   :
 4104               case ELEM_END   : return;
 4105               }
 4106   
 4107               assert _state == CUR && !_cur.isAttr() && !_cur.isText();
 4108   
 4109               _cur.toEnd();
 4110           }
 4111   
 4112           boolean toFirstAttr ( )
 4113           {
 4114               switch ( _state )
 4115               {
 4116               case ROOT_END   :
 4117               case ELEM_END   :
 4118               case ROOT_START : return false;
 4119               case CUR        : return _cur.toFirstAttr();
 4120               }
 4121   
 4122               assert _state == ELEM_START;
 4123   
 4124               if (!_cur.isAttr())
 4125                   return false;
 4126   
 4127               _state = CUR;
 4128   
 4129               return true;
 4130           }
 4131   
 4132           boolean toNextAttr ( )
 4133           {
 4134               assert _state == CUR;
 4135               return !_saveAttr && _cur.toNextAttr();
 4136           }
 4137   
 4138           String getAttrValue ( )
 4139           {
 4140               assert _state == CUR && _cur.isAttr();
 4141               return _cur.getValueAsString();
 4142           }
 4143   
 4144           void push ( )
 4145           {
 4146               if (_stateStackSize == _stateStack.length)
 4147               {
 4148                   int[] newStateStack = new int [ _stateStackSize * 2 ];
 4149                   System.arraycopy( _stateStack, 0, newStateStack, 0, _stateStackSize );
 4150                   _stateStack = newStateStack;
 4151               }
 4152   
 4153               _stateStack [ _stateStackSize++ ] = _state;
 4154               _cur.push();
 4155           }
 4156   
 4157           void pop ()
 4158           {
 4159               _cur.pop();
 4160               _state = _stateStack [ --_stateStackSize ];
 4161           }
 4162   
 4163           XmlDocumentProperties getDocProps ( ) { return Locale.getDocProps(_cur, false); }
 4164   
 4165           //
 4166           //
 4167           //
 4168   
 4169           private Cur _cur;
 4170           private Cur _end;
 4171   
 4172           private ArrayList _ancestorNamespaces;
 4173   
 4174           private QName _elem;
 4175   
 4176           private boolean _saveAttr;
 4177   
 4178           private static final int ROOT_START = 1;
 4179           private static final int ELEM_START = 2;
 4180           private static final int ROOT_END   = 3;
 4181           private static final int ELEM_END   = 4;
 4182           private static final int CUR        = 5;
 4183   
 4184           private int _state;
 4185   
 4186           private int[] _stateStack;
 4187           private int   _stateStackSize;
 4188       }
 4189   
 4190       private static final class PrettySaveCur extends SaveCur
 4191       {
 4192           PrettySaveCur ( SaveCur c, XmlOptions options )
 4193           {
 4194               _sb = new StringBuffer();
 4195               _stack = new ArrayList();
 4196   
 4197               _cur = c;
 4198   
 4199               assert options != null;
 4200   
 4201               _prettyIndent = 2;
 4202   
 4203               if (options.hasOption( XmlOptions.SAVE_PRETTY_PRINT_INDENT ))
 4204               {
 4205                   _prettyIndent =
 4206                       ((Integer) options.get( XmlOptions.SAVE_PRETTY_PRINT_INDENT )).intValue();
 4207               }
 4208   
 4209               if (options.hasOption( XmlOptions.SAVE_PRETTY_PRINT_OFFSET ))
 4210               {
 4211                   _prettyOffset =
 4212                       ((Integer) options.get( XmlOptions.SAVE_PRETTY_PRINT_OFFSET )).intValue();
 4213               }
 4214   
 4215               if (options.hasOption( XmlOptions.LOAD_SAVE_CDATA_BOOKMARKS ))
 4216               {
 4217                   _useCDataBookmarks = true;
 4218               }
 4219           }
 4220   
 4221           List getAncestorNamespaces ( ) { return _cur.getAncestorNamespaces(); }
 4222   
 4223           void release ( ) { _cur.release(); }
 4224   
 4225           int kind ( ) { return _txt == null ? _cur.kind() : TEXT; }
 4226   
 4227           QName  getName        ( ) { assert _txt == null; return _cur.getName(); }
 4228           String getXmlnsPrefix ( ) { assert _txt == null; return _cur.getXmlnsPrefix(); }
 4229           String getXmlnsUri    ( ) { assert _txt == null; return _cur.getXmlnsUri(); }
 4230   
 4231           boolean isXmlns       ( ) { return _txt == null ? _cur.isXmlns()      : false; }
 4232   
 4233           boolean hasChildren   ( ) { return _txt == null ? _cur.hasChildren() : false; }
 4234           boolean hasText       ( ) { return _txt == null ? _cur.hasText()     : false; }
 4235   
 4236           // _cur.isTextCData() is expensive do it only if useCDataBookmarks option is enabled
 4237           boolean isTextCData   ( ) { return _txt == null ? (_useCDataBookmarks && _cur.isTextCData())
 4238                                                           : _isTextCData; }
 4239   
 4240           boolean toFirstAttr   ( ) { assert _txt == null; return _cur.toFirstAttr(); }
 4241           boolean toNextAttr    ( ) { assert _txt == null; return _cur.toNextAttr(); }
 4242           String  getAttrValue  ( ) { assert _txt == null; return _cur.getAttrValue(); }
 4243   
 4244           void toEnd ( )
 4245           {
 4246               assert _txt == null;
 4247               _cur.toEnd();
 4248   
 4249               if (_cur.kind() == -ELEM)
 4250                   _depth--;
 4251           }
 4252   
 4253           boolean next ( )
 4254           {
 4255               int k;
 4256   
 4257               if (_txt != null)
 4258               {
 4259                   assert _txt.length() > 0;
 4260                   assert !_cur.isText();
 4261                   _txt = null;
 4262                   _isTextCData = false;
 4263                   k = _cur.kind();
 4264               }
 4265               else
 4266               {
 4267                   int prevKind = k = _cur.kind();
 4268   
 4269                   if (!_cur.next())
 4270                       return false;
 4271   
 4272                   _sb.delete( 0, _sb.length() );
 4273   
 4274                   assert _txt == null;
 4275   
 4276                   // place any text encountered in the buffer
 4277                   if (_cur.isText())
 4278                   {
 4279                       // _cur.isTextCData() is expensive do it only if useCDataBookmarks option is enabled
 4280                       _isTextCData = _useCDataBookmarks && _cur.isTextCData();
 4281                       CharUtil.getString( _sb, _cur.getChars(), _cur._offSrc, _cur._cchSrc );
 4282                       _cur.next();
 4283                       trim( _sb );
 4284                   }
 4285   
 4286                   k = _cur.kind();
 4287   
 4288                   // Check for non leaf, _prettyIndent < 0 means that the save is all on one line
 4289   
 4290                   if (_prettyIndent >= 0 &&
 4291                         prevKind != COMMENT && prevKind != PROCINST && (prevKind != ELEM || k != -ELEM))
 4292   //                if (prevKind != COMMENT && prevKind != PROCINST && (prevKind != ELEM || k != -ELEM))
 4293                   {
 4294                       if (_sb.length() > 0)
 4295                       {
 4296                           _sb.insert( 0, _newLine );
 4297                           spaces( _sb, _newLine.length(), _prettyOffset + _prettyIndent * _depth );
 4298                       }
 4299   
 4300                       if (k != -ROOT)
 4301                       {
 4302                           if (prevKind != ROOT)
 4303                               _sb.append( _newLine );
 4304   
 4305                           int d = k < 0 ? _depth - 1 : _depth;
 4306                           spaces( _sb, _sb.length(), _prettyOffset + _prettyIndent * d );
 4307                       }
 4308                   }
 4309   
 4310                   if (_sb.length() > 0)
 4311                   {
 4312                       _txt = _sb.toString();
 4313                       k = TEXT;
 4314                   }
 4315               }
 4316   
 4317               if (k == ELEM)
 4318                   _depth++;
 4319               else if (k == -ELEM)
 4320                   _depth--;
 4321   
 4322               return true;
 4323           }
 4324   
 4325           void push ( )
 4326           {
 4327               _cur.push();
 4328               _stack.add( _txt );
 4329               _stack.add( new Integer( _depth ) );
 4330               _isTextCData = false;
 4331           }
 4332   
 4333           void pop ( )
 4334           {
 4335               _cur.pop();
 4336               _depth = ((Integer) _stack.remove( _stack.size() - 1 )).intValue();
 4337               _txt = (String) _stack.remove( _stack.size() - 1 );
 4338               _isTextCData = false;
 4339           }
 4340   
 4341           Object getChars ( )
 4342           {
 4343               if (_txt != null)
 4344               {
 4345                   _offSrc = 0;
 4346                   _cchSrc = _txt.length();
 4347                   return _txt;
 4348               }
 4349   
 4350               Object o = _cur.getChars();
 4351   
 4352               _offSrc = _cur._offSrc;
 4353               _cchSrc = _cur._cchSrc;
 4354   
 4355               return o;
 4356           }
 4357   
 4358           XmlDocumentProperties getDocProps ( ) { return _cur.getDocProps(); }
 4359   
 4360           final static void spaces ( StringBuffer sb, int offset, int count )
 4361           {
 4362               while ( count-- > 0 )
 4363                   sb.insert( offset, ' ' );
 4364           }
 4365   
 4366           final static void trim ( StringBuffer sb )
 4367           {
 4368               int i;
 4369   
 4370               for ( i = 0 ; i < sb.length() ; i++ )
 4371                   if (!CharUtil.isWhiteSpace( sb.charAt( i ) ))
 4372                       break;
 4373   
 4374               sb.delete( 0, i );
 4375   
 4376               for ( i = sb.length() ; i > 0 ; i-- )
 4377                   if (!CharUtil.isWhiteSpace( sb.charAt( i - 1 ) ))
 4378                       break;
 4379   
 4380               sb.delete( i, sb.length() );
 4381           }
 4382   
 4383           private SaveCur _cur;
 4384   
 4385           private int _prettyIndent;
 4386           private int _prettyOffset;
 4387   
 4388           private String       _txt;
 4389           private StringBuffer _sb;
 4390   
 4391           private int          _depth;
 4392   
 4393           private ArrayList    _stack;
 4394           private boolean      _isTextCData = false;
 4395           private boolean      _useCDataBookmarks = false;
 4396       }
 4397   
 4398   
 4399       //
 4400       //
 4401       //
 4402   
 4403       private final Locale _locale;
 4404       private final long   _version;
 4405   
 4406       private SaveCur _cur;
 4407   
 4408       private List    _ancestorNamespaces;
 4409       private Map     _suggestedPrefixes;
 4410       protected XmlOptionCharEscapeMap _replaceChar;
 4411       private boolean _useDefaultNamespace;
 4412       private Map     _preComputedNamespaces;
 4413       private boolean _saveNamespacesFirst;
 4414   
 4415       private ArrayList _attrNames;
 4416       private ArrayList _attrValues;
 4417   
 4418       private ArrayList _namespaceStack;
 4419       private int       _currentMapping;
 4420       private HashMap   _uriMap;
 4421       private HashMap   _prefixMap;
 4422       private String    _initialDefaultUri;
 4423   
 4424       static final String _newLine =
 4425           SystemProperties.getProperty( "line.separator" ) == null
 4426               ? "\n"
 4427               : SystemProperties.getProperty( "line.separator" );
 4428   }

Save This Page
Home » xmlbeans-2.5.0-src » org.apache.xmlbeans.impl » store » [javadoc | source]