Minor refactoring in Emitter to improve performance: save calls to Constant.has()
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ab5c80c..62a3575 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -7,6 +7,9 @@
 	</properties>

 	<body>

           <release version="1.8-SNAPSHOT" date="in Mercurial" description="Performance improvement">

+            <action dev="py4fun" type="update">

+                Minor refactoring in Emitter to improve performance: save calls to Constant.has() (2010-09-13)

+            </action>

             <action dev="maslovalex" type="update">

                 Minor refactorings in Emitter to improve performance: create less Strings [r9185e0b3] (2010-09-10)

             </action>

diff --git a/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
index c263871..81b753d 100644
--- a/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
+++ b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
@@ -30,7 +30,6 @@
 
 import org.yaml.snakeyaml.DumperOptions;
 import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
-import org.yaml.snakeyaml.error.YAMLException;
 import org.yaml.snakeyaml.events.AliasEvent;
 import org.yaml.snakeyaml.events.CollectionEndEvent;
 import org.yaml.snakeyaml.events.CollectionStartEvent;
@@ -865,14 +864,6 @@
         if ("!".equals(tag)) {
             return tag;
         }
-        for (int i = 0; i < tag.length(); i++) {
-            char ch = tag.charAt(i);
-            if (Constant.URI_CHARS.hasNo(ch)) {
-                // TODO Tag (URI) may not contain non-ASCII characters
-                // (it seems that resources do not use proper encoding)
-                throw new YAMLException("Tag (URI) may not contain non-ASCII characters.");
-            }
-        }
         String handle = null;
         String suffix = tag;
         for (String prefix : tagPrefixes.keySet()) {
@@ -978,7 +969,8 @@
                 }
             }
             // Check for line breaks, special, and unicode characters.
-            if (Constant.LINEBR.has(ch)) {
+            boolean isLineBreak = Constant.LINEBR.has(ch);
+            if (isLineBreak) {
                 lineBreaks = true;
             }
             if (!(ch == '\n' || ('\u0020' <= ch && ch <= '\u007E'))) {
@@ -1005,7 +997,7 @@
                 }
                 previousSpace = true;
                 previousBreak = false;
-            } else if (Constant.LINEBR.has(ch)) {
+            } else if (isLineBreak) {
                 if (index == 0) {
                     leadingBreak = true;
                 }
@@ -1024,9 +1016,9 @@
 
             // Prepare for the next character.
             index++;
-            preceededByWhitespace = Constant.NULL_BL_T_LINEBR.has(ch);
-            followedByWhitespace = (index + 1 >= scalar.length() || Constant.NULL_BL_T_LINEBR
-                    .has(scalar.charAt(index + 1)));
+            preceededByWhitespace = Constant.NULL_BL_T.has(ch) || isLineBreak;
+            followedByWhitespace = (index + 1 >= scalar.length()
+                    || (Constant.NULL_BL_T.has(scalar.charAt(index + 1))) || isLineBreak);
         }
         // Let's decide what styles are allowed.
         boolean allowFlowPlain = true;
diff --git a/src/main/java/org/yaml/snakeyaml/scanner/Constant.java b/src/main/java/org/yaml/snakeyaml/scanner/Constant.java
index 346a32d..75b9b2d 100644
--- a/src/main/java/org/yaml/snakeyaml/scanner/Constant.java
+++ b/src/main/java/org/yaml/snakeyaml/scanner/Constant.java
@@ -26,6 +26,7 @@
     private final static String NULL_OR_LINEBR_S = "\0" + FULL_LINEBR_S;

     private final static String NULL_BL_LINEBR_S = " " + NULL_OR_LINEBR_S;

     private final static String NULL_BL_T_LINEBR_S = "\t" + NULL_BL_LINEBR_S;

+    private final static String NULL_BL_T_S = "\0 \t";

     private final static String URI_CHARS_S = ALPHA_S + "-;/?:@&=+$,_.!~*\'()[]%";

 

     public final static Constant LINEBR = new Constant(LINEBR_S);

@@ -33,6 +34,7 @@
     public final static Constant NULL_OR_LINEBR = new Constant(NULL_OR_LINEBR_S);

     public final static Constant NULL_BL_LINEBR = new Constant(NULL_BL_LINEBR_S);

     public final static Constant NULL_BL_T_LINEBR = new Constant(NULL_BL_T_LINEBR_S);

+    public final static Constant NULL_BL_T = new Constant(NULL_BL_T_S);

     public final static Constant URI_CHARS = new Constant(URI_CHARS_S);

 

     public final static Constant ALPHA = new Constant(ALPHA_S);

diff --git a/src/test/java/org/yaml/snakeyaml/StressEmitterTest.java b/src/test/java/org/yaml/snakeyaml/StressEmitterTest.java
new file mode 100644
index 0000000..f9da446
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/StressEmitterTest.java
@@ -0,0 +1,78 @@
+/**

+ * Copyright (c) 2008-2010, http://code.google.com/p/snakeyaml/

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.yaml.snakeyaml;

+

+import java.io.IOException;

+

+import junit.framework.Test;

+import junit.framework.TestCase;

+import junit.framework.TestSuite;

+

+public class StressEmitterTest extends TestCase {

+

+    public static void main(String args[]) {

+        junit.textui.TestRunner.run(suite());

+    }

+

+    public static Test suite() {

+        return new TestSuite(StressEmitterTest.class);

+    }

+

+    public void testPerformance() throws IOException {

+        JavaBeanLoader<Invoice> loader = new JavaBeanLoader<Invoice>(Invoice.class);

+        Invoice invoice = loader.load(Util.getLocalResource("specification/example2_27.yaml"));

+        JavaBeanDumper dumper = new JavaBeanDumper();

+        long time1 = System.nanoTime();

+        dumper.dump(invoice);

+        long time2 = System.nanoTime();

+        float duration = (time2 - time1) / 1000000;

+        System.out.println("\nSingle dump was " + duration + " ms.");

+

+        int[] range = new int[] { 1000, 2000 /* , 8000 */};

+        System.out.println("\nOne instance.");

+        for (int number : range) {

+            time1 = System.nanoTime();

+            for (int i = 0; i < number; i++) {

+                dumper.dump(invoice);

+            }

+            time2 = System.nanoTime();

+            duration = ((time2 - time1) / 1000000) / (float) number;

+            System.out.println("Duration for r=" + number + " was " + duration + " ms/dump.");

+            // cobertura may make it very slow

+            if (duration > 3) {

+                System.err.println("!!!!!! Too long. Expected <1 but was " + duration);

+            }

+        }

+

+        System.out.println("\nMany instances.");

+        for (int number : range) {

+            time1 = System.nanoTime();

+            for (int i = 0; i < number; i++) {

+                dumper = new JavaBeanDumper();

+                dumper.dump(invoice);

+            }

+            time2 = System.nanoTime();

+            duration = ((time2 - time1) / 1000000) / (float) number;

+            System.out.println("Duration for r=" + number + " was " + duration + " ms/dump.");

+            // cobertura may make it very slow

+            if (duration > 3) {

+                System.err.println("!!!!!! Too long. Expected <1 but was " + duration);

+            }

+            // assertTrue("duration=" + duration, duration < 3);

+        }

+    }

+}
\ No newline at end of file