001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.dbschema.migration.archiver.serializer;
043:
044: import java.lang.reflect.Array;
045: import java.lang.reflect.Field;
046: import java.lang.reflect.Modifier;
047:
048: import java.util.HashMap;
049: import java.util.Collection;
050: import java.util.Iterator;
051: import java.util.ArrayList;
052:
053: import java.io.IOException;
054: import java.io.BufferedWriter;
055: import java.io.File;
056: import java.io.FileWriter;
057: import java.io.OutputStreamWriter;
058: import java.io.OutputStream;
059:
060: /**
061: *
062: * @author Mark Munro
063: * @version %I%
064: */
065: public class XMLGraphSerializer extends Object {
066:
067: // Fields
068:
069: private BufferedWriter outStream;
070: private int indentLevel;
071: private boolean indent = true;
072: private HashMap ObjectMap;
073:
074: private static String indentChar = " ";
075: private static String startTag = "<";
076: private static String endTagNL = ">\n";
077: private static String endTag = ">";
078: private static String endEmptyTagNL = "/>\n";
079: private static String endEmptyTag = "/>";
080: private static String objectTag = "OBJECT";
081: private static String arrayTag = "ARRAY";
082: private static String rowTag = "ROW";
083: private static String classAttrib = "CLASS";
084: private static String IDAttrib = "ID";
085: private static String refAttrib = "REFERENCE";
086: private static String rowAttrib = "ROW";
087: private static String rowClassAttrib = "ROWCLASS";
088: private static String sizeAttrib = "SIZE";
089: private static String primitiveArray = "PRIMITIVE";
090: private static String startCDATA = "<![CDATA[";
091: private static String endCDATA = "]]>";
092:
093: //MBO added
094: private static final String encoding = "UTF-8";
095:
096: /** Creates new XMLGraphSerialzer */
097: private XMLGraphSerializer() {
098: this .ObjectMap = new HashMap();
099: }
100:
101: // MBO remove constructor
102: /*
103: public XMLGraphSerializer(File outputFile)
104: {
105: this();
106: try
107: {
108: this.outStream = new BufferedWriter( new FileWriter(outputFile));
109: }
110: catch (IOException lError)
111: {
112: lError.printStackTrace();
113: }
114: }
115: public XMLGraphSerializer(String outputFile)
116: {
117: this();
118: try
119: {
120: this.outStream = new BufferedWriter( new FileWriter(outputFile));
121: }
122: catch (IOException lError)
123: {
124: lError.printStackTrace();
125: }
126: }
127: */
128:
129: public XMLGraphSerializer(OutputStream outStream) {
130: this ();
131: //MBO
132: //this.outStream = new BufferedWriter( new OutputStreamWriter(outStream));
133: try {
134: this .outStream = new BufferedWriter(new OutputStreamWriter(
135: outStream, encoding));
136: } catch (java.io.UnsupportedEncodingException ex) {
137: throw new java.lang.RuntimeException(
138: "Problems creating OutputStreamWriter: " + ex);
139: }
140: }
141:
142: // Support / utility methods
143:
144: private String getObjectName(Object obj) {
145:
146: // The following methodf generates a uniqie name for the object.
147: // This name is used to reference the object within the XML document.
148: // Use the class name and hash code that way we produce a name that is
149: // at least partically readable.
150:
151: StringBuffer lReturn = new StringBuffer();
152: lReturn.append(obj.getClass().getName());
153: lReturn.append(obj.hashCode());
154:
155: return lReturn.toString();
156: }
157:
158: private void writeLevel(String value) throws IOException {
159: if (indent) {
160: for (int i = 0; i < this .indentLevel; i++) {
161: outStream.write(indentChar);
162: }
163: }
164:
165: outStream.write(value);
166: }
167:
168: private void writeLevel(char[] value) throws IOException {
169: if (indent) {
170: for (int i = 0; i < this .indentLevel; i++) {
171: outStream.write(indentChar);
172: }
173: }
174:
175: outStream.write(value);
176: }
177:
178: private void writeCDATA(String value) throws IOException {
179: // if ( indent )
180: // {
181: // for (int i = 0; i < this.indentLevel; i++)
182: // {
183: // outStream.write(indentChar);
184: // }
185: // }
186: outStream.write(startCDATA);
187: outStream.write(value);
188: outStream.write(endCDATA);
189: }
190:
191: private boolean recordObject(Object obj) {
192:
193: boolean lObjectRecordedAlready = false;
194:
195: String lObjectName = this .getObjectName(obj);
196:
197: if (!this .ObjectMap.containsKey(lObjectName)) {
198:
199: // This adds the object to the hash table for the first time
200: this .ObjectMap.put(lObjectName, obj);
201: } else
202: lObjectRecordedAlready = true;
203:
204: return lObjectRecordedAlready;
205: }
206:
207: private void addAttribute(String attributeName,
208: String attributeValue, StringBuffer tag) {
209: if (tag.length() > 0)
210: tag.append(' ');
211: tag.append(attributeName);
212: tag.append('=');
213: tag.append('\u0022');
214: tag.append(attributeValue);
215: tag.append('\u0022');
216:
217: }
218:
219: private boolean needsCDATA(String value) {
220:
221: boolean lNeedsCDATA = false;
222: char lChar;
223: int lStringLength = value.length();
224:
225: for (int i = 0; i < lStringLength; i++) {
226: lChar = value.charAt(i);
227: if (lChar == '<' || lChar == '>' || lChar == '&') {
228: lNeedsCDATA = true;
229: break;
230: }
231:
232: }
233: return lNeedsCDATA;
234: }
235:
236: public void DumpStatus() {
237: System.out
238: .println("Dumping state information for XMLGraphSerializer");
239: System.out.println("Object Map contains ");
240: Iterator lIterator = this .ObjectMap.values().iterator();
241: while (lIterator.hasNext()) {
242: Object lNext = lIterator.next();
243: System.out.println("Object Map contains object or class "
244: + lNext.getClass().getName());
245: System.out.println("Object state is " + lNext.toString());
246: }
247: System.out
248: .println("Dumping state information for XMLGraphSerializer - END");
249: }
250:
251: // main methods
252:
253: private void putStartTag(String tag, String elements,
254: boolean empty, boolean newLine) throws IOException {
255: this .writeLevel(startTag);
256: outStream.write(tag);
257: if (elements != null) {
258: outStream.write(' ');
259: outStream.write(elements);
260: }
261: if (empty) {
262: if (newLine)
263: outStream.write(endEmptyTagNL);
264: else
265: outStream.write(endEmptyTag);
266: } else {
267: if (newLine)
268: outStream.write(endTagNL);
269: else
270: outStream.write(endTag);
271:
272: if (this .indent)
273: this .indentLevel++;
274: }
275: }
276:
277: private void putEndTag(String tag, boolean doIndent)
278: throws IOException {
279: if (indent)
280: this .indentLevel--;
281:
282: if (indent && doIndent)
283: this .writeLevel("</");
284: else
285: outStream.write("</");
286: outStream.write(tag);
287: outStream.write(">\n");
288: }
289:
290: private void xlateObject(Object obj) throws IOException,
291: IllegalAccessException {
292:
293: try {
294:
295: if (obj == null) {
296: this .putStartTag(objectTag, null, true, true);
297:
298: } else if (obj instanceof String
299: || obj instanceof StringBuffer) {
300: this .xlateString(null, obj);
301: }
302:
303: else {
304: if (this .recordObject(obj)) {
305: // OK this object has already been recorded so process as a reference
306: this .xlateObjectReference(obj);
307: } else {
308: Class lClassType = obj.getClass();
309:
310: StringBuffer lClassAttributes = new StringBuffer();
311: this
312: .addAttribute(
313: classAttrib,
314: org.netbeans.modules.dbschema.migration.archiver.MapClassName
315: .getClassNameToken(lClassType
316: .getName()),
317: lClassAttributes);
318: this .addAttribute(IDAttrib,
319: this .getObjectName(obj), lClassAttributes);
320:
321: this .putStartTag(objectTag, lClassAttributes
322: .toString(), false, true);
323:
324: //
325:
326: ArrayList lFields;
327:
328: HashMap lFieldsMap = new HashMap();
329:
330: Class lClass = lClassType;
331: Field[] lTFields = null;
332:
333: while (lClass != null) {
334: lTFields = lClass.getDeclaredFields();
335: // lFields.ensureCapacity(lTFields.length);
336:
337: for (int i = 0; i < lTFields.length; i++) {
338: // lFields.add(lTFields[i]);
339: if (!lFieldsMap.containsKey(lTFields[i]
340: .getName()))
341: lFieldsMap.put(lTFields[i].getName(),
342: lTFields[i]);
343: }
344:
345: lClass = lClass.getSuperclass();
346: }
347:
348: lFields = new ArrayList(lFieldsMap.values());
349:
350: for (int i = 0; i < lFields.size(); i++) {
351: Field lCurrentField = (Field) (lFields.get(i));
352:
353: if (!Modifier.isTransient(lCurrentField
354: .getModifiers())
355: && !Modifier.isStatic(lCurrentField
356: .getModifiers())) {
357:
358: Class lCurrentFieldType = lCurrentField
359: .getType();
360: lCurrentField.setAccessible(true);
361: Object lRealValue = lCurrentField.get(obj);
362: String lFieldName = lCurrentField.getName();
363: if (lRealValue != null) {
364: if (lCurrentFieldType.isPrimitive()) {
365: this .xlatePrimitive(lFieldName,
366: lRealValue);
367: } else if (lRealValue instanceof java.lang.String
368: || lRealValue instanceof java.lang.StringBuffer) {
369: this .xlateString(lFieldName,
370: lRealValue);
371: } else if (lCurrentFieldType.isArray()) {
372: this .xlateArray(lFieldName,
373: lRealValue);
374: } else if (lRealValue instanceof Collection) {
375: this .xlateCollection(lFieldName,
376: lRealValue);
377: } else {
378: this .putStartTag(lFieldName, null,
379: false, true);
380: this .xlateObject(lRealValue);
381: this .putEndTag(lFieldName, true);
382: }
383: } else {
384: this .putStartTag(lFieldName, null,
385: false, true);
386: this .putStartTag(objectTag, null, true,
387: true);
388: this .putEndTag(lFieldName, true);
389:
390: }
391:
392: }
393: }
394: this .putEndTag(objectTag, true);
395: }
396: }
397:
398: } catch (IOException e1) {
399: e1.printStackTrace();
400: this .DumpStatus();
401: System.out
402: .println("IO Exception in XLateObject current object class "
403: + obj.getClass().getName());
404: System.out
405: .println("IO Exception in XLateObject current object is "
406: + obj);
407: this .outStream.close();
408: throw e1;
409:
410: } catch (IllegalAccessException e2) {
411: e2.printStackTrace();
412: this .DumpStatus();
413: System.out
414: .println("IO Exception in XLateObject current object class "
415: + obj.getClass().getName());
416: System.out
417: .println("IO Exception in XLateObject current object is "
418: + obj);
419: this .outStream.close();
420: throw e2;
421:
422: } catch (RuntimeException e3) {
423: e3.printStackTrace();
424: this .DumpStatus();
425: System.out
426: .println("IO Exception in XLateObject current object class "
427: + obj.getClass().getName());
428: System.out
429: .println("IO Exception in XLateObject current object is "
430: + obj);
431: this .outStream.close();
432: throw e3;
433: }
434: }
435:
436: private void xlateObjectReference(Object obj) throws IOException {
437: StringBuffer lReferenceAttributes = new StringBuffer();
438: this .addAttribute(refAttrib, this .getObjectName(obj),
439: lReferenceAttributes);
440:
441: this .putStartTag(objectTag, lReferenceAttributes.toString(),
442: true, true);
443:
444: }
445:
446: private void xlatePrimitive(String name, Object obj)
447: throws IOException {
448: Class lType = obj.getClass();
449: String lValue = obj.toString();
450:
451: this .putStartTag(name, null, false, false);
452:
453: if (lType == java.lang.Character.TYPE) {
454: // This is a character so check for CDATA requirements
455: if (this .needsCDATA(lValue)) {
456: this .writeCDATA(lValue);
457: } else
458: outStream.write(lValue);
459: } else
460: outStream.write(lValue);
461:
462: this .putEndTag(name, false);
463: }
464:
465: private void xlateString(String name, Object obj)
466: throws IOException {
467:
468: if (name != null)
469: this .putStartTag(name, null, false, false);
470:
471: String lValue = obj.toString();
472:
473: if (this .needsCDATA(lValue)) {
474: this .writeCDATA(lValue);
475: } else
476: outStream.write(lValue);
477:
478: if (name != null)
479: this .putEndTag(name, false);
480: }
481:
482: private void xlateArray(String name, Object obj)
483: throws IOException, IllegalAccessException {
484:
485: StringBuffer lArrayAttributes = new StringBuffer();
486:
487: int lArraySize = Array.getLength(obj);
488:
489: this .addAttribute(sizeAttrib, Integer.toString(lArraySize),
490: lArrayAttributes);
491: this
492: .addAttribute(classAttrib, primitiveArray,
493: lArrayAttributes);
494: this
495: .addAttribute(
496: rowClassAttrib,
497: org.netbeans.modules.dbschema.migration.archiver.MapClassName
498: .getClassNameToken(obj.getClass()
499: .getComponentType().getName()),
500: lArrayAttributes);
501:
502: // System.out.println( "Component Type is " + obj.getClass().getComponentType().getName() );
503: // System.out.println( "Class Type is " + obj.getClass().getName() );
504:
505: this .putStartTag(name, null, false, true);
506: this .putStartTag(arrayTag, lArrayAttributes.toString(), false,
507: true);
508:
509: for (int i = 0; i < lArraySize; i++) {
510:
511: Object lRow = Array.get(obj, i);
512:
513: if (lRow instanceof java.lang.String
514: || lRow instanceof java.lang.Number
515: || lRow instanceof java.lang.Character
516: || lRow instanceof java.lang.Boolean) {
517: this .xlateSimpleRow(i, lRow);
518: } else {
519: StringBuffer lRowAttributes = new StringBuffer();
520: this .addAttribute(rowAttrib, Integer.toString(i),
521: lRowAttributes);
522: this .putStartTag(rowTag, lRowAttributes.toString(),
523: false, true);
524: this .xlateObject(lRow);
525: this .putEndTag(rowTag, true);
526: }
527: }
528:
529: this .putEndTag(arrayTag, true);
530: this .putEndTag(name, true);
531:
532: }
533:
534: private void xlateCollection(String name, Object obj)
535: throws IOException, IllegalAccessException {
536:
537: StringBuffer lArrayAttributes = new StringBuffer();
538:
539: Collection lArray = (Collection) (obj);
540:
541: int lArraySize = lArray.size();
542:
543: this .addAttribute(sizeAttrib, Integer.toString(lArraySize),
544: lArrayAttributes);
545: this .addAttribute(classAttrib, lArray.getClass().getName(),
546: lArrayAttributes);
547: this .addAttribute(rowClassAttrib, "", lArrayAttributes);
548: this .putStartTag(name, null, false, true);
549: this .putStartTag(arrayTag, lArrayAttributes.toString(), false,
550: true);
551:
552: Iterator lIterator = lArray.iterator();
553: int lRowCount = 0;
554:
555: while (lIterator.hasNext()) {
556: Object lRow = lIterator.next();
557:
558: if (lRow instanceof java.lang.String
559: || lRow instanceof java.lang.Number
560: || lRow instanceof java.lang.Character
561: || lRow instanceof java.lang.Boolean) {
562: this .xlateSimpleRow(lRowCount, lRow);
563: } else {
564: StringBuffer lRowAttributes = new StringBuffer();
565: this .addAttribute(rowAttrib, Integer
566: .toString(lRowCount), lRowAttributes);
567: this .putStartTag(rowTag, lRowAttributes.toString(),
568: false, true);
569: this .xlateObject(lRow);
570: this .putEndTag(rowTag, true);
571: }
572: lRowCount++;
573: }
574:
575: this .putEndTag(arrayTag, true);
576: this .putEndTag(name, true);
577:
578: }
579:
580: private void xlateSimpleRow(int rowNumber, Object obj)
581: throws IOException {
582:
583: StringBuffer lRowAttributes = new StringBuffer();
584: this .addAttribute(rowAttrib, Integer.toString(rowNumber),
585: lRowAttributes);
586:
587: Class lObjectClass = obj.getClass();
588:
589: if (lObjectClass.isPrimitive()) {
590: if (lObjectClass == java.lang.Integer.TYPE)
591: this .addAttribute("ROWCLASS", "int", lRowAttributes);
592: else if (lObjectClass == java.lang.Short.TYPE)
593: this .addAttribute("ROWCLASS", "short", lRowAttributes);
594: else if (lObjectClass == java.lang.Long.TYPE)
595: this .addAttribute("ROWCLASS", "long", lRowAttributes);
596: else if (lObjectClass == java.lang.Float.TYPE)
597: this .addAttribute("ROWCLASS", "float", lRowAttributes);
598: else if (lObjectClass == java.lang.Double.TYPE)
599: this .addAttribute("ROWCLASS", "double", lRowAttributes);
600: else if (lObjectClass == java.lang.Boolean.TYPE)
601: this
602: .addAttribute("ROWCLASS", "boolean",
603: lRowAttributes);
604: else if (lObjectClass == java.lang.Character.TYPE)
605: this .addAttribute("ROWCLASS", "char", lRowAttributes);
606: else
607: this
608: .addAttribute("ROWCLASS", "unknown",
609: lRowAttributes);
610:
611: } else
612: this
613: .addAttribute(
614: "ROWCLASS",
615: org.netbeans.modules.dbschema.migration.archiver.MapClassName
616: .getClassNameToken(lObjectClass
617: .getName()), lRowAttributes);
618:
619: this .addAttribute("VALUE", obj.toString(), lRowAttributes);
620:
621: this .putStartTag(rowTag, lRowAttributes.toString(), true, true);
622:
623: }
624:
625: public void writeObject(Object obj) throws IOException {
626: try {
627: outStream.write("<?xml version=\"1.0\" encoding=\""
628: + encoding + "\" ?>\n\n");
629:
630: this .xlateObject(obj);
631: this .outStream.close();
632: } catch (IOException e1) {
633: e1.printStackTrace();
634: this .DumpStatus();
635: try {
636: this .outStream.close();
637: } catch (IOException lNotClosed) {
638: // Do nothing
639: }
640: throw e1;
641: } catch (IllegalAccessException e2) {
642: e2.printStackTrace();
643: this .DumpStatus();
644: try {
645: this .outStream.close();
646: } catch (IOException lNotClosed) {
647: // Do nothing
648: }
649: }
650:
651: }
652:
653: }
|