You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commons-dev@ws.apache.org by ve...@apache.org on 2009/01/26 20:33:48 UTC

svn commit: r737796 - in /webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src: main/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilter.java test/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilterTest.java

Author: veithen
Date: Mon Jan 26 19:33:48 2009
New Revision: 737796

URL: http://svn.apache.org/viewvc?rev=737796&view=rev
Log:
Some improvements and fixes in XMLFormatFilter.

Modified:
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilter.java
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilterTest.java

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilter.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilter.java?rev=737796&r1=737795&r2=737796&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilter.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilter.java Mon Jan 26 19:33:48 2009
@@ -18,45 +18,124 @@
 
 /**
  * Filter that reformats XML data so that it is properly indented.
+ * <p>
+ * Note that this filter only works if the stream is encoded in an
+ * ASCII compatible charset encoding (ASCII, UTF-8, ISO-8859-x, etc.).
  */
 public class XmlFormatFilter implements StreamFilter {
+    private static final int STATE_WHITESPACE = 0;
+    private static final int STATE_START_TAG = 1;
+    private static final int STATE_END_TAG = 2;
+    private static final int STATE_PI = 3;
+    private static final int STATE_TEXT = 4;
+    
     private final int tabWidth;
+    private int state = STATE_TEXT;
     private boolean firstIndent = true;
-    private int nextIndent = -1;
-    private int previousIndent = -1;
+    private boolean endTagRequiresIndent = false;
+    private int level = -1; // The current element level (root = 0)
     
     public XmlFormatFilter(int tabWidth) {
         this.tabWidth = tabWidth;
     }
 
+    private static boolean isSpace(int b) {
+        return b == ' ' || b == '\r' || b == '\n' || b == '\t';
+    }
+
+    private static boolean isNameStartChar(int b) {
+        // This covers the ASCII subset of the NameStartChar production, minus the colon
+        return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' || b == '_';
+    }
+
+    private void indent(Stream stream, int amount) {
+        if (firstIndent) {
+            firstIndent = false;
+        } else {
+            stream.insert((byte)'\n');
+        }
+        for (int i = 0; i < tabWidth * amount; i++) {
+            stream.insert((byte)' ');
+        }
+    }
+
     public void invoke(Stream stream) {
         try {
             while (stream.available() > 0) {
-                boolean doIndent = false;
-                if (stream.get(0) == '<' && stream.get(1) != '/') {
-                    previousIndent = nextIndent++;
-                    doIndent = true;
-                } else if (stream.get(0) == '<' && stream.get(1) == '/') {
-                    doIndent = previousIndent > nextIndent;
-                    previousIndent = nextIndent--;
-                } else if ((stream.get(0) == '/' || stream.get(0) == '?')
-                        && stream.get(1) == '>') {
-                    previousIndent = nextIndent--;
-                }
-                if (doIndent) {
-                    if (firstIndent) {
-                        firstIndent = false;
-                    } else {
-                        stream.insert((byte) '\n');
+                switch (state) {
+                    case STATE_WHITESPACE: {
+                        int i = 0;
+                        int c;
+                        while (isSpace(c = stream.get(i))) {
+                            i++;
+                        }
+                        if (c == '<') {
+                            stream.discard(i);
+                        } else {
+                            stream.skip(i);
+                        }
+                        state = STATE_TEXT;
+                        break;
                     }
-                    for (int i = tabWidth * nextIndent; i > 0; i--) {
-                        stream.insert((byte) ' ');
+                    case STATE_TEXT: {
+                        if (stream.get(0) == '<') {
+                            int c = stream.get(1);
+                            if (c == '/') {
+                                if (endTagRequiresIndent) {
+                                    indent(stream, level);
+                                }
+                                stream.skip(2);
+                                state = STATE_END_TAG;
+                            } else if (c == '?') {
+                                indent(stream, level+1);
+                                stream.skip(2);
+                                state = STATE_PI;
+                            } else if (isNameStartChar(c)) {
+                                indent(stream, ++level);
+                                stream.skip(2);
+                                state = STATE_START_TAG;
+                            } else {
+                                stream.skip(1);
+                            }
+                        } else {
+                            stream.skip(1);
+                        }
+                        break;
+                    }
+                    case STATE_START_TAG: {
+                        if (stream.get(0) == '/' && stream.get(1) == '>') {
+                            stream.skip(2);
+                            level--;
+                            state = STATE_WHITESPACE;
+                            endTagRequiresIndent = true;
+                        } else if (stream.get(0) == '>') {
+                            stream.skip(1);
+                            state = STATE_WHITESPACE;
+                            endTagRequiresIndent = false;
+                        } else {
+                            stream.skip(1);
+                        }
+                        break;
+                    }
+                    case STATE_END_TAG: {
+                        if (stream.get(0) == '>') {
+                            level--;
+                            state = STATE_WHITESPACE;
+                            endTagRequiresIndent = true;
+                        }
+                        stream.skip(1);
+                        break;
+                    }
+                    case STATE_PI: {
+                        if (stream.get(0) == '?' && stream.get(1) == '>') {
+                            stream.skip(2);
+                            state = STATE_WHITESPACE;
+                            endTagRequiresIndent = true;
+                        } else {
+                            stream.skip(1);
+                        }
+                        break;
                     }
-                }
-                if (stream.get(0) != '\n' && stream.get(0) != '\r') {
-                    stream.skip();
-                } else {
-                    stream.discard();
                 }
             }
         } catch (ArrayIndexOutOfBoundsException ex) {

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilterTest.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilterTest.java?rev=737796&r1=737795&r2=737796&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilterTest.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/XmlFormatFilterTest.java Mon Jan 26 19:33:48 2009
@@ -35,4 +35,14 @@
     public void test3() {
         assertFormat("<root>\n  <a>test</a>\n</root>", "<root><a>test</a></root>");
     }
+    
+    public void test4() {
+        assertFormat("<root>\n  <child>\n    <a/>\n  </child>\n</root>",
+                     "<root><child><a/></child></root>");
+    }
+
+    public void test5() {
+        assertFormat("<root>\n  <child>\n    <a>test</a>\n  </child>\n</root>",
+                     "<root><child><a>test</a></child></root>");
+    }
 }