Prize and Milestone as mutable state idioms

"Tyler Close" <[email protected]>
Newsgroups gmane.comp.lang.e.general
Message-ID <[email protected]>
Hi all,

I was recently refactoring the JSON serialization code in the Waterken
Server to make auditing its correctness easier. In doing so, I wrote
two small utility classes, each one to enforce the update pattern the
code was using for one of its variables. For example, I was using a
boolean variable to keep track of whether or not the most recent JSON
value had been fully and successfully output. The update pattern here
was that the variable started out false and then transitioned to
forever being true. Verifying that this property was correctly
implemented previously required examining the full file for mentions
of the boolean variable to check that it was only ever assigned the
value true. The Milestone class encapsulates a boolean variable that
can only be updated in this way. There's a similar story for the Prize
class. I'm thinking both of these classes may be generally useful and
help communicate the programmer's intention for a mutable variable. If
they are, I think I'll upgrade them to being reusable parts of the
ref_send library. So, I'm looking for answers to a few questions:

1. Do the Prize and Milestone classes make the JSON serialization code
more reviewable?
2. Do you suspect these classes would make other algorithms more reviewable?
3. Is the ref_send library the right place to host a collection of
these utility classes for capability programming idioms?

See the attached Joe-E source file for the current JSON serialization code.

--Tyler

_______________________________________________
e-lang mailing list
[email protected]
http://www.eros-os.org/mailman/listinfo/e-lang
ValueWriter.java (text/java, 9.2 KB)
// Copyright 2008 Waterken Inc. under the terms of the MIT X license
// found at http://www.opensource.org/licenses/mit-license.html
package org.waterken.syntax.json;

import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;

import org.joe_e.Struct;

/**
 * A JSON writer.
 * <p>
 * A client can only output a syntactically correct JSON value, or leave the
 * {@link ValueWriter} in a {@linkplain #isWritten detectable} error state.
 * </p>
 */
/* package */ final class
ValueWriter extends Struct {
    static protected final String newLine = "\r\n";
    static private   final String tab = "  ";
    
    static protected final class
    Prize<T> {
    	private T value;
    	
    	protected
    	Prize(final T value) {
    		this.value = value;
    	}
    	
    	protected T
    	claim() {
    		final T r = value;
    		value = null;
    		return r;
    	}
    }
    
    static protected final class
    Milestone {
    	private boolean marked;
    	
    	protected
    	Milestone(final boolean marked) {
    		this.marked = marked;
    	}
    	
    	protected boolean
    	is() { return marked; }
    	
    	protected void
    	mark() {
    		marked = true;
    	}
    }
    
    private final String indent;		// indentation for this JSON value
    private final Prize<Writer> output;
    private final Milestone written;
    
    protected
    ValueWriter(final String indent, final Writer out) {
        this.indent = indent;
        this.output = new Prize<Writer>(out);
        written = new Milestone(null == out);
    }
    
    /**
     * @return <code>true</code> if a single JSON value was successfully
     * 		   written, else <code>false</code>
     */
    protected boolean
    isWritten() { return written.is(); }
    
    protected ObjectWriter
    startObject() throws IOException {
        final Writer out = output.claim();
        out.write('{');
        return new ObjectWriter(out);
    }
    
    protected final class
    ObjectWriter {
        static private final String comma = "," + newLine;
        
        private final String inset;			// indentation for each member
        private final Writer out;			
        private       String prefix;		// current member separator prefix
        private       ValueWriter member;	// most recent member started	
        
        protected
        ObjectWriter(final Writer out) {
            inset = indent + tab;
            this.out = out;
            prefix = newLine;
            member = new ValueWriter(inset, null);
        }
        
        protected void
        close() throws IOException {
            if (!member.isWritten()) { throw new NullPointerException(); }
            
            member = null;		// prevent future calls to this object
            out.write(newLine);
            out.write(indent);
            out.write('}');
            written.mark();		// mark the containing value successful
        }
        
        protected ValueWriter
        startMember(final String name) throws IOException {
            if (!member.isWritten()) { throw new NullPointerException(); }
            
            member = new ValueWriter(inset, out); // prevent calls until after
            									  // new member is complete
            out.write(prefix);
            out.write(inset);
            writeStringTo(name, out);
            out.write(" : ");
            prefix = comma;
            return member;
        }
    }
    
    protected ArrayWriter
    startArray() throws IOException {
        final Writer out = output.claim();
        out.write('[');
        return new ArrayWriter(out);
    }
    
    protected final class
    ArrayWriter {
        static private final String comma = ", ";
        
        private final String inset;			// indentation for each element
        private final Writer out;
        private       String prefix;		// current element separator prefix
        private       ValueWriter element;	// most recent element started
        
        protected
        ArrayWriter(final Writer out) {
            inset = indent + tab;
            this.out = out;
            prefix = " ";
            element = new ValueWriter(inset, null);
        }

        protected void
        close() throws IOException {
            if (!element.isWritten()) { throw new NullPointerException(); }
            
            element = null;		// prevent future calls to this object
            out.write(" ]");
            written.mark();		// mark the containing value successful
        }
        
        protected ValueWriter
        startElement() throws IOException {
            if (!element.isWritten()) { throw new NullPointerException(); }
            
            element = new ValueWriter(inset, out); // prevent calls until after
			  									   // new element is complete
            out.write(prefix);
            prefix = comma;
            return element;
        }
    }
    
    protected void
    writeLink(final String URL) throws IOException {
        final Writer out = output.claim();
        out.write("{ \"@\" : ");
        writeStringTo(URL, out);
        out.write(" }");
        written.mark();
    }
    
    protected void
    writeNull() throws IOException {
        output.claim().write("null");
        written.mark();
    }
    
    protected void
    writeBoolean(final boolean value) throws IOException {
        output.claim().write(value ? "true" : "false");
        written.mark();
    }
    
    protected void
    writeByte(final byte value) throws IOException {
        output.claim().write(Byte.toString(value));
        written.mark();
    }
    
    protected void
    writeShort(final short value) throws IOException {
        output.claim().write(Short.toString(value));
        written.mark();
    }
    
    protected void
    writeInt(final int value) throws IOException {
        output.claim().write(Integer.toString(value));
        written.mark();
    }
    
    protected void
    writeLong(final long value) throws IOException {
        output.claim().write(Long.toString(value));
        written.mark();
    }
    
    protected void
    writeInteger(final BigInteger value) throws IOException {
        output.claim().write(value.toString());
        written.mark();
    }
    
    protected void
    writeFloat(final float value) throws IOException {
        output.claim().write(Float.toString(value));
        written.mark();
    }
    
    protected void
    writeDouble(final double value) throws IOException {
        output.claim().write(Double.toString(value));
        written.mark();
    }
    
    protected void
    writeDecimal(final BigDecimal value) throws IOException {
        output.claim().write(value.toString());
        written.mark();
    }
    
    protected void
    writeString(final String value) throws IOException {
        writeStringTo(value, output.claim());
        written.mark();
    }
    
    static private void
    writeStringTo(final String value, final Writer out) throws IOException {
        out.write('\"');
        final int len = value.length();
        for (int i = 0; i != len; ++i) {
            final char c = value.charAt(i);
            switch (c) {
            case '\"':
                out.write("\\\"");
                break;
            case '\\':
                out.write("\\\\");
                break;
            case '\b':
                out.write("\\b");
                break;
            case '\f':
                out.write("\\f");
                break;
            case '\n':
                out.write("\\n");
                break;
            case '\r':
                out.write("\\r");
                break;
            case '\t':
                out.write("\\t");
                break;
            case ' ':
                out.write(c);
                break;
            default:
                switch (Character.getType(c)) {
                case Character.UPPERCASE_LETTER:
                case Character.LOWERCASE_LETTER:
                case Character.TITLECASE_LETTER:
                case Character.MODIFIER_LETTER:
                case Character.OTHER_LETTER:
                case Character.NON_SPACING_MARK:
                case Character.ENCLOSING_MARK:
                case Character.COMBINING_SPACING_MARK:
                case Character.DECIMAL_DIGIT_NUMBER:
                case Character.LETTER_NUMBER:
                case Character.OTHER_NUMBER:
                case Character.DASH_PUNCTUATION:
                case Character.START_PUNCTUATION:
                case Character.END_PUNCTUATION:
                case Character.CONNECTOR_PUNCTUATION:
                case Character.OTHER_PUNCTUATION:
                case Character.MATH_SYMBOL:
                case Character.CURRENCY_SYMBOL:
                case Character.MODIFIER_SYMBOL:
                case Character.INITIAL_QUOTE_PUNCTUATION:
                case Character.FINAL_QUOTE_PUNCTUATION:
                    out.write(c);
                    break;
                default:
                    out.write("\\u");
                    final int unicode = c;
                    for (int shift = 16; 0 != shift;) {
                        shift -= 4;
                        final int hex = (unicode >> shift) & 0x0F;
                        out.write(hex < 10 ? '0' + hex : 'A' + (hex - 10));
                    }
                }
            }
        }
        out.write('\"');
    }
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.