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);
104 DerTag tag=new DerTag().parse(r, tempSB);
105 go = state.greatExp.eval(parsed, tag);
108 // don't add Whitespace to start of SB... saves removing later
111 } else if(!Character.isWhitespace(c)) {
118 } catch (IOException e) {
119 throw new ParseException(e);
123 public static final class DerTag {
125 public boolean isEndTag;
126 public List<Prop> props;
127 private boolean isXmlInfo;
137 public DerTag parse(Reader r, StringBuilder sb) throws ParseException {
140 boolean inQuotes = false, escaped = false;
145 if((ch = r.read())<0) throw new ParseException("Reader content ended before complete");
149 // TODO Check for !-- comments
163 } else if(inQuotes) {
183 // ns = sb.toString();
187 if(!isXmlInfo)sb.append(c);
193 } while(go && (ch=r.read())>=0);
194 } catch (IOException e) {
195 throw new ParseException(e);
200 private void endField(String tag, StringBuilder sb) {
202 name = sb.toString();
205 String value = sb.toString();
207 if(tag !=null && value != null) {
208 if(props==null)props = new ArrayList<>();
209 props.add(new Prop(tag,value));
214 public String toString() {
215 StringBuilder sb = new StringBuilder();
216 sb.append(isEndTag?"End":"Start");
218 sb.append(" Name: ");
220 if(props!=null) for(Prop p : props) {
227 return sb.toString();
231 private static class ArrayState {
232 public boolean firstObj = true;
233 public boolean didNext = false;
236 public static class State {
237 public GreatExpectations greatExp;
238 public DerTag unevaluated;
239 public Stack<ArrayState> arrayInfo;
240 private List<Prop> attribs;
242 public State(JaxInfo ji, DerTag dt) throws ParseException {
243 greatExp = new RootExpectations(this, ji, null);
248 public boolean hasAttributes() {
249 return attribs!=null && idx<attribs.size();
252 public void push(Prop prop) {
254 attribs = new ArrayList<>();
263 rv = attribs.get(idx++);
264 if(idx>=attribs.size())attribs = null;
270 private static abstract class GreatExpectations {
271 protected JaxInfo ji;
272 protected GreatExpectations prev;
273 private Map<String,String> ns;
275 public GreatExpectations(State state, JaxInfo curr, GreatExpectations prev, DerTag derTag) throws ParseException {
278 ji = getDerived(state, curr,derTag);
281 public abstract boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException;
283 // Recursively look back for any namespaces
284 protected Map<String,String> getNS() {
285 if(ns!=null)return ns;
292 private void addNS(Prop prop) {
293 Map<String,String> existingNS = getNS();
294 if(ns==null)ns = new HashMap<>();
295 // First make a copy of previous NSs so that we have everything we need, but can overwrite, if necessary
296 if(existingNS!=null && ns!=existingNS) {
299 ns.put(prop.tag, prop.value);
302 private JaxInfo getDerived(State state, JaxInfo ji, DerTag derTag) throws ParseException {
303 if(derTag==null)return ji;
305 List<Prop> props = derTag.props;
309 // Load Namespaces (if any)
310 for(Prop prop : props) {
311 if(prop.tag.startsWith("xmlns:")) {
315 for(Prop prop : props) {
316 if(prop.tag.endsWith(":type")) {
317 int idx = prop.tag.indexOf(':');
318 String potentialNS = "xmlns:"+prop.tag.substring(0,idx);
319 Map<String,String> ns = getNS();
320 boolean noNamespace = false;
324 String nsVal = ns.get(potentialNS);
325 if(nsVal==null) noNamespace = true;
327 derived = new Prop(Parsed.EXTENSION_TAG,prop.value);
332 throw new ParseException(prop.tag + " utilizes an invalid Namespace prefix");
334 } else if(!prop.tag.startsWith("xmlns")) {
339 return derived==null?ji:ji.getDerived(derived.value);
343 private static class RootExpectations extends GreatExpectations {
345 public RootExpectations(State state, JaxInfo curr, GreatExpectations prev) throws ParseException {
346 super(state,curr,prev, null);
350 public boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException {
351 if(derTag.isXmlInfo) {
352 parsed.event = START_DOC;
353 } else if(ji.name.equals(derTag.name)) {
354 if(derTag.isEndTag) {
355 parsed.event = END_DOC;
356 parsed.state.greatExp = prev;
358 //parsed.name = derTag.name;
359 parsed.event = START_OBJ;
360 parsed.state.greatExp = new ObjectExpectations(parsed.state,ji, this, false, derTag);
367 private static class ObjectExpectations extends GreatExpectations {
368 private boolean printName;
370 public ObjectExpectations(State state, JaxInfo curr, GreatExpectations prev, boolean printName, DerTag derTag) throws ParseException {
371 super(state, curr, prev, derTag);
372 this.printName=printName;
376 public boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException {
377 if(derTag.isEndTag && ji.name.equals(derTag.name)) {
378 parsed.state.greatExp = prev;
379 parsed.event = END_OBJ;
380 if(printName)parsed.name = ji.name;
383 for(JaxInfo memb : ji.members) {
384 if(memb.name.equals(derTag.name)) {
385 parsed.name = memb.name;
387 parsed.state.unevaluated = derTag; // evaluate within Array Context
388 parsed.event = START_ARRAY;
389 parsed.state.greatExp = new ArrayExpectations(parsed.state,memb,this);
391 } else if(memb.isObject()) {
392 if(derTag.isEndTag) {
393 throw new ParseException("Unexpected End Tag </" + derTag.name + '>');
395 parsed.event = START_OBJ;
397 parsed.state.greatExp = new ObjectExpectations(parsed.state, memb,this,true,derTag);
401 if(derTag.isEndTag) {
402 throw new ParseException("Misplaced End Tag </" + parsed.name + '>');
404 parsed.state.greatExp = new LeafExpectations(parsed.state,memb, this);
405 return true; // finish out Leaf without returning
411 throw new ParseException("Unexpected Tag <" + derTag.name + '>');
417 private static class LeafExpectations extends GreatExpectations {
418 public LeafExpectations(State state, JaxInfo curr, GreatExpectations prev) throws ParseException {
419 super(state, curr, prev, null);
423 public boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException {
424 if(ji.name.equals(derTag.name) && derTag.isEndTag) {
426 parsed.isString = ji.isString;
427 parsed.state.greatExp = prev;
429 throw new ParseException("Expected </" + ji.name + '>');
435 private static class ArrayExpectations extends GreatExpectations {
436 public ArrayExpectations(State state, JaxInfo ji, GreatExpectations prev) throws ParseException {
437 super(state, ji, prev,null);
438 if(state.arrayInfo==null)state.arrayInfo=new Stack<ArrayState>();
439 state.arrayInfo.push(new ArrayState());
442 public boolean eval(Parsed<State> parsed, DerTag derTag) throws ParseException {
443 if(ji.name.equals(derTag.name) && !derTag.isEndTag) {
445 if(derTag.isEndTag) {
446 throw new ParseException("Unexpected End Tag </" + derTag.name + '>');
448 ArrayState ai = parsed.state.arrayInfo.peek();
449 if(ai.firstObj || ai.didNext) {
452 parsed.event = START_OBJ;
453 parsed.name=derTag.name;
454 parsed.state.greatExp = new ObjectExpectations(parsed.state,ji,this,true, derTag);
458 parsed.state.unevaluated = derTag;
462 if(derTag.isEndTag) {
463 throw new ParseException("Misplaced End Tag </" + parsed.name + '>');
465 parsed.state.greatExp = new LeafExpectations(parsed.state, ji, this);
466 return true; // finish out Leaf without returning
469 } else { // Tag now different... Array is done
470 parsed.state.unevaluated = derTag;
471 parsed.event=END_ARRAY;
472 parsed.state.greatExp = prev;
473 parsed.state.arrayInfo.pop();
479 public Parsed<State> newParsed() throws ParseException {
480 return new Parsed<State>(new State(jaxInfo, null));
484 public TimeTaken start(Env env) {
485 return env.start("Rosetta XML In", Env.XML);