blob: 77853a3016c5e5ec46c86f8333159295f99c5487 [file] [log] [blame]
Sho SHIMIZUe4efe452015-08-26 15:06:55 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Sho SHIMIZUe4efe452015-08-26 15:06:55 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.ovsdb.rfc.utils;
17
18import io.netty.buffer.ByteBuf;
19import io.netty.buffer.ByteBufInputStream;
20
21import java.io.IOException;
22import java.util.List;
23import java.util.Stack;
24
Sho SHIMIZU8700d422015-08-26 18:13:44 -070025import org.onosproject.ovsdb.rfc.exception.UnsupportedException;
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070026import org.onosproject.ovsdb.rfc.jsonrpc.JsonReadContext;
27
28import com.fasterxml.jackson.core.JsonEncoding;
29import com.fasterxml.jackson.core.JsonParseException;
30import com.fasterxml.jackson.core.JsonParser;
31import com.fasterxml.jackson.core.io.IOContext;
32import com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper;
33import com.fasterxml.jackson.core.util.BufferRecycler;
34import com.fasterxml.jackson.databind.JsonNode;
35import com.fasterxml.jackson.databind.MappingJsonFactory;
36
37/**
38 * Decoder utility class.
39 */
40public final class JsonRpcReaderUtil {
41
42 /**
43 * Constructs a JsonRpcReaderUtil object. Utility classes should not have a
44 * public or default constructor, otherwise IDE will compile unsuccessfully.
45 * This class should not be instantiated.
46 */
47 private JsonRpcReaderUtil() {
48 }
49
50 /**
51 * Decode the bytes to Json object.
52 * @param in input of bytes
53 * @param out ouput of Json object list
54 * @param jrContext context for the last decoding process
55 * @throws IOException IOException
56 * @throws JsonParseException JsonParseException
57 */
58 public static void readToJsonNode(ByteBuf in, List<Object> out, JsonReadContext jrContext)
59 throws JsonParseException, IOException {
60 int lastReadBytes = jrContext.getLastReadBytes();
61 if (lastReadBytes == 0) {
62 if (in.readableBytes() < 4) {
63 return;
64 }
65 checkEncoding(in);
66 }
67
68 int i = lastReadBytes + in.readerIndex();
69 Stack<Byte> bufStack = jrContext.getBufStack();
70 for (; i < in.writerIndex(); i++) {
71 byte b = in.getByte(i);
72 switch (b) {
73 case '{':
74 if (!isDoubleQuote(bufStack)) {
75 bufStack.push(b);
76 jrContext.setStartMatch(true);
77 }
78 break;
79 case '}':
80 if (!isDoubleQuote(bufStack)) {
81 bufStack.pop();
82 }
83 break;
84 case '"':
85 if (in.getByte(i - 1) != '\\') {
86 if (!bufStack.isEmpty() && bufStack.peek() != '"') {
87 bufStack.push(b);
88 } else {
89 bufStack.pop();
90 }
91 }
92 break;
93 default:
94 break;
95 }
96
97 if (jrContext.isStartMatch() && bufStack.isEmpty()) {
98 ByteBuf buf = in.readSlice(i - in.readerIndex() + 1);
99 JsonParser jf = new MappingJsonFactory().createParser(new ByteBufInputStream(buf));
100 JsonNode jsonNode = jf.readValueAsTree();
101 out.add(jsonNode);
102 lastReadBytes = 0;
103 jrContext.setLastReadBytes(lastReadBytes);
104 break;
105 }
106 }
107
108 if (i >= in.writerIndex()) {
109 lastReadBytes = in.readableBytes();
110 jrContext.setLastReadBytes(lastReadBytes);
111 }
112 }
113
114 /**
115 * Filter the invalid characters before decoding.
116 * @param in input of bytes
117 * @param lastReadBytes the bytes for last decoding incomplete record
118 */
119 private static void fliterCharaters(ByteBuf in) {
120 while (in.isReadable()) {
121 int ch = in.getByte(in.readerIndex());
122 if ((ch != ' ') && (ch != '\n') && (ch != '\t') && (ch != '\r')) {
123 break;
124 } else {
125 in.readByte();
126 }
127 }
128 }
129
130 /**
131 * Check whether the peek of the stack element is double quote.
132 * @param jrContext context for the last decoding process
133 * @return boolean
134 */
135 private static boolean isDoubleQuote(Stack<Byte> bufStack) {
136 if (!bufStack.isEmpty() && bufStack.peek() == '"') {
137 return true;
138 }
139 return false;
140 }
141
142 /**
143 * Check whether the encoding is valid.
144 * @param in input of bytes
145 * @throws IOException this is an IO exception
146 * @throws UnsupportedException this is an unsupported exception
147 */
148 private static void checkEncoding(ByteBuf in) throws IOException {
149 int inputStart = 0;
150 int inputLength = 4;
151 fliterCharaters(in);
152 byte[] buff = new byte[4];
153 in.getBytes(in.readerIndex(), buff);
154 ByteSourceJsonBootstrapper strapper = new ByteSourceJsonBootstrapper(new IOContext(new BufferRecycler(),
155 null,
156 false),
157 buff, inputStart,
158 inputLength);
159 JsonEncoding jsonEncoding = strapper.detectEncoding();
160 if (!JsonEncoding.UTF8.equals(jsonEncoding)) {
161 throw new UnsupportedException("Only UTF-8 encoding is supported.");
162 }
163 }
164
165}