1. /*
  2. * @(#)XMLDecoder.java 1.20 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.beans;
  8. import java.io.*;
  9. import java.util.*;
  10. import org.xml.sax.*;
  11. import javax.xml.parsers.SAXParserFactory;
  12. import javax.xml.parsers.ParserConfigurationException;
  13. import javax.xml.parsers.SAXParser;
  14. /**
  15. * The <code>XMLDecoder</code> class is used to read XML documents
  16. * created using the <code>XMLEncoder</code> and is used just like
  17. * the <code>ObjectInputStream</code>. For example, one can use
  18. * the following fragment to read the first object defined
  19. * in an XML document written by the <code>XMLEncoder</code>
  20. * class:
  21. * <pre>
  22. * XMLDecoder d = new XMLDecoder(
  23. * new BufferedInputStream(
  24. * new FileInputStream("Test.xml")));
  25. * Object result = d.readObject();
  26. * d.close();
  27. * </pre>
  28. *
  29. * @see XMLEncoder
  30. * @see java.io.ObjectInputStream
  31. *
  32. * @since 1.4
  33. *
  34. * @version 1.5 11/20/00
  35. * @author Philip Milne
  36. */
  37. public class XMLDecoder {
  38. private InputStream in;
  39. private Object owner;
  40. private ExceptionListener exceptionListener;
  41. private ObjectHandler handler;
  42. /**
  43. * Creates a new input stream for reading archives
  44. * created by the <code>XMLEncoder</code> class.
  45. *
  46. * @param in The underlying stream.
  47. *
  48. * @see XMLEncoder#XMLEncoder(OutputStream)
  49. */
  50. public XMLDecoder(InputStream in) {
  51. this(in, null);
  52. }
  53. /**
  54. * Creates a new input stream for reading archives
  55. * created by the <code>XMLEncoder</code> class.
  56. *
  57. * @param in The underlying stream.
  58. * @param owner The owner of this stream.
  59. *
  60. */
  61. public XMLDecoder(InputStream in, Object owner) {
  62. this(in, owner, null);
  63. }
  64. /**
  65. * Creates a new input stream for reading archives
  66. * created by the <code>XMLEncoder</code> class.
  67. *
  68. * @param in the underlying stream.
  69. * @param owner the owner of this stream.
  70. *
  71. */
  72. public XMLDecoder(InputStream in, Object owner, ExceptionListener exceptionListener) {
  73. this.in = in;
  74. setOwner(owner);
  75. setExceptionListener(exceptionListener);
  76. Statement.setCaching(true);
  77. SAXParserFactory factory = SAXParserFactory.newInstance();
  78. try {
  79. SAXParser saxParser = factory.newSAXParser();
  80. handler = new ObjectHandler(this);
  81. saxParser.parse(in, handler);
  82. }
  83. catch (ParserConfigurationException e) {
  84. getExceptionListener().exceptionThrown(e);
  85. }
  86. catch (SAXException se) {
  87. Exception e = se.getException();
  88. getExceptionListener().exceptionThrown((e == null) ? se : e);
  89. }
  90. catch (IOException ioe) {
  91. getExceptionListener().exceptionThrown(ioe);
  92. }
  93. }
  94. /**
  95. * This method closes the input stream associated
  96. * with this stream.
  97. */
  98. public void close() {
  99. Statement.setCaching(false);
  100. try {
  101. in.close();
  102. }
  103. catch (IOException e) {
  104. getExceptionListener().exceptionThrown(e);
  105. }
  106. }
  107. /**
  108. * Sets the exception handler for this stream to <code>exceptionListener</code>.
  109. * The exception handler is notified when this stream catches recoverable
  110. * exceptions.
  111. *
  112. * @param exceptionListener The exception handler for this stream.
  113. *
  114. * @see #getExceptionListener
  115. */
  116. public void setExceptionListener(ExceptionListener exceptionListener) {
  117. this.exceptionListener = exceptionListener;
  118. }
  119. /**
  120. * Gets the exception handler for this stream.
  121. *
  122. * @return The exception handler for this stream.
  123. *
  124. * @see #setExceptionListener
  125. */
  126. public ExceptionListener getExceptionListener() {
  127. return (exceptionListener != null) ? exceptionListener : Statement.defaultExceptionListener;
  128. }
  129. /**
  130. * Reads the next object from the underlying input stream.
  131. *
  132. * @return the next object read
  133. *
  134. * @throws ArrayIndexOutOfBoundsException if the stream contains no objects (or no more objects)
  135. *
  136. * @see XMLEncoder#writeObject
  137. */
  138. public Object readObject() {
  139. return handler.dequeueResult();
  140. }
  141. /**
  142. * Sets the owner of this decoder to <code>owner</code>.
  143. *
  144. * @param owner The owner of this decoder.
  145. *
  146. * @see #getOwner
  147. */
  148. public void setOwner(Object owner) {
  149. this.owner = owner;
  150. }
  151. /**
  152. * Gets the owner of this decoder.
  153. *
  154. * @return The owner of this decoder.
  155. *
  156. * @see #setOwner
  157. */
  158. public Object getOwner() {
  159. return owner;
  160. }
  161. }
  162. class MutableExpression extends Expression {
  163. private Object property;
  164. private Vector argV = new Vector();
  165. private String capitalize(String propertyName) {
  166. if (propertyName.length() == 0) {
  167. return propertyName;
  168. }
  169. return propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
  170. }
  171. public MutableExpression() {
  172. super(null, null, null);
  173. }
  174. public Object[] getArguments() {
  175. return argV.toArray();
  176. }
  177. public String getMethodName() {
  178. if (property == null) {
  179. return super.getMethodName();
  180. }
  181. int setterArgs = (property instanceof String) ? 1 : 2;
  182. String methodName = (argV.size() == setterArgs) ? "set" : "get";
  183. if (property instanceof String) {
  184. return methodName + capitalize((String)property);
  185. }
  186. else {
  187. return methodName;
  188. }
  189. }
  190. public void addArg(Object arg) {
  191. argV.add(arg);
  192. }
  193. public void setTarget(Object target) {
  194. this.target = target;
  195. }
  196. public void setMethodName(String methodName) {
  197. this.methodName = methodName;
  198. }
  199. public void setProperty(Object property) {
  200. this.property = property;
  201. }
  202. }
  203. class ObjectHandler extends HandlerBase {
  204. private Hashtable environment;
  205. private Vector expStack;
  206. private StringBuffer chars;
  207. private XMLDecoder is;
  208. private int itemsRead = 0;
  209. public ObjectHandler(XMLDecoder is) {
  210. environment = new Hashtable();
  211. expStack = new Vector();
  212. chars = new StringBuffer();
  213. this.is = is;
  214. }
  215. private Object getValue(Expression exp) {
  216. try {
  217. return exp.getValue();
  218. }
  219. catch (Exception e) {
  220. is.getExceptionListener().exceptionThrown(e);
  221. return null;
  222. }
  223. }
  224. private void addArg(Object arg) {
  225. // System.out.println("addArg: " + instanceName(arg));
  226. lastExp().addArg(arg);
  227. }
  228. private Object pop(Vector v) {
  229. int last = v.size()-1;
  230. Object result = v.get(last);
  231. v.remove(last);
  232. return result;
  233. }
  234. private Object eval() {
  235. return getValue((Expression)lastExp());
  236. }
  237. private MutableExpression lastExp() {
  238. return (MutableExpression)expStack.lastElement();
  239. }
  240. Object dequeueResult() {
  241. // System.out.println("dequeueResult: " + expStack);
  242. Object[] results = lastExp().getArguments();
  243. return results[itemsRead++];
  244. }
  245. private boolean isPrimitive(String name) {
  246. return name != "void" && Statement.typeNameToClass(name) != null;
  247. }
  248. private void simulateException(String message) {
  249. Exception e = new Exception(message);
  250. e.fillInStackTrace();
  251. is.getExceptionListener().exceptionThrown(e);
  252. }
  253. private Class classForName(String name) {
  254. try {
  255. return Statement.classForName(name);
  256. }
  257. catch (ClassNotFoundException e) {
  258. is.getExceptionListener().exceptionThrown(e);
  259. }
  260. return null;
  261. }
  262. private HashMap getAttributes(AttributeList attrs) {
  263. HashMap attributes = new HashMap();
  264. if (attrs != null && attrs.getLength() > 0) {
  265. for(int i = 0; i < attrs.getLength(); i++) {
  266. attributes.put(attrs.getName(i), attrs.getValue(i));
  267. }
  268. }
  269. return attributes;
  270. }
  271. public void startElement(String name, AttributeList attrs) throws SAXException {
  272. // System.out.println("startElement" + name);
  273. name = name.intern(); // Xerces parser does not supply unique tag names.
  274. chars.setLength(0);
  275. if (name == "null" ||
  276. name == "string" ||
  277. name == "class" ||
  278. isPrimitive(name)) {
  279. return;
  280. }
  281. HashMap attributes = getAttributes(attrs);
  282. MutableExpression e = new MutableExpression();
  283. // Target
  284. String className = (String)attributes.get("class");
  285. if (className != null) {
  286. e.setTarget(classForName(className));
  287. }
  288. // Property
  289. Object property = attributes.get("property");
  290. String index = (String)attributes.get("index");
  291. if (index != null) {
  292. property = new Integer(index);
  293. e.addArg(property);
  294. }
  295. e.setProperty(property);
  296. // Method
  297. String methodName = (String)attributes.get("method");
  298. if (methodName == null && property == null) {
  299. methodName = "new";
  300. }
  301. e.setMethodName(methodName);
  302. // Tags
  303. if (name == "void") {
  304. if (e.getTarget() == null) { // this check is for "void class="foo" method= ..."
  305. e.setTarget(eval());
  306. }
  307. }
  308. else if (name == "array") {
  309. // The class attribute means sub-type for arrays.
  310. String subtypeName = (String)attributes.get("class");
  311. Class subtype = (subtypeName == null) ? Object.class : classForName(subtypeName);
  312. String length = (String)attributes.get("length");
  313. if (length != null) {
  314. e.setTarget(java.lang.reflect.Array.class);
  315. e.addArg(subtype);
  316. e.addArg(new Integer(length));
  317. }
  318. else {
  319. Class arrayClass = java.lang.reflect.Array.newInstance(subtype, 0).getClass();
  320. e.setTarget(arrayClass);
  321. }
  322. }
  323. else if (name == "java") {
  324. e.setValue(is); // The outermost scope is the stream itself.
  325. }
  326. else if (name == "object") {
  327. }
  328. else {
  329. simulateException("Unrecognized opening tag: " + name + " " + attrsToString(attrs));
  330. return;
  331. }
  332. // ids
  333. String idName = (String)attributes.get("id");
  334. if (idName != null) {
  335. environment.put(idName, e);
  336. }
  337. // idrefs
  338. String idrefName = (String)attributes.get("idref");
  339. if (idrefName != null) {
  340. e.setValue(lookup(idrefName));
  341. }
  342. // fields
  343. String fieldName = (String)attributes.get("field");
  344. if (fieldName != null) {
  345. e.setValue(getFieldValue(e.getTarget(), fieldName));
  346. }
  347. expStack.add(e);
  348. }
  349. private Object getFieldValue(Object target, String fieldName) {
  350. try {
  351. Class type = target.getClass();
  352. if (type == Class.class) {
  353. type = (Class)target;
  354. }
  355. java.lang.reflect.Field f = type.getField(fieldName);
  356. return f.get(target);
  357. }
  358. catch (Exception e) {
  359. is.getExceptionListener().exceptionThrown(e);
  360. return null;
  361. }
  362. }
  363. private String attrsToString(AttributeList attrs) {
  364. StringBuffer b = new StringBuffer();
  365. for (int i = 0; i < attrs.getLength (); i++) {
  366. b.append(attrs.getName(i)+"=\""+attrs.getValue(i)+"\" ");
  367. }
  368. return b.toString();
  369. }
  370. public void characters(char buf [], int offset, int len) throws SAXException {
  371. chars.append(new String(buf, offset, len));
  372. }
  373. private Object lookup(String s) {
  374. Expression e = (Expression)environment.get(s);
  375. if (e == null) {
  376. simulateException("Unbound variable: " + s);
  377. }
  378. return getValue(e);
  379. }
  380. public void endElement(String name) throws SAXException {
  381. // System.out.println("endElement: " + expStack);
  382. name = name.intern(); // Xerces parser does not supply unique tag names.
  383. if (name == "null") {
  384. addArg(null);
  385. return;
  386. }
  387. if (name == "java") {
  388. return;
  389. }
  390. if (name == "string") {
  391. addArg(chars.toString());
  392. return;
  393. }
  394. if (name == "class") {
  395. addArg(classForName(chars.toString()));
  396. return;
  397. }
  398. if (isPrimitive(name)) {
  399. Class wrapper = Expression.typeNameToClass(name);
  400. Expression e = new Expression(wrapper, "new", new Object[]{chars.toString()});
  401. addArg(getValue(e));
  402. return;
  403. }
  404. if (name == "object" || name == "array" || name == "void") {
  405. Expression e = (Expression)pop(expStack);
  406. Object value = getValue(e);
  407. if (name == "object" || name == "array") {
  408. addArg(value);
  409. }
  410. }
  411. else {
  412. simulateException("Unrecognized closing tag: " + name);
  413. }
  414. }
  415. }