View Javadoc
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  
19  import java.nio.file.FileSystems;
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Map.Entry;
25  
26  import org.objectweb.asm.Attribute;
27  
28  /**
29   * Utility class to manage the various options available for pack200.
30   */
31  public class PackingOptions {
32  
33      private static final Attribute[] EMPTY_ATTRIBUTE_ARRAY = {};
34      public static final long SEGMENT_LIMIT = 1_000_000L;
35      public static final String STRIP = "strip";
36      public static final String ERROR = "error";
37      public static final String PASS = "pass";
38      public static final String KEEP = "keep";
39  
40      // All options are initially set to their defaults
41      private boolean gzip = true;
42      private boolean stripDebug;
43      private boolean keepFileOrder = true;
44      private long segmentLimit = SEGMENT_LIMIT;
45      private int effort = 5;
46      private String deflateHint = KEEP;
47      private String modificationTime = KEEP;
48      private final List<String> passFiles = new ArrayList<>();
49      private String unknownAttributeAction = PASS;
50      private final Map<String, String> classAttributeActions = new HashMap<>();
51      private final Map<String, String> fieldAttributeActions = new HashMap<>();
52      private final Map<String, String> methodAttributeActions = new HashMap<>();
53      private final Map<String, String> codeAttributeActions = new HashMap<>();
54      private boolean verbose;
55      private String logFile;
56  
57      private Attribute[] unknownAttributeTypes;
58  
59      public void addClassAttributeAction(final String attributeName, final String action) {
60          classAttributeActions.put(attributeName, action);
61      }
62  
63      public void addCodeAttributeAction(final String attributeName, final String action) {
64          codeAttributeActions.put(attributeName, action);
65      }
66  
67      public void addFieldAttributeAction(final String attributeName, final String action) {
68          fieldAttributeActions.put(attributeName, action);
69      }
70  
71      public void addMethodAttributeAction(final String attributeName, final String action) {
72          methodAttributeActions.put(attributeName, action);
73      }
74  
75      private void addOrUpdateAttributeActions(final List<Attribute> prototypes, final Map<String, String> attributeActions, final int tag) {
76          if (attributeActions != null && attributeActions.size() > 0) {
77              NewAttribute newAttribute;
78              for (final Entry<String, String> entry : attributeActions.entrySet()) {
79                  final String name = entry.getKey();
80                  final String action = entry.getValue();
81                  boolean prototypeExists = false;
82                  for (final Object prototype : prototypes) {
83                      newAttribute = (NewAttribute) prototype;
84                      if (newAttribute.type.equals(name)) {
85                          // if the attribute exists, update its context
86                          newAttribute.addContext(tag);
87                          prototypeExists = true;
88                          break;
89                      }
90                  }
91                  // if no attribute is found, add a new attribute
92                  if (!prototypeExists) {
93                      if (ERROR.equals(action)) {
94                          newAttribute = new NewAttribute.ErrorAttribute(name, tag);
95                      } else if (STRIP.equals(action)) {
96                          newAttribute = new NewAttribute.StripAttribute(name, tag);
97                      } else if (PASS.equals(action)) {
98                          newAttribute = new NewAttribute.PassAttribute(name, tag);
99                      } 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 directory will be passed.
110      *
111      * @param passFileName the file name
112      */
113     public void addPassFile(String passFileName) {
114         String fileSeparator = FileSystems.getDefault().getSeparator();
115         if (fileSeparator.equals("\\")) {
116             // Need to escape backslashes for replaceAll(), which uses regex
117             fileSeparator += "\\";
118         }
119         passFileName = passFileName.replaceAll(fileSeparator, "/");
120         passFiles.add(passFileName);
121     }
122 
123     public String getDeflateHint() {
124         return deflateHint;
125     }
126 
127     public int getEffort() {
128         return effort;
129     }
130 
131     public String getLogFile() {
132         return logFile;
133     }
134 
135     public String getModificationTime() {
136         return modificationTime;
137     }
138 
139     private String getOrDefault(final Map<String, String> map, final String type, final String defaultValue) {
140         return map == null ? defaultValue : map.getOrDefault(type, defaultValue);
141     }
142 
143     public long getSegmentLimit() {
144         return segmentLimit;
145     }
146 
147     public String getUnknownAttributeAction() {
148         return unknownAttributeAction;
149     }
150 
151     public Attribute[] getUnknownAttributePrototypes() {
152         if (unknownAttributeTypes == null) {
153             final List<Attribute> prototypes = new ArrayList<>();
154             addOrUpdateAttributeActions(prototypes, classAttributeActions, AttributeDefinitionBands.CONTEXT_CLASS);
155             addOrUpdateAttributeActions(prototypes, methodAttributeActions, AttributeDefinitionBands.CONTEXT_METHOD);
156             addOrUpdateAttributeActions(prototypes, fieldAttributeActions, AttributeDefinitionBands.CONTEXT_FIELD);
157             addOrUpdateAttributeActions(prototypes, codeAttributeActions, AttributeDefinitionBands.CONTEXT_CODE);
158             unknownAttributeTypes = prototypes.toArray(EMPTY_ATTRIBUTE_ARRAY);
159         }
160         return unknownAttributeTypes;
161     }
162 
163     public String getUnknownClassAttributeAction(final String type) {
164         return getOrDefault(classAttributeActions, type, unknownAttributeAction);
165     }
166 
167     public String getUnknownCodeAttributeAction(final String type) {
168         return getOrDefault(codeAttributeActions, type, unknownAttributeAction);
169     }
170 
171     public String getUnknownFieldAttributeAction(final String type) {
172         return getOrDefault(fieldAttributeActions, type, unknownAttributeAction);
173     }
174 
175     public String getUnknownMethodAttributeAction(final String type) {
176         return getOrDefault(methodAttributeActions, type, unknownAttributeAction);
177     }
178 
179     public boolean isGzip() {
180         return gzip;
181     }
182 
183     public boolean isKeepDeflateHint() {
184         return KEEP.equals(deflateHint);
185     }
186 
187     public boolean isKeepFileOrder() {
188         return keepFileOrder;
189     }
190 
191     public boolean isPassFile(final String passFileName) {
192         for (String pass : passFiles) {
193             if (passFileName.equals(pass)) {
194                 return true;
195             }
196             if (!pass.endsWith(".class")) { // a whole directory is
197                 // passed
198                 if (!pass.endsWith("/")) {
199                     // Make sure we don't get any false positives (e.g.
200                     // exclude "org/apache/harmony/pack" should not match
201                     // files under "org/apache/harmony/pack200/")
202                     pass += "/";
203                 }
204                 return passFileName.startsWith(pass);
205             }
206         }
207         return false;
208     }
209 
210     public boolean isStripDebug() {
211         return stripDebug;
212     }
213 
214     public boolean isVerbose() {
215         return verbose;
216     }
217 
218     public void removePassFile(final String passFileName) {
219         passFiles.remove(passFileName);
220     }
221 
222     public void setDeflateHint(final String deflateHint) {
223         if (!KEEP.equals(deflateHint) && !"true".equals(deflateHint) && !"false".equals(deflateHint)) {
224             throw new IllegalArgumentException("Bad argument: -H " + deflateHint + " ? deflate hint should be either true, false or keep (default)");
225         }
226         this.deflateHint = deflateHint;
227     }
228 
229     /**
230      * Sets the compression effort level (0-9, equivalent to -E command line option)
231      *
232      * @param effort the compression effort level, 0-9.
233      */
234     public void setEffort(final int effort) {
235         this.effort = effort;
236     }
237 
238     public void setGzip(final boolean gzip) {
239         this.gzip = gzip;
240     }
241 
242     public void setKeepFileOrder(final boolean keepFileOrder) {
243         this.keepFileOrder = keepFileOrder;
244     }
245 
246     public void setLogFile(final String logFile) {
247         this.logFile = logFile;
248     }
249 
250     public void setModificationTime(final String modificationTime) {
251         if (!KEEP.equals(modificationTime) && !"latest".equals(modificationTime)) {
252             throw new IllegalArgumentException("Bad argument: -m " + modificationTime + " ? transmit modtimes should be either latest or keep (default)");
253         }
254         this.modificationTime = modificationTime;
255     }
256 
257     public void setQuiet(final boolean quiet) {
258         this.verbose = !quiet;
259     }
260 
261     /**
262      * Sets the segment limit (equivalent to -S command line option)
263      *
264      * @param segmentLimit - the limit in bytes
265      */
266     public void setSegmentLimit(final long segmentLimit) {
267         this.segmentLimit = segmentLimit;
268     }
269 
270     /**
271      * Sets strip debug attributes. If true, all debug attributes (i.e. LineNumberTable, SourceFile, LocalVariableTable and LocalVariableTypeTable attributes)
272      * are stripped when reading the input class files and not included in the output archive.
273      *
274      * @param stripDebug If true, all debug attributes.
275      */
276     public void setStripDebug(final boolean stripDebug) {
277         this.stripDebug = stripDebug;
278     }
279 
280     /**
281      * Sets the compressor behavior when an unknown attribute is encountered.
282      *
283      * @param unknownAttributeAction - the action to perform
284      */
285     public void setUnknownAttributeAction(final String unknownAttributeAction) {
286         this.unknownAttributeAction = unknownAttributeAction;
287         if (!PASS.equals(unknownAttributeAction) && !ERROR.equals(unknownAttributeAction) && !STRIP.equals(unknownAttributeAction)) {
288             throw new IllegalArgumentException("Incorrect option for -U, " + unknownAttributeAction);
289         }
290     }
291 
292     public void setVerbose(final boolean verbose) {
293         this.verbose = verbose;
294     }
295 
296 }