blob: 070808fd81190219d51495093ca41efef92b518e [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
Jian Li43f103d2017-02-03 02:00:35 +090021import java.io.DataInput;
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070022import java.io.IOException;
23import java.util.List;
24import java.util.Stack;
25
Sho SHIMIZU8700d422015-08-26 18:13:44 -070026import org.onosproject.ovsdb.rfc.exception.UnsupportedException;
Sho SHIMIZUe4efe452015-08-26 15:06:55 -070027import org.onosproject.ovsdb.rfc.jsonrpc.JsonReadContext;
28
29import com.fasterxml.jackson.core.JsonEncoding;
30import com.fasterxml.jackson.core.JsonParseException;
31import com.fasterxml.jackson.core.JsonParser;
32import com.fasterxml.jackson.core.io.IOContext;
33import com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper;
34import com.fasterxml.jackson.core.util.BufferRecycler;
35import com.fasterxml.jackson.databind.JsonNode;
36import com.fasterxml.jackson.databind.MappingJsonFactory;
37
38/**
39 * Decoder utility class.
40 */
41public final class JsonRpcReaderUtil {
42
43 /**
44 * Constructs a JsonRpcReaderUtil object. Utility classes should not have a
45 * public or default constructor, otherwise IDE will compile unsuccessfully.
46 * This class should not be instantiated.
47 */
48 private JsonRpcReaderUtil() {
49 }
50
51 /**
52 * Decode the bytes to Json object.
53 * @param in input of bytes
54 * @param out ouput of Json object list
55 * @param jrContext context for the last decoding process
56 * @throws IOException IOException
57 * @throws JsonParseException JsonParseException
58 */
59 public static void readToJsonNode(ByteBuf in, List<Object> out, JsonReadContext jrContext)
60 throws JsonParseException, IOException {
61 int lastReadBytes = jrContext.getLastReadBytes();
62 if (lastReadBytes == 0) {
63 if (in.readableBytes() < 4) {
64 return;
65 }
66 checkEncoding(in);
67 }
68
69 int i = lastReadBytes + in.readerIndex();
70 Stack<Byte> bufStack = jrContext.getBufStack();
71 for (; i < in.writerIndex(); i++) {
72 byte b = in.getByte(i);
73 switch (b) {
74 case '{':
75 if (!isDoubleQuote(bufStack)) {
76 bufStack.push(b);
77 jrContext.setStartMatch(true);
78 }
79 break;
80 case '}':
81 if (!isDoubleQuote(bufStack)) {
82 bufStack.pop();
83 }
84 break;
85 case '"':
86 if (in.getByte(i - 1) != '\\') {
87 if (!bufStack.isEmpty() && bufStack.peek() != '"') {
88 bufStack.push(b);
89 } else {
90 bufStack.pop();
91 }
92 }
93 break;
94 default:
95 break;
96 }
97
98 if (jrContext.isStartMatch() && bufStack.isEmpty()) {
99 ByteBuf buf = in.readSlice(i - in.readerIndex() + 1);
Jian Li43f103d2017-02-03 02:00:35 +0900100 JsonParser jf = new MappingJsonFactory().createParser((DataInput) new ByteBufInputStream(buf));
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700101 JsonNode jsonNode = jf.readValueAsTree();
102 out.add(jsonNode);
103 lastReadBytes = 0;
104 jrContext.setLastReadBytes(lastReadBytes);
105 break;
106 }
107 }
108
109 if (i >= in.writerIndex()) {
110 lastReadBytes = in.readableBytes();
111 jrContext.setLastReadBytes(lastReadBytes);
112 }
113 }
114
115 /**
116 * Filter the invalid characters before decoding.
117 * @param in input of bytes
118 * @param lastReadBytes the bytes for last decoding incomplete record
119 */
120 private static void fliterCharaters(ByteBuf in) {
121 while (in.isReadable()) {
122 int ch = in.getByte(in.readerIndex());
123 if ((ch != ' ') && (ch != '\n') && (ch != '\t') && (ch != '\r')) {
124 break;
125 } else {
126 in.readByte();
127 }
128 }
129 }
130
131 /**
132 * Check whether the peek of the stack element is double quote.
133 * @param jrContext context for the last decoding process
134 * @return boolean
135 */
136 private static boolean isDoubleQuote(Stack<Byte> bufStack) {
137 if (!bufStack.isEmpty() && bufStack.peek() == '"') {
138 return true;
139 }
140 return false;
141 }
142
143 /**
144 * Check whether the encoding is valid.
145 * @param in input of bytes
146 * @throws IOException this is an IO exception
147 * @throws UnsupportedException this is an unsupported exception
148 */
149 private static void checkEncoding(ByteBuf in) throws IOException {
150 int inputStart = 0;
151 int inputLength = 4;
152 fliterCharaters(in);
153 byte[] buff = new byte[4];
154 in.getBytes(in.readerIndex(), buff);
155 ByteSourceJsonBootstrapper strapper = new ByteSourceJsonBootstrapper(new IOContext(new BufferRecycler(),
156 null,
157 false),
158 buff, inputStart,
159 inputLength);
160 JsonEncoding jsonEncoding = strapper.detectEncoding();
161 if (!JsonEncoding.UTF8.equals(jsonEncoding)) {
162 throw new UnsupportedException("Only UTF-8 encoding is supported.");
163 }
164 }
165
166}