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