1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2 *
3 * This program and the accompanying materials are made available under
4 * the terms of the Common Public License v1.0 which accompanies this distribution,
5 * and is available at http://www.eclipse.org/legal/cpl-v10.html
6 *
7 * $Id: EMMAProperties.java,v 1.1.1.1.2.3 2004/07/16 23:32:03 vlad_r Exp $
8 */
9package com.vladium.emma;
10
11import java.io.File;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.Map;
15import java.util.Properties;
16import java.util.WeakHashMap;
17
18import com.vladium.util.ClassLoaderResolver;
19import com.vladium.util.IProperties;
20import com.vladium.util.Property;
21import com.vladium.emma.report.IReportProperties;
22import com.vladium.emma.report.ReportProperties;
23
24// ----------------------------------------------------------------------------
25/**
26 * A reflection of "${IAppConstants.APP_PROPERTY_RES_NAME}.properties" resource
27 * as viewed by a given classloader.
28 *
29 * @author Vlad Roubtsov, (C) 2003
30 */
31public
32abstract class EMMAProperties
33{
34    // public: ................................................................
35
36    public static final String GENERIC_PROPERTY_OVERRIDE_PREFIX = "D";
37
38    // [the DEFAULT_xxx settings duplicate the defaults in APP_DEFAULT_PROPERTIES_RES_NAME
39    // resource to provide a safe fallback option if that resource cannot be loaded]
40
41    public static final String DEFAULT_META_DATA_OUT_FILE       = "coverage.em";
42    public static final Boolean DEFAULT_META_DATA_OUT_MERGE     = Boolean.TRUE;
43    public static final String PREFIX_META_DATA                 = "metadata.";
44    public static final String PROPERTY_META_DATA_OUT_FILE      = PREFIX_META_DATA + "out.file";
45    public static final String PROPERTY_META_DATA_OUT_MERGE     = PREFIX_META_DATA + "out.merge";
46
47    public static final String DEFAULT_COVERAGE_DATA_OUT_FILE   = "coverage.ec";
48    public static final Boolean DEFAULT_COVERAGE_DATA_OUT_MERGE = Boolean.TRUE;
49    public static final String PREFIX_COVERAGE_DATA             = "coverage.";
50    public static final String PROPERTY_COVERAGE_DATA_OUT_FILE  = PREFIX_COVERAGE_DATA + "out.file";
51    public static final String PROPERTY_COVERAGE_DATA_OUT_MERGE = PREFIX_COVERAGE_DATA + "out.merge";
52
53    public static final String DEFAULT_SESSION_DATA_OUT_FILE    = "coverage.es";
54    public static final Boolean DEFAULT_SESSION_DATA_OUT_MERGE  = Boolean.TRUE;
55    public static final String PREFIX_SESSION_DATA              = "session.";
56    public static final String PROPERTY_SESSION_DATA_OUT_FILE   = PREFIX_SESSION_DATA + "out.file";
57    public static final String PROPERTY_SESSION_DATA_OUT_MERGE  = PREFIX_SESSION_DATA + "out.merge";
58
59    public static final String PROPERTY_TEMP_FILE_EXT           = ".et";
60
61    public static final Map SYSTEM_PROPERTY_REDIRECTS; // set in <clinit>
62
63
64    /**
65     * Global method used to create an appearance that all app work has been
66     * done at the same point in time (useful for setting archive and report
67     * timestamps etc).
68     *
69     * @return the result of System.currentTimeMillis (), evaluated on the
70     * first call only
71     */
72    public static synchronized long getTimeStamp ()
73    {
74        long result = s_timestamp;
75        if (result == 0)
76        {
77            s_timestamp = result = System.currentTimeMillis ();
78        }
79
80        return result;
81    }
82
83
84    public static String makeAppVersion (final int major, final int minor, final int build)
85    {
86        final StringBuffer buf = new StringBuffer ();
87
88        buf.append (major);
89        buf.append ('.');
90        buf.append (minor);
91        buf.append ('.');
92        buf.append (build);
93
94        return buf.toString ();
95    }
96
97
98    /**
99     * Wraps a Properties into a IProperties with the app's standard property
100     * mapping in place.
101     *
102     * @param properties [null results in null result]
103     */
104    public static IProperties wrap (final Properties properties)
105    {
106        if (properties == null) return null;
107
108        return IProperties.Factory.wrap (properties, ReportProperties.REPORT_PROPERTY_MAPPER);
109    }
110
111    /**
112     * Retrieves application properties as classloader resource with a given name.
113     * [as seen from ClassLoaderResolver.getClassLoader ()]. The result is cached
114     * using this loader as a weak key.
115     *
116     * @return properties [can be null]
117     */
118    public static synchronized IProperties getAppProperties ()
119    {
120        final ClassLoader loader = ClassLoaderResolver.getClassLoader ();
121
122        return getAppProperties (loader);
123    }
124
125    public static synchronized IProperties getAppProperties (final ClassLoader loader)
126    {
127        IProperties properties = (IProperties) s_properties.get (loader);
128
129        if (properties != null)
130            return properties;
131        else
132        {
133            final String appName = IAppConstants.APP_NAME_LC;
134
135            // note: this does not use Property.getAppProperties() by design,
136            // because that mechanism is not property alias-capable
137
138            final IProperties systemRedirects = wrap (Property.getSystemPropertyRedirects (EMMAProperties.SYSTEM_PROPERTY_REDIRECTS));
139            final IProperties appDefaults = wrap (Property.getProperties (appName + "_default.properties", loader));
140            final IProperties systemFile;
141            {
142                final String fileName = Property.getSystemProperty (appName + ".properties");
143                final File file = fileName != null
144                    ? new File (fileName)
145                    : null;
146
147                systemFile = wrap (Property.getLazyPropertiesFromFile (file));
148            }
149            final IProperties system = wrap (Property.getSystemProperties (appName));
150            final IProperties userOverrides = wrap (Property.getProperties (appName + ".properties", loader));
151
152            // "vertical" inheritance order:
153            //      (1) user overrides ("emma.properties" classloader resource)
154            //      (2) system properties (java.lang.System.getProperties(),
155            //                             filtered by the app prefix)
156            //      (3) system file properties ("emma.properties" system property,
157            //                                  interpreted as a property file)
158            //      (4) app defaults ("emma_default.properties" classloader resource)
159            //      (5) system property redirects (report.out.encoding->file.encoding,
160            //                                     report.out.dir->user.dir, etc)
161
162            properties = IProperties.Factory.combine (userOverrides,
163                         IProperties.Factory.combine (system,
164                         IProperties.Factory.combine (systemFile,
165                         IProperties.Factory.combine (appDefaults,
166                                                      systemRedirects))));
167
168            s_properties.put (loader, properties);
169
170            return properties;
171        }
172    }
173
174    // protected: .............................................................
175
176    // package: ...............................................................
177
178    // private: ...............................................................
179
180
181    private EMMAProperties () {} // prevent subclassing
182
183
184    private static long s_timestamp;
185
186    private static final Map /* ClassLoader->Properties */ s_properties; // set in <clinit>
187
188    static
189    {
190        s_properties = new WeakHashMap ();
191
192        final Map redirects = new HashMap ();
193        redirects.put (IReportProperties.PREFIX.concat (IReportProperties.OUT_ENCODING),
194                       "file.encoding");
195        redirects.put (IReportProperties.PREFIX.concat (IReportProperties.OUT_DIR),
196                       "user.dir");
197
198        SYSTEM_PROPERTY_REDIRECTS = Collections.unmodifiableMap (redirects);
199    }
200
201} // end of class
202// ----------------------------------------------------------------------------
203