blob: b7662efb0b3bf4fa631df197dcbe674d9a8547b1 [file] [log] [blame]
Sho SHIMIZUe4efe452015-08-26 15:06:55 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700118 */
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.
kdarapufce5abb2018-05-10 19:37:53 +0530132 * @param bufStack context for the last decoding process
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700133 * @return boolean
134 */
135 private static boolean isDoubleQuote(Stack<Byte> bufStack) {
kdarapufce5abb2018-05-10 19:37:53 +0530136 return !bufStack.isEmpty() && bufStack.peek() == '"';
Sho SHIMIZUe4efe452015-08-26 15:06:55 -0700137 }
138
139 /**
140 * Check whether the encoding is valid.
141 * @param in input of bytes
142 * @throws IOException this is an IO exception
143 * @throws UnsupportedException this is an unsupported exception
144 */
145 private static void checkEncoding(ByteBuf in) throws IOException {
146 int inputStart = 0;
147 int inputLength = 4;
148 fliterCharaters(in);
149 byte[] buff = new byte[4];
150 in.getBytes(in.readerIndex(), buff);
151 ByteSourceJsonBootstrapper strapper = new ByteSourceJsonBootstrapper(new IOContext(new BufferRecycler(),
152 null,
153 false),
154 buff, inputStart,
155 inputLength);
156 JsonEncoding jsonEncoding = strapper.detectEncoding();
157 if (!JsonEncoding.UTF8.equals(jsonEncoding)) {
158 throw new UnsupportedException("Only UTF-8 encoding is supported.");
159 }
160 }
161
162}