2 * ============LICENSE_START====================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
22 package org.onap.aaf.misc.rosetta;
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;
30 import java.util.Stack;
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;
36 public class InXML implements Parse<Reader, State> {
40 public InXML(JaxInfo jaxInfo) {
41 this.jaxInfo = jaxInfo;
44 public InXML(Class<?> cls, String ... rootNs) throws SecurityException, NoSuchFieldException, ClassNotFoundException, ParseException {
45 jaxInfo = JaxInfo.build(cls,rootNs);
50 public Parsed<State> parse(Reader r, Parsed<State> parsed) throws ParseException {
51 State state = parsed.state;
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;
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);
70 boolean inQuotes = false, escaped = false;
72 StringBuilder sb = parsed.sb, tempSB = new StringBuilder();
76 while(go && (ch=r.read())>=0) {
79 if(state.greatExp instanceof LeafExpectations) { // within a set of Tags, make a Quote
98 XmlEscape.xmlEscape(sb,r);
102 DerTag tag=new DerTag().parse(r, tempSB);
103 go = state.greatExp.eval(parsed, tag);
106 // don't add Whitespace to start of SB... saves removing later
109 } else if(!Character.isWhitespace(c)) {
116 } catch (IOException e) {
117 throw new ParseException(e);
121 public static final class DerTag {
123 public boolean isEndTag;
124 public List<Prop> props;
125 private boolean isXmlInfo;
135 public DerTag parse(Reader r, StringBuilder sb) throws ParseException {
138 boolean inQuotes = false, escaped = false;
143 if((ch = r.read())<0) throw new ParseException("Reader content ended before complete");
147 // TODO Check for !-- comments
161 } else if(inQuotes) {
181 // ns = sb.toString();
185 if(!isXmlInfo)sb.append(c);
191 } while(go && (ch=r.read())>=0);
192 } catch (IOException e) {
193 throw new ParseException(e);
198 private void endField(String tag, StringBuilder sb) {
200 name = sb.toString();
203 String value = sb.toString();
205 if(tag !=null && value != null) {
206 if(props==null)props = new ArrayList<Prop>();
207 props.add(new Prop(tag,value));
212 public String toString() {
213 StringBuilder sb = new StringBuilder();
214 sb.append(isEndTag?"End":"Start");
216 sb.append(" Name: ");
218 if(props!=null) for(Prop p : props) {
225 return sb.toString();
229 private static class ArrayState {
230 public boolean firstObj = true;
231 public boolean didNext = false;
234 public static class State {
235 public GreatExpectations greatExp;
236 public DerTag unevaluated;
237 public Stack<ArrayState> arrayInfo;
238 private List<Prop> attribs;
240 public State(JaxInfo ji, DerTag dt) throws ParseException {
241 greatExp = new RootExpectations(this, ji, null);
246 public boolean hasAttributes() {
247 return attribs!=null && idx<attribs.size();
250 public void push(Prop prop) {
252 attribs = new ArrayList<Prop>();
261 rv = attribs.get(idx++);
262 if(idx>=attribs.size())attribs = null;
268 private static abstract class GreatExpectations {
269 protected JaxInfo ji;
270 protected GreatExpectations prev;
271 private Map<String,String> ns;
273 public GreatExpectations(State state, JaxInfo curr, GreatExpectations prev, DerTag derTag) throws ParseException {
276 ji = getDerived(state, curr,derTag);
279 public abstract boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException;
281 // Recursively look back for any namespaces
282 protected Map<String,String> getNS() {
283 if(ns!=null)return ns;
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) {
297 ns.put(prop.tag, prop.value);
300 private JaxInfo getDerived(State state, JaxInfo ji, DerTag derTag) throws ParseException {
301 if(derTag==null)return ji;
303 List<Prop> props = derTag.props;
307 // Load Namespaces (if any)
308 for(Prop prop : props) {
309 if(prop.tag.startsWith("xmlns:")) {
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;
322 String nsVal = ns.get(potentialNS);
323 if(nsVal==null) noNamespace = true;
325 derived = new Prop(Parsed.EXTENSION_TAG,prop.value);
330 throw new ParseException(prop.tag + " utilizes an invalid Namespace prefix");
332 } else if(!prop.tag.startsWith("xmlns")) {
337 return derived==null?ji:ji.getDerived(derived.value);
341 private static class RootExpectations extends GreatExpectations {
343 public RootExpectations(State state, JaxInfo curr, GreatExpectations prev) throws ParseException {
344 super(state,curr,prev, null);
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;
356 //parsed.name = derTag.name;
357 parsed.event = START_OBJ;
358 parsed.state.greatExp = new ObjectExpectations(parsed.state,ji, this, false, derTag);
365 private static class ObjectExpectations extends GreatExpectations {
366 private boolean printName;
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;
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;
381 for(JaxInfo memb : ji.members) {
382 if(memb.name.equals(derTag.name)) {
383 parsed.name = memb.name;
385 parsed.state.unevaluated = derTag; // evaluate within Array Context
386 parsed.event = START_ARRAY;
387 parsed.state.greatExp = new ArrayExpectations(parsed.state,memb,this);
389 } else if(memb.isObject()) {
390 if(derTag.isEndTag) {
391 throw new ParseException("Unexpected End Tag </" + derTag.name + '>');
393 parsed.event = START_OBJ;
395 parsed.state.greatExp = new ObjectExpectations(parsed.state, memb,this,true,derTag);
399 if(derTag.isEndTag) {
400 throw new ParseException("Misplaced End Tag </" + parsed.name + '>');
402 parsed.state.greatExp = new LeafExpectations(parsed.state,memb, this);
403 return true; // finish out Leaf without returning
409 throw new ParseException("Unexpected Tag <" + derTag.name + '>');
415 private static class LeafExpectations extends GreatExpectations {
416 public LeafExpectations(State state, JaxInfo curr, GreatExpectations prev) throws ParseException {
417 super(state, curr, prev, null);
421 public boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException {
422 if(ji.name.equals(derTag.name) && derTag.isEndTag) {
424 parsed.isString = ji.isString;
425 parsed.state.greatExp = prev;
427 throw new ParseException("Expected </" + ji.name + '>');
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());
440 public boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException {
441 if(ji.name.equals(derTag.name) && !derTag.isEndTag) {
443 if(derTag.isEndTag) {
444 throw new ParseException("Unexpected End Tag </" + derTag.name + '>');
446 ArrayState ai = parsed.state.arrayInfo.peek();
447 if(ai.firstObj || ai.didNext) {
450 parsed.event = START_OBJ;
451 parsed.name=derTag.name;
452 parsed.state.greatExp = new ObjectExpectations(parsed.state,ji,this,true, derTag);
456 parsed.state.unevaluated = derTag;
460 if(derTag.isEndTag) {
461 throw new ParseException("Misplaced End Tag </" + parsed.name + '>');
463 parsed.state.greatExp = new LeafExpectations(parsed.state, ji, this);
464 return true; // finish out Leaf without returning
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();
477 public Parsed<State> newParsed() throws ParseException {
478 return new Parsed<State>(new State(jaxInfo, null));
482 public TimeTaken start(Env env) {
483 return env.start("Rosetta XML In", Env.XML);