AT&T 2.0.19 Code drop, stage 1
[aaf/authz.git] / misc / rosetta / src / main / java / org / onap / aaf / misc / rosetta / InXML.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.Reader;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Stack;
31
32 import org.onap.aaf.misc.env.Env;
33 import org.onap.aaf.misc.env.TimeTaken;
34 import org.onap.aaf.misc.rosetta.InXML.State;
35
36 public class InXML implements Parse<Reader, State> {
37         // package on purpose
38         JaxInfo jaxInfo;
39
40         public InXML(JaxInfo jaxInfo) {
41                 this.jaxInfo = jaxInfo;
42         }
43         
44         public InXML(Class<?> cls, String ... rootNs) throws SecurityException, NoSuchFieldException, ClassNotFoundException, ParseException {
45                 jaxInfo = JaxInfo.build(cls,rootNs);
46         }
47
48
49         // @Override
50         public Parsed<State> parse(Reader r, Parsed<State> parsed) throws ParseException {
51                 State state = parsed.state;
52                 
53                 // OK, before anything else, see if there is leftover processing, if so, do it!
54                 if(state.unevaluated!=null) {
55                         DerTag dt = state.unevaluated;
56                         state.unevaluated = null;
57                         if(!state.greatExp.eval(parsed, dt))return parsed;
58                 }
59
60                 if(state.hasAttributes()) {
61                         Prop prop = state.pop();
62                         parsed.event = Parse.ATTRIB;
63                         parsed.name = prop.tag;
64                         parsed.sb.append(prop.value);
65                         parsed.isString=true;
66                         return parsed;
67                 }
68                 int ch;
69                 char c;
70                 boolean inQuotes = false, escaped = false;
71
72                 StringBuilder sb = parsed.sb, tempSB = new StringBuilder();
73                 boolean go = true;
74                 
75                 try {
76                         while(go && (ch=r.read())>=0) {
77                                 c = (char)ch;
78                                 if(c == '"') {
79                                         if(state.greatExp instanceof LeafExpectations) { // within a set of Tags, make a Quote
80                                                 sb.append(c);
81                                         } else {
82                                                 if(inQuotes) {
83                                                         if(escaped) {
84                                                                 sb.append('\\');
85                                                                 sb.append(c);
86                                                                 escaped = false;
87                                                         } else {
88                                                                 inQuotes = false;
89                                                         }
90                                                 } else {
91                                                         parsed.isString=true;
92                                                         inQuotes = true;
93                                                 }
94                                         }
95                                 } else if(inQuotes) {
96                                         sb.append(c);
97                                 } else if(c=='&') {
98                                         XmlEscape.xmlEscape(sb,r);
99                                 } else {
100                                         switch(c) {
101                                                 case '<':
102                                                         DerTag tag=new DerTag().parse(r, tempSB);
103                                                         go = state.greatExp.eval(parsed, tag);
104                                                         break;
105                                                 default:
106                                                         // don't add Whitespace to start of SB... saves removing later
107                                                         if(sb.length()>0) {
108                                                                 sb.append(c);
109                                                         } else if(!Character.isWhitespace(c)) { 
110                                                                 sb.append(c);
111                                                         }
112                                                 }
113                                 }
114                         }
115                         return parsed;
116                 } catch (IOException e) {
117                         throw new ParseException(e);
118                 }
119         }
120         
121         public static final class DerTag {
122                 public String name;
123                 public boolean isEndTag;
124                 public List<Prop> props;
125                 private boolean isXmlInfo;
126                 //private String ns; 
127                 
128                 public DerTag() {
129                         name=null;
130                         isEndTag = false;
131                         props = null;
132                         isXmlInfo = false;
133                 }
134                 
135                 public DerTag parse(Reader r, StringBuilder sb) throws ParseException {
136                         int ch;
137                         char c;
138                         boolean inQuotes = false, escaped = false;
139                         boolean go = true;
140                         String tag = null;
141                         
142                         try {
143                                 if((ch = r.read())<0) throw new ParseException("Reader content ended before complete");
144                                 if(ch=='?') {
145                                         isXmlInfo = true;
146                                 }
147                                 // TODO Check for !-- comments
148                                 do {
149                                         c=(char)ch;
150                                         if(c=='"') {
151                                                         if(inQuotes) {
152                                                                 if(escaped) {
153                                                                         sb.append(c);
154                                                                         escaped = false;
155                                                                 } else {
156                                                                         inQuotes = false;
157                                                                 }
158                                                         } else {
159                                                                 inQuotes = true;
160                                                         }
161                                         } else if(inQuotes) {
162                                                 sb.append(c);
163                                         } else {
164                                                 switch(c) {
165                                                         case '/':
166                                                                 isEndTag = true;
167                                                                 break;
168                                                         case ' ':
169                                                                 endField(tag,sb);
170                                                                 tag = null;
171                                                                 break;
172                                                         case '>':
173                                                                 endField(tag,sb);
174                                                                 go = false;
175                                                                 break;
176                                                         case '=':
177                                                                 tag = sb.toString();
178                                                                 sb.setLength(0);
179                                                                 break;
180 //                                                      case ':':
181 //                                                              ns = sb.toString();
182 //                                                              sb.setLength(0);
183 //                                                              break;
184                                                         case '?':
185                                                                 if(!isXmlInfo)sb.append(c);
186                                                                 break;
187                                                         default:
188                                                                 sb.append(c);
189                                                 }
190                                         }
191                                 } while(go && (ch=r.read())>=0);
192                         } catch (IOException e) {
193                                 throw new ParseException(e);
194                         }
195                         return this;
196                 }
197
198                 private void endField(String tag, StringBuilder sb) {
199                         if(name==null) {
200                                 name = sb.toString();
201                                 sb.setLength(0);
202                         } else {
203                                 String value = sb.toString();
204                                 sb.setLength(0);
205                                 if(tag !=null && value != null) {
206                                         if(props==null)props = new ArrayList<Prop>();
207                                         props.add(new Prop(tag,value));
208                                 }
209                         }
210                 }
211                 
212                 public String toString() {
213                         StringBuilder sb = new StringBuilder();
214                         sb.append(isEndTag?"End":"Start");
215                         sb.append(" Tag\n");
216                         sb.append("  Name: ");
217                         sb.append(name);
218                         if(props!=null) for(Prop p : props) {
219                                 sb.append("\n     ");
220                                 sb.append(p.tag);
221                                 sb.append("=\"");
222                                 sb.append(p.value);
223                                 sb.append('"');
224                         }
225                         return sb.toString();
226                 }
227         }
228         
229         private static class ArrayState {
230                 public boolean firstObj = true;
231                 public boolean didNext = false;
232         }
233
234         public static class State {
235                 public GreatExpectations greatExp;
236                 public DerTag unevaluated;
237                 public Stack<ArrayState> arrayInfo;
238                 private List<Prop> attribs;
239                 private int idx;
240                 public State(JaxInfo ji, DerTag dt) throws ParseException {
241                         greatExp = new RootExpectations(this, ji, null);
242                         unevaluated = null;
243                         attribs = null;;
244                 }
245                 
246                 public boolean hasAttributes() {
247                         return attribs!=null && idx<attribs.size();
248                 }
249
250                 public void push(Prop prop) {
251                         if(attribs==null) {
252                                 attribs = new ArrayList<Prop>();
253                                 idx = 0;
254                         }
255                         attribs.add(prop);
256                 }
257                 
258                 public Prop pop() {
259                         Prop rv = null;
260                         if(attribs!=null) {
261                                 rv = attribs.get(idx++);
262                                 if(idx>=attribs.size())attribs = null;
263                         }
264                         return rv;
265                 }
266         }
267         
268         private static abstract class GreatExpectations {
269                 protected JaxInfo ji;
270                 protected GreatExpectations prev;
271                 private Map<String,String> ns;
272                 
273                 public GreatExpectations(State state, JaxInfo curr, GreatExpectations prev, DerTag derTag) throws ParseException {
274                         this.prev = prev;
275                         ns = null;
276                         ji = getDerived(state, curr,derTag);
277                 }
278                 
279                 public abstract boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException;
280
281                 // Recursively look back for any namespaces
282                 protected Map<String,String> getNS() {
283                         if(ns!=null)return ns;
284                         if(prev!=null) {
285                                 return prev.getNS();
286                         }
287                         return null;
288                 }
289
290                 private void addNS(Prop prop) {
291                         Map<String,String> existingNS = getNS();
292                         if(ns==null)ns = new HashMap<String,String>();
293                         // First make a copy of previous NSs so that we have everything we need, but can overwrite, if necessary
294                         if(existingNS!=null && ns!=existingNS) {
295                                 ns.putAll(ns);
296                         }
297                         ns.put(prop.tag, prop.value);
298                 }
299
300                 private JaxInfo getDerived(State state, JaxInfo ji, DerTag derTag) throws ParseException {
301                         if(derTag==null)return ji;
302                         
303                         List<Prop> props = derTag.props;
304                         
305                         Prop derived = null;
306                         if(props!=null) {
307                                 // Load Namespaces (if any)
308                                 for(Prop prop : props) {
309                                         if(prop.tag.startsWith("xmlns:")) {
310                                                 addNS(prop);
311                                         }
312                                 }
313                                 for(Prop prop : props) {
314                                         if(prop.tag.endsWith(":type")) {
315                                                 int idx = prop.tag.indexOf(':');
316                                                 String potentialNS = "xmlns:"+prop.tag.substring(0,idx);
317                                                 Map<String,String> ns = getNS();
318                                                 boolean noNamespace = false;
319                                                 if(ns==null) {
320                                                         noNamespace = true;
321                                                 } else {
322                                                         String nsVal = ns.get(potentialNS);
323                                                         if(nsVal==null) noNamespace = true;
324                                                         else {
325                                                                 derived = new Prop(Parsed.EXTENSION_TAG,prop.value);
326                                                                 state.push(derived);
327                                                         }
328                                                 }
329                                                 if(noNamespace) {
330                                                         throw new ParseException(prop.tag + " utilizes an invalid Namespace prefix");
331                                                 }
332                                         } else if(!prop.tag.startsWith("xmlns")) {
333                                                 state.push(prop);
334                                         }
335                                 }
336                         }
337                         return derived==null?ji:ji.getDerived(derived.value);
338                 }
339         }
340         
341         private static class RootExpectations extends GreatExpectations {
342                 
343                 public RootExpectations(State state, JaxInfo curr, GreatExpectations prev) throws ParseException {
344                         super(state,curr,prev, null);
345                 }
346                 
347                 // @Override
348                 public boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException {
349                         if(derTag.isXmlInfo) {
350                                 parsed.event = START_DOC;
351                         } else if(ji.name.equals(derTag.name)) {
352                                 if(derTag.isEndTag) {
353                                         parsed.event = END_DOC;
354                                         parsed.state.greatExp = prev;
355                                 } else {
356                                         //parsed.name = derTag.name;
357                                         parsed.event = START_OBJ;
358                                         parsed.state.greatExp = new ObjectExpectations(parsed.state,ji, this, false, derTag);   
359                                 }
360                         }
361                         return false;
362                 }
363         }
364         
365         private static class ObjectExpectations extends GreatExpectations {
366                 private boolean printName;
367
368                 public ObjectExpectations(State state, JaxInfo curr, GreatExpectations prev, boolean printName, DerTag derTag) throws ParseException {
369                         super(state, curr, prev, derTag);
370                         this.printName=printName;
371                 }
372
373                 // @Override
374                 public boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException {
375                         if(derTag.isEndTag && ji.name.equals(derTag.name)) {
376                                 parsed.state.greatExp = prev;
377                                 parsed.event = END_OBJ;
378                                 if(printName)parsed.name = ji.name;
379                         } else {
380                                 //Standard Members
381                                 for(JaxInfo memb : ji.members) {
382                                         if(memb.name.equals(derTag.name)) {
383                                                 parsed.name = memb.name;
384                                                 if(memb.isArray) {
385                                                         parsed.state.unevaluated = derTag; // evaluate within Array Context
386                                                         parsed.event = START_ARRAY;
387                                                         parsed.state.greatExp = new ArrayExpectations(parsed.state,memb,this);
388                                                         return false;
389                                                 } else if(memb.isObject()) {
390                                                         if(derTag.isEndTag) {
391                                                                 throw new ParseException("Unexpected End Tag </" + derTag.name + '>');
392                                                         } else {
393                                                                 parsed.event = START_OBJ;
394
395                                                                 parsed.state.greatExp = new ObjectExpectations(parsed.state, memb,this,true,derTag);
396                                                                 return false;
397                                                         }
398                                                 } else { // a leaf
399                                                         if(derTag.isEndTag) {
400                                                                  throw new ParseException("Misplaced End Tag </" + parsed.name + '>');
401                                                         } else {
402                                                                 parsed.state.greatExp = new LeafExpectations(parsed.state,memb, this);
403                                                                 return true; // finish out Leaf without returning
404                                                         }
405                                                 }
406                                         }
407                                 }
408
409                                 throw new ParseException("Unexpected Tag <" + derTag.name + '>');
410                         }
411                         return false;
412                 }
413         }
414         
415         private static class LeafExpectations extends GreatExpectations {
416                 public LeafExpectations(State state, JaxInfo curr, GreatExpectations prev) throws ParseException {
417                         super(state, curr, prev, null);
418                 }
419
420                 // @Override
421                 public boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException {
422                         if(ji.name.equals(derTag.name) && derTag.isEndTag) {
423                                 parsed.event = NEXT;
424                                 parsed.isString = ji.isString;
425                                 parsed.state.greatExp = prev;
426                         } else {
427                                 throw new ParseException("Expected </" + ji.name + '>');
428                         }
429                         return false;
430                 }               
431         }
432
433         private static class ArrayExpectations extends GreatExpectations {
434                 public ArrayExpectations(State state, JaxInfo ji, GreatExpectations prev) throws ParseException {
435                         super(state, ji, prev,null);
436                         if(state.arrayInfo==null)state.arrayInfo=new Stack<ArrayState>();
437                         state.arrayInfo.push(new ArrayState());
438                 }
439                 // @Override
440                 public boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException {
441                         if(ji.name.equals(derTag.name) && !derTag.isEndTag) {
442                                 if(ji.isObject()) {
443                                         if(derTag.isEndTag) {
444                                                 throw new ParseException("Unexpected End Tag </" + derTag.name + '>');
445                                         } else {
446                                                 ArrayState ai = parsed.state.arrayInfo.peek();  
447                                                 if(ai.firstObj || ai.didNext) {
448                                                         ai.firstObj = false;
449                                                         ai.didNext = false;
450                                                         parsed.event = START_OBJ;
451                                                         parsed.name=derTag.name;
452                                                         parsed.state.greatExp = new ObjectExpectations(parsed.state,ji,this,true, derTag);
453                                                 } else {
454                                                         ai.didNext = true;
455                                                         parsed.event = NEXT;
456                                                         parsed.state.unevaluated = derTag;
457                                                 }
458                                         }
459                                 } else { // a leave
460                                         if(derTag.isEndTag) {
461                                                  throw new ParseException("Misplaced End Tag </" + parsed.name + '>');
462                                         } else {
463                                                 parsed.state.greatExp = new LeafExpectations(parsed.state, ji, this);
464                                                 return true; // finish out Leaf without returning
465                                         }
466                                 }
467                         } else { // Tag now different... Array is done
468                                 parsed.state.unevaluated = derTag;
469                                 parsed.event=END_ARRAY;
470                                 parsed.state.greatExp = prev;
471                                 parsed.state.arrayInfo.pop();
472                         }
473                         return false;
474                 }               
475         }
476         // @Override
477         public Parsed<State> newParsed() throws ParseException {
478                 return new Parsed<State>(new State(jaxInfo, null));
479         }
480
481         // @Override
482         public TimeTaken start(Env env) {
483                 return env.start("Rosetta XML In", Env.XML);
484         }
485         
486 }