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.nio.file.FileSystems; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Map.Entry; 025 026import org.objectweb.asm.Attribute; 027 028/** 029 * Utility class to manage the various options available for pack200. 030 */ 031public class PackingOptions { 032 033 private static final Attribute[] EMPTY_ATTRIBUTE_ARRAY = {}; 034 public static final long SEGMENT_LIMIT = 1_000_000L; 035 public static final String STRIP = "strip"; 036 public static final String ERROR = "error"; 037 public static final String PASS = "pass"; 038 public static final String KEEP = "keep"; 039 040 // All options are initially set to their defaults 041 private boolean gzip = true; 042 private boolean stripDebug; 043 private boolean keepFileOrder = true; 044 private long segmentLimit = SEGMENT_LIMIT; 045 private int effort = 5; 046 private String deflateHint = KEEP; 047 private String modificationTime = KEEP; 048 private final List<String> passFiles = new ArrayList<>(); 049 private String unknownAttributeAction = PASS; 050 private final Map<String, String> classAttributeActions = new HashMap<>(); 051 private final Map<String, String> fieldAttributeActions = new HashMap<>(); 052 private final Map<String, String> methodAttributeActions = new HashMap<>(); 053 private final Map<String, String> codeAttributeActions = new HashMap<>(); 054 private boolean verbose; 055 private String logFile; 056 057 private Attribute[] unknownAttributeTypes; 058 059 public void addClassAttributeAction(final String attributeName, final String action) { 060 classAttributeActions.put(attributeName, action); 061 } 062 063 public void addCodeAttributeAction(final String attributeName, final String action) { 064 codeAttributeActions.put(attributeName, action); 065 } 066 067 public void addFieldAttributeAction(final String attributeName, final String action) { 068 fieldAttributeActions.put(attributeName, action); 069 } 070 071 public void addMethodAttributeAction(final String attributeName, final String action) { 072 methodAttributeActions.put(attributeName, action); 073 } 074 075 private void addOrUpdateAttributeActions(final List<Attribute> prototypes, final Map<String, String> attributeActions, final int tag) { 076 if (attributeActions != null && attributeActions.size() > 0) { 077 NewAttribute newAttribute; 078 for (final Entry<String, String> entry : attributeActions.entrySet()) { 079 final String name = entry.getKey(); 080 final String action = entry.getValue(); 081 boolean prototypeExists = false; 082 for (final Object prototype : prototypes) { 083 newAttribute = (NewAttribute) prototype; 084 if (newAttribute.type.equals(name)) { 085 // if the attribute exists, update its context 086 newAttribute.addContext(tag); 087 prototypeExists = true; 088 break; 089 } 090 } 091 // if no attribute is found, add a new attribute 092 if (!prototypeExists) { 093 if (ERROR.equals(action)) { 094 newAttribute = new NewAttribute.ErrorAttribute(name, tag); 095 } else if (STRIP.equals(action)) { 096 newAttribute = new NewAttribute.StripAttribute(name, tag); 097 } else if (PASS.equals(action)) { 098 newAttribute = new NewAttribute.PassAttribute(name, tag); 099 } else { 100 newAttribute = new NewAttribute(name, action, tag); 101 } 102 prototypes.add(newAttribute); 103 } 104 } 105 } 106 } 107 108 /** 109 * Tell the compressor to pass the file with the given name, or if the name is a directory name all files under that 110 * directory will be passed. 111 * 112 * @param passFileName the file name 113 */ 114 public void addPassFile(String passFileName) { 115 String fileSeparator = FileSystems.getDefault().getSeparator(); 116 if (fileSeparator.equals("\\")) { 117 // Need to escape backslashes for replaceAll(), which uses regex 118 fileSeparator += "\\"; 119 } 120 passFileName = passFileName.replaceAll(fileSeparator, "/"); 121 passFiles.add(passFileName); 122 } 123 124 public String getDeflateHint() { 125 return deflateHint; 126 } 127 128 public int getEffort() { 129 return effort; 130 } 131 132 public String getLogFile() { 133 return logFile; 134 } 135 136 public String getModificationTime() { 137 return modificationTime; 138 } 139 140 private String getOrDefault(final Map<String, String> map, final String type, final String defaultValue) { 141 return map == null ? defaultValue : map.getOrDefault(type, defaultValue); 142 } 143 144 public long getSegmentLimit() { 145 return segmentLimit; 146 } 147 148 public String getUnknownAttributeAction() { 149 return unknownAttributeAction; 150 } 151 152 public Attribute[] getUnknownAttributePrototypes() { 153 if (unknownAttributeTypes == null) { 154 final List<Attribute> prototypes = new ArrayList<>(); 155 addOrUpdateAttributeActions(prototypes, classAttributeActions, AttributeDefinitionBands.CONTEXT_CLASS); 156 addOrUpdateAttributeActions(prototypes, methodAttributeActions, AttributeDefinitionBands.CONTEXT_METHOD); 157 addOrUpdateAttributeActions(prototypes, fieldAttributeActions, AttributeDefinitionBands.CONTEXT_FIELD); 158 addOrUpdateAttributeActions(prototypes, codeAttributeActions, AttributeDefinitionBands.CONTEXT_CODE); 159 unknownAttributeTypes = prototypes.toArray(EMPTY_ATTRIBUTE_ARRAY); 160 } 161 return unknownAttributeTypes; 162 } 163 164 public String getUnknownClassAttributeAction(final String type) { 165 return getOrDefault(classAttributeActions, type, unknownAttributeAction); 166 } 167 168 public String getUnknownCodeAttributeAction(final String type) { 169 return getOrDefault(codeAttributeActions, type, unknownAttributeAction); 170 } 171 172 public String getUnknownFieldAttributeAction(final String type) { 173 return getOrDefault(fieldAttributeActions, type, unknownAttributeAction); 174 } 175 176 public String getUnknownMethodAttributeAction(final String type) { 177 return getOrDefault(methodAttributeActions, type, unknownAttributeAction); 178 } 179 180 public boolean isGzip() { 181 return gzip; 182 } 183 184 public boolean isKeepDeflateHint() { 185 return KEEP.equals(deflateHint); 186 } 187 188 public boolean isKeepFileOrder() { 189 return keepFileOrder; 190 } 191 192 public boolean isPassFile(final String passFileName) { 193 for (String pass : passFiles) { 194 if (passFileName.equals(pass)) { 195 return true; 196 } 197 if (!pass.endsWith(".class")) { // a whole directory is 198 // passed 199 if (!pass.endsWith("/")) { 200 // Make sure we don't get any false positives (e.g. 201 // exclude "org/apache/harmony/pack" should not match 202 // files under "org/apache/harmony/pack200/") 203 pass = pass + "/"; 204 } 205 return passFileName.startsWith(pass); 206 } 207 } 208 return false; 209 } 210 211 public boolean isStripDebug() { 212 return stripDebug; 213 } 214 215 public boolean isVerbose() { 216 return verbose; 217 } 218 219 public void removePassFile(final String passFileName) { 220 passFiles.remove(passFileName); 221 } 222 223 public void setDeflateHint(final String deflateHint) { 224 if (!KEEP.equals(deflateHint) && !"true".equals(deflateHint) && !"false".equals(deflateHint)) { 225 throw new IllegalArgumentException("Bad argument: -H " + deflateHint + " ? deflate hint should be either true, false or keep (default)"); 226 } 227 this.deflateHint = deflateHint; 228 } 229 230 /** 231 * Sets the compression effort level (0-9, equivalent to -E command line option) 232 * 233 * @param effort the compression effort level, 0-9. 234 */ 235 public void setEffort(final int effort) { 236 this.effort = effort; 237 } 238 239 public void setGzip(final boolean gzip) { 240 this.gzip = gzip; 241 } 242 243 public void setKeepFileOrder(final boolean keepFileOrder) { 244 this.keepFileOrder = keepFileOrder; 245 } 246 247 public void setLogFile(final String logFile) { 248 this.logFile = logFile; 249 } 250 251 public void setModificationTime(final String modificationTime) { 252 if (!KEEP.equals(modificationTime) && !"latest".equals(modificationTime)) { 253 throw new IllegalArgumentException("Bad argument: -m " + modificationTime + " ? transmit modtimes should be either latest or keep (default)"); 254 } 255 this.modificationTime = modificationTime; 256 } 257 258 public void setQuiet(final boolean quiet) { 259 this.verbose = !quiet; 260 } 261 262 /** 263 * Sets the segment limit (equivalent to -S command line option) 264 * 265 * @param segmentLimit - the limit in bytes 266 */ 267 public void setSegmentLimit(final long segmentLimit) { 268 this.segmentLimit = segmentLimit; 269 } 270 271 /** 272 * Sets strip debug attributes. If true, all debug attributes (i.e. LineNumberTable, SourceFile, LocalVariableTable and 273 * LocalVariableTypeTable attributes) are stripped when reading the input class files and not included in the output 274 * archive. 275 * 276 * @param stripDebug If true, all debug attributes. 277 */ 278 public void setStripDebug(final boolean stripDebug) { 279 this.stripDebug = stripDebug; 280 } 281 282 /** 283 * Sets the compressor behavior when an unknown attribute is encountered. 284 * 285 * @param unknownAttributeAction - the action to perform 286 */ 287 public void setUnknownAttributeAction(final String unknownAttributeAction) { 288 this.unknownAttributeAction = unknownAttributeAction; 289 if (!PASS.equals(unknownAttributeAction) && !ERROR.equals(unknownAttributeAction) && !STRIP.equals(unknownAttributeAction)) { 290 throw new IllegalArgumentException("Incorrect option for -U, " + unknownAttributeAction); 291 } 292 } 293 294 public void setVerbose(final boolean verbose) { 295 this.verbose = verbose; 296 } 297 298}