001    /**
002     * Copyright 2009-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.codehaus.mojo.properties;
017    
018    import java.io.File;
019    import java.io.IOException;
020    import java.text.SimpleDateFormat;
021    import java.util.ArrayList;
022    import java.util.Arrays;
023    import java.util.Collections;
024    import java.util.Date;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Properties;
028    import java.util.Set;
029    
030    import org.apache.commons.io.FileUtils;
031    import org.apache.maven.plugin.MojoExecutionException;
032    import org.apache.maven.plugin.MojoFailureException;
033    import org.codehaus.plexus.util.StringUtils;
034    
035    /**
036     * Write project properties to a file.
037     * 
038     * @author Jeff Caddel
039     * 
040     * @goal write-project-properties
041     */
042    public class WriteProjectProperties extends AbstractWritePropertiesMojo {
043        private static final String CR = "\r";
044        private static final String LF = "\n";
045        private static final String TAB = "\t";
046        private static final String[] ANT_ESCAPE_CHARS = { CR, LF, TAB, ":", "#", "=" };
047    
048        /**
049         * Comma separated list of characters to escape when writing property values. cr=carriage return, lf=linefeed,
050         * tab=tab. Any other values are taken literally.
051         * 
052         * @parameter default-value="cr,lf,tab" expression="${properties.escapeChars}"
053         */
054        private String escapeChars;
055    
056        /**
057         * If true, the plugin will create the properties file formatted the same way Ant formats properties files using the
058         * <code>echoproperties</code> task. This mode adds 3 custom properties at the top of the file, DSTAMP, TODAY, and
059         * TSTAMP. In this mode <code>escapeChars</code> is ignored and the 6 characters Ant escapes are used instead
060         * <code>CR</code>,<code>LF</code>,<code>TAB</code>,<code>:</code>,<code>#</code>,<code>=</code>
061         * 
062         * @parameter default-value="false" expression="${properties.antEchoPropertiesMode}"
063         */
064        private boolean antEchoPropertiesMode;
065    
066        /**
067         * If true, the plugin will include system properties when writing the properties file. System properties override
068         * both environment variables and project properties.
069         * 
070         * @parameter default-value="false" expression="${properties.includeSystemProperties}"
071         */
072        private boolean includeSystemProperties;
073    
074        /**
075         * If true, the plugin will include environment variables when writing the properties file. Environment variables
076         * are prefixed with "env". Environment variables override project properties.
077         * 
078         * @parameter default-value="false" expression="${properties.includeEnvironmentVariables}"
079         */
080        private boolean includeEnvironmentVariables;
081    
082        /**
083         * Comma separated set of properties to exclude when writing the properties file
084         * 
085         * @parameter expression="${properties.exclude}"
086         */
087        private String exclude;
088    
089        /**
090         * Comma separated set of properties to write to the properties file. If provided, only the properties matching
091         * those supplied here will be written to the properties file.
092         * 
093         * @parameter expression="${properties.include}"
094         */
095        private String include;
096    
097        @Override
098        public void execute() throws MojoExecutionException, MojoFailureException {
099            Properties properties = new Properties();
100            // Add project properties
101            properties.putAll(project.getProperties());
102            if (includeEnvironmentVariables) {
103                // Add environment variables, overriding any existing properties with the same key
104                properties.putAll(getEnvironmentVariables());
105            }
106            if (includeSystemProperties) {
107                // Add system properties, overriding any existing properties with the same key
108                properties.putAll(System.getProperties());
109            }
110    
111            // Remove properties as appropriate
112            trim(properties, exclude, include);
113    
114            String comment = "# " + new Date() + "\n";
115            List<String> escapeTokens = getEscapeChars(escapeChars);
116            if (antEchoPropertiesMode) {
117                escapeTokens = Arrays.asList(ANT_ESCAPE_CHARS);
118                comment = getAntHeader();
119                properties.remove("DSTAMP");
120                properties.remove("TODAY");
121                properties.remove("TSTAMP");
122            }
123    
124            getLog().info("Creating " + outputFile);
125            writeProperties(outputFile, comment, properties, escapeTokens);
126        }
127    
128        protected static Properties getEnvironmentVariables() {
129            String prefix = "env";
130            Map<String, String> map = System.getenv();
131            Properties props = new Properties();
132            for (String key : map.keySet()) {
133                String newKey = prefix + "." + key;
134                String value = map.get(key);
135                props.setProperty(newKey, value);
136            }
137            return props;
138        }
139    
140        protected void trim(Properties properties, String omitCSV, String includeCSV) {
141            List<String> omitKeys = ReadPropertiesMojo.getListFromCSV(omitCSV);
142            for (String key : omitKeys) {
143                properties.remove(key);
144            }
145            if (StringUtils.isBlank(includeCSV)) {
146                return;
147            }
148            List<String> includeKeys = ReadPropertiesMojo.getListFromCSV(includeCSV);
149            Set<String> keys = properties.stringPropertyNames();
150            for (String key : keys) {
151                if (!includeKeys.contains(key)) {
152                    properties.remove(key);
153                }
154            }
155        }
156    
157        protected String getAntHeader() {
158            SimpleDateFormat dstamp = new SimpleDateFormat("yyyyMMdd");
159            SimpleDateFormat today = new SimpleDateFormat("MMMM d yyyy");
160            SimpleDateFormat tstamp = new SimpleDateFormat("HHmm");
161            Date now = new Date();
162            StringBuilder sb = new StringBuilder();
163            sb.append("# Ant properties\n");
164            sb.append("# " + now + "\n");
165            sb.append("DSTAMP=" + dstamp.format(now) + "\n");
166            sb.append("TODAY=" + today.format(now) + "\n");
167            sb.append("TSTAMP=" + tstamp.format(now) + "\n");
168            return sb.toString();
169        }
170    
171        protected List<String> getEscapeChars(String escapeChars) {
172            List<String> tokens = ReadPropertiesMojo.getListFromCSV(escapeChars);
173            List<String> realTokens = new ArrayList<String>();
174            for (String token : tokens) {
175                String realToken = getRealToken(token);
176                realTokens.add(realToken);
177            }
178            return realTokens;
179        }
180    
181        protected String getRealToken(String token) {
182            if (token.equalsIgnoreCase("CR")) {
183                return CR;
184            } else if (token.equalsIgnoreCase("LF")) {
185                return LF;
186            } else if (token.equalsIgnoreCase("TAB")) {
187                return TAB;
188            } else {
189                return token;
190            }
191        }
192    
193        protected String getContent(String comment, Properties properties, List<String> escapeTokens) {
194            List<String> names = new ArrayList<String>(properties.stringPropertyNames());
195            Collections.sort(names);
196            StringBuilder sb = new StringBuilder();
197            if (!StringUtils.isBlank(comment)) {
198                sb.append(comment);
199            }
200            for (String name : names) {
201                String value = properties.getProperty(name);
202                String escapedValue = escape(value, escapeTokens);
203                sb.append(name + "=" + escapedValue + "\n");
204            }
205            return sb.toString();
206        }
207    
208        protected void writeProperties(File file, String comment, Properties properties, List<String> escapeTokens)
209                throws MojoExecutionException {
210            try {
211                String content = getContent(comment, properties, escapeTokens);
212                FileUtils.writeStringToFile(file, content);
213            } catch (IOException e) {
214                throw new MojoExecutionException("Error creating properties file", e);
215            }
216        }
217    
218        protected String escape(String s, List<String> escapeChars) {
219            for (String escapeChar : escapeChars) {
220                s = s.replace(escapeChar, getReplacementToken(escapeChar));
221            }
222            return s;
223        }
224    
225        protected String getReplacementToken(String escapeChar) {
226            if (escapeChar.equals(CR)) {
227                return "\\r";
228            } else if (escapeChar.equals(LF)) {
229                return "\\n";
230            } else if (escapeChar.equals(TAB)) {
231                return "\\t";
232            } else
233                return "\\" + escapeChar;
234        }
235    
236        public boolean isAntEchoPropertiesMode() {
237            return antEchoPropertiesMode;
238        }
239    
240        public void setAntEchoPropertiesMode(boolean antEchoPropertiesMode) {
241            this.antEchoPropertiesMode = antEchoPropertiesMode;
242        }
243    
244        public boolean isIncludeSystemProperties() {
245            return includeSystemProperties;
246        }
247    
248        public void setIncludeSystemProperties(boolean includeSystemProperties) {
249            this.includeSystemProperties = includeSystemProperties;
250        }
251    
252        public String getEscapeChars() {
253            return escapeChars;
254        }
255    
256        public void setEscapeChars(String escapeChars) {
257            this.escapeChars = escapeChars;
258        }
259    
260        public boolean isIncludeEnvironmentVariables() {
261            return includeEnvironmentVariables;
262        }
263    
264        public void setIncludeEnvironmentVariables(boolean includeEnvironmentVariables) {
265            this.includeEnvironmentVariables = includeEnvironmentVariables;
266        }
267    
268        public String getExclude() {
269            return exclude;
270        }
271    
272        public void setExclude(String exclude) {
273            this.exclude = exclude;
274        }
275    
276        public String getInclude() {
277            return include;
278        }
279    
280        public void setInclude(String include) {
281            this.include = include;
282        }
283    }