Browse Source

StrBuilder

Ranides Atterwim 10 years ago
parent
commit
938dbda7bc

+ 5 - 1
assira/src/main/java/net/ranides/assira/collection/arrays/NativeArray.java

@@ -241,7 +241,7 @@ public abstract class NativeArray implements Swapper, Comparable<NativeArray>, C
 	public static NativeArray wrap(Object array) {
         Class<?> c = array.getClass().getComponentType();
         if(null == c) {
-            throw new IllegalArgumentException(array + " is not an array.");
+            throw new IllegalArgumentException(array.getClass().getName() + " " + array + " is not an array.");
         }
         if(!c.isPrimitive()) {
             return new ObjectArray(array);
@@ -273,6 +273,10 @@ public abstract class NativeArray implements Swapper, Comparable<NativeArray>, C
         throw new UnsupportedOperationException("Not supported type: " + c.getName());
     }
     
+    public static NativeArray wrap(Object[] array) {
+		return new ObjectArray(array);
+    }
+    
     public static NativeArray wrap(int[] array) {
 		return new IntegerArray(array);
     }

+ 213 - 14
assira/src/main/java/net/ranides/assira/text/StrBuilder.java

@@ -9,6 +9,12 @@ package net.ranides.assira.text;
 import java.io.Reader;
 import java.io.Serializable;
 import java.io.Writer;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.EmptyStackException;
+import java.util.Iterator;
+import java.util.function.Function;
+import java.util.function.IntFunction;
 import net.ranides.assira.collection.arrays.ArrayAllocator;
 import net.ranides.assira.collection.arrays.ArrayUtils;
 import net.ranides.assira.collection.arrays.NativeArray;
@@ -24,6 +30,8 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
     
     private static final long serialVersionUID = 1L;
     
+    private static final StrBuilderOptions OPTIONS = new StrBuilderOptions();
+    
     protected static final char[] EMPTY = new char[0];
     
     protected static final int CAPACITY = 32;
@@ -32,9 +40,7 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
     
     private int size;
     
-    private String blank;
-    
-    private String endl;
+    Deque<BuilderState> options = new ArrayDeque<>(); // StrBuilderOptions.DEFAULT;
     
     public StrBuilder() {
         this(CAPACITY);
@@ -42,6 +48,11 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
 
     public StrBuilder(int initialCapacity) {
         buffer = new char[initialCapacity];
+        options.push(new BuilderState(OPTIONS));
+    }
+    
+    public StrBuilderOptions options() {
+        return options.peek();
     }
     
     @Override
@@ -103,6 +114,9 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
     
     public StrBuilder clear() {
         size = 0;
+        BuilderState state = options.peek().copy();
+        options.clear();
+        options.push(state);
         return this;
     }
     
@@ -115,7 +129,7 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
         NativeArrayUtils.reverse(size, NativeArray.wrap(buffer));
         return this;
     }
-    
+        
     public char[] getChars() {
         return getChars(0, size);
     }
@@ -179,22 +193,48 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
         return new SReader();
     }
     
-    public StrBuilder endl() {
-        return append(ValueUtils.or(endl, StringUtils.ENDL));
+    public StrBuilder open() {
+        options.push(options.peek().derive());
+        return append(options.peek().open());
     }
-        
-    public StrBuilder endl(String value) {
-        this.endl = value;
-        return this;
+    
+    public StrBuilder open(String open) {
+        options.push(options.peek().derive().open(open));
+        return append(open);
     }
-
-    public <T> StrBuilder blank(String value) {
-        this.blank = value;
+    
+    public StrBuilder open(String open, String close) {
+        options.push(options.peek().derive().open(open).close(close));
+        return append(open);
+    }
+    
+    public StrBuilder open(String open, String close, String separator) {
+        options.push(options.peek().derive().open(open).close(close).separator(separator));
+        return append(open);
+    }
+    
+    public StrBuilder close() {
+        if(options.size() <= 1) {
+            throw new IllegalStateException("There is not block to close.");
+        }
+        return append(options.pop().close());
+    }
+    
+    public StrBuilder item() {
+        BuilderState state = options.peek();
+        if(0 != state.counter++) {
+            append(state.separator());
+        }
         return this;
     }
     
+    public StrBuilder endl() {
+        return append(options.peek().endl());
+    }
+    
     public StrBuilder append() {
-        return null == blank ? this : append(blank);
+        String value = options.peek().blank();
+        return null == value ? this : append(value);
     }
 
     @Override
@@ -317,6 +357,67 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
         return append(String.valueOf(value));
     }
     
+    public StrBuilder append(Iterable<?> values) {
+		return append(values.iterator(), Object::toString);
+	}
+    
+    public <T> StrBuilder append(Iterable<? extends T> values, Function<T,String> function) {
+		return append(values.iterator(), function);
+	}
+    
+    public <T> StrBuilder append(Iterator<? extends T> values) {
+        return append(values, Object::toString);
+    }
+        
+    public <T> StrBuilder append(Iterator<? extends T> values, Function<T,String> function) {
+        if ( !values.hasNext() ) {
+            return this;
+        }
+        String separator = options.peek().separator();
+        for (;;) {
+            T item = values.next();
+            append(function.apply(item));
+            if ( !values.hasNext() ) {
+                return this;
+            }
+            append(separator);
+        }
+	}
+    
+    public <T> StrBuilder append(T[] values) {
+        return append(values, Object::toString);
+    }
+    
+    public <T> StrBuilder append(T[] values, Function<T,String> function) {
+        if ( 0==values.length ) {
+            return this;
+        }
+        String separator = options.peek().separator();
+		append(function.apply(values[0]));
+        for (int i=1; i<values.length; i++) {
+            append(separator).append(function.apply(values[i]));
+        }
+		return this;
+	}
+    
+    public <T> StrBuilder append(NativeArray values) {
+        return append(values, i -> String.valueOf(values.get(i)));
+    }
+    
+    public <T> StrBuilder append(NativeArray array, IntFunction<String> function) {
+        int n = array.size();
+        if ( 0==n ) {
+            return this;
+        }
+        String separator = options.peek().separator();
+		append(function.apply(0));
+        for (int i=1; i<n; i++) {
+            append(separator).append(function.apply(i));
+        }
+		return this;
+	}
+    
+    
     private final class SReader extends Reader {
         
         private int pos;
@@ -421,4 +522,102 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
         
     }
     
+    protected final class BuilderState extends StrBuilderOptions {
+        
+        private final StrBuilderOptions parent;
+        
+        protected int counter;
+        
+        private String blank;
+
+        private String endl;
+
+        private String separator;
+
+        private String open;
+
+        private String close;
+        
+        public BuilderState(StrBuilderOptions parent) {
+            this.parent = parent;
+        }
+        
+        public BuilderState derive() {
+            return new BuilderState(this);
+        }
+        
+        public BuilderState copy() {
+            BuilderState state = new BuilderState(OPTIONS);
+            
+            state.blank(this.blank());
+            state.endl(this.blank());
+            state.separator(this.separator());
+            state.open(this.open());
+            state.close(this.close());
+            
+            return state;
+        }
+
+        @Override
+        public StrBuilder builder() {
+            return StrBuilder.this;
+        }
+        
+        @Override
+        public String blank() {
+            return ValueUtils.or(blank, parent.blank());
+        }
+
+        @Override
+        public BuilderState blank(String value) {
+            this.blank = value;
+            return this;
+        }
+
+        @Override
+        public String endl() {
+            return ValueUtils.or(endl, parent.endl());
+        }
+
+        @Override
+        public BuilderState endl(String value) {
+            this.endl = value;
+            return this;
+        }
+
+        @Override
+        public String separator() {
+            return ValueUtils.or(separator, parent.separator());
+        }
+
+        @Override
+        public BuilderState separator(String value) {
+            this.separator = value;
+            return this;
+        }
+
+        @Override
+        public String open() {
+            return ValueUtils.or(open, parent.open());
+        }
+
+        @Override
+        public BuilderState open(String value) {
+            this.open = value;
+            return this;
+        }
+
+        @Override
+        public String close() {
+            return ValueUtils.or(close, parent.close());
+        }
+
+        @Override
+        public BuilderState close(String value) {
+            this.close = value;
+            return this;
+        }
+         
+    }
+    
 }

+ 65 - 0
assira/src/main/java/net/ranides/assira/text/StrBuilderOptions.java

@@ -0,0 +1,65 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.text;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class StrBuilderOptions {
+    
+    protected StrBuilderOptions() {
+        // do nothing
+    }
+    
+    public StrBuilder builder() {
+        throw new UnsupportedOperationException();
+    }
+    
+    public String blank() {
+        return null;
+    }
+
+    public StrBuilderOptions blank(String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String endl() {
+        return StringUtils.ENDL;
+    }
+
+    public StrBuilderOptions endl(String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String separator() {
+        return ", ";
+    }
+
+    public StrBuilderOptions separator(String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String open() {
+        return null;
+    }
+
+    public StrBuilderOptions open(String open) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String close() {
+        return null;
+    }
+
+    public StrBuilderOptions close(String close) {
+        throw new UnsupportedOperationException();
+    }
+    
+    
+    
+}

+ 61 - 0
assira/src/test/java/net/ranides/assira/text/StrBuilderTest.java

@@ -0,0 +1,61 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.text;
+
+import java.util.Arrays;
+import java.util.List;
+import net.ranides.assira.collection.arrays.NativeArray;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class StrBuilderTest {
+
+    @Test
+    public void testUsage() {
+        int[] array = new int[]{0xA2,0xB2,0xC2,0xD2};
+        List<Integer> list1 = Arrays.asList(0xA1,0xB1,0xC1,0xD1);
+        List<Integer> list2 = Arrays.asList(10,11,12,13);
+        List<Integer> list3 = Arrays.asList(3,4,5);
+        
+        StrBuilder sb = new StrBuilder();
+        sb
+            .options()
+                .open("{ ").close(" }").separator(",")
+            .builder()
+                .open().append(list1, Integer::toHexString).close()
+                .endl()
+                .open().append(NativeArray.wrap(array), i -> Integer.toHexString(array[i])).close()
+                .endl();
+        sb
+            .open("<", ">", ":").append(list2).close()
+            .endl()
+            .open("[", "]", " ")
+                .item().append("Q")
+                .item().append("P")
+                .item().open().append(list3).close()
+                .item().open("{","}").append(list3).close()
+            .close()
+            .endl();
+                
+        if(true) { // NOPMD
+            System.out.printf("%s%n", sb.toString());
+        }
+        
+        String expected = String.format(
+            "{ a1,b1,c1,d1 }%n" +
+            "{ a2,b2,c2,d2 }%n" +
+            "<10:11:12:13>%n" +
+            "[Q P [3 4 5] {3 4 5}]%n"
+        );
+        Assert.assertEquals(expected, sb.toString());
+    }
+    
+}