001: /*******************************************************************************
002: * Copyright (c) 2000, 2005 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.ui.macbundler;
011:
012: import java.io.*;
013: import java.util.*;
014: import java.util.List;
015:
016: import javax.xml.parsers.*;
017: import javax.xml.parsers.DocumentBuilder;
018: import javax.xml.transform.*;
019: import javax.xml.transform.Transformer;
020: import javax.xml.transform.dom.DOMSource;
021: import javax.xml.transform.stream.StreamResult;
022:
023: import org.w3c.dom.*;
024: import org.w3c.dom.Document;
025:
026: import org.eclipse.core.runtime.IProgressMonitor;
027:
028: public class BundleBuilder implements BundleAttributes {
029:
030: private List fProcesses = new ArrayList();
031: private BundleDescription fBundleDescription;
032:
033: public void createBundle(BundleDescription bd, IProgressMonitor pm)
034: throws IOException {
035:
036: fBundleDescription = bd;
037:
038: File tmp_dir = new File(bd.get(DESTINATIONDIRECTORY));
039: String app_dir_name = bd.get(APPNAME) + ".app"; //$NON-NLS-1$
040: File app_dir = new File(tmp_dir, app_dir_name);
041: if (app_dir.exists())
042: deleteDir(app_dir);
043: app_dir = createDir(tmp_dir, app_dir_name, false);
044:
045: File contents_dir = createDir(app_dir, "Contents", false); //$NON-NLS-1$
046: createPkgInfo(contents_dir);
047:
048: File macos_dir = createDir(contents_dir, "MacOS", false); //$NON-NLS-1$
049: String launcher_path = bd.get(LAUNCHER);
050: if (launcher_path == null)
051: throw new IOException();
052: String launcher = copyFile(macos_dir, launcher_path, null);
053:
054: File resources_dir = createDir(contents_dir, "Resources", false); //$NON-NLS-1$
055: File java_dir = createDir(resources_dir, "Java", false); //$NON-NLS-1$
056:
057: createInfoPList(contents_dir, resources_dir, java_dir, launcher);
058:
059: Iterator iter = fProcesses.iterator();
060: while (iter.hasNext()) {
061: Process p = (Process) iter.next();
062: try {
063: p.waitFor();
064: } catch (InterruptedException e) {
065: // silently ignore
066: }
067: }
068: }
069:
070: private void createInfoPList(File contents_dir, File resources_dir,
071: File java_dir, String launcher) throws IOException {
072:
073: File info = new File(contents_dir, "Info.plist"); //$NON-NLS-1$
074: FileOutputStream fos = new FileOutputStream(info);
075: BufferedOutputStream fOutputStream = new BufferedOutputStream(
076: fos);
077:
078: DocumentBuilder docBuilder = null;
079: DocumentBuilderFactory factory = DocumentBuilderFactory
080: .newInstance();
081: factory.setValidating(false);
082: try {
083: docBuilder = factory.newDocumentBuilder();
084: } catch (ParserConfigurationException ex) {
085: System.err
086: .println("createInfoPList: could not get XML builder"); //$NON-NLS-1$
087: }
088: Document doc = docBuilder.newDocument();
089:
090: Element plist = doc.createElement("plist"); //$NON-NLS-1$
091: doc.appendChild(plist);
092: plist.setAttribute("version", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$
093:
094: Element dict = doc.createElement("dict"); //$NON-NLS-1$
095: plist.appendChild(dict);
096:
097: pair(dict, "CFBundleExecutable", null, launcher); //$NON-NLS-1$
098: pair(dict, "CFBundleGetInfoString", GETINFO, null); //$NON-NLS-1$
099: pair(dict, "CFBundleInfoDictionaryVersion", null, "6.0"); //$NON-NLS-1$ //$NON-NLS-2$
100:
101: String iconName = null;
102: String appName = fBundleDescription.get(APPNAME, null);
103: if (appName != null)
104: iconName = appName + ".icns"; //$NON-NLS-1$
105: String fname = copyFile(resources_dir, fBundleDescription.get(
106: ICONFILE, null), iconName);
107: if (fname != null)
108: pair(dict, "CFBundleIconFile", null, fname); //$NON-NLS-1$
109:
110: pair(dict, "CFBundleIdentifier", IDENTIFIER, null); //$NON-NLS-1$
111: pair(dict, "CFBundleName", APPNAME, null); //$NON-NLS-1$
112: pair(dict, "CFBundlePackageType", null, "APPL"); //$NON-NLS-1$ //$NON-NLS-2$
113: pair(dict, "CFBundleShortVersionString", VERSION, null); //$NON-NLS-1$
114: pair(dict, "CFBundleSignature", SIGNATURE, "????"); //$NON-NLS-1$ //$NON-NLS-2$
115: pair(dict, "CFBundleVersion", null, "1.0.1"); //$NON-NLS-1$ //$NON-NLS-2$
116:
117: Element jdict = doc.createElement("dict"); //$NON-NLS-1$
118: add(dict, "Java", jdict); //$NON-NLS-1$
119:
120: pair(jdict, "JVMVersion", JVMVERSION, null); //$NON-NLS-1$
121: pair(jdict, "MainClass", MAINCLASS, null); //$NON-NLS-1$
122: pair(jdict, "WorkingDirectory", WORKINGDIR, null); //$NON-NLS-1$
123:
124: if (fBundleDescription.get(USES_SWT, false))
125: addTrue(jdict, "StartOnMainThread"); //$NON-NLS-1$
126:
127: String arguments = fBundleDescription.get(ARGUMENTS, null);
128: if (arguments != null) {
129: Element argArray = doc.createElement("array"); //$NON-NLS-1$
130: add(jdict, "Arguments", argArray); //$NON-NLS-1$
131: StringTokenizer st = new StringTokenizer(arguments);
132: while (st.hasMoreTokens()) {
133: String arg = st.nextToken();
134: Element type = doc.createElement("string"); //$NON-NLS-1$
135: argArray.appendChild(type);
136: type.appendChild(doc.createTextNode(arg));
137: }
138: }
139:
140: pair(jdict, "VMOptions", VMOPTIONS, null); //$NON-NLS-1$
141:
142: int[] id = new int[] { 0 };
143: ResourceInfo[] ris = fBundleDescription.getResources(true);
144: if (ris.length > 0) {
145: StringBuffer cp = new StringBuffer();
146: for (int i = 0; i < ris.length; i++) {
147: ResourceInfo ri = ris[i];
148: String e = processClasspathEntry(java_dir, ri.fPath, id);
149: if (cp.length() > 0)
150: cp.append(':');
151: cp.append(e);
152: }
153: add(jdict, "ClassPath", cp.toString()); //$NON-NLS-1$
154: }
155:
156: ris = fBundleDescription.getResources(false);
157: if (ris.length > 0) {
158: for (int i = 0; i < ris.length; i++) {
159: ResourceInfo ri = ris[i];
160: processClasspathEntry(java_dir, ri.fPath, id);
161: }
162: }
163:
164: try {
165: // Write the document to the stream
166: Transformer transformer = TransformerFactory.newInstance()
167: .newTransformer();
168: transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,
169: "-//Apple Computer//DTD PLIST 1.0//EN"); //$NON-NLS-1$
170: transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
171: "http://www.apple.com/DTDs/PropertyList-1.0.dtd"); //$NON-NLS-1$
172: transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
173: transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$
174: transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
175: transformer.setOutputProperty(
176: "{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$
177: DOMSource source = new DOMSource(doc);
178: StreamResult result = new StreamResult(fOutputStream);
179: transformer.transform(source, result);
180: } catch (TransformerException e) {
181: System.err
182: .println("createInfoPList: could not transform to XML"); //$NON-NLS-1$
183: }
184: }
185:
186: private void add(Element dict, String key, Element value) {
187: Document document = dict.getOwnerDocument();
188: Element k = document.createElement("key"); //$NON-NLS-1$
189: dict.appendChild(k);
190: k.appendChild(document.createTextNode(key));
191: dict.appendChild(value);
192: }
193:
194: private void create(Element parent, String s) {
195: Document document = parent.getOwnerDocument();
196: Element type = document.createElement("string"); //$NON-NLS-1$
197: parent.appendChild(type);
198: type.appendChild(document.createTextNode(s));
199: }
200:
201: private void createTrue(Element parent) {
202: Document document = parent.getOwnerDocument();
203: Element type = document.createElement("true"); //$NON-NLS-1$
204: parent.appendChild(type);
205: }
206:
207: private void add(Element dict, String key, String value) {
208: Document document = dict.getOwnerDocument();
209: Element k = document.createElement("key"); //$NON-NLS-1$
210: dict.appendChild(k);
211: k.appendChild(document.createTextNode(key));
212: create(dict, value);
213: }
214:
215: private void addTrue(Element dict, String key) {
216: Document document = dict.getOwnerDocument();
217: Element k = document.createElement("key"); //$NON-NLS-1$
218: dict.appendChild(k);
219: k.appendChild(document.createTextNode(key));
220: createTrue(dict);
221: }
222:
223: private void pair(Element dict, String outkey, String inkey,
224: String dflt) {
225: String value = null;
226: if (inkey != null)
227: value = fBundleDescription.get(inkey, dflt);
228: else
229: value = dflt;
230: if (value != null && value.trim().length() > 0) {
231: add(dict, outkey, value);
232: }
233: }
234:
235: private String processClasspathEntry(File java_dir, String name,
236: int[] id_ref) throws IOException {
237: File f = new File(name);
238: if (f.isDirectory()) {
239: int id = id_ref[0]++;
240: String archivename = "jar_" + id + ".jar"; //$NON-NLS-1$ //$NON-NLS-2$
241: File to = new File(java_dir, archivename);
242: zip(name, to.getAbsolutePath());
243: name = archivename;
244: } else {
245: name = copyFile(java_dir, name, null);
246: }
247: return "$JAVAROOT/" + name; //$NON-NLS-1$
248: }
249:
250: private void createPkgInfo(File contents_dir) throws IOException {
251: File pkgInfo = new File(contents_dir, "PkgInfo"); //$NON-NLS-1$
252: FileOutputStream os = new FileOutputStream(pkgInfo);
253: os
254: .write(("APPL" + fBundleDescription.get(SIGNATURE, "????")).getBytes()); //$NON-NLS-1$ //$NON-NLS-2$
255: os.close();
256: }
257:
258: private static void deleteDir(File dir) {
259: File[] files = dir.listFiles();
260: if (files != null) {
261: for (int i = 0; i < files.length; i++)
262: deleteDir(files[i]);
263: }
264: dir.delete();
265: }
266:
267: private File createDir(File parent_dir, String dir_name,
268: boolean remove) throws IOException {
269: File dir = new File(parent_dir, dir_name);
270: if (dir.exists()) {
271: if (!remove)
272: return dir;
273: deleteDir(dir);
274: }
275: if (!dir.mkdir())
276: throw new IOException("cannot create dir " + dir_name); //$NON-NLS-1$
277: return dir;
278: }
279:
280: private String copyFile(File todir, String fromPath, String toname)
281: throws IOException {
282: if (toname == null) {
283: int pos = fromPath.lastIndexOf('/');
284: if (pos >= 0)
285: toname = fromPath.substring(pos + 1);
286: else
287: toname = fromPath;
288: }
289: File to = new File(todir, toname);
290: fProcesses.add(Runtime.getRuntime().exec(
291: new String[] {
292: "/bin/cp", fromPath, to.getAbsolutePath() })); //$NON-NLS-1$
293: return toname;
294: }
295:
296: private void zip(String dir, String dest) throws IOException {
297: fProcesses.add(Runtime.getRuntime().exec(
298: new String[] {
299: "/usr/bin/jar", "cf", dest, "-C", dir, "." })); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
300: }
301: }
|