NewAttribute.java

  1. /*
  2.  *  Licensed to the Apache Software Foundation (ASF) under one or more
  3.  *  contributor license agreements.  See the NOTICE file distributed with
  4.  *  this work for additional information regarding copyright ownership.
  5.  *  The ASF licenses this file to You under the Apache License, Version 2.0
  6.  *  (the "License"); you may not use this file except in compliance with
  7.  *  the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.commons.compress.harmony.unpack200.bytecode;

  18. import java.io.DataOutputStream;
  19. import java.io.IOException;
  20. import java.util.ArrayList;
  21. import java.util.List;

  22. /**
  23.  * A compressor-defined class file attribute.
  24.  */
  25. public class NewAttribute extends BCIRenumberedAttribute {

  26.     // Bytecode-related value (either a bytecode index or a length)
  27.     private abstract static class AbstractBcValue {

  28.         int actualValue;

  29.         public void setActualValue(final int value) {
  30.             this.actualValue = value;
  31.         }

  32.     }

  33.     private static final class BCIndex extends AbstractBcValue {

  34.         private final int index;

  35.         BCIndex(final int index) {
  36.             this.index = index;
  37.         }
  38.     }

  39.     private static final class BCLength extends AbstractBcValue {

  40.         private final int length;

  41.         BCLength(final int length) {
  42.             this.length = length;
  43.         }
  44.     }

  45.     private static final class BCOffset extends AbstractBcValue {

  46.         private final int offset;
  47.         private int index;

  48.         BCOffset(final int offset) {
  49.             this.offset = offset;
  50.         }

  51.         public void setIndex(final int index) {
  52.             this.index = index;
  53.         }

  54.     }

  55.     private final List<Integer> lengths = new ArrayList<>();

  56.     private final List<Object> body = new ArrayList<>();

  57.     private ClassConstantPool pool;

  58.     private final int layoutIndex;

  59.     public NewAttribute(final CPUTF8 attributeName, final int layoutIndex) {
  60.         super(attributeName);
  61.         this.layoutIndex = layoutIndex;
  62.     }

  63.     public void addBCIndex(final int length, final int value) {
  64.         lengths.add(Integer.valueOf(length));
  65.         body.add(new BCIndex(value));
  66.     }

  67.     public void addBCLength(final int length, final int value) {
  68.         lengths.add(Integer.valueOf(length));
  69.         body.add(new BCLength(value));
  70.     }

  71.     public void addBCOffset(final int length, final int value) {
  72.         lengths.add(Integer.valueOf(length));
  73.         body.add(new BCOffset(value));
  74.     }

  75.     public void addInteger(final int length, final long value) {
  76.         lengths.add(Integer.valueOf(length));
  77.         body.add(Long.valueOf(value));
  78.     }

  79.     public void addToBody(final int length, final Object value) {
  80.         lengths.add(Integer.valueOf(length));
  81.         body.add(value);
  82.     }

  83.     public int getLayoutIndex() {
  84.         return layoutIndex;
  85.     }

  86.     /*
  87.      * (non-Javadoc)
  88.      *
  89.      * @see org.apache.commons.compress.harmony.unpack200.bytecode.Attribute#getLength()
  90.      */
  91.     @Override
  92.     protected int getLength() {
  93.         int length = 0;
  94.         for (final Integer len : lengths) {
  95.             length += len.intValue();
  96.         }
  97.         return length;
  98.     }

  99.     @Override
  100.     protected ClassFileEntry[] getNestedClassFileEntries() {
  101.         int total = 1;
  102.         for (final Object element : body) {
  103.             if (element instanceof ClassFileEntry) {
  104.                 total++;
  105.             }
  106.         }
  107.         final ClassFileEntry[] nested = new ClassFileEntry[total];
  108.         nested[0] = getAttributeName();
  109.         int i = 1;
  110.         for (final Object element : body) {
  111.             if (element instanceof ClassFileEntry) {
  112.                 nested[i] = (ClassFileEntry) element;
  113.                 i++;
  114.             }
  115.         }
  116.         return nested;
  117.     }

  118.     @Override
  119.     protected int[] getStartPCs() {
  120.         // Don't need to return anything here as we've overridden renumber
  121.         return null;
  122.     }

  123.     @Override
  124.     public void renumber(final List<Integer> byteCodeOffsets) {
  125.         if (!renumbered) {
  126.             Object previous = null;
  127.             for (final Object obj : body) {
  128.                 if (obj instanceof BCIndex) {
  129.                     final BCIndex bcIndex = (BCIndex) obj;
  130.                     bcIndex.setActualValue(byteCodeOffsets.get(bcIndex.index).intValue());
  131.                 } else if (obj instanceof BCOffset) {
  132.                     final BCOffset bcOffset = (BCOffset) obj;
  133.                     if (previous instanceof BCIndex) {
  134.                         final int index = ((BCIndex) previous).index + bcOffset.offset;
  135.                         bcOffset.setIndex(index);
  136.                         bcOffset.setActualValue(byteCodeOffsets.get(index).intValue());
  137.                     } else if (previous instanceof BCOffset) {
  138.                         final int index = ((BCOffset) previous).index + bcOffset.offset;
  139.                         bcOffset.setIndex(index);
  140.                         bcOffset.setActualValue(byteCodeOffsets.get(index).intValue());
  141.                     } else {
  142.                         // Not sure if this should be able to happen
  143.                         bcOffset.setActualValue(byteCodeOffsets.get(bcOffset.offset).intValue());
  144.                     }
  145.                 } else if (obj instanceof BCLength) {
  146.                     // previous must be a BCIndex
  147.                     final BCLength bcLength = (BCLength) obj;
  148.                     final BCIndex prevIndex = (BCIndex) previous;
  149.                     final int index = prevIndex.index + bcLength.length;
  150.                     final int actualLength = byteCodeOffsets.get(index).intValue() - prevIndex.actualValue;
  151.                     bcLength.setActualValue(actualLength);
  152.                 }
  153.                 previous = obj;
  154.             }
  155.             renumbered = true;
  156.         }
  157.     }

  158.     @Override
  159.     protected void resolve(final ClassConstantPool pool) {
  160.         super.resolve(pool);
  161.         for (final Object element : body) {
  162.             if (element instanceof ClassFileEntry) {
  163.                 ((ClassFileEntry) element).resolve(pool);
  164.             }
  165.         }
  166.         this.pool = pool;
  167.     }

  168.     /*
  169.      * (non-Javadoc)
  170.      *
  171.      * @see org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry#toString()
  172.      */
  173.     @Override
  174.     public String toString() {
  175.         return attributeName.underlyingString();
  176.     }

  177.     /*
  178.      * (non-Javadoc)
  179.      *
  180.      * @see org.apache.commons.compress.harmony.unpack200.bytecode.Attribute#writeBody(java.io.DataOutputStream)
  181.      */
  182.     @Override
  183.     protected void writeBody(final DataOutputStream dos) throws IOException {
  184.         for (int i = 0; i < lengths.size(); i++) {
  185.             final int length = lengths.get(i).intValue();
  186.             final Object obj = body.get(i);
  187.             long value = 0;
  188.             if (obj instanceof Long) {
  189.                 value = ((Long) obj).longValue();
  190.             } else if (obj instanceof ClassFileEntry) {
  191.                 value = pool.indexOf((ClassFileEntry) obj);
  192.             } else if (obj instanceof AbstractBcValue) {
  193.                 value = ((AbstractBcValue) obj).actualValue;
  194.             }
  195.             // Write
  196.             switch (length) {
  197.             case 1:
  198.                 dos.writeByte((int) value);
  199.                 break;
  200.             case 2:
  201.                 dos.writeShort((int) value);
  202.                 break;
  203.             case 4:
  204.                 dos.writeInt((int) value);
  205.                 break;
  206.             case 8:
  207.                 dos.writeLong(value);
  208.                 break;
  209.             default:
  210.                 break;
  211.             }
  212.         }
  213.     }

  214. }