IcBands.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.pack200;

  18. import java.io.IOException;
  19. import java.io.OutputStream;
  20. import java.util.ArrayList;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Objects;
  25. import java.util.Set;
  26. import java.util.TreeSet;

  27. /**
  28.  * Inner class bands (corresponds to the {@code ic_bands} set of bands in the pack200 specification)
  29.  */
  30. public class IcBands extends BandSet {

  31.     static class IcTuple implements Comparable<IcTuple> {

  32.         protected CPClass C; // this class
  33.         protected int F; // flags
  34.         protected CPClass C2; // outer class
  35.         protected CPUTF8 N; // name

  36.         IcTuple(final CPClass C, final int F, final CPClass C2, final CPUTF8 N) {
  37.             this.C = C;
  38.             this.F = F;
  39.             this.C2 = C2;
  40.             this.N = N;
  41.         }

  42.         @Override
  43.         public int compareTo(final IcTuple arg0) {
  44.             return C.compareTo(arg0.C);
  45.         }

  46.         @Override
  47.         public boolean equals(final Object o) {
  48.             if (o instanceof IcTuple) {
  49.                 final IcTuple icT = (IcTuple) o;
  50.                 return C.equals(icT.C) && F == icT.F && Objects.equals(C2, icT.C2) && Objects.equals(N, icT.N);
  51.             }
  52.             return false;
  53.         }

  54.         public boolean isAnonymous() {
  55.             final String className = C.toString();
  56.             final String innerName = className.substring(className.lastIndexOf('$') + 1);
  57.             return Character.isDigit(innerName.charAt(0));
  58.         }

  59.         @Override
  60.         public String toString() {
  61.             return C.toString();
  62.         }

  63.     }

  64.     private final Set<IcTuple> innerClasses = new TreeSet<>();
  65.     private final CpBands cpBands;
  66.     private int bit16Count;

  67.     private final Map<String, List<IcTuple>> outerToInner = new HashMap<>();

  68.     public IcBands(final SegmentHeader segmentHeader, final CpBands cpBands, final int effort) {
  69.         super(effort, segmentHeader);
  70.         this.cpBands = cpBands;
  71.     }

  72.     public void addInnerClass(final String name, final String outerName, final String innerName, int flags) {
  73.         if (outerName != null || innerName != null) {
  74.             if (namesArePredictable(name, outerName, innerName)) {
  75.                 final IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
  76.                 addToMap(outerName, innerClass);
  77.                 innerClasses.add(innerClass);
  78.             } else {
  79.                 flags |= 1 << 16;
  80.                 final IcTuple icTuple = new IcTuple(cpBands.getCPClass(name), flags, cpBands.getCPClass(outerName), cpBands.getCPUtf8(innerName));
  81.                 final boolean added = innerClasses.add(icTuple);
  82.                 if (added) {
  83.                     bit16Count++;
  84.                     addToMap(outerName, icTuple);
  85.                 }
  86.             }
  87.         } else {
  88.             final IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null);
  89.             addToMap(getOuter(name), innerClass);
  90.             innerClasses.add(innerClass);
  91.         }
  92.     }

  93.     private void addToMap(final String outerName, final IcTuple icTuple) {
  94.         List<IcTuple> tuples = outerToInner.get(outerName);
  95.         if (tuples == null) {
  96.             tuples = new ArrayList<>();
  97.             outerToInner.put(outerName, tuples);
  98.         } else {
  99.             for (final IcTuple tuple : tuples) {
  100.                 if (icTuple.equals(tuple)) {
  101.                     return;
  102.                 }
  103.             }
  104.         }
  105.         tuples.add(icTuple);
  106.     }

  107.     /**
  108.      * All input classes for the segment have now been read in, so this method is called so that this class can calculate/complete anything it could not do
  109.      * while classes were being read.
  110.      */
  111.     public void finaliseBands() {
  112.         segmentHeader.setIc_count(innerClasses.size());
  113.     }

  114.     public IcTuple getIcTuple(final CPClass inner) {
  115.         for (final IcTuple icTuple : innerClasses) {
  116.             if (icTuple.C.equals(inner)) {
  117.                 return icTuple;
  118.             }
  119.         }
  120.         return null;
  121.     }

  122.     public List<IcTuple> getInnerClassesForOuter(final String outerClassName) {
  123.         return outerToInner.get(outerClassName);
  124.     }

  125.     private String getOuter(final String name) {
  126.         return name.substring(0, name.lastIndexOf('$'));
  127.     }

  128.     private boolean namesArePredictable(final String name, final String outerName, final String innerName) {
  129.         // TODO: Could be multiple characters, not just $
  130.         return name.equals(outerName + '$' + innerName) && innerName.indexOf('$') == -1;
  131.     }

  132.     @Override
  133.     public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
  134.         PackingUtils.log("Writing internal class bands...");
  135.         final int[] ic_this_class = new int[innerClasses.size()];
  136.         final int[] ic_flags = new int[innerClasses.size()];
  137.         final int[] ic_outer_class = new int[bit16Count];
  138.         final int[] ic_name = new int[bit16Count];

  139.         int index2 = 0;
  140.         final List<IcTuple> innerClassesList = new ArrayList<>(innerClasses);
  141.         for (int i = 0; i < ic_this_class.length; i++) {
  142.             final IcTuple icTuple = innerClassesList.get(i);
  143.             ic_this_class[i] = icTuple.C.getIndex();
  144.             ic_flags[i] = icTuple.F;
  145.             if ((icTuple.F & 1 << 16) != 0) {
  146.                 ic_outer_class[index2] = icTuple.C2 == null ? 0 : icTuple.C2.getIndex() + 1;
  147.                 ic_name[index2] = icTuple.N == null ? 0 : icTuple.N.getIndex() + 1;
  148.                 index2++;
  149.             }
  150.         }
  151.         byte[] encodedBand = encodeBandInt("ic_this_class", ic_this_class, Codec.UDELTA5);
  152.         outputStream.write(encodedBand);
  153.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_this_class[" + ic_this_class.length + "]");

  154.         encodedBand = encodeBandInt("ic_flags", ic_flags, Codec.UNSIGNED5);
  155.         outputStream.write(encodedBand);
  156.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_flags[" + ic_flags.length + "]");

  157.         encodedBand = encodeBandInt("ic_outer_class", ic_outer_class, Codec.DELTA5);
  158.         outputStream.write(encodedBand);
  159.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_outer_class[" + ic_outer_class.length + "]");

  160.         encodedBand = encodeBandInt("ic_name", ic_name, Codec.DELTA5);
  161.         outputStream.write(encodedBand);
  162.         PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_name[" + ic_name.length + "]");
  163.     }

  164. }