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.insane.model;
043:
044: import java.io.*;
045: import java.io.DataOutputStream;
046: import java.io.FileInputStream;
047: import java.nio.ByteBuffer;
048: import java.nio.MappedByteBuffer;
049: import java.nio.channels.FileChannel;
050: import java.util.*;
051: import javax.imageio.stream.FileImageInputStream;
052: import javax.xml.parsers.SAXParser;
053: import javax.xml.parsers.SAXParserFactory;
054: import org.xml.sax.*;
055: import org.xml.sax.SAXParseException;
056: import org.xml.sax.helpers.DefaultHandler;
057:
058: /**
059: * Parses a SimpleXMLVisitor XML output and converts it into simple binary
060: * format suitable for mmap usage.
061: * This version does a two-pass over the XML dump to spare memory during
062: * conversion. Estimated memory consumption during the scan is about objects*30B
063: *
064: * The format (version 'SBIH'): <pre>
065: INT version
066: INT refsOffset
067: INT objsOffset
068: CLASS_TYPEs
069: REF_TYPEs (static refs come first)
070: OBJECTs
071:
072:
073:
074: OBJECT:
075: INT classTypeOffset
076: INT heapSize
077: INT numOutgoing
078: INT numIncomming
079: REFs outgoing
080: REFs incomming
081:
082: REF:
083: INT refTypeOffset (or negative iff array index)
084: INT objectOffset
085:
086: REF_TYPE:
087: STR referenceName
088: INT staticOffset (0 for null static ref, -1 for nonstatic ref)
089:
090: STR:
091: INT len
092: UTF8 text
093:
094: CLASS_TYPE:
095: STR className
096: INT numInstances
097: INTs objectOffsets
098: </pre>
099:
100: *
101: * @author Nenik
102: */
103: // XXX use UTF8
104: final class InsaneConverter {
105:
106: private File from;
107: private File to;
108:
109: // parsing intermediate data
110: private Map<String, ClassInfo> classInfo = new LinkedHashMap<String, ClassInfo>();
111: private Map<String, RefInfo> refInfo = new LinkedHashMap<String, RefInfo>();
112: // private Map instanceInfo = new HashMap(); //new LinkedHashMap(); // Map<String id, InstanceInfo>
113:
114: private ObjectSet instanceInfo = new ObjectSet();
115:
116: private int refsOffset;
117: private int objsOffset;
118: private int totalOffset;
119:
120: boolean prescan = true;
121:
122: MappedByteBuffer store;
123:
124: private InsaneConverter(File from, File to) {
125: this .from = from;
126: this .to = to;
127: }
128:
129: ByteBuffer getByteBuffer(int offset) {
130: if (offset < 12)
131: throw new IllegalArgumentException("bad offset " + offset);
132: return ((ByteBuffer) store.duplicate().position(offset))
133: .slice();
134: }
135:
136: // Holder for info about each object or array type
137: private class ClassInfo {
138: String className;
139: int offset;
140: int countOrOffset;
141:
142: ClassInfo(String className) {
143: this .className = className;
144: }
145:
146: void register(InstanceInfo inst) { // {throws IOException {
147: if (prescan) {
148: countOrOffset++;
149: } else {
150: ByteBuffer buff = getByteBuffer(countOrOffset);
151: inst.storeOffset(buff);
152: countOrOffset += 4;
153: }
154: }
155:
156: int computeNextOffset(int currentOffset) {
157: offset = currentOffset;
158: return currentOffset + computeStringSize(className) + 4 + 4
159: * countOrOffset;
160: }
161:
162: void storeHeader() {
163: ByteBuffer buff = getByteBuffer(offset);
164: storeString(buff, className);
165: buff.putInt(countOrOffset);
166: countOrOffset = buff.position() + offset;
167: }
168:
169: void storeOffset(ByteBuffer buff) { // throws IOException {
170: buff.putInt(offset);
171: }
172: }
173:
174: private ClassInfo getClassInfo(String className) {
175: ClassInfo ret = classInfo.get(className);
176: if (ret == null) {
177: ret = new ClassInfo(className);
178: classInfo.put(className, ret);
179: }
180: return ret;
181: }
182:
183: private static Object[] EMPTY = new Object[0];
184:
185: private static class InstanceInfo {
186: private int id1;
187: private int offset;
188: private int incommingCountOrPtr;
189: private int outgoingCountOrPtr;
190:
191: InstanceInfo(String str) {
192: id1 = Integer.parseInt(str, 16);
193: }
194:
195: void process(InsaneConverter converter, ClassInfo type, int size) { // store the header
196: if (converter.prescan) {
197: // do nothing here
198: } else {
199: // store header
200: ByteBuffer buff = converter.getByteBuffer(offset);
201: type.storeOffset(buff);
202: buff.putInt(size);
203: buff.putInt(outgoingCountOrPtr);
204: buff.putInt(incommingCountOrPtr);
205:
206: // recompute offsets
207: int outCount = outgoingCountOrPtr;
208: outgoingCountOrPtr = offset + buff.position();
209: incommingCountOrPtr = outgoingCountOrPtr + 8 * outCount;
210: }
211: }
212:
213: void registerIncommingReference(InsaneConverter converter,
214: RefInfo ref, InstanceInfo inst) {
215: incommingCountOrPtr = registerReference(converter,
216: incommingCountOrPtr, ref, inst);
217: }
218:
219: void registerOutgoingReference(InsaneConverter converter,
220: RefInfo ref, InstanceInfo inst) {
221: outgoingCountOrPtr = registerReference(converter,
222: outgoingCountOrPtr, ref, inst);
223: }
224:
225: private int registerReference(InsaneConverter converter,
226: int ptr, RefInfo ref, InstanceInfo inst) {
227: if (converter.prescan) {
228: return ptr + 1;
229: } else {
230: // XXX
231: ByteBuffer buff = converter.getByteBuffer(ptr);
232: ref.storeOffset(buff);
233: if (inst == null) {
234: buff.putInt(0);
235: } else {
236: inst.storeOffset(buff);
237: }
238: return ptr + 8;
239: }
240: }
241:
242: int computeNextOffset(int currentOffset) {
243: offset = currentOffset;
244: return currentOffset + 4 + 4 + 4 + 4 + 8
245: * outgoingCountOrPtr + 8 * incommingCountOrPtr;
246: }
247:
248: void storeOffset(ByteBuffer buff) {// throws IOException {
249: buff.putInt(offset);
250: }
251:
252: public boolean equals(Object o) {
253: if (o instanceof InstanceInfo) {
254: return id1 == ((InstanceInfo) o).id1;
255: }
256: return false;
257: }
258:
259: public int hashCode() {
260: return 61315 * id1;
261: }
262:
263: }
264:
265: void createInstanceInfo(String strId, String type, int size,
266: String val) {
267: InstanceInfo template = new InstanceInfo(strId);
268:
269: InstanceInfo ii = (InstanceInfo) instanceInfo.get(template);
270: if (ii == null) {
271: if (!prescan)
272: throw new IllegalArgumentException(
273: "Unknown element during second pass:" + strId);
274: ii = template;
275: instanceInfo.put(ii);
276: }
277: ClassInfo cls = getClassInfo(type);
278: ii.process(this , cls, size);
279: cls.register(ii);
280: }
281:
282: InstanceInfo getInstance(String strId) {
283: InstanceInfo template = new InstanceInfo(strId);
284: return (InstanceInfo) instanceInfo.get(template);
285: }
286:
287: private class RefInfo {
288: int offset;
289: String refName;
290: InstanceInfo instance;
291:
292: RefInfo(String name, InstanceInfo inst) {
293: refName = name;
294: instance = inst;
295: }
296:
297: // REF_TYPE:
298: // STR referenceName
299: // INT staticOffset (0 for null static ref, -1 for nonstatic ref)
300:
301: int computeNextOffset(int currentOffset) {
302: offset = currentOffset;
303: return currentOffset + computeStringSize(refName) + 4;
304: }
305:
306: void storeHeader() throws IOException {
307: ByteBuffer buff = getByteBuffer(offset);
308: storeString(buff, refName);
309: if (instance == null) {
310: buff.putInt(-1);
311: } else {
312: instance.storeOffset(buff);
313: }
314: }
315:
316: void storeOffset(ByteBuffer buff) { // throws IOException {
317: buff.putInt(offset);
318: }
319: }
320:
321: void registerReference(String type, String fromId, String toId) {
322: InstanceInfo from = fromId == null ? null : getInstance(fromId);
323: InstanceInfo to = getInstance(toId);
324:
325: RefInfo ref = refInfo.get(type);
326: if (ref == null) {
327: if (from == null) { // static
328: ref = new RefInfo(type, to);
329: } else {
330: ref = new RefInfo(type, null);
331: }
332: refInfo.put(type, ref);
333: }
334:
335: if (from != null)
336: from.registerOutgoingReference(this , ref, to);
337: to.registerIncommingReference(this , ref, from);
338: }
339:
340: private void process() throws Exception {
341: // parse
342: parse(new FileInputStream(from));
343: // compute offsets
344: compute();
345: // store headers
346:
347: RandomAccessFile raf = new RandomAccessFile(to, "rw");
348: store = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0,
349: totalOffset);
350: raf.close();
351: storeHeaders();
352:
353: // second pass
354: prescan = false;
355: parse(new FileInputStream(from));
356: store.force();
357: }
358:
359: private void compute() {
360: int currentOffset = 4 + 4 + 4;
361:
362: // compute offset of classes
363: for (Iterator it = classInfo.values().iterator(); it.hasNext();) {
364: ClassInfo info = (ClassInfo) it.next();
365: currentOffset = info.computeNextOffset(currentOffset);
366: }
367:
368: refsOffset = currentOffset;
369:
370: // compute offsets of refs
371: for (Iterator it = refInfo.values().iterator(); it.hasNext();) {
372: RefInfo info = (RefInfo) it.next();
373: currentOffset = info.computeNextOffset(currentOffset);
374: }
375:
376: objsOffset = currentOffset;
377:
378: // compute offsets of instances
379: for (Iterator it = instanceInfo.iterator(); it.hasNext();) {
380: InstanceInfo info = (InstanceInfo) it.next();
381: currentOffset = info.computeNextOffset(currentOffset);
382: }
383: totalOffset = currentOffset;
384: }
385:
386: private void storeHeaders() throws IOException {
387: // store header
388: store.put("SBIH".getBytes());
389: store.putInt(refsOffset);
390: store.putInt(objsOffset);
391:
392: // store classes
393: for (Iterator it = classInfo.values().iterator(); it.hasNext();) {
394: ClassInfo info = (ClassInfo) it.next();
395: info.storeHeader();
396: }
397:
398: // store refs
399: for (Iterator it = refInfo.values().iterator(); it.hasNext();) {
400: RefInfo info = (RefInfo) it.next();
401: info.storeHeader();
402: }
403:
404: /* // store instances
405: for (Iterator it = instanceInfo.values().iterator(); it.hasNext(); ) {
406: InstanceInfo info = (InstanceInfo)it.next();
407: info.store(out);
408: }
409: */
410: }
411:
412: private static void storeString(ByteBuffer buff, String str) { //throws IOException {
413: byte[] data = str.getBytes();
414: buff.putInt(data.length);
415: buff.put(data);
416: }
417:
418: private static int computeStringSize(String str) {
419: return 4 + str.getBytes().length;
420: }
421:
422: private void parse(InputStream source) throws Exception {
423: class Handler extends DefaultHandler {
424: private int depth = 0;
425:
426: public void startElement(String namespaceURI,
427: String localName, String qName, Attributes atts)
428: throws SAXException {
429: if (depth == 0) {
430: if (!"insane".equals(qName))
431: throw new SAXException("format");
432: } else if (depth != 1) {
433: throw new SAXException("format");
434: } else {
435: if ("object".equals(qName)) {
436: String id = atts.getValue("id");
437: String type = atts.getValue("type");
438: String size = atts.getValue("size");
439: String val = atts.getValue("value");
440: createInstanceInfo(id, type, Integer
441: .parseInt(size), val);
442: } else if ("ref".equals(qName)) {
443: String from = atts.getValue("from");
444: String name = atts.getValue("name");
445: String to = atts.getValue("to");
446: registerReference(name, from, to);
447: } else {
448: throw new SAXException("format");
449: }
450: }
451: depth++;
452: }
453:
454: public void endElement(String namespaceURI,
455: String localName, String qName) throws SAXException {
456: depth--;
457: }
458:
459: }
460:
461: Handler h = new Handler();
462: SAXParserFactory fact = SAXParserFactory.newInstance();
463: SAXParser parser = fact.newSAXParser();
464: parser.getXMLReader().setContentHandler(h);
465: parser.getXMLReader().parse(new InputSource(source));
466: }
467:
468: public static void convert(File from, File to) throws Exception {
469: InsaneConverter conv = new InsaneConverter(from, to);
470: conv.process();
471: }
472:
473: }
|