Package org.jboss.errai.bus.server.io

Source Code of org.jboss.errai.bus.server.io.JSONStreamDecoder$Context

/*
* Copyright 2010 JBoss, a divison Red Hat, 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.
*
*/

package org.jboss.errai.bus.server.io;

import org.jboss.errai.common.client.types.DecodingContext;
import org.jboss.errai.common.client.types.UHashMap;
import org.jboss.errai.common.client.types.UnsatisfiedForwardLookup;
import org.mvel2.CompileException;
import org.mvel2.util.StringAppender;

import java.io.*;
import java.nio.CharBuffer;
import java.util.*;

import static org.jboss.errai.bus.server.io.TypeDemarshallHelper.demarshallAll;
import static org.jboss.errai.common.client.protocols.SerializationParts.ENCODED_TYPE;

/**
* High-performance stream JSON parser. Provides the decoding algorithm to interpret the Errai Wire Protcol,
* including serializable types.  This parser always assumes the outer payload is a Map. So it probably shouldn't
* be used as a general parser.
*
* @author Mike Brock
* @since 1.1
*/
public class JSONStreamDecoder {
    private CharBuffer buffer;
    private BufferedReader reader;

    private char carry;

    private int read;
    private boolean initial = true;

    private DecodingContext decodingContext = new DecodingContext();

    public JSONStreamDecoder(InputStream inStream) {
        this.buffer = CharBuffer.allocate(25);
        try {
            this.reader = new BufferedReader(
                    new InputStreamReader(inStream, "UTF-8")
            );
        } catch (UnsupportedEncodingException e) {
            throw new Error("UTF-8 is not supported by this JVM?", e);
        }
    }

    public static Object decode(InputStream instream) throws IOException {
        return new JSONStreamDecoder(instream).parse();
    }

    public char read() throws IOException {
        if (carry != 0) {
            char c = carry;
            carry = 0;
            return c;
        }
        if (read <= 0) {
            if (!initial) buffer.rewind();
            initial = false;
            if ((read = reader.read(buffer)) <= 0) {
                return 0;
            }
            buffer.rewind();
        }
        read--;
        return buffer.get();
    }

    public Object parse() {
        try {
            Context ctx = new Context();
            Object v = _parse(ctx, null, false);

            if (decodingContext.isUnsatisfiedDependencies()) {
                TypeDemarshallHelper.resolveDependencies(decodingContext);
            }

            return v;
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Object _parse(Context ctx, Object collection, boolean map) throws IOException {
        char c;
        StringAppender appender;
        while ((c = read()) != 0) {
            switch (c) {
                case '[':
                    ctx.addValue(_parse(new Context(), new ArrayList(), false));
                    break;

                case '{':
                    ctx.addValue(_parse(new Context(), new UHashMap(), true));
                    break;

                case ']':
                case '}':
                    if (map && ctx.encodedType) {
                        ctx.encodedType = false;
                        try {
                            return demarshallAll(ctx.record(collection, decodingContext), decodingContext);
                        }
                        catch (Exception e) {
                            throw new RuntimeException("Could not demarshall object", e);
                        }
                    } else {
                        return ctx.record(collection, decodingContext);
                    }

                case ',':
                    ctx.record(collection, decodingContext);
                    break;

                case '"':
                case '\'':
                    char term = c;
                    appender = new StringAppender();
                    StrCapture:
                    while ((c = read()) != 0) {
                        switch (c) {
                            case '\\':
                                appender.append(handleEscapeSequence());
                                break;
                            case '"':
                            case '\'':
                                if (c == term) {
                                    ctx.addValue(appender.toString());
                                    term = 0;
                                    break StrCapture;
                                }
                                break;
                            default:
                                appender.append(c);
                        }
                    }

                    if (term != 0) {
                        throw new RuntimeException("unterminated string literal");
                    }

                    break;

                case ':':
                    continue;

                default:
                    if (isValidNumberPart(c)) {
                        ctx.addValue(parseNumber(c));
                        break;
                    } else if (Character.isJavaIdentifierPart(c)) {
                        appender = new StringAppender().append(c);

                        while (((c = read()) != 0) && Character.isJavaIdentifierPart(c)) {
                            appender.append(c);
                        }

                        String s = appender.toString();

                        if (s.length() > 4) ctx.addValue(s);
                        else if ("null".equals(s)) {
                            ctx.addValue(null);
                        } else if ("true".equals(s)) {
                            ctx.addValue(Boolean.TRUE);
                        } else if ("false".equals(s)) {
                            ctx.addValue(Boolean.FALSE);
                        } else {
                            ctx.addValue(s);
                        }

                        if (c != 0) carry = c;
                    }
            }
        }


        return ctx.record(collection, decodingContext);
    }

    public char handleEscapeSequence() throws IOException {
        char c;
        switch (c = read()) {
            case '\\':
                return '\\';
            case 'b':
                return '\b';
            case 'f':
                return '\f';
            case 't':
                return '\t';
            case 'r':
                return '\r';
            case 'n':
                return '\n';
            case '\'':
                return '\'';
            case '"':
                return '\"';
            default:
                throw new CompileException("illegal escape sequence: " + c);
        }
    }

    public Number parseNumber(char c) throws IOException {
        long val = 0;
        double dVal = 0;

        double factor = 1;
        boolean dbl = false;

        char[] buf = new char[21];
        int len = 0;
        do {
            buf[len++] = c;
        } while ((c = read()) != 0 && isValidNumberPart(c));

        if (c != 0) {
            carry = c;
        }

        if (len == 1 && buf[0] == '-') return null;

        for (int i = len - 1; i != -1; i--) {
            switch (buf[i]) {
                case '.':
                    dVal = ((double) val) / factor;
                    val = 0;
                    factor = 1;
                    dbl = true;
                    continue;
                case '-':
                    if (i != 0) {
                        throw new NumberFormatException(new String(buf));
                    }
                    val = -val;
                    break;
                case '1':
                    val += factor;
                    break;
                case '2':
                    val += 2 * factor;
                    break;
                case '3':
                    val += 3 * factor;
                    break;
                case '4':
                    val += 4 * factor;
                    break;
                case '5':
                    val += 5 * factor;
                    break;
                case '6':
                    val += 6 * factor;
                    break;
                case '7':
                    val += 7 * factor;
                    break;
                case '8':
                    val += 8 * factor;
                    break;
                case '9':
                    val += 9 * factor;
                    break;
            }

            factor *= 10;
        }
        if (dbl) {
            return dVal + val;
        } else {
            return val;
        }
    }

    private static boolean isValidNumberPart(char c) {
        switch (c) {
            case '.':
            case '-':
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                return true;
            default:
                return false;
        }
    }

    private static class Context {
        Object lhs;
        Object rhs;
        boolean encodedType = false;

        private Context() {
        }

        private Object addValue(Object val) {
            if (lhs == null) {
                return lhs = val;
            } else {
                return rhs = val;
            }
        }

        private Object getValue() {
            if (rhs != null) {
                return rhs;
            } else {
                return lhs;
            }
        }

        private void removeValue() {
            if (rhs != null) {
                rhs = null;
            } else {
                lhs = null;
            }
        }

        private Object record(Object collection, DecodingContext ctx) {
            try {
                if (lhs != null) {
                    if (collection instanceof Map) {
                        if (!encodedType) encodedType = ENCODED_TYPE.equals(lhs);

                        boolean rec = true;
                        if (lhs instanceof UnsatisfiedForwardLookup) {
                            ctx.addUnsatisfiedDependency(collection, (UnsatisfiedForwardLookup) lhs);
                            if (!(rhs instanceof UnsatisfiedForwardLookup)) {
                                ((UnsatisfiedForwardLookup) lhs).setVal(rhs);
                                ((UnsatisfiedForwardLookup) lhs).setPath("{}");
                            }
                            rec = false;
                        }
                        if (rhs instanceof UnsatisfiedForwardLookup) {
                            ctx.addUnsatisfiedDependency(collection, (UnsatisfiedForwardLookup) rhs);
                            ((UnsatisfiedForwardLookup) rhs).setKey(lhs);
                            ((UnsatisfiedForwardLookup) rhs).setPath(String.valueOf(lhs));
                            rec = false;
                        }

                        //noinspection unchecked
                        if (rec)
                            ((Map) collection).put(lhs, rhs);

                    } else {
                        if (collection == null) return lhs;

                        if (lhs instanceof UnsatisfiedForwardLookup) {
                            ctx.addUnsatisfiedDependency(collection, (UnsatisfiedForwardLookup) lhs);
                        } else {
                            //noinspection unchecked
                            ((Collection) collection).add(lhs);
                        }
                    }
                }
                return collection;
            }
            catch (ClassCastException e) {
                throw new RuntimeException("error building collection", e);
            }
            finally {
                lhs = rhs = null;
            }
        }
    }

 
}
TOP

Related Classes of org.jboss.errai.bus.server.io.JSONStreamDecoder$Context

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.