1/*
2 * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.java.util.jar.pack;
27
28import java.io.IOException;
29import java.io.InputStream;
30import java.io.PrintStream;
31import java.io.PrintWriter;
32import java.security.AccessController;
33import java.security.PrivilegedAction;
34import java.util.ArrayList;
35import java.util.Collection;
36import java.util.Comparator;
37import java.util.HashMap;
38import java.util.List;
39import java.util.Map;
40import java.util.Properties;
41import java.util.Set;
42import java.util.SortedMap;
43import java.util.TreeMap;
44import java.util.jar.Pack200;
45
46/**
47 * Control block for publishing Pack200 options to the other classes.
48 */
49
50final class PropMap implements SortedMap<String, String>  {
51    private final TreeMap<String, String> theMap = new TreeMap<>();;
52
53    // Override:
54    public String put(String key, String value) {
55        String oldValue = theMap.put(key, value);
56        return oldValue;
57    }
58
59    // All this other stuff is private to the current package.
60    // Outide clients of Pack200 do not need to use it; they can
61    // get by with generic SortedMap functionality.
62    private static Map<String, String> defaultProps;
63    static {
64        Properties props = new Properties();
65
66        // Allow implementation selected via -Dpack.disable.native=true
67        String propValue = getPropertyValue(Utils.DEBUG_DISABLE_NATIVE, "false");
68        props.put(Utils.DEBUG_DISABLE_NATIVE,
69                  String.valueOf(Boolean.parseBoolean(propValue)));
70
71        // Set the DEBUG_VERBOSE from system
72        int verbose = 0;
73        try {
74            verbose = Integer.decode(getPropertyValue(Utils.DEBUG_VERBOSE, "0"));
75        } catch (NumberFormatException e) {
76        }
77        props.put(Utils.DEBUG_VERBOSE, String.valueOf(verbose));
78
79        // The segment size is unlimited
80        props.put(Pack200.Packer.SEGMENT_LIMIT, "-1");
81
82        // Preserve file ordering by default.
83        props.put(Pack200.Packer.KEEP_FILE_ORDER, Pack200.Packer.TRUE);
84
85        // Preserve all modification times by default.
86        props.put(Pack200.Packer.MODIFICATION_TIME, Pack200.Packer.KEEP);
87
88        // Preserve deflation hints by default.
89        props.put(Pack200.Packer.DEFLATE_HINT, Pack200.Packer.KEEP);
90
91        // Pass through files with unrecognized attributes by default.
92        props.put(Pack200.Packer.UNKNOWN_ATTRIBUTE, Pack200.Packer.PASS);
93
94        // Pass through files with unrecognized format by default, also
95        // allow system property to be set
96        props.put(Utils.CLASS_FORMAT_ERROR,
97                  getPropertyValue(Utils.CLASS_FORMAT_ERROR, Pack200.Packer.PASS));
98
99        // Default effort is 5, midway between 1 and 9.
100        props.put(Pack200.Packer.EFFORT, "5");
101
102        // Define certain attribute layouts by default.
103        // Do this after the previous props are put in place,
104        // to allow override if necessary.
105        String propFile = "intrinsic.properties";
106
107        PrivilegedAction<InputStream> pa =
108            () -> PackerImpl.class.getResourceAsStream(propFile);
109        try (InputStream propStr = AccessController.doPrivileged(pa)) {
110            if (propStr == null) {
111                throw new RuntimeException(propFile + " cannot be loaded");
112            }
113            props.load(propStr);
114        } catch (IOException ee) {
115            throw new RuntimeException(ee);
116        }
117
118        for (Map.Entry<Object, Object> e : props.entrySet()) {
119            String key = (String) e.getKey();
120            String val = (String) e.getValue();
121            if (key.startsWith("attribute.")) {
122                e.setValue(Attribute.normalizeLayoutString(val));
123            }
124        }
125
126        @SuppressWarnings({"unchecked", "rawtypes"})
127        HashMap<String, String> temp = new HashMap(props);  // shrink to fit
128        defaultProps = temp;
129    }
130
131    private static String getPropertyValue(String key, String defaultValue) {
132        PrivilegedAction<String> pa = () -> System.getProperty(key);
133        String s = AccessController.doPrivileged(pa);
134        return s != null ? s : defaultValue;
135    }
136
137    PropMap() {
138        theMap.putAll(defaultProps);
139    }
140
141    // Return a view of this map which includes only properties
142    // that begin with the given prefix.  This is easy because
143    // the map is sorted, and has a subMap accessor.
144    SortedMap<String, String> prefixMap(String prefix) {
145        int len = prefix.length();
146        if (len == 0)
147            return this;
148        char nextch = (char)(prefix.charAt(len-1) + 1);
149        String limit = prefix.substring(0, len-1)+nextch;
150        //System.out.println(prefix+" => "+subMap(prefix, limit));
151        return subMap(prefix, limit);
152    }
153
154    String getProperty(String s) {
155        return get(s);
156    }
157    String getProperty(String s, String defaultVal) {
158        String val = getProperty(s);
159        if (val == null)
160            return defaultVal;
161        return val;
162    }
163    String setProperty(String s, String val) {
164        return put(s, val);
165    }
166
167    // Get sequence of props for "prefix", and "prefix.*".
168    List<String> getProperties(String prefix) {
169        Collection<String> values = prefixMap(prefix).values();
170        List<String> res = new ArrayList<>(values.size());
171        res.addAll(values);
172        while (res.remove(null));
173        return res;
174    }
175
176    private boolean toBoolean(String val) {
177        return Boolean.valueOf(val).booleanValue();
178    }
179    boolean getBoolean(String s) {
180        return toBoolean(getProperty(s));
181    }
182    boolean setBoolean(String s, boolean val) {
183        return toBoolean(setProperty(s, String.valueOf(val)));
184    }
185    int toInteger(String val) {
186        return toInteger(val, 0);
187    }
188    int toInteger(String val, int def) {
189        if (val == null)  return def;
190        if (Pack200.Packer.TRUE.equals(val))   return 1;
191        if (Pack200.Packer.FALSE.equals(val))  return 0;
192        return Integer.parseInt(val);
193    }
194    int getInteger(String s, int def) {
195        return toInteger(getProperty(s), def);
196    }
197    int getInteger(String s) {
198        return toInteger(getProperty(s));
199    }
200    int setInteger(String s, int val) {
201        return toInteger(setProperty(s, String.valueOf(val)));
202    }
203
204    long toLong(String val) {
205        try {
206            return val == null ? 0 : Long.parseLong(val);
207        } catch (java.lang.NumberFormatException nfe) {
208            throw new IllegalArgumentException("Invalid value");
209        }
210    }
211    long getLong(String s) {
212        return toLong(getProperty(s));
213    }
214    long setLong(String s, long val) {
215        return toLong(setProperty(s, String.valueOf(val)));
216    }
217
218    int getTime(String s) {
219        String sval = getProperty(s, "0");
220        if (Utils.NOW.equals(sval)) {
221            return (int)((System.currentTimeMillis()+500)/1000);
222        }
223        long lval = toLong(sval);
224        final long recentSecondCount = 1000000000;
225
226        if (lval < recentSecondCount*10 && !"0".equals(sval))
227            Utils.log.warning("Supplied modtime appears to be seconds rather than milliseconds: "+sval);
228
229        return (int)((lval+500)/1000);
230    }
231
232    void list(PrintStream out) {
233        PrintWriter outw = new PrintWriter(out);
234        list(outw);
235        outw.flush();
236    }
237    void list(PrintWriter out) {
238        out.println("#"+Utils.PACK_ZIP_ARCHIVE_MARKER_COMMENT+"[");
239        Set<Map.Entry<String, String>> defaults = defaultProps.entrySet();
240        for (Map.Entry<String, String> e : theMap.entrySet()) {
241            if (defaults.contains(e))  continue;
242            out.println("  " + e.getKey() + " = " + e.getValue());
243        }
244        out.println("#]");
245    }
246
247    @Override
248    public int size() {
249        return theMap.size();
250    }
251
252    @Override
253    public boolean isEmpty() {
254        return theMap.isEmpty();
255    }
256
257    @Override
258    public boolean containsKey(Object key) {
259        return theMap.containsKey(key);
260    }
261
262    @Override
263    public boolean containsValue(Object value) {
264        return theMap.containsValue(value);
265    }
266
267    @Override
268    public String get(Object key) {
269        return theMap.get(key);
270    }
271
272    @Override
273    public String remove(Object key) {
274       return theMap.remove(key);
275    }
276
277    @Override
278    public void putAll(Map<? extends String, ? extends String> m) {
279       theMap.putAll(m);
280    }
281
282    @Override
283    public void clear() {
284        theMap.clear();
285    }
286
287    @Override
288    public Set<String> keySet() {
289       return theMap.keySet();
290    }
291
292    @Override
293    public Collection<String> values() {
294       return theMap.values();
295    }
296
297    @Override
298    public Set<Map.Entry<String, String>> entrySet() {
299        return theMap.entrySet();
300    }
301
302    @Override
303    public Comparator<? super String> comparator() {
304        return theMap.comparator();
305    }
306
307    @Override
308    public SortedMap<String, String> subMap(String fromKey, String toKey) {
309        return theMap.subMap(fromKey, toKey);
310    }
311
312    @Override
313    public SortedMap<String, String> headMap(String toKey) {
314        return theMap.headMap(toKey);
315    }
316
317    @Override
318    public SortedMap<String, String> tailMap(String fromKey) {
319        return theMap.tailMap(fromKey);
320    }
321
322    @Override
323    public String firstKey() {
324        return theMap.firstKey();
325    }
326
327    @Override
328    public String lastKey() {
329       return theMap.lastKey();
330    }
331}
332