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