1 /*
2 	Launch4j (http://launch4j.sourceforge.net/)
3 	Cross-platform Java application wrapper for creating Windows native executables.
4 
5 	Copyright (c) 2004, 2015 Grzegorz Kowal
6 	All rights reserved.
7 
8 	Redistribution and use in source and binary forms, with or without modification,
9 	are permitted provided that the following conditions are met:
10 
11 	1. Redistributions of source code must retain the above copyright notice,
12 	   this list of conditions and the following disclaimer.
13 
14 	2. Redistributions in binary form must reproduce the above copyright notice,
15 	   this list of conditions and the following disclaimer in the documentation
16 	   and/or other materials provided with the distribution.
17 
18 	3. Neither the name of the copyright holder nor the names of its contributors
19 	   may be used to endorse or promote products derived from this software without
20 	   specific prior written permission.
21 
22 	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24 	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26 	FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 	AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 	OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 
34 /*
35  * Created on Apr 22, 2005
36  */
37 package net.sf.launch4j.config;
38 
39 import java.io.BufferedWriter;
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.FileOutputStream;
43 import java.io.IOException;
44 import java.io.OutputStreamWriter;
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.Properties;
48 
49 import javax.xml.parsers.DocumentBuilder;
50 import javax.xml.parsers.DocumentBuilderFactory;
51 
52 import org.w3c.dom.Document;
53 import org.w3c.dom.ls.DOMImplementationLS;
54 import org.w3c.dom.ls.LSSerializer;
55 
56 import net.sf.launch4j.Util;
57 
58 import com.thoughtworks.xstream.XStream;
59 import com.thoughtworks.xstream.io.xml.DomDriver;
60 
61 /**
62  * @author Copyright (C) 2014 Grzegorz Kowal
63  */
64 public class ConfigPersister {
65 
66 	private static final ConfigPersister _instance = new ConfigPersister();
67 
68 	private final XStream _xstream;
69 	private Config _config;
70 	private File _configPath;
71 
ConfigPersister()72 	private ConfigPersister() {
73 		_xstream = new XStream(new DomDriver());
74     	_xstream.alias("launch4jConfig", Config.class);
75     	_xstream.alias("classPath", ClassPath.class);
76     	_xstream.alias("jre", Jre.class);
77     	_xstream.alias("splash", Splash.class);
78     	_xstream.alias("versionInfo", VersionInfo.class);
79 
80     	_xstream.addImplicitCollection(Config.class, "headerObjects", "obj",
81     			String.class);
82     	_xstream.addImplicitCollection(Config.class, "libs", "lib", String.class);
83     	_xstream.addImplicitCollection(Config.class, "variables", "var", String.class);
84     	_xstream.addImplicitCollection(ClassPath.class, "paths", "cp", String.class);
85     	_xstream.addImplicitCollection(Jre.class, "options", "opt", String.class);
86 	}
87 
getInstance()88 	public static ConfigPersister getInstance() {
89 		return _instance;
90 	}
91 
getConfig()92 	public Config getConfig() {
93 		return _config;
94 	}
95 
getConfigPath()96 	public File getConfigPath() {
97 		return _configPath;
98 	}
99 
getOutputPath()100 	public File getOutputPath() throws IOException {
101 		if (_config.getOutfile().isAbsolute()) {
102 			return _config.getOutfile().getParentFile();
103 		}
104 		File parent = _config.getOutfile().getParentFile();
105 		return (parent != null) ? new File(_configPath, parent.getPath()) : _configPath;
106 	}
107 
getOutputFile()108 	public File getOutputFile() throws IOException {
109 		return _config.getOutfile().isAbsolute()
110 			? _config.getOutfile()
111 			: new File(getOutputPath(), _config.getOutfile().getName());
112 	}
113 
createBlank()114 	public void createBlank() {
115 		_config = new Config();
116 		_config.setJre(new Jre());
117 		_configPath = null;
118 	}
119 
setAntConfig(Config c, File basedir)120 	public void setAntConfig(Config c, File basedir) {
121 		_config = c;
122 		_configPath = basedir;
123 	}
124 
load(File f)125 	public void load(File f) throws ConfigPersisterException {
126 	    try {
127 			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
128 			DocumentBuilder builder = factory.newDocumentBuilder();
129 			Document doc = builder.parse(f);
130 
131 			DOMImplementationLS domImplementation = (DOMImplementationLS) doc.getImplementation();
132 		    LSSerializer lsSerializer = domImplementation.createLSSerializer();
133 		    String configString = lsSerializer.writeToString(doc);
134 
135 	    	_config = (Config) _xstream.fromXML(convertToCurrent(configString));
136 	    	setConfigPath(f);
137 		} catch (Exception e) {
138 			throw new ConfigPersisterException(e);
139 		}
140 	}
141 
142 	/**
143 	 * Imports launch4j 1.x.x config file.
144 	 */
loadVersion1(File f)145 	public void loadVersion1(File f) throws ConfigPersisterException {
146 		try {
147 			Props props = new Props(f);
148 			_config = new Config();
149 			String header = props.getProperty(Config.HEADER);
150 			_config.setHeaderType(header == null
151 					|| header.toLowerCase().equals("guihead.bin") ? Config.GUI_HEADER
152 															: Config.CONSOLE_HEADER);
153 			_config.setJar(props.getFile(Config.JAR));
154 			_config.setOutfile(props.getFile(Config.OUTFILE));
155 			_config.setJre(new Jre());
156 			_config.getJre().setPath(props.getProperty(Jre.PATH));
157 			_config.getJre().setMinVersion(props.getProperty(Jre.MIN_VERSION));
158 			_config.getJre().setMaxVersion(props.getProperty(Jre.MAX_VERSION));
159 
160 			String args = props.getProperty(Jre.ARGS);
161 
162 			if (args != null) {
163 				List<String> jreOptions = new ArrayList<String>();
164 				jreOptions.add(args);
165 				_config.getJre().setOptions(jreOptions);
166 			}
167 
168 			_config.setCmdLine(props.getProperty(Config.JAR_ARGS));
169 			_config.setChdir("true".equals(props.getProperty(Config.CHDIR))
170 					? "." : null);
171 			_config.setStayAlive("true".equals(props.getProperty(Config.STAY_ALIVE)));
172 			_config.setErrTitle(props.getProperty(Config.ERR_TITLE));
173 			_config.setIcon(props.getFile(Config.ICON));
174 
175 			File splashFile = props.getFile(Splash.SPLASH_FILE);
176 
177 			if (splashFile != null) {
178 				_config.setSplash(new Splash());
179 				_config.getSplash().setFile(splashFile);
180 				String waitfor = props.getProperty("waitfor");		// 1.x
181 				_config.getSplash().setWaitForWindow(waitfor != null
182 													&& !waitfor.equals(""));
183 				String splashTimeout = props.getProperty(Splash.TIMEOUT);
184 				if (splashTimeout != null) {
185 					_config.getSplash().setTimeout(Integer.parseInt(splashTimeout));
186 				}
187 				_config.getSplash().setTimeoutErr("true".equals(
188 						props.getProperty(Splash.TIMEOUT_ERR)));
189 			} else {
190 				_config.setSplash(null);
191 			}
192 			setConfigPath(f);
193 		} catch (IOException e) {
194 			throw new ConfigPersisterException(e);
195 		}
196 	}
197 
save(File f)198 	public void save(File f) throws ConfigPersisterException {
199 		try {
200 			BufferedWriter w = new BufferedWriter(
201 					new OutputStreamWriter(new FileOutputStream(f), "UTF-8"));
202 			w.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
203 	    	_xstream.toXML(_config, w);
204 	    	w.close();
205 	    	setConfigPath(f);
206 		} catch (Exception e) {
207 			throw new ConfigPersisterException(e);
208 		}
209 	}
210 
211 	/**
212 	 * Converts 2.x config to current format.
213 	 */
convertToCurrent(String configString)214 	private String convertToCurrent(String configString) {
215     	return configString
216     			.replaceAll("<headerType>0<", "<headerType>gui<")
217     			.replaceAll("<headerType>1<", "<headerType>console<")
218     			.replaceAll("jarArgs>", "cmdLine>")
219     			.replaceAll("<jarArgs[ ]*/>", "<cmdLine/>")
220     			.replaceAll("args>", "opt>")
221     			.replaceAll("<args[ ]*/>", "<opt/>")
222     			.replaceAll("<dontUsePrivateJres>false</dontUsePrivateJres>",
223     					"<jdkPreference>" + Jre.JDK_PREFERENCE_PREFER_JRE + "</jdkPreference>")
224     			.replaceAll("<dontUsePrivateJres>true</dontUsePrivateJres>",
225     					"<jdkPreference>" + Jre.JDK_PREFERENCE_JRE_ONLY + "</jdkPreference>")
226     			.replaceAll("<initialHeapSize>0</initialHeapSize>", "")
227     			.replaceAll("<maxHeapSize>0</maxHeapSize>", "")
228     			.replaceAll("<customProcName>.*</customProcName>", "");
229 	}
230 
setConfigPath(File configFile)231 	private void setConfigPath(File configFile) {
232 		_configPath = configFile.getAbsoluteFile().getParentFile();
233 	}
234 
235 	private class Props {
236 		final Properties _properties = new Properties();
237 
Props(File f)238 		public Props(File f) throws IOException {
239 			FileInputStream is = null;
240 			try {
241 				is = new FileInputStream(f);
242 				_properties.load(is);
243 			} finally {
244 				Util.close(is);
245 			}
246 		}
247 
248 		/**
249 		 * Get property and remove trailing # comments.
250 		 */
getProperty(String key)251 		public String getProperty(String key) {
252 			String p = _properties.getProperty(key);
253 
254 			if (p == null) {
255 				return null;
256 			}
257 
258 			int x = p.indexOf('#');
259 
260 			if (x == -1) {
261 				return p;
262 			}
263 
264 			do {
265 				x--;
266 			} while (x > 0 && (p.charAt(x) == ' ' || p.charAt(x) == '\t'));
267 
268 			return (x == 0) ? "" : p.substring(0, x + 1);
269 		}
270 
getFile(String key)271 		public File getFile(String key) {
272 			String value = getProperty(key);
273 			return value != null ? new File(value) : null;
274 		}
275 	}
276 }
277