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.lang.reflect.Field;
25 import java.lang.reflect.Type;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
31 import javax.xml.bind.annotation.XmlElement;
32 import javax.xml.bind.annotation.XmlRootElement;
33 import javax.xml.bind.annotation.XmlSchema;
34 import javax.xml.bind.annotation.XmlType;
35 import javax.xml.datatype.XMLGregorianCalendar;
37 public class JaxInfo {
38 private static final String DEFAULT = "##default";
39 public static final int DATA = 0;
40 public static final int ARRAY = 1;
41 public static final int OBJECT = 2;
43 public final String name;
44 public final Class<?> clss;
45 public Map<String, JaxInfo> extensions; // Classes, which might be found at runtime, that extend this class. Lazy Instantiation
46 public final JaxInfo[] members;
47 public final boolean isArray;
48 public final boolean isString;
49 public final boolean required;
50 public final boolean nillable;
52 public boolean isObject() {return members!=null;}
54 private JaxInfo(String n, String ns, Class<?> c, JaxInfo[] members, boolean string, boolean array, boolean required, boolean nillable) {
58 this.members = members;
59 this.isString = string;
61 this.required = required;
62 this.nillable = nillable;
67 public int getType() {
68 if(isArray)return ARRAY;
69 else if(members!=null)return OBJECT;
73 public JaxInfo getDerived(String derivedName) {
76 if(extensions == null) {
77 extensions = new HashMap<String,JaxInfo>();
80 derived = extensions.get(derivedName);
84 //TODO for the moment, Classes are in same package
85 Package pkg = clss.getPackage();
87 Class<?> dc = getClass().getClassLoader().loadClass(pkg.getName()+'.'+Character.toUpperCase(derivedName.charAt(0))+derivedName.substring(1));
88 derived = JaxInfo.build(dc, this); // Use this JAXInfo's name so the tags are correct
89 extensions.put(derivedName, derived);
90 } catch (Exception e) {
97 public static JaxInfo get(JaxInfo[] fields, String name) {
98 for(JaxInfo f : fields) {
99 if(name.equals(f.name)) return f;
105 * Build up JAXB Information (recursively)
110 * @throws SecurityException
111 * @throws NoSuchFieldException
112 * @throws ClassNotFoundException
113 * @throws ParseException
115 public static JaxInfo build(Class<?> cls, JaxInfo parent) throws NoSuchFieldException, ClassNotFoundException, ParseException {
116 return new JaxInfo(parent.name,parent.ns, cls,buildFields(cls,parent.ns),parent.isString, parent.isArray,parent.required,parent.nillable);
119 * Build up JAXB Information (recursively)
124 * @throws SecurityException
125 * @throws NoSuchFieldException
126 * @throws ClassNotFoundException
127 * @throws ParseException
129 public static JaxInfo build(Class<?> cls, String ... rootNns) throws SecurityException, NoSuchFieldException, ClassNotFoundException, ParseException {
131 if(rootNns.length>0 && rootNns[0]!=null) {
132 defaultNS = rootNns[0];
134 Package pkg = cls.getPackage();
135 XmlSchema xs = pkg.getAnnotation(XmlSchema.class);
136 defaultNS = xs==null?"":xs.namespace();
139 if(rootNns.length>1) {
142 XmlRootElement xre = cls.getAnnotation(XmlRootElement.class);
146 XmlType xt = cls.getAnnotation(XmlType.class);
150 throw new ParseException("Need a JAXB Object with XmlRootElement, or stipulate in parms");
155 return new JaxInfo(name,defaultNS, cls,buildFields(cls,defaultNS),false,false,false,false);
158 // Build up the name and members of this particular class
159 // This is recursive, if a member is a JAXB Object as well.
160 private static JaxInfo[] buildFields(Class<?> clazz, String defaultNS) throws SecurityException, NoSuchFieldException, ClassNotFoundException {
161 ArrayList<JaxInfo> fields = null; // allow for lazy instantiation, because many structures won't have XmlType
162 Class<?> cls = clazz;
163 // Build up Method names from JAXB Annotations
165 while((xt = cls.getAnnotation(XmlType.class))!=null) {
166 if(fields==null)fields = new ArrayList<JaxInfo>();
167 for(String field : xt.propOrder()) {
168 if("".equals(field)) break; // odd bug. "" returned when no fields exist, rather than empty array
169 Field rf = cls.getDeclaredField(field);
170 Class<?> ft = rf.getType();
172 boolean required = false;
173 boolean nillable = false;
174 String xmlName = field;
175 String namespace = defaultNS;
177 XmlElement xe = rf.getAnnotation(XmlElement.class);
180 required = xe.required();
182 if(DEFAULT.equals(xmlName)) {
185 namespace = xe.namespace();
186 if(DEFAULT.equals(namespace)) {
187 namespace = defaultNS;
190 // If object is a List, then it is possible multiple, per XML/JAXB evaluation
191 if(ft.isAssignableFrom(List.class)) {
192 Type t = rf.getGenericType();
193 String classname = t.toString();
194 int start = classname.indexOf('<');
195 int end = classname.indexOf('>');
196 Class<?> genClass = Class.forName(classname.substring(start+1, end));
197 xe = genClass.getAnnotation(XmlElement.class);
198 if(xe!=null && !DEFAULT.equals(xe.namespace())) {
199 namespace = xe.namespace();
201 // add recursed recursed member, marked as array
202 fields.add(new JaxInfo(xmlName,namespace,genClass,buildFields(genClass,namespace), genClass.equals(String.class),true,required,nillable));
204 boolean isString = ft.equals(String.class) || ft.equals(XMLGregorianCalendar.class);
205 // add recursed member
206 fields.add(new JaxInfo(xmlName,namespace,ft,buildFields(ft,namespace),isString,false,required,nillable));
209 cls = cls.getSuperclass();
212 JaxInfo[] rv = new JaxInfo[fields.size()];
221 public StringBuilder dump(StringBuilder sb, int idx) {
222 for(int i=0;i<idx;++i)sb.append(' ');
226 sb.append(clss.getName());
228 if(isArray)sb.append(" (array)");
229 if(required)sb.append(" (required)");
230 if(nillable)sb.append(" (nillable)");
232 for(JaxInfo f : members) {
240 public String toString() {
241 StringBuilder sb = new StringBuilder();
242 sb.append("Structure of ");
243 sb.append(clss.getName());
246 return sb.toString();