All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.xml.XMLEmitter Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
package com.hfg.xml;

import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.DefaultHandler;

import com.hfg.util.StringUtil;

//------------------------------------------------------------------------------
/**
  
  @author J. Alex Taylor, hairyfatguy.com
 */
//------------------------------------------------------------------------------


public class XMLEmitter extends DefaultHandler implements LexicalHandler
{

   //###########################################################################
   // PUBLIC FIELDS
   //###########################################################################

   public static final int STYLE_COMPACT  = 1;
   public static final int STYLE_INDENTED = 2;
   
   //###########################################################################
   // PRIVATE FIELDS
   //###########################################################################

   private StringBuffer mCurrentData;
   private StringBuffer mCurrentComment;
   private int          mPreviousTagType;
   private int          mCurrentIndent;
   private XMLTag       mDanglingStartTag;

   private PrintWriter  mWriter;
   private int          mPrintStyle  = STYLE_COMPACT;
   private int          mIndentSize  = 2;

   private Stack        mSafeToIndentStack = new Stack();
   private boolean      mSafeToIndent;

   private static int   START_TAG    = 1;
   private static int   END_TAG      = -1;

   //###########################################################################
   // CONSTRUCTORS
   //###########################################################################
     
   //---------------------------------------------------------------------------
   /**
     Default constructor - prints to System.out
    */
   public XMLEmitter()
   {
      mWriter = new PrintWriter(System.out);
   }   
     
   //---------------------------------------------------------------------------
   /**
     Prints XML to the specified stream
    */
   public XMLEmitter(OutputStream inOutputStream)
   {
      mWriter = new PrintWriter(inOutputStream);
   }

   //---------------------------------------------------------------------------
   /**
     Prints XML to the specified stream
    */
   public XMLEmitter(PrintWriter inWriter)
   {
      mWriter = inWriter;
   }

   
   //###########################################################################
   // PUBLIC METHODS
   //###########################################################################

   //---------------------------------------------------------------------------
   public void setPrintStyle(int inValue)
   {
      if (inValue < STYLE_COMPACT 
          || inValue > STYLE_INDENTED)
      {
         throw new RuntimeException(inValue + " is not a valid print style!");
      }
      
      mPrintStyle = inValue;
   }

   //---------------------------------------------------------------------------
   /**
     Sets the number of spaces each tag level is indented
    */
   public void setIndentSize(int inValue)
   {
      mIndentSize = inValue;
   }

   //---------------------------------------------------------------------------
   public int getIndentSize()
   {
      return mIndentSize;
   }
   
   //---------------------------------------------------------------------------
   public void startDocument()
   {
      mCurrentData = new StringBuffer();
      mCurrentComment = new StringBuffer();
      mPreviousTagType = END_TAG;
      mCurrentIndent = -mIndentSize;

      mSafeToIndentStack = new Stack();
      mSafeToIndent = true;
   }

   //---------------------------------------------------------------------------
   public void endDocument()
   {
      mWriter.flush();
   }

   //---------------------------------------------------------------------------
   public void startElement(String uri, String local, String raw, Attributes attrs)
   {
      if (mDanglingStartTag != null)
      {
         emitStartTag(false);

         if (mCurrentData.length() > 0)
         {
             emitContent();
         }
      }

      List attributes = new ArrayList();
      for (int i = 0; i < attrs.getLength(); i++)
      {
         attributes.add(new XMLAttribute(attrs.getQName(i), attrs.getValue(i)));
      }

      mDanglingStartTag = new XMLTag(raw, attributes);

/*
      boolean safeToIndent = true;

      if (mCurrentData.length() > 0)
      {
         if (mPrintStyle == STYLE_INDENTED)
         {  
         	safeToIndent = contentLooksSafeToIndent(mCurrentData.toString());

            if (safeToIndent) 
            {
               mPrintStream.println();
               mPrintStream.print(StringUtil.polyChar(' ', mCurrentIndent + mIndentSize));
            }
         }
         
         mPrintStream.print(mCurrentData.toString());

         if (mPrintStyle == STYLE_INDENTED && safeToIndent) mPrintStream.println();           
      } 
      
   
      List attributes = new ArrayList();
      for (int i = 0; i < attrs.getLength(); i++)
      {
         attributes.add(new XMLAttribute(attrs.getQName(i), attrs.getValue(i)));
      }
      
      if (mPrintStyle == STYLE_INDENTED)
      {
         mCurrentIndent += mIndentSize;
         mPrintStream.println();
         mPrintStream.print(StringUtil.polyChar(' ', mCurrentIndent));
         mPreviousTagType = START_TAG;
      }
      
      mPrintStream.print(XMLUtil.composeStartTag(raw, attributes));
            
      mCurrentData = new StringBuffer();
*/
   }

   //---------------------------------------------------------------------------
   public void endElement(String uri, String local, String raw)
   {
/*
      // Print any tag contents
      if (mCurrentData.length() > 0)
      {
         if (mPrintStyle == STYLE_INDENTED)
         {  
         	safeToIndent = contentLooksSafeToIndent(mCurrentData.toString());

            if (safeToIndent) 
            {
               mPrintStream.println();
               mPrintStream.print(StringUtil.polyChar(' ', mCurrentIndent + mIndentSize));
            }
         }
         
         mPrintStream.print(mCurrentData.toString());

         if (mPrintStyle == STYLE_INDENTED && safeToIndent) mPrintStream.println();           
      }  
      else if (mPrintStyle == STYLE_INDENTED
               && mPreviousTagType == START_TAG)
      {      
         mPrintStream.println();
      }
      
      mCurrentData = new StringBuffer();
*/
      if (mDanglingStartTag != null
          && mCurrentData.length() == 0
          && mCurrentComment.length() == 0)
      {
         emitStartTag(true);
         mDanglingStartTag = null;
      }
      else
      {
         if (mDanglingStartTag != null)
         {
            emitStartTag(false);
            mDanglingStartTag = null;
         }
 /*
         if (mCurrentComment.length() > 0)
         {
            emitComment();
         }
*/
         if (mCurrentData.length() > 0)
         {
            emitContent();
         }

         popIndentState();

         // Write the end tag
         if (mPrintStyle == STYLE_INDENTED)
         {
            if (mSafeToIndent)
            {
               mWriter.println();
//               if (mPreviousTagType == END_TAG) mPrintStream.println();
               mWriter.print(StringUtil.polyChar(' ', mCurrentIndent));
            }
            mCurrentIndent -= mIndentSize;
            mPreviousTagType = END_TAG;
         }

         mWriter.print(XMLUtil.composeEndTag(raw));
      }
   }
   
   
   //---------------------------------------------------------------------------
   public void characters(char[] ch, int start, int length)
   {
//      System.out.print("In characters(): ");
/*      if (mDanglingStartTag != null)
      {
         emitStartTag(false);
         mDanglingStartTag = null;
      }
*/

      if (mCurrentData.length() > 0)
      {
         mCurrentData.append(ch, start, length);

         if (mPrintStyle == STYLE_INDENTED
             && mSafeToIndent == true
             && mCurrentData.length() > 0)
         {
            mSafeToIndent = contentLooksSafeToIndent(mCurrentData.toString());
         }

         if (mDanglingStartTag != null)
         {
            emitStartTag(false);
            mDanglingStartTag = null;
         }

         emitContent();
      }
      else
      {
         mCurrentData.append(ch, start, length);

         if (mPrintStyle == STYLE_INDENTED
             && mSafeToIndent == true
             && mCurrentData.length() > 0)
         {
            mSafeToIndent = contentLooksSafeToIndent(mCurrentData.toString());
         }

         if (null == mDanglingStartTag)
         {
            emitContent();
         }

      }

/*
      String content = new String(ch, start, length);

      boolean safeToIndent = true;

      // Print any tag contents
      if (content.trim().length() > 0)
      {
         if (mPrintStyle == STYLE_INDENTED)
         {
         	safeToIndent = contentLooksSafeToIndent(content);

            if (safeToIndent)
            {
               mPrintStream.println();
               mPrintStream.print(StringUtil.polyChar(' ', mCurrentIndent + mIndentSize));
            }
         }

         mPrintStream.print(content);

//         if (mPrintStyle == STYLE_INDENTED && safeToIndent) mPrintStream.println();
      }
      else if (mPrintStyle == STYLE_INDENTED
               && mPreviousTagType == START_TAG)
      {
//         mPrintStream.println();
      }
*/

   }





   //---------------------------------------------------------------------------
   public void warning(SAXParseException e)
   {
      e.printStackTrace();
   }


   //---------------------------------------------------------------------------
   public void error(SAXParseException e)
   {
      e.printStackTrace();
   }


   //---------------------------------------------------------------------------
   public void skippedEntity(String name)
   throws SAXException
   {
 //            System.out.println("In skippedEntity(): " + name);

   }

   //---------------------------------------------------------------------------
   public void unparsedEntityDecl(String name,
                                  String publicId,
                                  String systemId,
                                  String notationName)
   throws SAXException
   {
 //            System.out.println("In unparsedEntityDecl(): " + name);

   }



   // LexicalHandler methods

   //--------------------------------------------------------------------------
   public void comment(char[] inChars, int offset, int length)
   {
//      System.out.println("comment: '" + new String(inChars, offset, length) + "'");


      if (mDanglingStartTag != null)
      {
         emitStartTag(false);
         mDanglingStartTag = null;
      }

      if (mCurrentData.length() > 0)
      {
         emitContent();
      }

 //     popIndentState();

      mCurrentComment.append(inChars, offset, length);
      emitComment();
/*
      if (mCurrentComment.length() > 0)
      {
         mCurrentComment.append(inChars, offset, length);

         if (mPrintStyle == STYLE_INDENTED
             && mSafeToIndent == true
             && mCurrentComment.length() > 0)
         {
            mSafeToIndent = contentLooksSafeToIndent(mCurrentComment.toString());
         }

         if (mDanglingStartTag != null)
         {
            emitStartTag(false);
            mDanglingStartTag = null;
         }

         emitComment();
      }
      else
      {
         mCurrentComment.append(inChars, offset, length);

         if (mPrintStyle == STYLE_INDENTED
             && mSafeToIndent == true
             && mCurrentComment.length() > 0)
         {
            mSafeToIndent = contentLooksSafeToIndent(mCurrentComment.toString());
         }

         if (null == mDanglingStartTag)
         {
            emitComment();
         }
      }
*/

   }

   //--------------------------------------------------------------------------
   public void startCDATA()
   {
   }

   //--------------------------------------------------------------------------
   public void endCDATA()
   {
   }

   //--------------------------------------------------------------------------
   public void startEntity(String name)
   {
   }

   //--------------------------------------------------------------------------
   public void endEntity(String name)
   {
   }

   //--------------------------------------------------------------------------
   public void startDTD(String name, String publicId, String systemId)
   {
   }

   //--------------------------------------------------------------------------
   public void endDTD()
   {
   }









   //---------------------------------------------------------------------------
   // Tags like the HTML pre tag are sensitive to leading and trailing spaces.
   private boolean contentLooksSafeToIndent(String inContent)
   {
      boolean response = true;

      if (Character.isWhitespace(inContent.charAt(0))
          || Character.isWhitespace(inContent.charAt(inContent.length() - 1)))
      {
         response = false;
      }

      return response;
   }

   //---------------------------------------------------------------------------
   private void emitStartTag(boolean inIsEmptyTag)
   {
/*
      if (mCurrentData.length() > 0)
      {
         if (mPrintStyle == STYLE_INDENTED)
         {
         	safeToIndent = contentLooksSafeToIndent(mCurrentData.toString());

            if (safeToIndent)
            {
               mPrintStream.println();
               mPrintStream.print(StringUtil.polyChar(' ', mCurrentIndent + mIndentSize));
            }
         }

         mPrintStream.print(mCurrentData.toString());

//         if (mPrintStyle == STYLE_INDENTED && safeToIndent) mPrintStream.println();
      }
 */

      if (mPrintStyle == STYLE_INDENTED)
      {
         mCurrentIndent += mIndentSize;
         if (mSafeToIndentStack.size() == 0
             || ((Boolean) mSafeToIndentStack.peek()).booleanValue())
         {
            mWriter.println();
            mWriter.print(StringUtil.polyChar(' ', mCurrentIndent));
         }

         if (inIsEmptyTag)
         {
            mCurrentIndent -= mIndentSize;
//            mPreviousTagType = END_TAG;
         }
         else
         {
            mPreviousTagType = START_TAG;
            pushIndentState();
            mSafeToIndent = true;
         }
      }

      mWriter.print(XMLUtil.composeStartTag(mDanglingStartTag.getTagName(),
                                            mDanglingStartTag.getAttributes(),
                                            inIsEmptyTag));

   }

   //---------------------------------------------------------------------------
   private void emitContent()
   {

      if (mPrintStyle == STYLE_INDENTED)
      {
         if (mSafeToIndent)
         {
            // Re-evaluate
            mSafeToIndent = contentLooksSafeToIndent(mCurrentData.toString());
         }

         if (mSafeToIndent)
         {
            mWriter.println();
            mWriter.print(StringUtil.polyChar(' ', mCurrentIndent + mIndentSize));
         }
      }

      mWriter.print(mCurrentData.toString());
      mCurrentData.setLength(0);

   }

   //---------------------------------------------------------------------------
   private void emitComment()
   {

      if (mPrintStyle == STYLE_INDENTED)
      {
 /*
         if (mSafeToIndent)
         {
            // Re-evaluate
            mSafeToIndent = contentLooksSafeToIndent(mCurrentComment.toString());
         }
*/
         mSafeToIndent = true;

         if (mSafeToIndent)
         {
            mWriter.println();
            mWriter.print(StringUtil.polyChar(' ', mCurrentIndent + mIndentSize));
         }
      }

      mWriter.print("");

      mCurrentComment.setLength(0);

   }

   //---------------------------------------------------------------------------
   private void pushIndentState()
   {
      mSafeToIndentStack.push(new Boolean(mSafeToIndent));
   }

   //---------------------------------------------------------------------------
   private void popIndentState()
   {
      mSafeToIndent = ((Boolean) mSafeToIndentStack.pop()).booleanValue();
   }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy