/* * Copyright 2005 Joe Walker * * 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.directwebremoting.json.parse.impl; import java.math.BigDecimal; import java.util.LinkedList; import org.directwebremoting.json.parse.JsonDecoder; import org.directwebremoting.json.parse.JsonParseException; /** * A stateful implementation of {@link JsonDecoder} where we track the stack * of objects and allow a subclass to have a simpler set of things to do * @author Joe Walker [joe at getahead dot ltd dot uk] */ public abstract class StatefulJsonDecoder implements JsonDecoder { /** * Create a new object. * This method should not actually add the object to the <code>parent</code>. * That will be done by {@link #addMemberToArray(Object, Object)} * @param parent The parent object that this will be added to. This will be * null if we are creating the root object * @param propertyName The member name if we are adding a property to an * object or null if we are adding to an array */ protected abstract Object createObject(Object parent, String propertyName) throws JsonParseException; /** * Create a new array. * This method should not actually add the object to the <code>parent</code>. * That will be done by {@link #addMemberToArray(Object, Object)} * @param parent The parent object that this will be added to. This will be * null if we are creating the root object * @param propertyName The member name if we are adding a property to an * object or null if we are adding to an array */ protected abstract Object createArray(Object parent, String propertyName) throws JsonParseException; /** * Add the given <code>value</code> to the <code>propertyName</code> property * of the <code>parent</code> object. * @throws JsonParseException If there are any errors in adding the value */ protected abstract void addMemberToObject(Object parent, String propertyName, Object member) throws JsonParseException; /** * Add the given <code>value</code> to the <code>parent</code> array. * @throws JsonParseException If there are any errors in adding the value */ protected abstract void addMemberToArray(Object parent, Object member) throws JsonParseException; /* (non-Javadoc) * @see org.directwebremoting.json.parse.JsonDecoder#getRoot() */ public Object getRoot() throws JsonParseException { return last; } /* (non-Javadoc) * @see org.directwebremoting.json.parse.impl.StatefulJsonDecoder#beginObject() */ public void beginObject(String propertyName) throws JsonParseException { Object obj = createObject(stackPeek(), propertyName); stack.addLast(obj); } /* (non-Javadoc) * @see org.directwebremoting.json.parse.impl.StatefulJsonDecoder#endObject() */ public void endObject(String propertyName) throws JsonParseException { this.last = stack.removeLast(); // Don't try to add the top level object to its parent if (!stack.isEmpty()) { add(propertyName, last); } } /* (non-Javadoc) * @see org.directwebremoting.json.parse.impl.StatefulJsonDecoder#beginArray() */ public void beginArray(String propertyName) throws JsonParseException { Object obj = createArray(stackPeek(), propertyName); stack.addLast(obj); } /* (non-Javadoc) * @see org.directwebremoting.json.parse.impl.StatefulJsonDecoder#endArray() */ public void endArray(String propertyName) throws JsonParseException { this.last = stack.removeLast(); // Don't add the top level object to its parent if (!stack.isEmpty()) { add(propertyName, last); } } /** * Add the add methods do basically the same thing - add themselves to the * current object or array. */ public void add(String propertyName, Object value) throws JsonParseException { if (propertyName == null) { addMemberToArray(stackPeek(), value); } else { addMemberToObject(stackPeek(), propertyName, value); } } /* (non-Javadoc) * @see org.directwebremoting.json.parse.JsonDecoder#addString(java.lang.String) */ public void addString(String propertyName, String value) throws JsonParseException { add(propertyName, value); } /* (non-Javadoc) * @see org.directwebremoting.json.parse.JsonDecoder#addNumber(java.lang.String, java.lang.String, java.lang.String, java.lang.String) */ public void addNumber(String propertyName, String intPart, String floatPart, String expPart) throws JsonParseException { Object value = realizeNumber(intPart, floatPart, expPart); add(propertyName, value); } /* (non-Javadoc) * @see org.directwebremoting.json.parse.JsonDecoder#addBoolean(boolean) */ public void addBoolean(String propertyName, boolean value) throws JsonParseException { add(propertyName, value); } /* (non-Javadoc) * @see org.directwebremoting.json.parse.JsonDecoder#addNull() */ public void addNull(String propertyName) throws JsonParseException { add(propertyName, null); } /** * Peek at the top of the stack without changing it, or return null if there * is nothing on the stack. */ protected Object stackPeek() { if (stack.isEmpty()) { return null; } else { return stack.getLast(); } } /** * The stack of objects that we have created. Sometimes (particularly with * objects there are a number of states within an object, so we need to * maintain a separate mode stack. */ protected final LinkedList<Object> stack = new LinkedList<Object>(); /** * {@link #getRoot()} is called after we have removed the last object on the * stack, so we need to remember what it was. */ protected Object last = null; /** * Create a {@link BigDecimal}, double, int, or long depending on the input * strings. * @param intPart A string of [0-9]* representing the integer part of the * number. * @param floatPart A string of \.[0-9]* representing the floating point part * of the number. This INCLUDES the period * @param expPart A string of [eE][+-]?[0-9]* representing the integer part * of the number. This includes the 'e' or 'E'. */ public static Object realizeNumber(String intPart, String floatPart, String expPart) { if (expPart != null) { return new BigDecimal(intPart + floatPart + expPart); } else if (floatPart != null) { return Double.parseDouble(intPart + floatPart); } else { try { return Integer.parseInt(intPart); } catch (NumberFormatException ex) { try { return Long.parseLong(intPart); } catch (NumberFormatException ex2) { return new BigDecimal(intPart); } } } } }