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