net.agkn.field_stripe.stripe.XMLFieldStripeReader.java Source code

Java tutorial

Introduction

Here is the source code for net.agkn.field_stripe.stripe.XMLFieldStripeReader.java

Source

package net.agkn.field_stripe.stripe;

/*
 * Copyright 2012 Aggregate Knowledge, Inc.
 *
 * 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. 
 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;

import net.agkn.field_stripe.exception.DeveloperException;
import net.agkn.field_stripe.exception.OperationFailedException;
import net.agkn.field_stripe.record.IField;
import net.agkn.field_stripe.record.PrimitiveType;
import net.agkn.field_stripe.stripe.Instruction.Kind;
import net.jcip.annotations.NotThreadSafe;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;

/**
 * An XML-based {@link IFieldStripeReader} that reads {@link PrimitiveType primitive}
 * based leaf-fields. This is used primarily for debugging as it is easily 
 * human and machine readable. It is hand-rolled due to its simplicity and to 
 * limit the dependencies (rather than using a third-party XML library). All 
 * instructions <i>must</i> be newline delimited.<p/>
 * 
 * @author rgrzywinski
 * @see XMLFieldStripeWriter
 */
@NotThreadSafe
public class XMLFieldStripeReader implements IFieldStripeReader {
    // the field for which this is a reader and the primitive type of the field
    private final IField field;
    private final PrimitiveType fieldType;

    // ------------------------------------------------------------------------
    private final BufferedReader reader;

    // ========================================================================
    /**
     * @param field the {@link IField} for which this reader is reading. This
     *        cannot be <code>null</code> and its type must be a {@link PrimitiveType}.
     * @param reader the {@link Reader} from which the XML is read. This cannot
     *        be <code>null</code>.
     */
    public XMLFieldStripeReader(final IField field, final Reader reader) {
        this.field = field;
        this.fieldType = (PrimitiveType) field.getType();

        this.reader = new BufferedReader(reader);
    }

    // ========================================================================
    /* (non-Javadoc)
     * @see net.agkn.field_stripe.stripe.IFieldStripeReader#readNextState()
     */
    @Override
    public Instruction readInstruction() throws OperationFailedException {
        // read the next line. If there isn't one then return null (by contract)
        final String line;
        try {
            line = reader.readLine();
            if (StringUtils.isBlank(line))
                return null/*end-of-stream (by contract)*/;
        } catch (final IOException ioe) {
            throw new OperationFailedException(ioe);
        }

        // extract the element name (between "<" and either "/>" or ">")
        if (line.length() < 3)
            throw new OperationFailedException("Invalid instruction in field \"" + field.getName() + "\": " + line);
        final int endIndex = line.indexOf('>');
        if (endIndex < 0)
            throw new OperationFailedException("Invalid instruction in field \"" + field.getName() + "\": " + line);
        final String elementName = line.substring(0, (endIndex + 1/*include the '>'*/));

        // determine the instruction and based on it extract any value that
        // might exist
        if (XMLFieldStripeWriter.UNSET.equals(elementName))
            return Instruction.UNSET;
        else if (XMLFieldStripeWriter.PARENT_IS_UNSET_START.equals(elementName))
            return new Instruction(Kind.UNSET_PARENT, Integer.parseInt(extractValue(line)));
        else if (XMLFieldStripeWriter.REPEATED_PARENT_START.equals(elementName))
            return new Instruction(Kind.REPEATED_PARENT, Integer.parseInt(extractValue(line)));
        else if (XMLFieldStripeWriter.REPEATED_VALUE.equals(elementName))
            return Instruction.REPEATED_VALUE;
        else if (XMLFieldStripeWriter.VALUE_START.equals(elementName))
            return new Instruction(Kind.VALUE, parseValue(extractValue(line)));
        else /*unknown element name*/
            throw new OperationFailedException(
                    "Unknown instruction in field \"" + field.getName() + "\": " + elementName);
    }

    /**
     * Extracts and returns the value from the specified XML instruction string.
     */
    private String extractValue(final String instructionString) throws OperationFailedException {
        // the value exists between the begin and end XML elements (specifically,
        // between the first ">" and "<")
        final int beginIndex = instructionString.indexOf('>');
        if (beginIndex < 0)
            throw new OperationFailedException(
                    "Invalid instruction in field \"" + field.getName() + "\": " + instructionString);
        final int endIndex = instructionString.indexOf('<', beginIndex);
        if (endIndex < 0)
            throw new OperationFailedException(
                    "Invalid instruction in field \"" + field.getName() + "\": " + instructionString);
        return instructionString.substring((beginIndex + 1/*after '>'*/), endIndex);
    }

    /**
     * Parses the specified non-<code>null</code> string value into the appropriate
     * type based on the {@link IField field's} {@link IField#getType() type}.
     */
    private Object parseValue(final String stringValue) {
        switch (fieldType) {
        case BYTE:
            return Byte.parseByte(stringValue);
        case SHORT:
            return Short.parseShort(stringValue);
        case INT:
            return Integer.parseInt(stringValue);
        case LONG:
            return Long.parseLong(stringValue);
        case FLOAT:
            return Float.parseFloat(stringValue);
        case DOUBLE:
            return Double.parseDouble(stringValue);
        case BOOLEAN:
            return Boolean.parseBoolean(stringValue);
        case STRING:
            return StringEscapeUtils.unescapeXml(stringValue);

        default:
            throw new DeveloperException("Unknown field type in field \"" + field.getName() + ".");
        }
    }
}