6c8683780e7c679560b8f883eaa3cacf91c26388
[aaf/authz.git] / misc / rosetta / src / main / java / org / onap / aaf / misc / rosetta / OutJson.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.misc.rosetta;
23
24 import java.io.IOException;
25 import java.io.Writer;
26 import java.util.Stack;
27
28 import org.onap.aaf.misc.env.util.IndentPrintWriter;
29
30 public class OutJson extends Out {
31
32     @Override
33     public<IN,S> void extract(IN in, Writer writer, Parse<IN, S> prs, boolean ... options) throws IOException, ParseException {
34         Parsed<S> p = prs.newParsed();
35         IndentPrintWriter ipw;
36         if (options.length>0 && options[0]) { // is Pretty
37             ipw = writer instanceof IndentPrintWriter?(IndentPrintWriter)writer:new IndentPrintWriter(writer);
38             writer = ipw;
39         } else {
40             ipw = null;
41         }
42         
43         // If it's a fragment, print first Object Name.  If root Object, skip first name
44         Stack<LevelStack> jsonLevel = new Stack<LevelStack>();
45         jsonLevel.push(new LevelStack(options.length>1 && options[1]));
46         boolean print = true, hadData=false;
47         char afterName=0, beforeName=0, maybe = 0, prev=0;
48         
49         int count = 0;
50         while ((p = prs.parse(in,p.reuse())).valid()) {
51             ++count;
52             switch(p.event) {
53                 case 1: 
54                     continue;
55                 case 2:
56                     if (count==2) { // it's empty, write open/close on it's own
57                         writer.append('{');
58                         writer.append('}');
59                     }
60                     writer.flush();
61                     return;
62                 case '{':
63                     afterName = '{';
64                     if (jsonLevel.peek().printObjectName) {
65                         print = true;
66                     } else { // don't print names on first
67                         print=false; 
68                     }
69                     maybe=jsonLevel.peek().listItem();
70                     jsonLevel.push(new LevelStack(true));
71                     break;
72                 case '}':
73                     if (p.hasData()) { // if we have data, we print that, so may need to prepend a comma.
74                         maybe = jsonLevel.peek().listItem();
75                     } else { // No data means just print, 
76                         p.name = ""; // XML tags come through with names, but no data
77                     } 
78                     print = true;
79                     jsonLevel.pop();
80                     afterName = p.event;
81                     break;
82                 case '[':
83                     afterName = p.event;
84                     if ((prev==',' && !hadData) || prev==']')maybe=',';
85                     else maybe = jsonLevel.peek().listItem();
86
87                     jsonLevel.push(new LevelStack(false));
88                     print=true;
89                     break;
90                 case ']':
91                     afterName = p.event;
92                     if (p.hasData()) {
93                         if (prev==',' && !hadData)maybe=',';
94                         else maybe = jsonLevel.peek().listItem();
95                     } else {
96                         p.name = ""; // XML tags come through with names, but no data
97                     } 
98                     jsonLevel.pop();
99
100                     print = true;
101                     break;
102                 case   3:
103                 case ',':
104                     if (!p.hasData()) {
105                         p.isString=false;
106                         print=false;
107                     } else {
108                         maybe=jsonLevel.peek().listItem();
109                         print = true;
110                     }
111                     break;
112                 default:
113                     print = true;
114             }
115         
116             if (maybe!=0) {
117                 if (ipw==null)writer.append(maybe); 
118                 else ipw.println(maybe);
119                 maybe = 0;
120             }
121             
122             if (beforeName!=0) {
123                 if (ipw==null)writer.append(beforeName);
124                 else ipw.println(beforeName);
125                 beforeName = 0;
126             }
127             if (print) {
128                 if (p.hasName()) {
129                     writer.append('"');
130                     if (p.event==3)writer.append("__");
131                     writer.append(p.name);
132                     writer.append("\":");
133                 } 
134                 if (p.hasData()) {
135                     if (p.isString) {
136                         writer.append('"');
137                         escapedWrite(writer, p.sb);
138                         writer.append('"');
139                     } else if (p.sb.length()>0) {
140                         writer.append(p.sb);
141                     }
142                 }
143             }
144             if (afterName!=0) {
145                 if (ipw==null)writer.append(afterName);
146                 else {
147                     switch(afterName) {
148                         case '{':
149                             ipw.println(afterName);
150                             ipw.inc();
151                             break;
152                         case '}':
153                             ipw.dec();
154                             ipw.println();
155                             ipw.print(afterName);
156                             break;
157                         case ']':
158                             if (prev=='}' || prev==',')ipw.println();
159                             ipw.dec();
160                             ipw.print(afterName);
161                             break;
162
163                         case ',':
164                             ipw.println(afterName);
165                             break;
166                         default:
167                             ipw.print(afterName);
168                     }
169                 }
170                 afterName = 0;
171             }
172             
173             if (ipw!=null) {
174                 switch(p.event) {
175                     case '[':
176                         ipw.inc();
177                         ipw.println();
178                         break;
179                 }
180             }
181             prev = p.event;
182             hadData = p.hasData();
183
184         }
185         writer.flush();
186     }
187
188     private void escapedWrite(Writer writer, StringBuilder sb) throws IOException {
189         char c;
190         for (int i=0;i<sb.length();++i) {
191             switch(c=sb.charAt(i)) {
192                 case '\\':
193                     writer.append(c);
194                     if (i<sb.length()) {
195                         c=sb.charAt(++i);
196                         writer.append(c);
197                     }
198                     break;
199                 case '"':
200                     writer.append('\\');
201                     // Passthrough on purpose
202                 default:
203                     writer.append(c);
204             }
205         }
206
207         
208     }
209
210     @Override
211     public String logName() {
212         return "Rosetta JSON";
213     }
214
215     private static class LevelStack {
216         public boolean printObjectName=false;
217         private boolean first_n_List=true;
218         
219         public LevelStack(boolean printObjectName) {
220             this.printObjectName = printObjectName;
221         }
222         
223         public char listItem() {
224             if (first_n_List) {
225                 first_n_List=false;
226                 return 0;
227             } else {
228                 return ',';
229             }
230         }
231     }
232 }