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.IOException; 020import java.io.OutputStream; 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027import java.util.TreeSet; 028 029/** 030 * Inner class bands (corresponds to the <code>ic_bands</code> set of bands in the pack200 specification) 031 */ 032public class IcBands extends BandSet { 033 034 private final Set innerClasses = new TreeSet(); 035 private final CpBands cpBands; 036 private int bit16Count = 0; 037 038 private final Map outerToInner = new HashMap(); 039 040 public IcBands(final SegmentHeader segmentHeader, final CpBands cpBands, final int effort) { 041 super(effort, segmentHeader); 042 this.cpBands = cpBands; 043 } 044 045 /** 046 * All input classes for the segment have now been read in, so this method is called so that this class can 047 * calculate/complete anything it could not do while classes were being read. 048 */ 049 public void finaliseBands() { 050 segmentHeader.setIc_count(innerClasses.size()); 051 } 052 053 @Override 054 public void pack(final OutputStream out) throws IOException, Pack200Exception { 055 PackingUtils.log("Writing internal class bands..."); 056 final int[] ic_this_class = new int[innerClasses.size()]; 057 final int[] ic_flags = new int[innerClasses.size()]; 058 final int[] ic_outer_class = new int[bit16Count]; 059 final int[] ic_name = new int[bit16Count]; 060 061 int index2 = 0; 062 final List innerClassesList = new ArrayList(innerClasses); 063 for (int i = 0; i < ic_this_class.length; i++) { 064 final IcTuple icTuple = (IcTuple) innerClassesList.get(i); 065 ic_this_class[i] = icTuple.C.getIndex(); 066 ic_flags[i] = icTuple.F; 067 if ((icTuple.F & (1 << 16)) != 0) { 068 ic_outer_class[index2] = icTuple.C2 == null ? 0 : icTuple.C2.getIndex() + 1; 069 ic_name[index2] = icTuple.N == null ? 0 : icTuple.N.getIndex() + 1; 070 index2++; 071 } 072 } 073 byte[] encodedBand = encodeBandInt("ic_this_class", ic_this_class, Codec.UDELTA5); 074 out.write(encodedBand); 075 PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_this_class[" + ic_this_class.length + "]"); 076 077 encodedBand = encodeBandInt("ic_flags", ic_flags, Codec.UNSIGNED5); 078 out.write(encodedBand); 079 PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_flags[" + ic_flags.length + "]"); 080 081 encodedBand = encodeBandInt("ic_outer_class", ic_outer_class, Codec.DELTA5); 082 out.write(encodedBand); 083 PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_outer_class[" + ic_outer_class.length + "]"); 084 085 encodedBand = encodeBandInt("ic_name", ic_name, Codec.DELTA5); 086 out.write(encodedBand); 087 PackingUtils.log("Wrote " + encodedBand.length + " bytes from ic_name[" + ic_name.length + "]"); 088 } 089 090 public void addInnerClass(final String name, final String outerName, final String innerName, int flags) { 091 if (outerName != null || innerName != null) { 092 if (namesArePredictable(name, outerName, innerName)) { 093 final IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null); 094 addToMap(outerName, innerClass); 095 innerClasses.add(innerClass); 096 } else { 097 flags |= (1 << 16); 098 final IcTuple icTuple = new IcTuple(cpBands.getCPClass(name), flags, cpBands.getCPClass(outerName), 099 cpBands.getCPUtf8(innerName)); 100 final boolean added = innerClasses.add(icTuple); 101 if (added) { 102 bit16Count++; 103 addToMap(outerName, icTuple); 104 } 105 } 106 } else { 107 final IcTuple innerClass = new IcTuple(cpBands.getCPClass(name), flags, null, null); 108 addToMap(getOuter(name), innerClass); 109 innerClasses.add(innerClass); 110 } 111 } 112 113 public List getInnerClassesForOuter(final String outerClassName) { 114 return (List) outerToInner.get(outerClassName); 115 } 116 117 private String getOuter(final String name) { 118 return name.substring(0, name.lastIndexOf('$')); 119 } 120 121 private void addToMap(final String outerName, final IcTuple icTuple) { 122 List tuples = (List) outerToInner.get(outerName); 123 if (tuples == null) { 124 tuples = new ArrayList(); 125 outerToInner.put(outerName, tuples); 126 tuples.add(icTuple); 127 } else { 128 for (final Iterator iterator = tuples.iterator(); iterator.hasNext();) { 129 final IcTuple icT = (IcTuple) iterator.next(); 130 if (icTuple.equals(icT)) { 131 return; 132 } 133 } 134 tuples.add(icTuple); 135 } 136 } 137 138 private boolean namesArePredictable(final String name, final String outerName, final String innerName) { 139 // TODO: Could be multiple characters, not just $ 140 return name.equals(outerName + '$' + innerName) && innerName.indexOf('$') == -1; 141 } 142 143 class IcTuple implements Comparable { 144 145 protected CPClass C; // this class 146 protected int F; // flags 147 protected CPClass C2; // outer class 148 protected CPUTF8 N; // name 149 150 public IcTuple(final CPClass C, final int F, final CPClass C2, final CPUTF8 N) { 151 this.C = C; 152 this.F = F; 153 this.C2 = C2; 154 this.N = N; 155 } 156 157 @Override 158 public boolean equals(final Object o) { 159 if (o instanceof IcTuple) { 160 final IcTuple icT = (IcTuple) o; 161 return C.equals(icT.C) && F == icT.F && (C2 != null ? C2.equals(icT.C2) : icT.C2 == null) 162 && (N != null ? N.equals(icT.N) : icT.N == null); 163 } 164 return false; 165 } 166 167 @Override 168 public String toString() { 169 return C.toString(); 170 } 171 172 @Override 173 public int compareTo(final Object arg0) { 174 return C.compareTo(((IcTuple) arg0).C); 175 } 176 177 public boolean isAnonymous() { 178 final String className = C.toString(); 179 final String innerName = className.substring(className.lastIndexOf('$') + 1); 180 return Character.isDigit(innerName.charAt(0)); 181 } 182 183 } 184 185 public IcTuple getIcTuple(final CPClass inner) { 186 for (final Iterator iterator = innerClasses.iterator(); iterator.hasNext();) { 187 final IcTuple icTuple = (IcTuple) iterator.next(); 188 if (icTuple.C.equals(inner)) { 189 return icTuple; 190 } 191 } 192 return null; 193 } 194 195}