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 */ 017package org.apache.commons.compress.harmony.pack200; 018 019import java.io.ByteArrayInputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.OutputStream; 023import java.io.StringReader; 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 029 030import org.apache.commons.compress.harmony.pack200.AttributeDefinitionBands.AttributeDefinition; 031import org.objectweb.asm.Label; 032 033/** 034 * Set of bands relating to a non-predefined attribute that has had a layout definition given to pack200 (e.g. via one 035 * of the -C, -M, -F or -D command line options) 036 */ 037public class NewAttributeBands extends BandSet { 038 039 protected List attributeLayoutElements; 040 private int[] backwardsCallCounts; 041 private final CpBands cpBands; 042 private final AttributeDefinition def; 043 private boolean usedAtLeastOnce; 044 045 // used when parsing 046 private Integral lastPIntegral; 047 048 public NewAttributeBands(final int effort, final CpBands cpBands, final SegmentHeader header, 049 final AttributeDefinition def) throws IOException { 050 super(effort, header); 051 this.def = def; 052 this.cpBands = cpBands; 053 parseLayout(); 054 } 055 056 public void addAttribute(final NewAttribute attribute) { 057 usedAtLeastOnce = true; 058 final InputStream stream = new ByteArrayInputStream(attribute.getBytes()); 059 for (final Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) { 060 final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next(); 061 layoutElement.addAttributeToBand(attribute, stream); 062 } 063 } 064 065 @Override 066 public void pack(final OutputStream out) throws IOException, Pack200Exception { 067 for (final Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) { 068 final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next(); 069 layoutElement.pack(out); 070 } 071 } 072 073 public String getAttributeName() { 074 return def.name.getUnderlyingString(); 075 } 076 077 public int getFlagIndex() { 078 return def.index; 079 } 080 081 public int[] numBackwardsCalls() { 082 return backwardsCallCounts; 083 } 084 085 public boolean isUsedAtLeastOnce() { 086 return usedAtLeastOnce; 087 } 088 089 private void parseLayout() throws IOException { 090 final String layout = def.layout.getUnderlyingString(); 091 if (attributeLayoutElements == null) { 092 attributeLayoutElements = new ArrayList(); 093 final StringReader stream = new StringReader(layout); 094 AttributeLayoutElement e; 095 while ((e = readNextAttributeElement(stream)) != null) { 096 attributeLayoutElements.add(e); 097 } 098 resolveCalls(); 099 } 100 } 101 102 /** 103 * Resolve calls in the attribute layout and returns the number of backwards callables 104 * 105 * @param tokens - the attribute layout as a List of AttributeElements 106 */ 107 private void resolveCalls() { 108 for (int i = 0; i < attributeLayoutElements.size(); i++) { 109 final AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements.get(i); 110 if (element instanceof Callable) { 111 final Callable callable = (Callable) element; 112 final List body = callable.body; // Look for calls in the body 113 for (int iIndex = 0; iIndex < body.size(); iIndex++) { 114 final LayoutElement layoutElement = (LayoutElement) body.get(iIndex); 115 // Set the callable for each call 116 resolveCallsForElement(i, callable, layoutElement); 117 } 118 } 119 } 120 int backwardsCallableIndex = 0; 121 for (int i = 0; i < attributeLayoutElements.size(); i++) { 122 final AttributeLayoutElement element = (AttributeLayoutElement) attributeLayoutElements.get(i); 123 if (element instanceof Callable) { 124 final Callable callable = (Callable) element; 125 if (callable.isBackwardsCallable) { 126 callable.setBackwardsCallableIndex(backwardsCallableIndex); 127 backwardsCallableIndex++; 128 } 129 } 130 } 131 backwardsCallCounts = new int[backwardsCallableIndex]; 132 } 133 134 private void resolveCallsForElement(final int i, final Callable currentCallable, 135 final LayoutElement layoutElement) { 136 if (layoutElement instanceof Call) { 137 final Call call = (Call) layoutElement; 138 int index = call.callableIndex; 139 if (index == 0) { // Calls the parent callable 140 call.setCallable(currentCallable); 141 } else if (index > 0) { // Forwards call 142 for (int k = i + 1; k < attributeLayoutElements.size(); k++) { 143 final AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements.get(k); 144 if (el instanceof Callable) { 145 index--; 146 if (index == 0) { 147 call.setCallable((Callable) el); 148 break; 149 } 150 } 151 } 152 } else { // Backwards call 153 for (int k = i - 1; k >= 0; k--) { 154 final AttributeLayoutElement el = (AttributeLayoutElement) attributeLayoutElements.get(k); 155 if (el instanceof Callable) { 156 index++; 157 if (index == 0) { 158 call.setCallable((Callable) el); 159 break; 160 } 161 } 162 } 163 } 164 } else if (layoutElement instanceof Replication) { 165 final List children = ((Replication) layoutElement).layoutElements; 166 for (final Iterator iterator = children.iterator(); iterator.hasNext();) { 167 final LayoutElement object = (LayoutElement) iterator.next(); 168 resolveCallsForElement(i, currentCallable, object); 169 } 170 } 171 } 172 173 private AttributeLayoutElement readNextAttributeElement(final StringReader stream) throws IOException { 174 stream.mark(1); 175 final int nextChar = stream.read(); 176 if (nextChar == -1) { 177 return null; 178 } 179 if (nextChar == '[') { 180 final List body = readBody(getStreamUpToMatchingBracket(stream)); 181 return new Callable(body); 182 } 183 stream.reset(); 184 return readNextLayoutElement(stream); 185 } 186 187 private LayoutElement readNextLayoutElement(final StringReader stream) throws IOException { 188 final int nextChar = stream.read(); 189 if (nextChar == -1) { 190 return null; 191 } 192 193 switch (nextChar) { 194 // Integrals 195 case 'B': 196 case 'H': 197 case 'I': 198 case 'V': 199 return new Integral(new String(new char[] {(char) nextChar})); 200 case 'S': 201 case 'F': 202 return new Integral(new String(new char[] {(char) nextChar, (char) stream.read()})); 203 case 'P': 204 stream.mark(1); 205 if (stream.read() != 'O') { 206 stream.reset(); 207 lastPIntegral = new Integral("P" + (char) stream.read()); 208 return lastPIntegral; 209 } else { 210 lastPIntegral = new Integral("PO" + (char) stream.read(), lastPIntegral); 211 return lastPIntegral; 212 } 213 case 'O': 214 stream.mark(1); 215 if (stream.read() != 'S') { 216 stream.reset(); 217 return new Integral("O" + (char) stream.read(), lastPIntegral); 218 } else { 219 return new Integral("OS" + (char) stream.read(), lastPIntegral); 220 } 221 222 // Replication 223 case 'N': 224 final char uint_type = (char) stream.read(); 225 stream.read(); // '[' 226 final String str = readUpToMatchingBracket(stream); 227 return new Replication("" + uint_type, str); 228 229 // Union 230 case 'T': 231 String int_type = "" + (char) stream.read(); 232 if (int_type.equals("S")) { 233 int_type += (char) stream.read(); 234 } 235 final List unionCases = new ArrayList(); 236 UnionCase c; 237 while ((c = readNextUnionCase(stream)) != null) { 238 unionCases.add(c); 239 } 240 stream.read(); // '(' 241 stream.read(); // ')' 242 stream.read(); // '[' 243 List body = null; 244 stream.mark(1); 245 final char next = (char) stream.read(); 246 if (next != ']') { 247 stream.reset(); 248 body = readBody(getStreamUpToMatchingBracket(stream)); 249 } 250 return new Union(int_type, unionCases, body); 251 252 // Call 253 case '(': 254 final int number = readNumber(stream).intValue(); 255 stream.read(); // ')' 256 return new Call(number); 257 // Reference 258 case 'K': 259 case 'R': 260 final StringBuilder string = new StringBuilder("").append((char) nextChar).append((char) stream.read()); 261 final char nxt = (char) stream.read(); 262 string.append(nxt); 263 if (nxt == 'N') { 264 string.append((char) stream.read()); 265 } 266 return new Reference(string.toString()); 267 } 268 return null; 269 } 270 271 /** 272 * Read a UnionCase from the stream 273 * 274 * @param stream 275 * @return 276 * @throws IOException If an I/O error occurs. 277 */ 278 private UnionCase readNextUnionCase(final StringReader stream) throws IOException { 279 stream.mark(2); 280 stream.read(); // '(' 281 char next = (char) stream.read(); 282 if (next == ')') { 283 stream.reset(); 284 return null; 285 } 286 stream.reset(); 287 stream.read(); // '(' 288 final List tags = new ArrayList(); 289 Integer nextTag; 290 do { 291 nextTag = readNumber(stream); 292 if (nextTag != null) { 293 tags.add(nextTag); 294 stream.read(); // ',' or ')' 295 } 296 } while (nextTag != null); 297 stream.read(); // '[' 298 stream.mark(1); 299 next = (char) stream.read(); 300 if (next == ']') { 301 return new UnionCase(tags); 302 } 303 stream.reset(); 304 return new UnionCase(tags, readBody(getStreamUpToMatchingBracket(stream))); 305 } 306 307 /** 308 * An AttributeLayoutElement is a part of an attribute layout and has one or more bands associated with it, which 309 * transmit the AttributeElement data for successive Attributes of this type. 310 */ 311 public interface AttributeLayoutElement { 312 313 void addAttributeToBand(NewAttribute attribute, InputStream stream); 314 315 void pack(OutputStream out) throws IOException, Pack200Exception; 316 317 void renumberBci(IntList bciRenumbering, Map labelsToOffsets); 318 319 } 320 321 public abstract class LayoutElement implements AttributeLayoutElement { 322 323 protected int getLength(final char uint_type) { 324 int length = 0; 325 switch (uint_type) { 326 case 'B': 327 length = 1; 328 break; 329 case 'H': 330 length = 2; 331 break; 332 case 'I': 333 length = 4; 334 break; 335 case 'V': 336 length = 0; 337 break; 338 } 339 return length; 340 } 341 } 342 343 public class Integral extends LayoutElement { 344 345 private final String tag; 346 347 private final List band = new ArrayList(); 348 private final BHSDCodec defaultCodec; 349 350 // used for bytecode offsets (OH and POH) 351 private Integral previousIntegral; 352 private int previousPValue; 353 354 public Integral(final String tag) { 355 this.tag = tag; 356 this.defaultCodec = getCodec(tag); 357 } 358 359 public Integral(final String tag, final Integral previousIntegral) { 360 this.tag = tag; 361 this.defaultCodec = getCodec(tag); 362 this.previousIntegral = previousIntegral; 363 } 364 365 public String getTag() { 366 return tag; 367 } 368 369 @Override 370 public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) { 371 Object val = null; 372 int value = 0; 373 if (tag.equals("B") || tag.equals("FB")) { 374 value = readInteger(1, stream) & 0xFF; // unsigned byte 375 } else if (tag.equals("SB")) { 376 value = readInteger(1, stream); 377 } else if (tag.equals("H") || tag.equals("FH")) { 378 value = readInteger(2, stream) & 0xFFFF; // unsigned short 379 } else if (tag.equals("SH")) { 380 value = readInteger(2, stream); 381 } else if (tag.equals("I") || tag.equals("FI")) { 382 value = readInteger(4, stream); 383 } else if (tag.equals("SI")) { 384 value = readInteger(4, stream); 385 } else if (tag.equals("V") || tag.equals("FV") || tag.equals("SV")) { 386 // Not currently supported 387 } else if (tag.startsWith("PO") || tag.startsWith("OS")) { 388 final char uint_type = tag.substring(2).toCharArray()[0]; 389 final int length = getLength(uint_type); 390 value = readInteger(length, stream); 391 value += previousIntegral.previousPValue; 392 val = attribute.getLabel(value); 393 previousPValue = value; 394 } else if (tag.startsWith("P")) { 395 final char uint_type = tag.substring(1).toCharArray()[0]; 396 final int length = getLength(uint_type); 397 value = readInteger(length, stream); 398 val = attribute.getLabel(value); 399 previousPValue = value; 400 } else if (tag.startsWith("O")) { 401 final char uint_type = tag.substring(1).toCharArray()[0]; 402 final int length = getLength(uint_type); 403 value = readInteger(length, stream); 404 value += previousIntegral.previousPValue; 405 val = attribute.getLabel(value); 406 previousPValue = value; 407 } 408 if (val == null) { 409 val = Integer.valueOf(value); 410 } 411 band.add(val); 412 } 413 414 @Override 415 public void pack(final OutputStream out) throws IOException, Pack200Exception { 416 PackingUtils.log("Writing new attribute bands..."); 417 final byte[] encodedBand = encodeBandInt(tag, integerListToArray(band), defaultCodec); 418 out.write(encodedBand); 419 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + tag + "[" + band.size() + "]"); 420 } 421 422 public int latestValue() { 423 return ((Integer) band.get(band.size() - 1)).intValue(); 424 } 425 426 @Override 427 public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) { 428 if (tag.startsWith("O") || tag.startsWith("PO")) { 429 renumberOffsetBci(previousIntegral.band, bciRenumbering, labelsToOffsets); 430 } else if (tag.startsWith("P")) { 431 for (int i = band.size() - 1; i >= 0; i--) { 432 final Object label = band.get(i); 433 if (label instanceof Integer) { 434 break; 435 } 436 if (label instanceof Label) { 437 band.remove(i); 438 final Integer bytecodeIndex = (Integer) labelsToOffsets.get(label); 439 band.add(i, Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue()))); 440 } 441 } 442 } 443 } 444 445 private void renumberOffsetBci(final List relative, final IntList bciRenumbering, final Map labelsToOffsets) { 446 for (int i = band.size() - 1; i >= 0; i--) { 447 final Object label = band.get(i); 448 if (label instanceof Integer) { 449 break; 450 } 451 if (label instanceof Label) { 452 band.remove(i); 453 final Integer bytecodeIndex = (Integer) labelsToOffsets.get(label); 454 final Integer renumberedOffset = Integer 455 .valueOf(bciRenumbering.get(bytecodeIndex.intValue()) - ((Integer) relative.get(i)).intValue()); 456 band.add(i, renumberedOffset); 457 } 458 } 459 } 460 461 } 462 463 /** 464 * A replication is an array of layout elements, with an associated count 465 */ 466 public class Replication extends LayoutElement { 467 468 private final Integral countElement; 469 470 private final List layoutElements = new ArrayList(); 471 472 public Integral getCountElement() { 473 return countElement; 474 } 475 476 public List getLayoutElements() { 477 return layoutElements; 478 } 479 480 public Replication(final String tag, final String contents) throws IOException { 481 this.countElement = new Integral(tag); 482 final StringReader stream = new StringReader(contents); 483 LayoutElement e; 484 while ((e = readNextLayoutElement(stream)) != null) { 485 layoutElements.add(e); 486 } 487 } 488 489 @Override 490 public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) { 491 countElement.addAttributeToBand(attribute, stream); 492 final int count = countElement.latestValue(); 493 for (int i = 0; i < count; i++) { 494 for (final Iterator iterator = layoutElements.iterator(); iterator.hasNext();) { 495 final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next(); 496 layoutElement.addAttributeToBand(attribute, stream); 497 } 498 } 499 } 500 501 @Override 502 public void pack(final OutputStream out) throws IOException, Pack200Exception { 503 countElement.pack(out); 504 for (final Iterator iterator = layoutElements.iterator(); iterator.hasNext();) { 505 final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next(); 506 layoutElement.pack(out); 507 } 508 } 509 510 @Override 511 public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) { 512 for (final Iterator iterator = layoutElements.iterator(); iterator.hasNext();) { 513 final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next(); 514 layoutElement.renumberBci(bciRenumbering, labelsToOffsets); 515 } 516 } 517 } 518 519 /** 520 * A Union is a type of layout element where the tag value acts as a selector for one of the union cases 521 */ 522 public class Union extends LayoutElement { 523 524 private final Integral unionTag; 525 private final List unionCases; 526 private final List defaultCaseBody; 527 528 public Union(final String tag, final List unionCases, final List body) { 529 this.unionTag = new Integral(tag); 530 this.unionCases = unionCases; 531 this.defaultCaseBody = body; 532 } 533 534 @Override 535 public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) { 536 unionTag.addAttributeToBand(attribute, stream); 537 final long tag = unionTag.latestValue(); 538 boolean defaultCase = true; 539 for (int i = 0; i < unionCases.size(); i++) { 540 final UnionCase element = (UnionCase) unionCases.get(i); 541 if (element.hasTag(tag)) { 542 defaultCase = false; 543 element.addAttributeToBand(attribute, stream); 544 } 545 } 546 if (defaultCase) { 547 for (int i = 0; i < defaultCaseBody.size(); i++) { 548 final LayoutElement element = (LayoutElement) defaultCaseBody.get(i); 549 element.addAttributeToBand(attribute, stream); 550 } 551 } 552 } 553 554 @Override 555 public void pack(final OutputStream out) throws IOException, Pack200Exception { 556 unionTag.pack(out); 557 for (final Iterator iterator = unionCases.iterator(); iterator.hasNext();) { 558 final UnionCase unionCase = (UnionCase) iterator.next(); 559 unionCase.pack(out); 560 } 561 for (final Iterator iterator = defaultCaseBody.iterator(); iterator.hasNext();) { 562 final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next(); 563 layoutElement.pack(out); 564 } 565 } 566 567 @Override 568 public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) { 569 for (final Iterator iterator = unionCases.iterator(); iterator.hasNext();) { 570 final UnionCase unionCase = (UnionCase) iterator.next(); 571 unionCase.renumberBci(bciRenumbering, labelsToOffsets); 572 } 573 for (final Iterator iterator = defaultCaseBody.iterator(); iterator.hasNext();) { 574 final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next(); 575 layoutElement.renumberBci(bciRenumbering, labelsToOffsets); 576 } 577 } 578 579 public Integral getUnionTag() { 580 return unionTag; 581 } 582 583 public List getUnionCases() { 584 return unionCases; 585 } 586 587 public List getDefaultCaseBody() { 588 return defaultCaseBody; 589 } 590 } 591 592 public class Call extends LayoutElement { 593 594 private final int callableIndex; 595 private Callable callable; 596 597 public Call(final int callableIndex) { 598 this.callableIndex = callableIndex; 599 } 600 601 public void setCallable(final Callable callable) { 602 this.callable = callable; 603 if (callableIndex < 1) { 604 callable.setBackwardsCallable(); 605 } 606 } 607 608 @Override 609 public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) { 610 callable.addAttributeToBand(attribute, stream); 611 if (callableIndex < 1) { 612 callable.addBackwardsCall(); 613 } 614 } 615 616 @Override 617 public void pack(final OutputStream out) { 618 // do nothing here as pack will be called on the callable at another time 619 } 620 621 @Override 622 public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) { 623 // do nothing here as renumberBci will be called on the callable at another time 624 } 625 626 public int getCallableIndex() { 627 return callableIndex; 628 } 629 630 public Callable getCallable() { 631 return callable; 632 } 633 } 634 635 /** 636 * Constant Pool Reference 637 */ 638 public class Reference extends LayoutElement { 639 640 private final String tag; 641 642 private List band; 643 644 private boolean nullsAllowed = false; 645 646 public Reference(final String tag) { 647 this.tag = tag; 648 nullsAllowed = tag.indexOf('N') != -1; 649 } 650 651 @Override 652 public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) { 653 final int index = readInteger(4, stream); 654 if (tag.startsWith("RC")) { // Class 655 band.add(cpBands.getCPClass(attribute.readClass(index))); 656 } else if (tag.startsWith("RU")) { // UTF8 String 657 band.add(cpBands.getCPUtf8(attribute.readUTF8(index))); 658 } else if (tag.startsWith("RS")) { // Signature 659 band.add(cpBands.getCPSignature(attribute.readUTF8(index))); 660 } else { // Constant 661 band.add(cpBands.getConstant(attribute.readConst(index))); 662 } 663 // TODO method and field references 664 } 665 666 public String getTag() { 667 return tag; 668 } 669 670 @Override 671 public void pack(final OutputStream out) throws IOException, Pack200Exception { 672 int[] ints; 673 if (nullsAllowed) { 674 ints = cpEntryOrNullListToArray(band); 675 } else { 676 ints = cpEntryListToArray(band); 677 } 678 final byte[] encodedBand = encodeBandInt(tag, ints, Codec.UNSIGNED5); 679 out.write(encodedBand); 680 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + tag + "[" + ints.length + "]"); 681 } 682 683 @Override 684 public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) { 685 // nothing to do here 686 } 687 688 } 689 690 public class Callable implements AttributeLayoutElement { 691 692 private final List body; 693 694 private boolean isBackwardsCallable; 695 696 private int backwardsCallableIndex; 697 698 public Callable(final List body) throws IOException { 699 this.body = body; 700 } 701 702 public void setBackwardsCallableIndex(final int backwardsCallableIndex) { 703 this.backwardsCallableIndex = backwardsCallableIndex; 704 } 705 706 public void addBackwardsCall() { 707 backwardsCallCounts[backwardsCallableIndex]++; 708 } 709 710 public boolean isBackwardsCallable() { 711 return isBackwardsCallable; 712 } 713 714 /** 715 * Tells this Callable that it is a backwards callable 716 */ 717 public void setBackwardsCallable() { 718 this.isBackwardsCallable = true; 719 } 720 721 @Override 722 public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) { 723 for (final Iterator iterator = body.iterator(); iterator.hasNext();) { 724 final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next(); 725 layoutElement.addAttributeToBand(attribute, stream); 726 } 727 } 728 729 @Override 730 public void pack(final OutputStream out) throws IOException, Pack200Exception { 731 for (final Iterator iterator = body.iterator(); iterator.hasNext();) { 732 final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next(); 733 layoutElement.pack(out); 734 } 735 } 736 737 @Override 738 public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) { 739 for (final Iterator iterator = body.iterator(); iterator.hasNext();) { 740 final AttributeLayoutElement layoutElement = (AttributeLayoutElement) iterator.next(); 741 layoutElement.renumberBci(bciRenumbering, labelsToOffsets); 742 } 743 } 744 745 public List getBody() { 746 return body; 747 } 748 } 749 750 /** 751 * A Union case 752 */ 753 public class UnionCase extends LayoutElement { 754 755 private final List body; 756 757 private final List tags; 758 759 public UnionCase(final List tags) { 760 this.tags = tags; 761 this.body = Collections.EMPTY_LIST; 762 } 763 764 public boolean hasTag(final long l) { 765 return tags.contains(Integer.valueOf((int) l)); 766 } 767 768 public UnionCase(final List tags, final List body) throws IOException { 769 this.tags = tags; 770 this.body = body; 771 } 772 773 @Override 774 public void addAttributeToBand(final NewAttribute attribute, final InputStream stream) { 775 for (int i = 0; i < body.size(); i++) { 776 final LayoutElement element = (LayoutElement) body.get(i); 777 element.addAttributeToBand(attribute, stream); 778 } 779 } 780 781 @Override 782 public void pack(final OutputStream out) throws IOException, Pack200Exception { 783 for (int i = 0; i < body.size(); i++) { 784 final LayoutElement element = (LayoutElement) body.get(i); 785 element.pack(out); 786 } 787 } 788 789 @Override 790 public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) { 791 for (int i = 0; i < body.size(); i++) { 792 final LayoutElement element = (LayoutElement) body.get(i); 793 element.renumberBci(bciRenumbering, labelsToOffsets); 794 } 795 } 796 797 public List getBody() { 798 return body; 799 } 800 } 801 802 /** 803 * Utility method to get the contents of the given stream, up to the next ']', (ignoring pairs of brackets '[' and 804 * ']') 805 * 806 * @param stream 807 * @return 808 * @throws IOException If an I/O error occurs. 809 */ 810 private StringReader getStreamUpToMatchingBracket(final StringReader stream) throws IOException { 811 final StringBuffer sb = new StringBuffer(); 812 int foundBracket = -1; 813 while (foundBracket != 0) { 814 final char c = (char) stream.read(); 815 if (c == ']') { 816 foundBracket++; 817 } 818 if (c == '[') { 819 foundBracket--; 820 } 821 if (!(foundBracket == 0)) { 822 sb.append(c); 823 } 824 } 825 return new StringReader(sb.toString()); 826 } 827 828 private int readInteger(final int i, final InputStream stream) { 829 int result = 0; 830 for (int j = 0; j < i; j++) { 831 try { 832 result = result << 8 | stream.read(); 833 } catch (final IOException e) { 834 throw new RuntimeException("Error reading unknown attribute"); 835 } 836 } 837 // use casting to preserve sign 838 if (i == 1) { 839 result = (byte) result; 840 } 841 if (i == 2) { 842 result = (short) result; 843 } 844 return result; 845 } 846 847 /** 848 * Returns the {@link BHSDCodec} that should be used for the given layout element 849 * 850 * @param layoutElement 851 */ 852 private BHSDCodec getCodec(final String layoutElement) { 853 if (layoutElement.indexOf('O') >= 0) { 854 return Codec.BRANCH5; 855 } 856 if (layoutElement.indexOf('P') >= 0) { 857 return Codec.BCI5; 858 } 859 if (layoutElement.indexOf('S') >= 0 && layoutElement.indexOf("KS") < 0 //$NON-NLS-1$ 860 && layoutElement.indexOf("RS") < 0) { //$NON-NLS-1$ 861 return Codec.SIGNED5; 862 } 863 if (layoutElement.indexOf('B') >= 0) { 864 return Codec.BYTE1; 865 } 866 return Codec.UNSIGNED5; 867 } 868 869 /** 870 * Utility method to get the contents of the given stream, up to the next ']', (ignoring pairs of brackets '[' and 871 * ']') 872 * 873 * @param stream 874 * @return 875 * @throws IOException If an I/O error occurs. 876 */ 877 private String readUpToMatchingBracket(final StringReader stream) throws IOException { 878 final StringBuffer sb = new StringBuffer(); 879 int foundBracket = -1; 880 while (foundBracket != 0) { 881 final char c = (char) stream.read(); 882 if (c == ']') { 883 foundBracket++; 884 } 885 if (c == '[') { 886 foundBracket--; 887 } 888 if (!(foundBracket == 0)) { 889 sb.append(c); 890 } 891 } 892 return sb.toString(); 893 } 894 895 /** 896 * Read a number from the stream and return it 897 * 898 * @param stream 899 * @return 900 * @throws IOException If an I/O error occurs. 901 */ 902 private Integer readNumber(final StringReader stream) throws IOException { 903 stream.mark(1); 904 final char first = (char) stream.read(); 905 final boolean negative = first == '-'; 906 if (!negative) { 907 stream.reset(); 908 } 909 stream.mark(100); 910 int i; 911 int length = 0; 912 while ((i = (stream.read())) != -1 && Character.isDigit((char) i)) { 913 length++; 914 } 915 stream.reset(); 916 if (length == 0) { 917 return null; 918 } 919 final char[] digits = new char[length]; 920 final int read = stream.read(digits); 921 if (read != digits.length) { 922 throw new IOException("Error reading from the input stream"); 923 } 924 return Integer.valueOf(Integer.parseInt((negative ? "-" : "") + new String(digits))); 925 } 926 927 /** 928 * Read a 'body' section of the layout from the given stream 929 * 930 * @param stream 931 * @return List of LayoutElements 932 * @throws IOException If an I/O error occurs. 933 */ 934 private List readBody(final StringReader stream) throws IOException { 935 final List layoutElements = new ArrayList(); 936 LayoutElement e; 937 while ((e = readNextLayoutElement(stream)) != null) { 938 layoutElements.add(e); 939 } 940 return layoutElements; 941 } 942 943 /** 944 * Renumber any bytecode indexes or offsets as described in section 5.5.2 of the pack200 specification 945 * 946 * @param bciRenumbering TODO 947 * @param labelsToOffsets TODO 948 */ 949 public void renumberBci(final IntList bciRenumbering, final Map labelsToOffsets) { 950 for (final Iterator iterator = attributeLayoutElements.iterator(); iterator.hasNext();) { 951 final AttributeLayoutElement element = (AttributeLayoutElement) iterator.next(); 952 element.renumberBci(bciRenumbering, labelsToOffsets); 953 } 954 } 955 956}