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