View Javadoc
1   /*
2     (C) Copyright IBM Corp. 2006, 2013
3   
4     THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE
5     ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE
6     CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
7   
8     You can obtain a current copy of the Eclipse Public License from
9     http://www.opensource.org/licenses/eclipse-1.0.php
10  
11    @author : Endre Bak, ebak@de.ibm.com
12   * 
13   * Flag       Date        Prog         Description
14   * -------------------------------------------------------------------------------
15   * 1689085    2007-04-10  ebak         Embedded object enhancements for Pegasus
16   * 1712656    2007-05-04  ebak         Correct type identification for SVC CIMOM
17   * 1719991    2007-05-16  ebak         FVT: regression ClassCastException in EmbObjHandler
18   * 1820763    2007-10-29  ebak         Supporting the EmbeddedInstance qualifier
19   * 1848607    2007-12-11  ebak         Strict EmbeddedObject types
20   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
21   * 2204488 	  2008-10-28  raman_arora  Fix code to remove compiler warnings
22   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
23   * 2763216    2009-04-14  blaschke-oss Code cleanup: visible spelling/grammar errors
24   * 2957387    2010-03-03  blaschke-oss EmbededObject XML attribute must not be all uppercases
25   * 3281781    2011-04-11  blaschke-oss fail to parse Embedded Instance parameter
26   * 3513353    2012-03-30  blaschke-oss TCK: CIMDataType arrays must have length >= 1
27   * 3513349    2012-03-31  blaschke-oss TCK: CIMDataType must not accept null string
28   *    2691    2013-10-18  blaschke-oss RETURNVALUE should not require PARAMTYPE attribute
29   */
30  
31  package org.metricshub.wbem.sblim.cimclient.internal.cimxml.sax;
32  
33  /*-
34   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
35   * WBEM Java Client
36   * ჻჻჻჻჻჻
37   * Copyright 2023 - 2025 MetricsHub
38   * ჻჻჻჻჻჻
39   * Licensed under the Apache License, Version 2.0 (the "License");
40   * you may not use this file except in compliance with the License.
41   * You may obtain a copy of the License at
42   *
43   *      http://www.apache.org/licenses/LICENSE-2.0
44   *
45   * Unless required by applicable law or agreed to in writing, software
46   * distributed under the License is distributed on an "AS IS" BASIS,
47   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
48   * See the License for the specific language governing permissions and
49   * limitations under the License.
50   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
51   */
52  
53  import org.metricshub.wbem.javax.cim.CIMDataType;
54  import org.metricshub.wbem.javax.cim.CIMObjectPath;
55  import org.metricshub.wbem.sblim.cimclient.internal.cim.CIMHelper;
56  import org.metricshub.wbem.sblim.cimclient.internal.cimxml.sax.node.AbstractValueNode;
57  import org.metricshub.wbem.sblim.cimclient.internal.cimxml.sax.node.ArrayIf;
58  import org.metricshub.wbem.sblim.cimclient.internal.cimxml.sax.node.Node;
59  import org.metricshub.wbem.sblim.cimclient.internal.cimxml.sax.node.QualifiedNodeHandler;
60  import org.metricshub.wbem.sblim.cimclient.internal.cimxml.sax.node.ValueArrayNode;
61  import org.metricshub.wbem.sblim.cimclient.internal.cimxml.sax.node.ValueNode;
62  import org.xml.sax.Attributes;
63  import org.xml.sax.SAXException;
64  
65  /**
66   * EmbObjHandler helps the parsing of embedded object elements.
67   */
68  
69  public class EmbObjHandler {
70  	private String iNodeName;
71  
72  	private CIMDataType iRawType;
73  
74  	private boolean iHasEmbObjAttr, iHasEmbInstAttr;
75  
76  	private SAXSession iSession;
77  
78  	private CIMDataType iType;
79  
80  	private Object iValue;
81  
82  	private QualifiedNodeHandler iQualiHandler;
83  
84  	private AbstractValueNode iAbsValNode;
85  
86  	/**
87  	 * Generic initialization.
88  	 *
89  	 * @param pHandler
90  	 * @param pNodeName
91  	 * @param pAttribs
92  	 * @param pSession
93  	 * @param pQNodeHandler
94  	 * @param pCheckEmbObjAttrib
95  	 * @return EmbObjHandler
96  	 * @throws SAXException
97  	 */
98  	public static EmbObjHandler init(
99  		EmbObjHandler pHandler,
100 		String pNodeName,
101 		Attributes pAttribs,
102 		SAXSession pSession,
103 		QualifiedNodeHandler pQNodeHandler,
104 		boolean pCheckEmbObjAttrib
105 	)
106 		throws SAXException {
107 		if (pHandler == null) pHandler = new EmbObjHandler();
108 		pHandler.initInst(pNodeName, pAttribs, pSession, pQNodeHandler, pCheckEmbObjAttrib);
109 		return pHandler;
110 	}
111 
112 	private EmbObjHandler() {
113 		// init() used for instantiation
114 	}
115 
116 	/**
117 	 * Generic instance initialization.
118 	 *
119 	 * @param pNodeName
120 	 * @param pAttribs
121 	 * @param pSession
122 	 * @param pQNodeHandler
123 	 * @param pCheckEmbObjAttrib
124 	 * @throws SAXException
125 	 */
126 	public void initInst(
127 		String pNodeName,
128 		Attributes pAttribs,
129 		SAXSession pSession,
130 		QualifiedNodeHandler pQNodeHandler,
131 		boolean pCheckEmbObjAttrib
132 	)
133 		throws SAXException {
134 		this.iSession = pSession;
135 		this.iNodeName = pNodeName;
136 		this.iQualiHandler = pQNodeHandler;
137 		this.iType = null;
138 		this.iValue = null;
139 		this.iAbsValNode = null;
140 		this.iRawType = Node.getCIMType(pAttribs, true);
141 		if (this.iRawType == null) {
142 			this.iRawType = Node.getParamType(pAttribs);
143 			/*
144 			 * if (iRawType==null) throw new SAXException( iNodeName+" must
145 			 * contain a TYPE or a PARAMTYPE attribute!" );
146 			 */
147 			/*
148 			 * SVC CIMOM doesn't add TYPE attribute for the RETURNVALUE element,
149 			 * but it adds it to the VALUE element.
150 			 */
151 		}
152 
153 		if (pCheckEmbObjAttrib) {
154 			// DSPs call for EmbeddedObject AND XML is case sensitive, BUT there
155 			// are probably CIMOMs out there that still use EMBEDDEDOBJECT
156 			String embObjStr = pAttribs.getValue("EmbeddedObject");
157 			if (embObjStr == null) {
158 				embObjStr = pAttribs.getValue("EMBEDDEDOBJECT");
159 			}
160 			if (embObjStr == null) {
161 				this.iHasEmbInstAttr = this.iHasEmbObjAttr = false;
162 			} else if ("instance".equalsIgnoreCase(embObjStr)) {
163 				this.iHasEmbInstAttr = true;
164 				this.iHasEmbObjAttr = false;
165 			} else if ("object".equalsIgnoreCase(embObjStr)) {
166 				this.iHasEmbInstAttr = false;
167 				this.iHasEmbObjAttr = true;
168 			} else {
169 				throw new SAXException(
170 					"EmbeddedObject attribute's value must be \"object\" or \"instance\". " + embObjStr + " is invalid!"
171 				);
172 			}
173 		} else {
174 			this.iHasEmbInstAttr = this.iHasEmbObjAttr = false;
175 		}
176 	}
177 
178 	/**
179 	 * isEmbeddedObject For this function Object means CLASS or INSTANCE. Object
180 	 * can have other meanings at different places !!! :(
181 	 *
182 	 * @return boolean
183 	 */
184 	private boolean isEmbeddedObject() {
185 		return (
186 			this.iHasEmbInstAttr ||
187 			this.iHasEmbObjAttr ||
188 			(this.iQualiHandler != null && (this.iQualiHandler.isEmbeddedObject() || this.iQualiHandler.isEmbeddedInstance()))
189 		);
190 	}
191 
192 	/**
193 	 * isEmbeddedClass
194 	 *
195 	 * @return boolean
196 	 */
197 	private boolean isEmbeddedClass() {
198 		return this.iHasEmbObjAttr || (this.iQualiHandler != null && this.iQualiHandler.isEmbeddedObject());
199 	}
200 
201 	/**
202 	 * isEmbeddedInstance
203 	 *
204 	 * @return boolean
205 	 */
206 	private boolean isEmbeddedInstance() {
207 		return this.iHasEmbInstAttr || (this.iQualiHandler != null && this.iQualiHandler.isEmbeddedInstance());
208 	}
209 
210 	/**
211 	 * getValue
212 	 *
213 	 * @return Object
214 	 * @throws SAXException
215 	 */
216 	public Object getValue() throws SAXException {
217 		transform();
218 		return this.iValue;
219 	}
220 
221 	/**
222 	 * getType
223 	 *
224 	 * @return Object
225 	 * @throws SAXException
226 	 */
227 	public CIMDataType getType() throws SAXException {
228 		transform();
229 		return this.iType;
230 	}
231 
232 	/**
233 	 * getRawType
234 	 *
235 	 * @return the type which is retrieved from the XML attributes
236 	 */
237 	public CIMDataType getRawType() {
238 		return this.iRawType;
239 	}
240 
241 	/**
242 	 * getArrayType useful e.g. for PROPERTY.ARRAY
243 	 *
244 	 * @return CIMDataType
245 	 * @throws SAXException
246 	 */
247 	public CIMDataType getArrayType() throws SAXException {
248 		transform();
249 		return this.iType.isArray() ? this.iType : CIMHelper.UnboundedArrayDataType(this.iType.getType());
250 	}
251 
252 	/**
253 	 * addValueNode
254 	 *
255 	 * @param pValueNode
256 	 *            - can be ValueNode ore ValueArrayNode for Embedded Objects
257 	 */
258 	public void addValueNode(AbstractValueNode pValueNode) {
259 		if (
260 			isEmbeddedObject() &&
261 			!(pValueNode == null || pValueNode instanceof ValueNode || pValueNode instanceof ValueArrayNode)
262 		) throw new IllegalArgumentException(
263 			"pValueNode's type can be ValueNode or ValueArrayNode or it can be null. " +
264 			pValueNode.getClass().getName() +
265 			" is an invalid type!"
266 		);
267 		this.iAbsValNode = pValueNode;
268 	}
269 
270 	private void transform() throws SAXException {
271 		if (this.iType != null) return;
272 		if (this.iAbsValNode == null) {
273 			if (isEmbeddedObject()) {
274 				if (this.iRawType != CIMDataType.STRING_T) throw new SAXException(
275 					"Embedded Object CIM-XML element's type must be string. " + this.iRawType + " is invalid!"
276 				);
277 				if (this.iSession.strictEmbObjParsing()) {
278 					/*
279 					 * Here the assumption is that Object = CLASS, Instance =
280 					 * INSTANCE.
281 					 */
282 					this.iType = isEmbeddedInstance() ? CIMDataType.OBJECT_T : CIMDataType.CLASS_T;
283 				} else {
284 					/*
285 					 * for valueless EmbeddedObject="object" the type can not be
286 					 * determined since Pegasus's CIMObject can contain both
287 					 * CIMClass and CIMInstance. Is it true for Pegasus 2.7.0
288 					 * too?
289 					 */
290 					this.iType = isEmbeddedInstance() ? CIMDataType.OBJECT_T : CIMDataType.STRING_T;
291 				}
292 			} else {
293 				this.iType = this.iRawType;
294 			}
295 			this.iValue = null;
296 		} else {
297 			setType();
298 			if (isEmbeddedObject()) {
299 				transformEmbObj();
300 			} else {
301 				transformNormObj();
302 			}
303 		}
304 	}
305 
306 	private void transformEmbObj() throws SAXException {
307 		if (this.iAbsValNode instanceof ValueNode) {
308 			String valueStr = (String) ((ValueNode) this.iAbsValNode).getValue();
309 			this.iValue = CIMObjectFactory.getEmbeddedObj(this.iRawType, valueStr, this.iSession);
310 			this.iType = CIMObjectFactory.getCIMObjScalarType(this.iValue);
311 		} else { // ValueArrayNode
312 			this.iValue = CIMObjectFactory.getEmbeddedObjA(this.iRawType, (ValueArrayNode) this.iAbsValNode, this.iSession);
313 			this.iType = CIMObjectFactory.getCIMObjArrayType(this.iValue);
314 		}
315 		if (isEmbeddedInstance() && this.iType.getType() != CIMDataType.OBJECT) throw new SAXException(
316 			this.iNodeName + " element is an EmbeddedInstance with non INSTANCE value. " + "It's not valid!"
317 		);
318 		if (
319 			isEmbeddedClass() && this.iType.getType() != CIMDataType.CLASS && this.iType.getType() != CIMDataType.OBJECT
320 		) throw new SAXException(
321 			this.iNodeName + " element is an EmbeddedObject with non CLASS/INSTANCE value. It's not valid!"
322 		);
323 	}
324 
325 	private void transformNormObj() throws SAXException {
326 		if (this.iAbsValNode instanceof ValueNode) {
327 			this.iType = this.iRawType;
328 			this.iValue = CIMObjectFactory.getObject(this.iType, (ValueNode) this.iAbsValNode);
329 		} else if (this.iAbsValNode instanceof ValueArrayNode) {
330 			this.iType = CIMHelper.UnboundedArrayDataType(this.iRawType.getType());
331 			this.iValue = CIMObjectFactory.getObject(this.iRawType, (ValueArrayNode) this.iAbsValNode);
332 		} else {
333 			this.iValue = this.iAbsValNode.getValue();
334 			if (this.iAbsValNode instanceof ArrayIf) {
335 				if (this.iValue instanceof CIMObjectPath[]) this.iType = new CIMDataType("", 0);
336 			} else {
337 				if (this.iValue instanceof CIMObjectPath) {
338 					this.iType = new CIMDataType(((CIMObjectPath) this.iValue).getObjectName());
339 				} else {
340 					this.iType = this.iRawType;
341 				}
342 			}
343 		}
344 	}
345 
346 	/**
347 	 * Required to handle the output XML of some non-standard CIMOMs like SVC
348 	 * which adds the TYPE attribute to the sub VALUE or VALUE.ARRAY XML
349 	 * element.
350 	 *
351 	 * @throws SAXException
352 	 */
353 	private void setType() throws SAXException {
354 		if (this.iType != null || this.iRawType != null) return;
355 		this.iRawType = this.iAbsValNode.getType();
356 		if (this.iRawType == null) this.iRawType =
357 			(this.iAbsValNode instanceof ArrayIf ? CIMDataType.STRING_ARRAY_T : CIMDataType.STRING_T);
358 	}
359 }