View Javadoc
1   /*
2     XMLPullParser.java
3   
4     (C) Copyright IBM Corp. 2005, 2013
5   
6     THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE
7     ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE
8     CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
9   
10    You can obtain a current copy of the Eclipse Public License from
11    http://www.opensource.org/licenses/eclipse-1.0.php
12  
13    @author : Roberto Pineiro, IBM, roberto.pineiro@us.ibm.com
14   * @author : Chung-hao Tan, IBM, chungtan@us.ibm.com
15   * 
16   * 
17   * Change History
18   * Flag       Date        Prog         Description
19   *------------------------------------------------------------------------------- 
20   * 18274      2005-09-12  fiuczy       String values get corrupted by CIM client
21   * 1535756    2006-08-07  lupusalex    Make code warning free
22   * 1663270    2007-02-19  ebak         Minor performance problems
23   * 1708584    2007-04-27  ebak         CloseableIterator might not clean up streams
24   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
25   * 2204488 	  2008-10-28  raman_arora  Fix code to remove compiler warnings
26   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
27   * 2531371    2009-02-10  raman_arora  Upgrade client to JDK 1.5 (Phase 2)
28   * 2714989    2009-03-26  blaschke-oss Code cleanup from redundant null check et al
29   * 2763216    2009-04-14  blaschke-oss Code cleanup: visible spelling/grammar errors
30   * 3001359    2010-05-18  blaschke-oss XMLPullParser.CharString equals() method broken
31   * 3019252    2010-06-21  blaschke-oss Methods concatenate strings using + in a loop
32   * 3026316    2010-07-07  blaschke-oss XMLPullParser unused fields
33   * 3026417    2010-07-07  blaschke-oss XMLAttributeValue does not use iHash field
34   * 3028518    2010-07-14  blaschke-oss Additional StringBuilder use
35   * 3048749    2010-08-20  blaschke-oss Hex digit parsing logic error in XMLPullParser
36   * 3304058    2011-05-20  blaschke-oss Use same date format in change history
37   *    2639    2013-05-11  blaschke-oss CDATA parsing broken in PULL parser
38   */
39  
40  package org.metricshub.wbem.sblim.cimclient.internal.pullparser;
41  
42  /*-
43   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
44   * WBEM Java Client
45   * ჻჻჻჻჻჻
46   * Copyright 2023 - 2025 MetricsHub
47   * ჻჻჻჻჻჻
48   * Licensed under the Apache License, Version 2.0 (the "License");
49   * you may not use this file except in compliance with the License.
50   * You may obtain a copy of the License at
51   *
52   *      http://www.apache.org/licenses/LICENSE-2.0
53   *
54   * Unless required by applicable law or agreed to in writing, software
55   * distributed under the License is distributed on an "AS IS" BASIS,
56   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
57   * See the License for the specific language governing permissions and
58   * limitations under the License.
59   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
60   */
61  
62  import java.io.IOException;
63  import java.io.Reader;
64  import java.util.ArrayList;
65  import java.util.logging.Level;
66  import org.metricshub.wbem.sblim.cimclient.internal.cimxml.sax.XMLDefaultHandlerImpl;
67  import org.metricshub.wbem.sblim.cimclient.internal.logging.LogAndTraceBroker;
68  import org.xml.sax.Attributes;
69  
70  /**
71   * Class XMLPullParser is responsible for XML parsing.
72   *
73   */
74  public class XMLPullParser {
75  
76  	/**
77  	 * Class XMLAttributes is an Attributes implementation.
78  	 */
79  	class XMLAttributes implements Attributes {
80  
81  		public int getIndex(String qName) {
82  			/*
83  			 * ebak: this is not good, since this Vector contains
84  			 * XMLAttributeValues return iAttributeNames.indexOf(qName);
85  			 */
86  			for (int i = 0; i < XMLPullParser.this.iAttributeNames.size(); i++) {
87  				XMLAttributeValue xmlAttrValue = XMLPullParser.this.iAttributeNames.get(i);
88  				if (qName.equals(xmlAttrValue.toString())) return i;
89  			}
90  			return -1;
91  		}
92  
93  		/**
94  		 * @param uri
95  		 * @param localName
96  		 * @return int getIndex
97  		 */
98  		public int getIndex(String uri, String localName) {
99  			return 0;
100 		}
101 
102 		public int getLength() {
103 			return XMLPullParser.this.iTotalAttributes;
104 		}
105 
106 		/**
107 		 * @param index
108 		 * @return String getLocalName
109 		 */
110 		public String getLocalName(int index) {
111 			return EMPTY;
112 		}
113 
114 		public String getQName(int index) {
115 			return XMLPullParser.this.iAttributeNames.get(index).getText();
116 		}
117 
118 		/**
119 		 * @param index
120 		 * @return String getType
121 		 */
122 		public String getType(int index) {
123 			return EMPTY;
124 		}
125 
126 		/**
127 		 * @param qName
128 		 * @return String getType
129 		 */
130 		public String getType(String qName) {
131 			return EMPTY;
132 		}
133 
134 		/**
135 		 * @param uri
136 		 * @param localName
137 		 * @return String getType
138 		 */
139 		public String getType(String uri, String localName) {
140 			return EMPTY;
141 		}
142 
143 		/**
144 		 * @param index
145 		 * @return String getURI
146 		 */
147 		public String getURI(int index) {
148 			return EMPTY;
149 		}
150 
151 		public String getValue(int index) {
152 			return XMLPullParser.this.iAttributeValues.get(index).getText();
153 		}
154 
155 		public String getValue(String qName) {
156 			/*
157 			 * ebak: the implementation have to return null if the attribute not
158 			 * found ebak: indexOf not good here, because iAttributeNames
159 			 * doesn't contain Strings
160 			 */
161 			int idx = getIndex(qName);
162 			if (idx < 0) return null;
163 			return getValue(idx);
164 		}
165 
166 		/**
167 		 * @param uri
168 		 * @param localName
169 		 * @return String getType
170 		 */
171 		public String getValue(String uri, String localName) {
172 			return EMPTY;
173 		}
174 	}
175 
176 	class XMLAttributeValue {
177 		int iCurrentPos;
178 
179 		int iBegin, iLen;
180 
181 		String iText;
182 
183 		private boolean iTranslate;
184 
185 		/**
186 		 * Ctor.
187 		 *
188 		 * @param begin
189 		 * @param len
190 		 * @param translate
191 		 */
192 		public XMLAttributeValue(int begin, int len, boolean translate) {
193 			this.iBegin = begin;
194 			this.iLen = len;
195 			this.iTranslate = translate;
196 		}
197 
198 		/**
199 		 * Ctor.
200 		 *
201 		 * @param begin
202 		 * @param len
203 		 */
204 		public XMLAttributeValue(int begin, int len) {
205 			this.iBegin = begin;
206 			this.iLen = len;
207 			this.iTranslate = true;
208 		}
209 
210 		/**
211 		 * getText
212 		 *
213 		 * @return String
214 		 */
215 		public String getText() {
216 			if (this.iText == null) {
217 				if (this.iTranslate) {
218 					try {
219 						// Integer hashKey = new Integer(hash);
220 						// text = (String)stringTable.get(hashKey);
221 						// text = null;
222 						// if (text == null) {
223 						this.iText = _getChars();
224 						// stringTable.put(hashKey, text);
225 						// } else {
226 						// // System.out.println("Found:"+text);
227 						// cnt++;
228 						// }
229 					} catch (Exception e) {
230 						LogAndTraceBroker.getBroker().trace(Level.WARNING, "exception while decoding CHARACTERS XML", e);
231 						this.iText = new String(XMLPullParser.this.iBufferChar, this.iBegin, this.iLen);
232 					}
233 				} else {
234 					// Integer hashKey = new Integer(hash);
235 					// text = (String)stringTable.get(hashKey);
236 					// text = null;
237 					// if (text == null) {
238 					this.iText = new String(XMLPullParser.this.iBufferChar, this.iBegin, this.iLen);
239 					// stringTable.put(hashKey, text);
240 					// } else {
241 					// // System.out.println("Found:"+text);
242 					// cnt++;
243 					// }
244 				}
245 			}
246 			return this.iText;
247 		}
248 
249 		// public void setTranslate(boolean translate) {
250 		// this.translate = translate;
251 		// }
252 
253 		/**
254 		 * init
255 		 *
256 		 * @param begin
257 		 * @param len
258 		 */
259 		public void init(int begin, int len) {
260 			this.iBegin = begin;
261 			this.iLen = len;
262 			this.iText = null;
263 		}
264 
265 		/**
266 		 * setTranslate
267 		 *
268 		 * @param translate
269 		 */
270 		public void setTranslate(boolean translate) {
271 			this.iTranslate = translate;
272 		}
273 
274 		@Override
275 		public String toString() {
276 			return getText();
277 		}
278 
279 		protected String _getChars() throws XMLPullParserException {
280 			StringBuffer attributeValue = new StringBuffer();
281 			int last = this.iBegin + this.iLen;
282 			char ch;
283 			// char prevCh = '\0'; //18274
284 			for (this.iCurrentPos = this.iBegin; this.iCurrentPos < last; this.iCurrentPos++) {
285 				ch = XMLPullParser.this.iBufferChar[this.iCurrentPos];
286 				if (ch == '&') {
287 					// try {
288 					// System.out.println(_currentPos);
289 					int ref = parseReference();
290 
291 					if (ref > -1) attributeValue.append((char) ref);
292 					// }
293 					// catch (Exception e) {
294 					// e.printStackTrace();
295 					// }
296 					// } else if (ch == '\t'
297 					// || ch == '\r'
298 					// || ch == '\n') {
299 					//
300 					// if (ch != '\n' || prevCh != '\r'){
301 					// attributeValue.append(' ');
302 					// }
303 				} else {
304 					attributeValue.append(ch);
305 				}
306 				// prevCh = ch; //18274
307 			}
308 			return attributeValue.toString();
309 		}
310 
311 		protected int parseReference() throws XMLPullParserException {
312 			this.iCurrentPos++;
313 			char ch1 = XMLPullParser.this.iBufferChar[this.iCurrentPos++];
314 			if (ch1 == '#') {
315 				ch1 = XMLPullParser.this.iBufferChar[this.iCurrentPos++];
316 				if (ch1 == 'x') {
317 					int value = 0;
318 					do {
319 						ch1 = XMLPullParser.this.iBufferChar[this.iCurrentPos++];
320 						if (ch1 >= '0' && ch1 <= '9') value = value * 16 + (ch1 - '0'); else if (
321 							ch1 >= 'A' && ch1 <= 'F' || ch1 >= 'a' && ch1 <= 'f'
322 						) value = value * 16 + (Character.toUpperCase(ch1) - 'A' + 10); else if (
323 							ch1 == ';'
324 						) break; else throw new XMLPullParserException(
325 							"invalid character while parsing hex encoded number " + escape(ch1)
326 						);
327 					} while (true);
328 					this.iCurrentPos--; // 18274
329 					return (char) value;
330 				}
331 				int value = 0;
332 				if (ch1 >= '0' && ch1 <= '9') {
333 					do {
334 						if (ch1 >= '0' && ch1 <= '9') {
335 							value = value * 10 + (ch1 - '0');
336 							ch1 = XMLPullParser.this.iBufferChar[this.iCurrentPos++];
337 						} else if (ch1 == ';') break; else throw new XMLPullParserException(
338 							"invalid character while parsing decimal encoded number: " + escape(ch1)
339 						);
340 					} while (true);
341 					this.iCurrentPos--; // 18274
342 					return (char) value;
343 				}
344 				throw new XMLPullParserException("invalid number format");
345 			}
346 			int startPos = this.iCurrentPos - 1;
347 			if (isValidStartElementNameChar(ch1)) {
348 				do {
349 					ch1 = XMLPullParser.this.iBufferChar[this.iCurrentPos++];
350 					if (ch1 == ';') break;
351 					if (!isValidElementNameChar(ch1)) throw new XMLPullParserException(
352 						"invalid reference character " + escape(ch1)
353 					);
354 				} while (true);
355 			} else {
356 				throw new XMLPullParserException("expected valid name start character for value reference");
357 			}
358 			this.iCurrentPos--;
359 			ch1 = XMLPullParser.this.iBufferChar[startPos];
360 			char ch2 = XMLPullParser.this.iBufferChar[startPos + 1];
361 			char ch3 = XMLPullParser.this.iBufferChar[startPos + 2];
362 
363 			if (ch1 == 'l' && ch2 == 't' && ch3 == ';') {
364 				return '<';
365 			} else if (ch1 == 'g' && ch2 == 't' && ch3 == ';') {
366 				return '>';
367 			} else {
368 				char ch4 = XMLPullParser.this.iBufferChar[startPos + 3];
369 				if (ch1 == 'a' && ch2 == 'm' && ch3 == 'p' && ch4 == ';') {
370 					return '&';
371 				}
372 				char ch5 = XMLPullParser.this.iBufferChar[startPos + 4];
373 				if (ch1 == 'a' && ch2 == 'p' && ch3 == 'o' && ch4 == 's' && ch5 == ';') {
374 					return '\'';
375 				} else if (ch1 == 'q' && ch2 == 'u' && ch3 == 'o' && ch4 == 't' && ch5 == ';') {
376 					return '\"';
377 				} else {
378 					// TODO return reference
379 				}
380 			}
381 			return -1;
382 		}
383 	}
384 
385 	/**
386 	 * ATTRIBUTE
387 	 */
388 	public static final int ATTRIBUTE = 10;
389 
390 	/**
391 	 * CDATA
392 	 */
393 	public static final int CDATA = 12;
394 
395 	/**
396 	 * CHARACTERS
397 	 */
398 	public static final int CHARACTERS = 4;
399 
400 	/**
401 	 * COMMENT
402 	 */
403 	public static final int COMMENT = 5;
404 
405 	/**
406 	 * DTD
407 	 */
408 	public static final int DTD = 11;
409 
410 	/**
411 	 * EMPTY
412 	 */
413 	public static final String EMPTY = "";
414 
415 	/**
416 	 * END_DOCUMENT
417 	 */
418 	public static final int END_DOCUMENT = 8;
419 
420 	/**
421 	 * END_ELEMENT
422 	 */
423 	public static final int END_ELEMENT = 2;
424 
425 	/**
426 	 * ENTITY_DECLARATION
427 	 */
428 	public static final int ENTITY_DECLARATION = 15;
429 
430 	/**
431 	 * ENTITY_REFERENCE
432 	 */
433 	public static final int ENTITY_REFERENCE = 9;
434 
435 	/**
436 	 * NAMESPACE
437 	 */
438 	public static final int NAMESPACE = 13;
439 
440 	/**
441 	 * NOTATION_DECLARATION
442 	 */
443 	public static final int NOTATION_DECLARATION = 14;
444 
445 	/**
446 	 * PROCESSING_INSTRUCTION
447 	 */
448 	public static final int PROCESSING_INSTRUCTION = 3;
449 
450 	/**
451 	 * SPACE
452 	 */
453 	public static final int SPACE = 6;
454 
455 	/**
456 	 * START_DOCUMENT
457 	 */
458 	public static final int START_DOCUMENT = 7;
459 
460 	/**
461 	 * START_ELEMENT
462 	 */
463 	public static final int START_ELEMENT = 1;
464 
465 	/**
466 	 * main
467 	 *
468 	 * @param args
469 	 */
470 	public static void main(String[] args) {
471 		// this did testing
472 	}
473 
474 	// TODO: ebak: this function seems to be wrong, because "synchronizes" to
475 	// IRETURNVALUE
476 	/**
477 	 * next
478 	 *
479 	 * @param reader
480 	 * @param parserHdlr
481 	 * @return boolean
482 	 * @throws Exception
483 	 */
484 	public static boolean next(XMLPullParser reader, XMLDefaultHandlerImpl parserHdlr) throws Exception {
485 		while (reader.hasNext()) {
486 			int event = reader.next();
487 			switch (event) {
488 				case START_ELEMENT:
489 					parserHdlr.startElement(EMPTY, EMPTY, reader.getElementName(), reader.getAttributes());
490 					break;
491 				case END_ELEMENT:
492 					parserHdlr.endElement(EMPTY, EMPTY, reader.getElementName());
493 
494 					String lastElementName = null;
495 					if (reader.getElementNames().size() > 0) {
496 						ArrayList<String> elementNames = reader.getElementNames();
497 						lastElementName = elementNames.get(elementNames.size() - 1);
498 					}
499 
500 					if (lastElementName != null && lastElementName.equalsIgnoreCase("IRETURNVALUE")) {
501 						return true;
502 					}
503 					break;
504 				case CHARACTERS:
505 					char[] buf = reader.getText().toCharArray();
506 					parserHdlr.characters(buf, 0, buf.length);
507 					break;
508 				case END_DOCUMENT:
509 					return false;
510 			}
511 		}
512 		return false;
513 	}
514 
515 	ArrayList<XMLAttributeValue> iAttributeNames = new ArrayList<XMLAttributeValue>();
516 
517 	Attributes iAttributes;
518 
519 	ArrayList<XMLAttributeValue> iAttributeValues = new ArrayList<XMLAttributeValue>();
520 
521 	char[] iBufferChar = null;
522 
523 	XMLAttributeValue iCharacters;
524 
525 	boolean iClosingElementNamePending;
526 
527 	int iColNumber = 1;
528 
529 	int iCurrentPosition = 0;
530 
531 	int iCurrentState = 0;
532 
533 	String iElementName;
534 
535 	ArrayList<String> iElementNames = new ArrayList<String>();
536 
537 	int iEndCharacters;
538 
539 	int iFinishChar = 0;
540 
541 	Reader iInstream;
542 
543 	/*
544 	 * ebak: implement close
545 	 */
546 	boolean iClosed;
547 
548 	int iLineNumber = 1;
549 
550 	boolean iSeenEpilog;
551 
552 	boolean iSeenProlog;
553 
554 	int iStartCharacters;
555 
556 	int iTotalAttributes;
557 
558 	/**
559 	 * Ctor.
560 	 *
561 	 * @param in
562 	 */
563 	public XMLPullParser(Reader in) {
564 		this.iInstream = in;
565 		reset();
566 	}
567 
568 	/**
569 	 * close
570 	 *
571 	 * @throws IOException
572 	 */
573 	public void close() throws IOException {
574 		if (this.iClosed) return;
575 		this.iClosed = true;
576 		this.iInstream.close();
577 	}
578 
579 	/**
580 	 * getAttributes
581 	 *
582 	 * @return Attributes
583 	 */
584 	public Attributes getAttributes() {
585 		if (this.iCurrentState != START_ELEMENT) return null;
586 
587 		if (this.iAttributes == null) {
588 			this.iAttributes = new XMLAttributes();
589 		}
590 		return this.iAttributes;
591 	}
592 
593 	/**
594 	 * getElementName
595 	 *
596 	 * @return String
597 	 */
598 	public String getElementName() {
599 		return this.iElementName;
600 	}
601 
602 	/**
603 	 * getElementNames
604 	 *
605 	 * @return Vector
606 	 */
607 	public ArrayList<String> getElementNames() {
608 		return this.iElementNames;
609 	}
610 
611 	/**
612 	 * getLevel
613 	 *
614 	 * @return int
615 	 */
616 	public int getLevel() {
617 		return this.iElementNames.size();
618 	}
619 
620 	/**
621 	 * getText
622 	 *
623 	 * @return String
624 	 */
625 	public String getText() {
626 		String result = null;
627 		if (this.iCurrentState == CHARACTERS && this.iCharacters != null) {
628 			return this.iCharacters.getText();
629 		}
630 		return result;
631 	}
632 
633 	/**
634 	 * hasNext
635 	 *
636 	 * @return boolean
637 	 */
638 	public boolean hasNext() {
639 		return !this.iClosed && this.iCurrentState != END_DOCUMENT;
640 	}
641 
642 	/**
643 	 * next
644 	 *
645 	 * @return int
646 	 * @throws IOException
647 	 */
648 	public int next() throws IOException {
649 		char ch;
650 		resetAttributes();
651 		ensureCapacity();
652 		if (this.iClosingElementNamePending) {
653 			this.iClosingElementNamePending = false;
654 			this.iElementNames.remove(this.iElementNames.size() - 1);
655 			this.iCurrentState = END_ELEMENT;
656 			return this.iCurrentState;
657 		}
658 		do {
659 			ch = (char) getNextCharCheckingEOF();
660 			if (ch == '<') {
661 				ch = (char) getNextChar();
662 				if (ch == '?') {
663 					if (this.iSeenProlog) {
664 						throw new XMLPullParserException(
665 							"The processing instruction target matching \"[xX][mM][lL]\" is not allowed."
666 						);
667 					}
668 					this.iSeenProlog = true;
669 					parsePI();
670 					ch = (char) getNextChar();
671 					ch = skipOptionalSpaces(ch);
672 					if (ch != '<') throw new XMLPullParserException(this, "Content is not allowed in prolog.");
673 					goBack();
674 
675 					this.iCurrentState = START_DOCUMENT;
676 					return this.iCurrentState;
677 				} else if (ch == '!') {
678 					ch = (char) getNextChar();
679 					if (ch == '-') {
680 						parseComment();
681 
682 						this.iCurrentState = COMMENT;
683 						return this.iCurrentState;
684 					} else if (ch == '[') {
685 						parseCDATA();
686 						this.iCurrentState = CHARACTERS;
687 						return this.iCurrentState;
688 					} else throw new XMLPullParserException(this, "unexpected char " + escape(ch));
689 				} else if (ch == '/') {
690 					parseEndElement();
691 					this.iCurrentState = END_ELEMENT;
692 					return this.iCurrentState;
693 				} else if (ch == '&') {
694 					parseUnknown();
695 				} else if (isValidStartElementNameChar(ch)) {
696 					if (!this.iSeenProlog) {
697 						this.iSeenProlog = true;
698 						this.iCurrentState = START_DOCUMENT;
699 						goBack();
700 						goBack();
701 						return this.iCurrentState;
702 					}
703 					parseStartElement(ch);
704 					this.iCurrentState = START_ELEMENT;
705 					return this.iCurrentState;
706 				} else {
707 					throw new XMLPullParserException(this, "unexpected char " + escape(ch));
708 				}
709 			} else {
710 				this.iStartCharacters = this.iCurrentPosition - 1;
711 				boolean amp = false;
712 				// int hash = 0;
713 				// int n = 0;
714 				do {
715 					// n = (n+1)&15;
716 					// hash = hash*primes[n]+ch;
717 
718 					ch = (char) getNextCharCheckingEOF();
719 					if (ch == (char) -1) {
720 						if (this.iElementNames.size() != 0) throw new XMLPullParserException(this, "unexpected EOF ");
721 
722 						this.iCurrentState = END_DOCUMENT;
723 						return this.iCurrentState;
724 					} else if (ch == '\r' || ch == '\n') {
725 						/* :) */
726 					} else {
727 						if (!isSpace(ch) && ch != '<' && this.iElementNames.size() == 0) {
728 							if (!this.iSeenProlog) throw new XMLPullParserException(
729 								this,
730 								"Content is not allowed in trailing section."
731 							);
732 							throw new XMLPullParserException(this, "Content is not allowed in trailing section.");
733 						}
734 					}
735 					amp = false;
736 					if (ch == '&') {
737 						amp = true;
738 						int i = parseReference();
739 						ch = (char) (i & 0xFFFF);
740 					}
741 				} while (ch != '<' || amp);
742 				this.iEndCharacters = this.iCurrentPosition;
743 				goBack();
744 				if (this.iElementNames.size() > 0) {
745 					if (this.iCharacters == null) this.iCharacters =
746 						new XMLAttributeValue(this.iStartCharacters, this.iEndCharacters - this.iStartCharacters - 1); else {
747 						this.iCharacters.init(this.iStartCharacters, this.iEndCharacters - this.iStartCharacters - 1);
748 						this.iCharacters.setTranslate(true);
749 					}
750 
751 					this.iCurrentState = CHARACTERS;
752 					return this.iCurrentState;
753 				}
754 			}
755 		} while (true);
756 	}
757 
758 	/**
759 	 * reset
760 	 */
761 	public void reset() {
762 		this.iSeenProlog = true;
763 		this.iCurrentState = 0;
764 		this.iClosingElementNamePending = false;
765 		this.iColNumber = 1;
766 		this.iLineNumber = 1;
767 		this.iElementName = null;
768 		this.iElementNames.clear();
769 		this.iAttributeNames.clear();
770 		this.iAttributeValues.clear();
771 		this.iAttributes = null;
772 
773 		this.iStartCharacters = 0;
774 		this.iEndCharacters = 0;
775 
776 		this.iTotalAttributes = 0;
777 		this.iSeenProlog = false;
778 		this.iSeenEpilog = false;
779 	}
780 
781 	@Override
782 	public String toString() {
783 		switch (this.iCurrentState) {
784 			case START_ELEMENT: {
785 					StringBuilder sb = new StringBuilder("START ELEM: <");
786 					sb.append(this.iElementName);
787 					if (this.iAttributeNames.size() > 0) {
788 						sb.append(" ");
789 						for (int i = 0; i < this.iAttributeNames.size(); i++) {
790 							sb.append(this.iAttributeNames.get(i));
791 							sb.append("=\"");
792 							sb.append(this.iAttributeValues.get(i));
793 							sb.append("\" ");
794 						}
795 					}
796 					sb.append(">");
797 					return sb.toString();
798 				}
799 			case END_ELEMENT: {
800 					String s = "END ELEM: </" + this.iElementName + ">";
801 					return s;
802 				}
803 			case CHARACTERS: {
804 					return "CHARACTERS: \"" + getText(); // .replaceAll("\n",
805 					// "\\\\n").replaceAll("\r",
806 					// "\\\\r").replaceAll("\t",
807 					// "\\\\t")+"\"";
808 				}
809 		}
810 		return "UNKOWN";
811 	}
812 
813 	protected char _getNextChar() {
814 		return (char) -1;
815 	}
816 
817 	protected void addAttribute(int begName, int lenName, int begValue, int lenValue) {
818 		if (this.iAttributeNames.size() > this.iTotalAttributes) {
819 			XMLAttributeValue attribute = this.iAttributeValues.get(this.iTotalAttributes);
820 			XMLAttributeValue name = this.iAttributeNames.get(this.iTotalAttributes);
821 			this.iTotalAttributes++;
822 			attribute.init(begValue, lenValue);
823 			attribute.setTranslate(true);
824 			name.init(begName, lenName);
825 			name.setTranslate(false);
826 		} else {
827 			XMLAttributeValue attribute = new XMLAttributeValue(begValue, lenValue);
828 			XMLAttributeValue name = new XMLAttributeValue(begName, lenName, false);
829 			this.iTotalAttributes++;
830 			this.iAttributeNames.add(name);
831 			this.iAttributeValues.add(attribute);
832 		}
833 	}
834 
835 	protected void ensureCapacity() {
836 		if (this.iBufferChar == null) this.iBufferChar = new char[1024];
837 
838 		if (this.iCurrentPosition >= (8 * this.iBufferChar.length) / 10) {
839 			System.arraycopy(
840 				this.iBufferChar,
841 				this.iCurrentPosition,
842 				this.iBufferChar,
843 				0,
844 				this.iFinishChar - this.iCurrentPosition
845 			);
846 			this.iFinishChar -= this.iCurrentPosition;
847 			this.iCurrentPosition = 0;
848 		}
849 	}
850 
851 	protected String escape(char ch) {
852 		String result;
853 		if (ch == '\n') result = "\'\\n\'";
854 		if (ch == '\r') result = "\'\\r\'";
855 		if (ch == '\t') result = "\'\\t\'";
856 		if (ch == '\'') result = "\'\\'\'";
857 		if (ch > '\177' || ch < ' ') result = "\'\\u" + Integer.toHexString(ch) + "\'"; else result = "\'" + ch + "\'";
858 		return result;
859 	}
860 
861 	protected int getChar() throws IOException {
862 		if (this.iFinishChar <= this.iCurrentPosition) {
863 			if (this.iBufferChar == null) {
864 				this.iBufferChar = new char[1024];
865 			} else if (this.iFinishChar >= this.iBufferChar.length) {
866 				char[] tmp = this.iBufferChar;
867 				this.iBufferChar = new char[this.iBufferChar.length << 1];
868 				System.arraycopy(tmp, 0, this.iBufferChar, 0, tmp.length);
869 			}
870 
871 			int total = this.iInstream.read(this.iBufferChar, this.iFinishChar, this.iBufferChar.length - this.iFinishChar);
872 
873 			if (total <= 0) {
874 				return -1;
875 			}
876 			this.iFinishChar += total;
877 		}
878 		return this.iBufferChar[this.iCurrentPosition++];
879 	}
880 
881 	protected int getNextChar() throws IOException {
882 		int ch;
883 
884 		if (this.iFinishChar <= this.iCurrentPosition) {
885 			ch = getChar();
886 		} else ch = this.iBufferChar[this.iCurrentPosition++];
887 
888 		if (ch == -1) throw new XMLPullParserException(this, "unexpected end of document");
889 		if (ch == '\n') {
890 			this.iLineNumber++;
891 			this.iColNumber = 1;
892 		} else this.iColNumber++;
893 
894 		return (char) ch;
895 	}
896 
897 	protected int getNextCharCheckingEOF() throws IOException {
898 		int ch;
899 
900 		if (this.iFinishChar <= this.iCurrentPosition) {
901 			ch = getChar();
902 		} else ch = this.iBufferChar[this.iCurrentPosition++];
903 
904 		if (ch == '\n') {
905 			this.iLineNumber++;
906 			this.iColNumber = 1;
907 		} else this.iColNumber++;
908 
909 		return (char) ch;
910 	}
911 
912 	protected void goBack() {
913 		this.iCurrentPosition--;
914 		if (this.iColNumber > 1) {
915 			this.iColNumber--;
916 		}
917 	}
918 
919 	protected boolean isSpace(char ch) {
920 		return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
921 	}
922 
923 	protected boolean isValidElementNameChar(char ch) {
924 		return (
925 			(
926 				ch < 256 &&
927 				(
928 					ch >= 'A' &&
929 					ch <= 'Z' ||
930 					ch >= 'a' &&
931 					ch <= 'z' ||
932 					ch == '_' ||
933 					ch == ':' ||
934 					ch == '-' ||
935 					ch == '.' ||
936 					ch >= '0' &&
937 					ch <= '9' ||
938 					ch == '\267'
939 				)
940 			) ||
941 			ch >= '\300' &&
942 			ch <= '\u02FF' ||
943 			ch >= '\u0370' &&
944 			ch <= '\u037D' ||
945 			ch >= '\u0300' &&
946 			ch <= '\u036F' ||
947 			ch >= '\u037F' &&
948 			ch <= '\u2027' ||
949 			ch >= '\u202A' &&
950 			ch <= '\u218F' ||
951 			ch >= '\u2800' &&
952 			ch <= '\uFFEF'
953 		);
954 		// return isValidStartElementNameChar(ch)
955 		// || (ch < 256 && (ch == '-'
956 		// || ch == '.'
957 		// || ch >= '0' && ch <= '9'
958 		// || ch == '\267'))
959 		// || ch >= '\u0300' && ch <= '\u036F'
960 		// || ch >= '\u0400' && ch <= '\u2027'
961 		// || ch >= '\u202A' && ch <= '\u218F'
962 		// || ch >= '\u2800' && ch <= '\uFFEF';
963 	}
964 
965 	protected boolean isValidStartElementNameChar(char ch) {
966 		return (
967 			(ch < 256 && (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z' || ch == '_' || ch == ':')) ||
968 			(
969 				ch >= '\300' &&
970 				ch <= '\u02FF' ||
971 				ch >= '\u0370' &&
972 				ch <= '\u037D' ||
973 				ch >= '\u037F' &&
974 				ch <= '\u0400' ||
975 				ch >= '\u0400' &&
976 				ch <= '\u2027' ||
977 				ch >= '\u202A' &&
978 				ch <= '\u218F' ||
979 				ch >= '\u2800' &&
980 				ch <= '\uFFEF'
981 			)
982 		);
983 	}
984 
985 	protected void parseAttribute(char ch) throws IOException {
986 		int startAttributeName = this.iCurrentPosition - 1;
987 		int endAttributeName;
988 		// int hash = 0;
989 		// int n = 0;
990 		do {
991 			// n = (n+1)&15;
992 			// hash = hash*primes[n]+ch;
993 
994 			ch = (char) getNextChar();
995 		} while (isValidElementNameChar(ch));
996 		endAttributeName = this.iCurrentPosition;
997 		// String attributeName = new String(bufferChar, startAttributeName,
998 		// endAttributeName - startAttributeName-1);
999 		// attributeNames.add(attributeName);
1000 
1001 		ch = skipOptionalSpaces(ch);
1002 
1003 		if (ch != '=') throw new XMLPullParserException(
1004 			this,
1005 			"missing character \'=\'instead " + escape(ch) + " was found "
1006 		);
1007 
1008 		ch = (char) getNextChar();
1009 		ch = skipOptionalSpaces(ch);
1010 
1011 		char delimiter;
1012 		if (ch != '\"' && ch != '\'') throw new XMLPullParserException(
1013 			this,
1014 			"missing character \'\"\' or \'\'\' instead " + escape(ch) + " was found "
1015 		);
1016 		delimiter = ch;
1017 
1018 		// StringBuffer attributeValue = new StringBuffer();
1019 		int startAttributeValue = this.iCurrentPosition;
1020 		char prevCh = '\0';
1021 		// int hashvalue = 0;
1022 		// n = 0;
1023 		do {
1024 			// n = (n+1)&15;
1025 			// hashvalue = hashvalue *primes[n] +ch;
1026 
1027 			ch = (char) getNextChar();
1028 			if (ch == delimiter) break; else if (ch == '<' || ch == '>') throw new XMLPullParserException(
1029 				this,
1030 				"illegal character " + escape(ch)
1031 			); else if (ch == '&') {
1032 				int ref = parseReference();
1033 				ch = (char) (ref & 0xffff);
1034 				// attributeValue.append((char)ref);
1035 			} else if (ch == '\t' || ch == '\r' || ch == '\n') {
1036 				if (ch != '\n' || prevCh != '\r') {
1037 					// attributeValue.append(' ');
1038 				}
1039 			} else {
1040 				// attributeValue.append(ch);
1041 			}
1042 			prevCh = ch;
1043 		} while (true);
1044 		int endAttributeValue = this.iCurrentPosition;
1045 		// attributeValues.add(attributeValue.toString());
1046 		addAttribute(
1047 			startAttributeName,
1048 			endAttributeName - startAttributeName - 1,
1049 			startAttributeValue,
1050 			endAttributeValue - startAttributeValue - 1
1051 		);
1052 
1053 		return;
1054 	}
1055 
1056 	protected int parseCDATA() throws IOException {
1057 		char ch;
1058 
1059 		if (
1060 			(char) getNextChar() != 'C' ||
1061 			(char) getNextChar() != 'D' ||
1062 			(char) getNextChar() != 'A' ||
1063 			(char) getNextChar() != 'T' ||
1064 			(char) getNextChar() != 'A' ||
1065 			(char) getNextChar() != '['
1066 		) throw new XMLPullParserException("CDATA must start with \"<![CDATA[\".");
1067 		boolean braketFound = false;
1068 		boolean doubleBraket = false;
1069 		int startCharacter = this.iCurrentPosition;
1070 		do {
1071 			ch = (char) getNextCharCheckingEOF();
1072 			if (ch == ']') {
1073 				if (braketFound) doubleBraket = true;
1074 				braketFound = true;
1075 			} else if (ch == '>' && doubleBraket) {
1076 				break;
1077 			} else {
1078 				braketFound = false;
1079 				doubleBraket = false;
1080 			}
1081 			if (ch == (char) -1) throw new XMLPullParserException(
1082 				"XML document structures must start and end within the same entity."
1083 			);
1084 		} while (true);
1085 
1086 		int endCharacter = this.iCurrentPosition - 3;
1087 
1088 		this.iCharacters.setTranslate(false);
1089 		this.iCharacters.init(startCharacter, endCharacter - startCharacter);
1090 
1091 		return -1;
1092 	}
1093 
1094 	protected int parseComment() throws IOException {
1095 		char ch;
1096 		ch = (char) getNextChar();
1097 		if (ch != '-') throw new XMLPullParserException("Comment must start with \"<!--\".");
1098 		boolean dashFound = false;
1099 		boolean doubleDash = false;
1100 		do {
1101 			ch = (char) getNextCharCheckingEOF();
1102 			if (ch == '-') {
1103 				if (dashFound) doubleDash = true;
1104 				dashFound = true;
1105 			} else if (ch == '>' && doubleDash) {
1106 				break;
1107 			} else {
1108 				dashFound = false;
1109 				doubleDash = false;
1110 			}
1111 			if (ch == (char) -1) throw new XMLPullParserException(
1112 				"XML document structures must start and end within the same entity."
1113 			);
1114 		} while (true);
1115 
1116 		// if (!seenProlog) {
1117 		// ch = (char)getNextChar();
1118 		// skipOptionalSpaces(ch);
1119 		// goBack();
1120 		// }
1121 		return -1;
1122 	}
1123 
1124 	protected void parseEndElement() throws IOException {
1125 		int startElementName;
1126 		int endElementName;
1127 
1128 		char ch;
1129 
1130 		startElementName = this.iCurrentPosition;
1131 		do {
1132 			ch = (char) getNextChar();
1133 		} while (isValidElementNameChar(ch));
1134 
1135 		endElementName = this.iCurrentPosition;
1136 		this.iElementName = new String(this.iBufferChar, startElementName, endElementName - startElementName - 1);
1137 
1138 		if (
1139 			!this.iElementNames.get(this.iElementNames.size() - 1).equals(this.iElementName.toUpperCase())
1140 		) throw new XMLPullParserException(
1141 			this,
1142 			"The content of elements must consist of well-formed character data or markup."
1143 		);
1144 
1145 		this.iElementNames.remove(this.iElementNames.size() - 1);
1146 
1147 		ch = skipOptionalSpaces(ch);
1148 		if (ch != '>') throw new XMLPullParserException(
1149 			this,
1150 			"\'=\' was expected, but \'" + escape(ch) + "\' was found instead"
1151 		);
1152 
1153 		if (this.iElementNames.size() == 0) this.iSeenEpilog = true;
1154 	}
1155 
1156 	protected int parsePI() throws IOException {
1157 		char ch;
1158 		ch = (char) getNextChar();
1159 		boolean dashFound = false;
1160 		do {
1161 			ch = (char) getNextCharCheckingEOF();
1162 			if (ch == '?') {
1163 				dashFound = true;
1164 			} else if (ch == '>' && dashFound) {
1165 				break;
1166 			} else {
1167 				dashFound = false;
1168 			}
1169 			if (ch == (char) -1) throw new XMLPullParserException(
1170 				"XML document structures must start and end within the same entity."
1171 			);
1172 		} while (true);
1173 
1174 		return -1;
1175 	}
1176 
1177 	protected int parseReference() throws IOException {
1178 		char ch1 = (char) getNextChar();
1179 		if (ch1 == '#') {
1180 			ch1 = (char) getNextChar();
1181 			if (ch1 == 'x') {
1182 				int value = 0;
1183 				do {
1184 					ch1 = (char) getNextChar();
1185 					if (ch1 >= '0' && ch1 <= '9') value = value * 16 + (ch1 - '0'); else if (
1186 						ch1 >= 'A' && ch1 <= 'F' || ch1 >= 'a' && ch1 <= 'f'
1187 					) value = value * 16 + (Character.toUpperCase(ch1) - 'A' + 10); else if (
1188 						ch1 == ';'
1189 					) break; else throw new XMLPullParserException(
1190 						this,
1191 						"invalid character while parsing hex encoded number " + escape(ch1)
1192 					);
1193 				} while (true);
1194 				return (char) value;
1195 			}
1196 			int value = 0;
1197 			if (ch1 >= '0' && ch1 <= '9') {
1198 				do {
1199 					if (ch1 >= '0' && ch1 <= '9') {
1200 						value = value * 10 + (ch1 - '0');
1201 						ch1 = (char) getNextChar();
1202 					} else if (ch1 == ';') break; else throw new XMLPullParserException(
1203 						this,
1204 						"invalid character while parsing decimal encoded number: " + escape(ch1)
1205 					);
1206 				} while (true);
1207 				return (char) value;
1208 			}
1209 			throw new XMLPullParserException(this, "invalid number format");
1210 		}
1211 		int startPos = this.iCurrentPosition - 1;
1212 		if (isValidStartElementNameChar(ch1)) {
1213 			do {
1214 				ch1 = (char) getNextChar();
1215 				if (ch1 == ';') break;
1216 				if (!isValidElementNameChar(ch1)) throw new XMLPullParserException(
1217 					"invalid reference character " + escape(ch1)
1218 				);
1219 			} while (true);
1220 		} else {
1221 			throw new XMLPullParserException(this, "expected valid name start character for value reference");
1222 		}
1223 		goBack();
1224 		ch1 = this.iBufferChar[startPos];
1225 		char ch2 = this.iBufferChar[startPos + 1];
1226 		char ch3 = this.iBufferChar[startPos + 2];
1227 
1228 		if (ch1 == 'l' && ch2 == 't' && ch3 == ';') {
1229 			return '<';
1230 		} else if (ch1 == 'g' && ch2 == 't' && ch3 == ';') {
1231 			return '>';
1232 		} else {
1233 			char ch4 = this.iBufferChar[startPos + 3];
1234 			if (ch1 == 'a' && ch2 == 'm' && ch3 == 'p' && ch4 == ';') {
1235 				return '&';
1236 			}
1237 			char ch5 = this.iBufferChar[startPos + 4];
1238 			if (ch1 == 'a' && ch2 == 'p' && ch3 == 'o' && ch4 == 's' && ch5 == ';') {
1239 				return '\'';
1240 			} else if (ch1 == 'q' && ch2 == 'u' && ch3 == 'o' && ch4 == 't' && ch5 == ';') {
1241 				return '\"';
1242 			} else {
1243 				// TODO return reference
1244 			}
1245 		}
1246 		return -1;
1247 	}
1248 
1249 	protected int parseStartElement(char ch) throws IOException {
1250 		int startElementName;
1251 		int endElementName;
1252 
1253 		resetAttributes();
1254 
1255 		startElementName = this.iCurrentPosition - 1;
1256 
1257 		do {
1258 			// n = (n+1)&15;
1259 			// hash = hash*primes[n]+ch;
1260 
1261 			ch = (char) getNextChar();
1262 		} while (isValidElementNameChar(ch));
1263 
1264 		endElementName = this.iCurrentPosition;
1265 		// Integer hashKey = new Integer(hash);
1266 		// elementName = (String)stringTable.get(hashKey);
1267 		// elementName = null;
1268 		// if (elementName == null) {
1269 		this.iElementName = new String(this.iBufferChar, startElementName, endElementName - startElementName - 1);
1270 		// stringTable.put(hashKey, elementName);
1271 		// } else {
1272 		// // System.out.println("Found: "+elementName);
1273 		// cnt++;
1274 		// }
1275 		this.iElementNames.add(this.iElementName.toUpperCase());
1276 
1277 		do {
1278 			ch = skipOptionalSpaces(ch);
1279 			if (ch == '>') {
1280 				break;
1281 			} else if (ch == '/') {
1282 				ch = (char) getNextChar();
1283 				if (ch != '>') {
1284 					throw new XMLPullParserException(this, "\'=\' was expected, but \'" + escape(ch) + "\' was found instead");
1285 				}
1286 				this.iClosingElementNamePending = true;
1287 			} else if (isValidStartElementNameChar(ch)) {
1288 				parseAttribute(ch);
1289 				ch = (char) getNextChar();
1290 			} else throw new XMLPullParserException(
1291 				this,
1292 				"Element type \"CIM\" must be followed by either attribute specifications, \">\" or \"/>\"."
1293 			);
1294 		} while (true);
1295 		return -1;
1296 	}
1297 
1298 	protected void parseUnknown() throws IOException {
1299 		char ch;
1300 		do {
1301 			ch = (char) getNextChar();
1302 			if (ch == '<') throw new XMLPullParserException("\'>\' was expected, but \'<\' was found instead.");
1303 		} while (ch != '>');
1304 	}
1305 
1306 	protected void resetAttributes() {
1307 		this.iTotalAttributes = 0;
1308 		// attributeNames.setSize(0);
1309 		// ebak: clear attributes
1310 		this.iAttributeNames.clear();
1311 		this.iAttributeValues.clear();
1312 		// /clear attributes
1313 	}
1314 
1315 	protected char skipOptionalSpaces(char ch) throws IOException {
1316 		while (isSpace(ch)) {
1317 			ch = (char) getNextChar();
1318 		}
1319 		return ch;
1320 	}
1321 
1322 	protected char skipRequiredSpaces(char ch) throws IOException {
1323 		if (!isSpace(ch)) throw new XMLPullParserException(this, "space expected");
1324 		do {
1325 			ch = (char) getNextChar();
1326 		} while (isSpace(ch));
1327 		return ch;
1328 	}
1329 }