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