001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.harmony.pack200;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.io.StringReader;
022: import java.util.ArrayList;
023: import java.util.Iterator;
024: import java.util.List;
025:
026: import org.apache.harmony.pack200.bytecode.Attribute;
027: import org.apache.harmony.pack200.bytecode.CPClass;
028: import org.apache.harmony.pack200.bytecode.CPDouble;
029: import org.apache.harmony.pack200.bytecode.CPFieldRef;
030: import org.apache.harmony.pack200.bytecode.CPFloat;
031: import org.apache.harmony.pack200.bytecode.CPInteger;
032: import org.apache.harmony.pack200.bytecode.CPInterfaceMethodRef;
033: import org.apache.harmony.pack200.bytecode.CPLong;
034: import org.apache.harmony.pack200.bytecode.CPMethodRef;
035: import org.apache.harmony.pack200.bytecode.CPNameAndType;
036: import org.apache.harmony.pack200.bytecode.CPString;
037: import org.apache.harmony.pack200.bytecode.CPUTF8;
038: import org.apache.harmony.pack200.bytecode.NewAttribute;
039:
040: /**
041: * Set of bands relating to a non-predefined attribute
042: */
043: public class NewAttributeBands extends BandSet {
044:
045: private AttributeLayout attributeLayout;
046:
047: private List attributes;
048:
049: private int backwardsCallCount;
050:
051: private List attributeLayoutElements;
052:
053: public NewAttributeBands(Segment segment,
054: AttributeLayout attributeLayout) throws IOException {
055: super (segment);
056: this .attributeLayout = attributeLayout;
057: parseLayout();
058: attributeLayout.setBackwardsCallCount(backwardsCallCount);
059: }
060:
061: /*
062: * (non-Javadoc)
063: *
064: * @see org.apache.harmony.pack200.BandSet#unpack(java.io.InputStream)
065: */
066: public void unpack(InputStream in) throws IOException,
067: Pack200Exception {
068: // does nothing - use parseAttributes instead
069: }
070:
071: /**
072: * Returns the list of attributes read in by this band set. This method
073: * should only be called after unpack() or it will return null.
074: * @return List of Attributes
075: */
076: public List getAttributes() {
077: return attributes;
078: }
079:
080: /**
081: * Parse the bands relating to this AttributeLayout and return the correct
082: * class file attributes as a List of {@link Attribute}
083: * @throws Pack200Exception
084: */
085: public List parseAttributes(InputStream in, int occurrenceCount)
086: throws IOException, Pack200Exception {
087: for (Iterator iter = attributeLayoutElements.iterator(); iter
088: .hasNext();) {
089: AttributeLayoutElement element = (AttributeLayoutElement) iter
090: .next();
091: element.readBands(in, occurrenceCount);
092: }
093:
094: List attributes = new ArrayList();
095: for (int i = 0; i < occurrenceCount; i++) {
096: attributes.add(getOneAttribute(i, attributeLayoutElements));
097: }
098: return attributes;
099: }
100:
101: /**
102: * Get one attribute at the given index from the various bands.
103: * The correct bands must have already been read in.
104: * @param index
105: * @param elements
106: * @return
107: */
108: private Attribute getOneAttribute(int index, List elements) {
109: NewAttribute attribute = new NewAttribute(attributeLayout
110: .getName());
111: for (Iterator iter = elements.iterator(); iter.hasNext();) {
112: AttributeLayoutElement element = (AttributeLayoutElement) iter
113: .next();
114: element.addToAttribute(index, attribute);
115: }
116: return attribute;
117: }
118:
119: /**
120: * Tokenise the layout into AttributeElements
121: * @return a List of AttributeElements
122: * @throws IOException
123: */
124: private void parseLayout() throws IOException {
125: if (attributeLayoutElements == null) {
126: attributeLayoutElements = new ArrayList();
127: StringReader stream = new StringReader(attributeLayout
128: .getLayout());
129: AttributeLayoutElement e;
130: while ((e = readNextAttributeElement(stream)) != null) {
131: attributeLayoutElements.add(e);
132: }
133: resolveCalls();
134: }
135: }
136:
137: /**
138: * Resolve calls in the attribute layout and returns the number of backwards calls
139: *
140: * @param tokens -
141: * the attribute layout as a List of AttributeElements
142: */
143: private void resolveCalls() {
144: int backwardsCalls = 0;
145: for (int i = 0; i < attributeLayoutElements.size(); i++) {
146: AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements
147: .get(i);
148: if (element instanceof Callable) {
149: Callable callable = (Callable) element;
150: List body = callable.body; // Look for calls in the body
151: for (Iterator iter = body.iterator(); iter.hasNext();) {
152: LayoutElement layoutElement = (LayoutElement) iter
153: .next();
154: if (layoutElement instanceof Call) {
155: // Set the callable for each call
156: Call call = (Call) layoutElement;
157: int index = call.callableIndex;
158: if (index == 0) { // Calls the parent callable
159: backwardsCalls++;
160: call.setCallable(callable);
161: } else if (index > 0) { // Forwards call
162: for (int k = i; k < attributeLayoutElements
163: .size(); k++) {
164: AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements
165: .get(k);
166: if (el instanceof Callable) {
167: index--;
168: if (index == 0) {
169: call.setCallable((Callable) el);
170: break;
171: }
172: }
173: }
174: } else { // Backwards call
175: backwardsCalls++;
176: for (int k = i; k >= 0; k--) {
177: AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements
178: .get(k);
179: if (el instanceof Callable) {
180: index++;
181: if (index == 0) {
182: call.setCallable((Callable) el);
183: break;
184: }
185: }
186: }
187: }
188: }
189: }
190: }
191: }
192: backwardsCallCount = backwardsCalls;
193: }
194:
195: private AttributeLayoutElement readNextAttributeElement(
196: StringReader stream) throws IOException {
197: int nextChar = stream.read();
198: if (nextChar == -1) {
199: return null;
200: }
201: if (nextChar == '[') {
202: List body = readBody(getStreamUpToMatchingBracket(stream));
203: return new Callable(body);
204: } else {
205: return readNextLayoutElement(stream);
206: }
207: }
208:
209: private LayoutElement readNextLayoutElement(StringReader stream)
210: throws IOException {
211: int nextChar = stream.read();
212: if (nextChar == -1) {
213: return null;
214: }
215: switch (nextChar) {
216: // Integrals
217: case 'B':
218: case 'H':
219: case 'I':
220: case 'V':
221: return new Integral(new String(
222: new char[] { (char) nextChar }));
223: case 'S':
224: case 'F':
225: return new Integral(new String(new char[] {
226: (char) nextChar, (char) stream.read() }));
227: case 'P':
228: stream.mark(1);
229: if (stream.read() != 'O') {
230: stream.reset();
231: return new Integral("P" + (char) stream.read());
232: } else {
233: return new Integral("PO" + (char) stream.read());
234: }
235: case 'O':
236: stream.mark(1);
237: if (stream.read() != 'S') {
238: stream.reset();
239: return new Integral("O" + (char) stream.read());
240: } else {
241: return new Integral("OS" + (char) stream.read());
242: }
243:
244: // Replication
245: case 'N':
246: char uint_type = (char) stream.read();
247: stream.read(); // '['
248: String str = readUpToMatchingBracket(stream);
249: return new Replication("" + uint_type, str);
250:
251: // Union
252: case 'T':
253: String int_type = "" + (char) stream.read();
254: if (int_type.equals("S")) {
255: int_type += (char) stream.read();
256: }
257: List unionCases = new ArrayList();
258: UnionCase c;
259: while ((c = readNextUnionCase(stream)) != null) {
260: unionCases.add(c);
261: }
262: stream.read(); // '('
263: stream.read(); // '('
264: stream.read(); // '['
265: List body = null;
266: stream.mark(1);
267: char next = (char) stream.read();
268: if (next != ']') {
269: stream.reset();
270: body = readBody(getStreamUpToMatchingBracket(stream));
271: }
272: return new Union(int_type, unionCases, body);
273:
274: // Call
275: case '(':
276: int number = readNumber(stream);
277: stream.read(); // ')'
278: return new Call(number);
279: // Reference
280: case 'K':
281: case 'R':
282: String string = "" + nextChar + (char) stream.read();
283: char nxt = (char) stream.read();
284: string += nxt;
285: if (nxt == 'N') {
286: string += (char) stream.read();
287: }
288: return new Reference(string);
289: }
290: return null;
291: }
292:
293: /**
294: * Read a UnionCase from the stream
295: * @param stream
296: * @return
297: * @throws IOException
298: */
299: private UnionCase readNextUnionCase(StringReader stream)
300: throws IOException {
301: stream.mark(2);
302: stream.read(); // '('
303: char next = (char) stream.read();
304: if (next == ')') {
305: stream.reset();
306: return null;
307: }
308: List tags = new ArrayList();
309: while (next != ')') {
310: tags.add(new Integer(readNumber(stream)));
311: next = (char) stream.read();
312: }
313: stream.read(); // '['
314: stream.mark(1);
315: next = (char) stream.read();
316: if (next == ']') {
317: return new UnionCase(tags);
318: } else {
319: stream.reset();
320: return new UnionCase(tags,
321: readBody(getStreamUpToMatchingBracket(stream)));
322: }
323: }
324:
325: /**
326: * An AttributeLayoutElement is a part of an attribute layout and has one or more
327: * bands associated with it, which transmit the AttributeElement data for
328: * successive Attributes of this type.
329: */
330: private interface AttributeLayoutElement {
331:
332: /**
333: * Read the bands associated with this part of the layout
334: *
335: * @param in
336: * @param count
337: * @throws Pack200Exception
338: * @throws IOException
339: */
340: public void readBands(InputStream in, int count)
341: throws IOException, Pack200Exception;
342:
343: /**
344: * Add the band data for this element at the given index to the attribute
345: *
346: * @param index
347: * @param attribute
348: */
349: public void addToAttribute(int index, NewAttribute attribute);
350:
351: }
352:
353: private abstract class LayoutElement implements
354: AttributeLayoutElement {
355:
356: protected int getLength(char uint_type) {
357: int length = 0;
358: ;
359: switch (uint_type) {
360: case 'B':
361: length = 1;
362: break;
363: case 'H':
364: length = 2;
365: break;
366: case 'I':
367: length = 4;
368: break;
369: case 'V':
370: length = 0;
371: break;
372: }
373: return length;
374: }
375: }
376:
377: private class Integral extends LayoutElement {
378:
379: private String tag;
380: private long[] band;
381:
382: public Integral(String tag) {
383: this .tag = tag;
384: }
385:
386: public void readBands(InputStream in, int count)
387: throws IOException, Pack200Exception {
388: band = decodeBandLong(
389: attributeLayout.getName() + "_" + tag, in,
390: getCodec(tag), count);
391: }
392:
393: public void addToAttribute(int n, NewAttribute attribute) {
394: long value = band[n];
395: if (tag.equals("B") || tag.equals("FB")) {
396: attribute.addInteger(1, value);
397: } else if (tag.equals("SB")) {
398: attribute.addInteger(1, (byte) value);
399: } else if (tag.equals("H") || tag.equals("FH")) {
400: attribute.addInteger(2, value);
401: } else if (tag.equals("SH")) {
402: attribute.addInteger(2, (short) value);
403: } else if (tag.equals("I") || tag.equals("FI")) {
404: attribute.addInteger(4, value);
405: } else if (tag.equals("SI")) {
406: attribute.addInteger(4, (int) value);
407: } else if (tag.equals("V") || tag.equals("FV")
408: || tag.equals("SV")) {
409: // Don't add V's - they shouldn't be written out to the class file
410: } else if (tag.startsWith("PO")) {
411: char uint_type = tag.substring(2).toCharArray()[0];
412: int length = getLength(uint_type);
413: attribute.addBCOffset(length, (int) value);
414: } else if (tag.startsWith("P")) {
415: char uint_type = tag.substring(1).toCharArray()[0];
416: int length = getLength(uint_type);
417: attribute.addBCIndex(length, (int) value);
418: } else if (tag.startsWith("OS")) {
419: char uint_type = tag.substring(1).toCharArray()[0];
420: int length = getLength(uint_type);
421: if (length == 1) {
422: value = (byte) value;
423: } else if (length == 2) {
424: value = (short) value;
425: } else if (length == 4) {
426: value = (int) value;
427: }
428: attribute.addBCLength(length, (int) value);
429: } else if (tag.startsWith("O")) {
430: char uint_type = tag.substring(1).toCharArray()[0];
431: int length = getLength(uint_type);
432: attribute.addBCLength(length, (int) value);
433: }
434: }
435:
436: long getValue(int index) {
437: return band[index];
438: }
439:
440: }
441:
442: /**
443: * A replication is an array of layout elements, with an associated count
444: */
445: private class Replication extends LayoutElement {
446:
447: private Integral countElement;
448:
449: private List layoutElements = new ArrayList();
450:
451: public Replication(String tag, String contents)
452: throws IOException {
453: this .countElement = new Integral(tag);
454: StringReader stream = new StringReader(contents);
455: LayoutElement e;
456: while ((e = readNextLayoutElement(stream)) != null) {
457: layoutElements.add(e);
458: }
459: }
460:
461: public void readBands(InputStream in, int count)
462: throws IOException, Pack200Exception {
463: countElement.readBands(in, count);
464: int arrayCount = 0;
465: for (int i = 0; i < count; i++) {
466: arrayCount += countElement.getValue(i);
467: }
468: for (Iterator iter = layoutElements.iterator(); iter
469: .hasNext();) {
470: LayoutElement element = (LayoutElement) iter.next();
471: element.readBands(in, arrayCount);
472: }
473: }
474:
475: public void addToAttribute(int index, NewAttribute attribute) {
476: // Add the count value
477: countElement.addToAttribute(index, attribute);
478:
479: // Add the corresponding array values
480: int offset = 0;
481: for (int i = 0; i < index; i++) {
482: offset += countElement.getValue(i);
483: }
484: long numElements = countElement.getValue(index);
485: for (int i = offset; i < offset + numElements; i++) {
486: for (Iterator iter = layoutElements.iterator(); iter
487: .hasNext();) {
488: LayoutElement element = (LayoutElement) iter.next();
489: element.addToAttribute(i, attribute);
490: }
491: }
492: }
493: }
494:
495: /**
496: * A Union is a type of layout element where the tag value acts as a
497: * selector for one of the union cases
498: */
499: private class Union extends LayoutElement {
500:
501: private Integral unionTag;
502: private List unionCases;
503: private List defaultCaseBody;
504: private int[] caseCounts;
505: private int defaultCount;
506:
507: public Union(String tag, List unionCases, List body) {
508: this .unionTag = new Integral(tag);
509: this .unionCases = unionCases;
510: this .defaultCaseBody = body;
511: }
512:
513: public void readBands(InputStream in, int count)
514: throws IOException, Pack200Exception {
515: unionTag.readBands(in, count);
516: long[] values = unionTag.band;
517: // Count the band size for each union case then read the bands
518: caseCounts = new int[unionCases.size()];
519: for (int i = 0; i < caseCounts.length; i++) {
520: UnionCase unionCase = (UnionCase) unionCases.get(i);
521: for (int j = 0; j < values.length; j++) {
522: if (unionCase.hasTag(values[j])) {
523: caseCounts[i]++;
524: }
525: }
526: unionCase.readBands(in, caseCounts[i]);
527: }
528: // Count number of default cases then read the default bands
529: for (int i = 0; i < values.length; i++) {
530: boolean found = false;
531: for (Iterator iter = unionCases.iterator(); iter
532: .hasNext();) {
533: UnionCase unionCase = (UnionCase) iter.next();
534: if (unionCase.hasTag(values[i])) {
535: found = true;
536: }
537: }
538: if (!found) {
539: defaultCount++;
540: }
541: }
542: if (defaultCaseBody != null) {
543: for (Iterator iter = defaultCaseBody.iterator(); iter
544: .hasNext();) {
545: LayoutElement element = (LayoutElement) iter.next();
546: element.readBands(in, defaultCount);
547: }
548: }
549: }
550:
551: public void addToAttribute(int n, NewAttribute attribute) {
552: unionTag.addToAttribute(n, attribute);
553: int offset = 0;
554: long[] tagBand = unionTag.band;
555: long tag = unionTag.getValue(n);
556: boolean defaultCase = true;
557: for (Iterator iter = unionCases.iterator(); iter.hasNext();) {
558: UnionCase element = (UnionCase) iter.next();
559: if (element.hasTag(tag)) {
560: defaultCase = false;
561: for (int j = 0; j < n; j++) {
562: if (element.hasTag(tagBand[j])) {
563: offset++;
564: }
565: }
566: element.addToAttribute(offset, attribute);
567: }
568: }
569: if (defaultCase) {
570: // default case
571: int defaultOffset = 0;
572: for (int j = 0; j < n; j++) {
573: boolean found = false;
574: for (Iterator iter = unionCases.iterator(); iter
575: .hasNext();) {
576: UnionCase element = (UnionCase) iter.next();
577: if (element.hasTag(tagBand[j])) {
578: found = true;
579: }
580: }
581: if (!found) {
582: defaultOffset++;
583: }
584: }
585: if (defaultCaseBody != null) {
586: for (Iterator iter = defaultCaseBody.iterator(); iter
587: .hasNext();) {
588: LayoutElement element = (LayoutElement) iter
589: .next();
590: element
591: .addToAttribute(defaultOffset,
592: attribute);
593: }
594: }
595: }
596: }
597:
598: }
599:
600: private class Call extends LayoutElement {
601:
602: private int callableIndex;
603: private Callable callable;
604:
605: public Call(int callableIndex) {
606: this .callableIndex = callableIndex;
607: }
608:
609: public void setCallable(Callable callable) {
610: this .callable = callable;
611: if (callableIndex < 1) {
612: callable.setBackwardsCallable();
613: }
614: }
615:
616: public void readBands(InputStream in, int count) {
617: /*
618: * We don't read anything here, but we need to pass the extra count
619: * to the callable if it's a forwards call. For backwards callables
620: * the count is transmitted directly in the attribute bands and
621: * so it is added later.
622: */
623: if (callableIndex > 0) {
624: callable.addCount(count);
625: }
626: }
627:
628: public void addToAttribute(int n, NewAttribute attribute) {
629: callable.addNextToAttribute(attribute);
630: }
631: }
632:
633: /**
634: * Constant Pool Reference
635: */
636: private class Reference extends LayoutElement {
637:
638: private String tag;
639:
640: private Object band;
641:
642: private int length;
643:
644: public Reference(String tag) {
645: this .tag = tag;
646: length = getLength(tag.charAt(tag.length()));
647: }
648:
649: public void readBands(InputStream in, int count)
650: throws IOException, Pack200Exception {
651: if (tag.startsWith("KI")) { // Integer
652: band = parseCPIntReferences(attributeLayout.getName(),
653: in, Codec.UNSIGNED5, count);
654: } else if (tag.startsWith("KJ")) { // Long
655: band = parseCPLongReferences(attributeLayout.getName(),
656: in, Codec.UNSIGNED5, count);
657: } else if (tag.startsWith("KF")) { // Float
658: band = parseCPFloatReferences(
659: attributeLayout.getName(), in, Codec.UNSIGNED5,
660: count);
661: } else if (tag.startsWith("KD")) { // Double
662: band = parseCPDoubleReferences(attributeLayout
663: .getName(), in, Codec.UNSIGNED5, count);
664: } else if (tag.startsWith("KS")) { // String
665: band = parseCPStringReferences(attributeLayout
666: .getName(), in, Codec.UNSIGNED5, count);
667: } else if (tag.startsWith("RC")) { // Class
668: band = parseCPClassReferences(
669: attributeLayout.getName(), in, Codec.UNSIGNED5,
670: count);
671: } else if (tag.startsWith("RS")) { // Signature
672: band = parseCPSignatureReferences(attributeLayout
673: .getName(), in, Codec.UNSIGNED5, count);
674: } else if (tag.startsWith("RD")) { // Descriptor
675: band = parseCPDescriptorReferences(attributeLayout
676: .getName(), in, Codec.UNSIGNED5, count);
677: } else if (tag.startsWith("RF")) { // Field Reference
678: band = parseCPFieldRefReferences(attributeLayout
679: .getName(), in, Codec.UNSIGNED5, count);
680: } else if (tag.startsWith("RM")) { // Method Reference
681: band = parseCPMethodRefReferences(attributeLayout
682: .getName(), in, Codec.UNSIGNED5, count);
683: } else if (tag.startsWith("RI")) { // Interface Method Reference
684: band = parseCPInterfaceMethodRefReferences(
685: attributeLayout.getName(), in, Codec.UNSIGNED5,
686: count);
687: } else if (tag.startsWith("RU")) { // UTF8 String
688: band = parseCPUTF8References(attributeLayout.getName(),
689: in, Codec.UNSIGNED5, count);
690: }
691: }
692:
693: public void addToAttribute(int n, NewAttribute attribute) {
694: if (tag.startsWith("KI")) { // Integer
695: attribute
696: .addCPConstant(length, ((CPInteger[]) band)[n]);
697: } else if (tag.startsWith("KJ")) { // Long
698: attribute.addCPConstant(length, ((CPLong[]) band)[n]);
699: } else if (tag.startsWith("KF")) { // Float
700: attribute.addCPConstant(length, ((CPFloat[]) band)[n]);
701: } else if (tag.startsWith("KD")) { // Double
702: attribute.addCPConstant(length, ((CPDouble[]) band)[n]);
703: } else if (tag.startsWith("KS")) { // String
704: attribute.addCPConstant(length, ((CPString[]) band)[n]);
705: } else if (tag.startsWith("RC")) { // Class
706: attribute.addCPClass(length, ((CPClass[]) band)[n]);
707: } else if (tag.startsWith("RS")) { // Signature
708: attribute.addCPUTF8(length, ((CPUTF8[]) band)[n]);
709: } else if (tag.startsWith("RD")) { // Descriptor
710: attribute.addCPNameAndType(length,
711: ((CPNameAndType[]) band)[n]);
712: } else if (tag.startsWith("RF")) { // Field Reference
713: attribute.addCPFieldRef(length,
714: ((CPFieldRef[]) band)[n]);
715: } else if (tag.startsWith("RM")) { // Method Reference
716: attribute.addCPMethodRef(length,
717: ((CPMethodRef[]) band)[n]);
718: } else if (tag.startsWith("RI")) { // Interface Method Reference
719: attribute.addCPIMethodRef(length,
720: ((CPInterfaceMethodRef[]) band)[n]);
721: } else if (tag.startsWith("RU")) { // UTF8 String
722: attribute.addCPUTF8(length, ((CPUTF8[]) band)[n]);
723: }
724: }
725:
726: }
727:
728: private class Callable implements AttributeLayoutElement {
729:
730: private List body;
731:
732: private boolean isBackwardsCallable;
733:
734: public Callable(List body) throws IOException {
735: this .body = body;
736: }
737:
738: private int count;
739: private int index;
740:
741: /**
742: * Used by calls when adding band contents to attributes
743: * so they don't have to keep track of the internal index
744: * of the callable
745: * @param attribute
746: */
747: public void addNextToAttribute(NewAttribute attribute) {
748: for (Iterator iter = body.iterator(); iter.hasNext();) {
749: LayoutElement element = (LayoutElement) iter.next();
750: element.addToAttribute(index, attribute);
751: }
752: index++;
753: }
754:
755: /**
756: * Adds the count of a call to this callable (ie the number of calls)
757: * @param count
758: */
759: public void addCount(int count) {
760: this .count += count;
761: }
762:
763: public void readBands(InputStream in, int count)
764: throws IOException, Pack200Exception {
765: count += this .count;
766: for (Iterator iter = body.iterator(); iter.hasNext();) {
767: LayoutElement element = (LayoutElement) iter.next();
768: element.readBands(in, count);
769: }
770: }
771:
772: public void addToAttribute(int n, NewAttribute attribute) {
773: // Ignore n because bands also contain element parts from calls
774: for (Iterator iter = body.iterator(); iter.hasNext();) {
775: LayoutElement element = (LayoutElement) iter.next();
776: element.addToAttribute(index, attribute);
777: }
778: index++;
779: }
780:
781: public boolean isBackwardsCallable() {
782: return isBackwardsCallable;
783: }
784:
785: /**
786: * Tells this Callable that it is a backwards callable
787: */
788: public void setBackwardsCallable() {
789: this .isBackwardsCallable = true;
790: }
791: }
792:
793: /**
794: * A Union case
795: */
796: private class UnionCase extends LayoutElement {
797:
798: private List body;
799:
800: private List tags;
801:
802: public UnionCase(List tags) {
803: this .tags = tags;
804: }
805:
806: public boolean hasTag(long l) {
807: return tags.contains(new Integer((int) l));
808: }
809:
810: public UnionCase(List tags, List body) throws IOException {
811: this .tags = tags;
812: this .body = body;
813: }
814:
815: public void readBands(InputStream in, int count)
816: throws IOException, Pack200Exception {
817: if (body != null) {
818: for (Iterator iter = body.iterator(); iter.hasNext();) {
819: LayoutElement element = (LayoutElement) iter.next();
820: element.readBands(in, count);
821: }
822: }
823: }
824:
825: public void addToAttribute(int index, NewAttribute attribute) {
826: if (body != null) {
827: for (Iterator iter = body.iterator(); iter.hasNext();) {
828: LayoutElement element = (LayoutElement) iter.next();
829: element.addToAttribute(index, attribute);
830: }
831: }
832: }
833: }
834:
835: /**
836: * Utility method to get the contents of the given stream, up to the next ']',
837: * (ignoring pairs of brackets '[' and ']')
838: * @param stream
839: * @return
840: * @throws IOException
841: */
842: private StringReader getStreamUpToMatchingBracket(
843: StringReader stream) throws IOException {
844: StringBuffer sb = new StringBuffer();
845: int foundBracket = -1;
846: while (foundBracket != 0) {
847: char c = (char) stream.read();
848: if (c == ']') {
849: foundBracket++;
850: }
851: if (c == '[') {
852: foundBracket--;
853: }
854: if (!(foundBracket == 0)) {
855: sb.append(c);
856: }
857: }
858: return new StringReader(sb.toString());
859: }
860:
861: /**
862: * Returns the codec that should be used for the given layout element
863: * @param layoutElement
864: * @return
865: */
866: public BHSDCodec getCodec(String layoutElement) {
867: if (layoutElement.indexOf("O") >= 0) { //$NON-NLS-1$
868: return Codec.BRANCH5;
869: } else if (layoutElement.indexOf("P") >= 0) { //$NON-NLS-1$
870: return Codec.BCI5;
871: } else if (layoutElement.indexOf("S") >= 0 && layoutElement.indexOf("KS") < 0 //$NON-NLS-1$ //$NON-NLS-2$
872: && layoutElement.indexOf("RS") < 0) { //$NON-NLS-1$
873: return Codec.SIGNED5;
874: } else if (layoutElement.indexOf("B") >= 0) { //$NON-NLS-1$
875: return Codec.BYTE1;
876: } else {
877: return Codec.UNSIGNED5;
878: }
879: }
880:
881: /**
882: * Utility method to get the contents of the given stream, up to the next ']',
883: * (ignoring pairs of brackets '[' and ']')
884: * @param stream
885: * @return
886: * @throws IOException
887: */
888: private String readUpToMatchingBracket(StringReader stream)
889: throws IOException {
890: StringBuffer sb = new StringBuffer();
891: int foundBracket = -1;
892: while (foundBracket != 0) {
893: char c = (char) stream.read();
894: if (c == ']') {
895: foundBracket++;
896: }
897: if (c == '[') {
898: foundBracket--;
899: }
900: if (!(foundBracket == 0)) {
901: sb.append(c);
902: }
903: }
904: return sb.toString();
905: }
906:
907: /**
908: * Read a number from the stream and return it
909: * @param stream
910: * @return
911: * @throws IOException
912: */
913: private int readNumber(StringReader stream) throws IOException {
914: stream.mark(1);
915: char first = (char) stream.read();
916: boolean negative = first == '-';
917: if (!negative) {
918: stream.reset();
919: }
920: stream.mark(100);
921: int i;
922: int length = 0;
923: while ((i = (stream.read())) != -1
924: && Character.isDigit((char) i)) {
925: length++;
926: }
927: stream.reset();
928: char[] digits = new char[length];
929: stream.read(digits);
930: return Integer.parseInt((negative ? "-" : "")
931: + new String(digits));
932: }
933:
934: /**
935: * Read a 'body' section of the layout from the given stream
936: * @param stream
937: * @return List of LayoutElements
938: * @throws IOException
939: */
940: private List readBody(StringReader stream) throws IOException {
941: List layoutElements = new ArrayList();
942: LayoutElement e;
943: while ((e = readNextLayoutElement(stream)) != null) {
944: layoutElements.add(e);
945: }
946: return layoutElements;
947: }
948:
949: public int getBackwardsCallCount() {
950: return backwardsCallCount;
951: }
952:
953: /**
954: * Once the attribute bands have been read the callables can be informed
955: * about the number of times each is subject to a backwards call. This
956: * method is used to set this information.
957: *
958: * @param backwardsCalls
959: * one int for each backwards callable, which contains the number
960: * of times that callable is subject to a backwards call.
961: * @throws IOException
962: */
963: public void setBackwardsCalls(int[] backwardsCalls)
964: throws IOException {
965: int index = 0;
966: parseLayout();
967: for (Iterator iter = attributeLayoutElements.iterator(); iter
968: .hasNext();) {
969: AttributeLayoutElement element = (AttributeLayoutElement) iter
970: .next();
971: if (element instanceof Callable
972: && ((Callable) element).isBackwardsCallable()) {
973: ((Callable) element).addCount(backwardsCalls[index]);
974: index++;
975: }
976: }
977: }
978:
979: }
|