001: /*******************************************************************************
002: * Copyright (c) 2005, 2007 IBM Corporation and others. All rights reserved.
003: * This program and the accompanying materials are made available under the
004: * terms of the Eclipse Public License v1.0 which accompanies this distribution,
005: * and is available at http://www.eclipse.org/legal/epl-v10.html
006: *
007: * Contributors:
008: * IBM Corporation - initial API and implementation
009: * G&H Softwareentwicklung GmbH - internationalization implementation (bug 150933)
010: * Michael Seele - remove offline-allowed (bug 153403)
011: ******************************************************************************/package org.eclipse.pde.internal.build.tasks;
012:
013: import java.io.*;
014: import java.util.*;
015: import java.util.zip.ZipEntry;
016: import java.util.zip.ZipFile;
017: import javax.xml.parsers.*;
018: import org.xml.sax.*;
019: import org.xml.sax.helpers.DefaultHandler;
020:
021: /**
022: *
023: * @since 3.1
024: */
025: public class JNLPGenerator extends DefaultHandler {
026:
027: private SAXParser parser;
028: private File featureRoot;
029:
030: private String codebase;
031: private String j2se;
032:
033: /**
034: * id = ???
035: * version = jnlp.version
036: * label = information.title
037: * provider-name = information.vendor
038: * image = information.icon
039: * feature.description = information.description
040: * feature.includes = extension
041: * feature.plugin = jar
042: */
043: private final static SAXParserFactory parserFactory = SAXParserFactory
044: .newInstance();
045: private PrintWriter out;
046: private String destination;
047: private String provider;
048: private String label;
049: private String version;
050: private String id;
051: private String description;
052: private boolean resourceWritten = false;
053: private String currentOS = null;
054: private String currentArch = null;
055: private Locale locale = null;
056: private PropertyResourceBundle nlsBundle = null;
057: private boolean generateOfflineAllowed;
058: private Config[] configs;
059:
060: /**
061: * For testing purposes only.
062: */
063: public static void main(String[] args) {
064: JNLPGenerator generator = new JNLPGenerator(args[0], args[1],
065: args[2], args[3]);
066: generator.process();
067: }
068:
069: /**
070: * Constructs a feature parser.
071: */
072: public JNLPGenerator(String feature, String destination,
073: String codebase, String j2se) {
074: this (feature, destination, codebase, j2se, Locale.getDefault(),
075: true, null);
076: }
077:
078: /**
079: * Constructs a feature parser.
080: */
081: public JNLPGenerator(String feature, String destination,
082: String codebase, String j2se, Locale locale,
083: boolean generateOfflineAllowed, String configs) {
084: super ();
085: this .featureRoot = new File(feature);
086: this .destination = destination;
087: this .codebase = codebase;
088: this .j2se = j2se;
089: this .locale = locale;
090: this .generateOfflineAllowed = generateOfflineAllowed;
091: try {
092: parserFactory.setNamespaceAware(true);
093: parser = parserFactory.newSAXParser();
094: } catch (ParserConfigurationException e) {
095: System.out.println(e);
096: } catch (SAXException e) {
097: System.out.println(e);
098: }
099: setConfigInfo(configs);
100: }
101:
102: /**
103: * Parses the specified url and constructs a feature
104: */
105: public void process() {
106: InputStream in = null;
107: final String FEATURE_XML = "feature.xml"; //$NON-NLS-1$
108:
109: try {
110: ZipFile featureArchive = null;
111: InputStream nlsStream = null;
112: if (featureRoot.isFile()) {
113: featureArchive = new ZipFile(featureRoot);
114: nlsStream = getNLSStream(featureArchive);
115: ZipEntry featureXML = featureArchive
116: .getEntry(FEATURE_XML);
117: in = featureArchive.getInputStream(featureXML);
118: } else {
119: nlsStream = getNLSStream(this .featureRoot);
120: in = new BufferedInputStream(new FileInputStream(
121: new File(featureRoot, FEATURE_XML)));
122: }
123: try {
124: if (nlsStream != null) {
125: nlsBundle = new PropertyResourceBundle(nlsStream);
126: nlsStream.close();
127: }
128: } catch (IOException e) {
129: // do nothing
130: }
131: try {
132: parser.parse(new InputSource(in), this );
133: writeResourceEpilogue();
134: writeEpilogue();
135: } catch (SAXException e) {
136: //Ignore the exception
137: } finally {
138: in.close();
139: if (out != null)
140: out.close();
141: if (featureArchive != null)
142: featureArchive.close();
143: }
144: } catch (IOException e) {
145: //Ignore the exception
146: }
147: }
148:
149: /**
150: * Search for nls properties files and return the stream if files are found.
151: * First try to load the default properties file, then one with the default
152: * locale settings and if nothing matches, return the stream of the first
153: * properties file found.
154: */
155: private InputStream getNLSStream(File root) {
156: String appendix = ".properties"; //$NON-NLS-1$
157: String[] potentials = createNLSPotentials();
158:
159: Map validEntries = new HashMap();
160: File[] files = root.listFiles();
161: for (int i = 0; i < files.length; i++) {
162: String filename = files[i].getName();
163: if (filename.endsWith(appendix)) {
164: validEntries.put(filename, files[i]);
165: }
166: }
167: InputStream stream = null;
168: if (validEntries.size() > 0) {
169: for (int i = 0; i < potentials.length; i++) {
170: File file = (File) validEntries.get(potentials[i]);
171: if (file != null) {
172: try {
173: stream = new BufferedInputStream(
174: new FileInputStream(file));
175: break;
176: } catch (IOException e) {
177: // do nothing
178: }
179: }
180: }
181: if (stream == null) {
182: File file = (File) validEntries.values().iterator()
183: .next();
184: try {
185: stream = new BufferedInputStream(
186: new FileInputStream(file));
187: } catch (IOException e) {
188: // do nothing
189: }
190: }
191: }
192: return stream;
193: }
194:
195: /**
196: * Search for nls properties files and return the stream if files are found.
197: * First try to load the default properties file, then one with the default
198: * locale settings and if nothing matches, return the stream of the first
199: * founded properties file.
200: */
201: private InputStream getNLSStream(ZipFile featureArchive) {
202: String appendix = ".properties"; //$NON-NLS-1$
203: String[] potentials = createNLSPotentials();
204:
205: Map validEntries = new HashMap();
206: for (Enumeration enumeration = featureArchive.entries(); enumeration
207: .hasMoreElements();) {
208: ZipEntry entry = (ZipEntry) enumeration.nextElement();
209: String entryName = entry.getName();
210: if (entryName.endsWith(appendix)) {
211: validEntries.put(entryName, entry);
212: }
213: }
214: InputStream stream = null;
215: if (validEntries.size() > 0) {
216: for (int i = 0; i < potentials.length; i++) {
217: ZipEntry entry = (ZipEntry) validEntries
218: .get(potentials[i]);
219: if (entry != null) {
220: try {
221: stream = featureArchive.getInputStream(entry);
222: break;
223: } catch (IOException e) {
224: // do nothing
225: }
226: }
227: }
228: if (stream == null) {
229: ZipEntry entry = (ZipEntry) validEntries.values()
230: .iterator().next();
231: try {
232: stream = featureArchive.getInputStream(entry);
233: } catch (IOException e) {
234: // do nothing
235: }
236: }
237: }
238: return stream;
239: }
240:
241: private String[] createNLSPotentials() {
242: String suffix = "feature"; //$NON-NLS-1$
243: String appendix = ".properties"; //$NON-NLS-1$
244:
245: String language = locale.getLanguage();
246: String country = locale.getCountry();
247: String variant = locale.getVariant();
248:
249: String potential1 = '_' + language + '_' + country + '_'
250: + variant;
251: String potential2 = '_' + language + '_' + country;
252: String potential3 = '_' + language;
253: String potential4 = ""; //$NON-NLS-1$
254:
255: String[] potentials = new String[] { potential1, potential2,
256: potential3, potential4 };
257: for (int i = 0; i < potentials.length; i++) {
258: potentials[i] = suffix + potentials[i] + appendix;
259: }
260: return potentials;
261: }
262:
263: public void startElement(String uri, String localName,
264: String qName, Attributes attributes) throws SAXException {
265: try {
266: if ("feature".equals(localName)) { //$NON-NLS-1$
267: processFeature(attributes);
268: } else if ("includes".equals(localName)) { //$NON-NLS-1$
269: processIncludes(attributes);
270: } else if ("description".equals(localName)) { //$NON-NLS-1$
271: processDescription(attributes);
272: } else if ("plugin".equals(localName)) { //$NON-NLS-1$
273: processPlugin(attributes);
274: }
275: } catch (IOException e) {
276: throw new SAXException(e);
277: }
278: }
279:
280: private void processPlugin(Attributes attributes)
281: throws IOException {
282: writePrologue();
283: String pluginId = attributes.getValue("id"); //$NON-NLS-1$
284: String pluginVersion = attributes.getValue("version"); //$NON-NLS-1$
285: String os = attributes.getValue("os"); //$NON-NLS-1$
286: String ws = attributes.getValue("ws"); //$NON-NLS-1$
287: String arch = attributes.getValue("arch"); //$NON-NLS-1$
288: if (isValidEnvironment(os, ws, arch)) {
289: writeResourcePrologue(os, ws, arch);
290: out
291: .println("\t\t<jar href=\"plugins/" + pluginId + "_" + pluginVersion + ".jar\"/>"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
292: }
293: }
294:
295: private void writeResourceEpilogue() {
296: if (!resourceWritten)
297: return;
298: out.println("\t</resources>"); //$NON-NLS-1$
299: resourceWritten = false;
300: currentOS = null;
301: }
302:
303: private void writeResourcePrologue(String os, String ws, String arch) {
304: if (os == null)
305: os = ws;
306: os = convertOS(os);
307: arch = convertArch(arch);
308: if (resourceWritten && osMatch(os) && archMatch(arch))
309: return;
310: if (resourceWritten)
311: writeResourceEpilogue();
312: out
313: .println("\t<resources" + (os == null ? "" : " os=\"" + os + "\"") + (arch == null ? "" : " arch=\"" + arch + "\"") + ">"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$//$NON-NLS-7$ //$NON-NLS-8$
314: resourceWritten = true;
315: currentOS = os;
316: currentArch = arch;
317: }
318:
319: private String convertOS(String os) {
320: if (os == null)
321: return null;
322: if ("win32".equalsIgnoreCase(os)) //$NON-NLS-1$
323: return "Windows"; //$NON-NLS-1$
324: if ("macosx".equalsIgnoreCase(os)) //$NON-NLS-1$
325: return "Mac"; //$NON-NLS-1$
326: if ("linux".equalsIgnoreCase(os)) //$NON-NLS-1$
327: return "Linux"; //$NON-NLS-1$
328: if ("solaris".equalsIgnoreCase(os)) //$NON-NLS-1$
329: return "Solaris"; //$NON-NLS-1$
330: if ("hpux".equalsIgnoreCase(os)) //$NON-NLS-1$
331: return "HP-UX"; //$NON-NLS-1$
332: if ("aix".equalsIgnoreCase(os)) //$NON-NLS-1$
333: return "AIX"; //$NON-NLS-1$
334: return os;
335: }
336:
337: private boolean osMatch(String os) {
338: if (os == currentOS)
339: return true;
340: if (os == null)
341: return false;
342: return os.equals(currentOS);
343: }
344:
345: private String convertArch(String arch) {
346: if (arch == null)
347: return null;
348:
349: if ("x86".equals(arch)) //$NON-NLS-1$
350: return "x86"; //$NON-NLS-1$
351:
352: if ("PA_RISC".equals(arch)) //$NON-NLS-1$
353: return "PA_RISC"; //$NON-NLS-1$
354:
355: if ("ppc".equals(arch)) //$NON-NLS-1$
356: return "ppc"; //$NON-NLS-1$
357:
358: if ("sparc".equals(arch)) //$NON-NLS-1$
359: return "sparc"; //$NON-NLS-1$
360:
361: if ("x86_64".equals(arch))//$NON-NLS-1$
362: return "x86_64"; //$NON-NLS-1$
363:
364: if ("ia64".equals(arch)) //$NON-NLS-1$
365: return "ia64"; //$NON-NLS-1$
366:
367: if ("ia64_32".equals(arch)) //$NON-NLS-1$
368: return "ia64_32"; //$NON-NLS-1$
369:
370: return arch;
371: }
372:
373: private boolean archMatch(String arch) {
374: if (arch == currentOS)
375: return true;
376: if (arch == null)
377: return false;
378: return arch.equals(currentArch);
379: }
380:
381: private void processDescription(Attributes attributes) {
382: }
383:
384: private void processIncludes(Attributes attributes)
385: throws IOException {
386: writePrologue();
387: String inclusionId = attributes.getValue("id"); //$NON-NLS-1$
388: String inclusionVersion = attributes.getValue("version"); //$NON-NLS-1$
389: String name = attributes.getValue("name"); //$NON-NLS-1$
390: String os = attributes.getValue("os"); //$NON-NLS-1$
391: String ws = attributes.getValue("ws"); //$NON-NLS-1$
392: String arch = attributes.getValue("arch"); //$NON-NLS-1$
393: if (isValidEnvironment(os, ws, arch)) {
394: writeResourcePrologue(os, ws, arch);
395: out.print("\t\t<extension ");//$NON-NLS-1$
396: if (name != null)
397: out.print("name=\"" + name + "\" "); //$NON-NLS-1$ //$NON-NLS-2$
398: if (inclusionId != null) {
399: out.print("href=\"features/" + inclusionId); //$NON-NLS-1$
400: if (inclusionVersion != null)
401: out.print('_' + inclusionVersion);
402: out.print(".jnlp\" "); //$NON-NLS-1$
403: }
404: out.println("/>"); //$NON-NLS-1$
405: }
406: }
407:
408: private void processFeature(Attributes attributes) {
409: id = attributes.getValue("id"); //$NON-NLS-1$
410: version = attributes.getValue("version"); //$NON-NLS-1$
411: label = processNLS(attributes.getValue("label")); //$NON-NLS-1$
412: provider = processNLS(attributes.getValue("provider-name")); //$NON-NLS-1$
413: }
414:
415: /**
416: * Search for a human readable string in the feature.properties file(s) if
417: * the given string is a translateable key.
418: *
419: * @param string a translateable key or a normal string(nothing is done)
420: *
421: * @return a translateabled string or the given string if it is not a
422: * translateable key
423: */
424: private String processNLS(String string) {
425: if (string == null)
426: return null;
427: string = string.trim();
428: if (!string.startsWith("%")) { //$NON-NLS-1$
429: return string;
430: }
431: if (string.startsWith("%%")) { //$NON-NLS-1$
432: return string.substring(1);
433: }
434: int index = string.indexOf(" "); //$NON-NLS-1$
435: String key = index == -1 ? string : string.substring(0, index);
436: String dflt = index == -1 ? string : string
437: .substring(index + 1);
438: if (nlsBundle == null) {
439: return dflt;
440: }
441: try {
442: return nlsBundle.getString(key.substring(1));
443: } catch (MissingResourceException e) {
444: return dflt;
445: }
446: }
447:
448: private void writePrologue() throws IOException {
449: if (out != null)
450: return;
451: if (destination == null) {
452: featureRoot.getParentFile();
453: destination = featureRoot.getParent() + '/';
454: }
455: if (destination.endsWith("/") || destination.endsWith("\\")) //$NON-NLS-1$ //$NON-NLS-2$
456: destination = new File(featureRoot.getParentFile(), id
457: + "_" + version + ".jnlp").getAbsolutePath(); //$NON-NLS-1$ //$NON-NLS-2$
458: out = new PrintWriter(new BufferedOutputStream(
459: new FileOutputStream(destination)));
460: writePrologue();
461: out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); //$NON-NLS-1$
462: out.print("<jnlp spec=\"1.0+\" "); //$NON-NLS-1$
463: if (codebase != null)
464: out.print("codebase=\"" + codebase); //$NON-NLS-1$
465: out.println("\">"); //$NON-NLS-1$
466: out.println("\t<information>"); //$NON-NLS-1$
467: if (label != null)
468: out.println("\t\t<title>" + label + "</title>"); //$NON-NLS-1$ //$NON-NLS-2$
469: if (provider != null)
470: out.println("\t\t<vendor>" + provider + "</vendor>"); //$NON-NLS-1$ //$NON-NLS-2$
471: if (description != null)
472: out
473: .println("\t\t<description>" + description + "</description>"); //$NON-NLS-1$ //$NON-NLS-2$
474: if (generateOfflineAllowed)
475: out.println("\t\t<offline-allowed/>"); //$NON-NLS-1$
476: out.println("\t</information>"); //$NON-NLS-1$
477: out.println("\t<security>"); //$NON-NLS-1$
478: out.println("\t\t<all-permissions/>"); //$NON-NLS-1$
479: out.println("\t</security>"); //$NON-NLS-1$
480: out.println("\t<component-desc/>"); //$NON-NLS-1$
481: out.println("\t<resources>"); //$NON-NLS-1$
482: out.println("\t\t<j2se version=\"" + j2se + "\" />"); //$NON-NLS-1$ //$NON-NLS-2$
483: out.println("\t</resources>"); //$NON-NLS-1$
484: }
485:
486: private void writeEpilogue() {
487: out.println("</jnlp>"); //$NON-NLS-1$
488: }
489:
490: private boolean isMatching(String candidateValues, String siteValues) {
491: if (candidateValues == null)
492: return true;
493: if (siteValues == null)
494: return false;
495: if ("*".equals(candidateValues))return true; //$NON-NLS-1$
496: if ("".equals(candidateValues))return true; //$NON-NLS-1$
497: StringTokenizer siteTokens = new StringTokenizer(siteValues,
498: ","); //$NON-NLS-1$
499: //$NON-NLS-1$
500: while (siteTokens.hasMoreTokens()) {
501: StringTokenizer candidateTokens = new StringTokenizer(
502: candidateValues, ","); //$NON-NLS-1$
503: String siteValue = siteTokens.nextToken();
504: while (candidateTokens.hasMoreTokens()) {
505: if (siteValue.equalsIgnoreCase(candidateTokens
506: .nextToken()))
507: return true;
508: }
509: }
510: return false;
511: }
512:
513: private boolean isValidEnvironment(String os, String ws, String arch) {
514: if (configs.length == 0)
515: return true;
516: for (int i = 0; i < configs.length; i++) {
517: if (isMatching(os, configs[i].getOs())
518: && isMatching(ws, configs[i].getWs())
519: && isMatching(arch, configs[i].getArch()))
520: return true;
521: }
522: return false;
523: }
524:
525: private void setConfigInfo(String spec) {
526: if (spec != null && spec.startsWith("$")) { //$NON-NLS-1$
527: configs = new Config[0];
528: return;
529: }
530: if (spec == null) {
531: configs = new Config[] { Config.genericConfig() };
532: return;
533: }
534: StringTokenizer tokens = new StringTokenizer(spec, "&"); //$NON-NLS-1$
535: int configNbr = tokens.countTokens();
536: ArrayList configInfos = new ArrayList(configNbr);
537: while (tokens.hasMoreElements()) {
538: String aConfig = tokens.nextToken();
539: StringTokenizer configTokens = new StringTokenizer(aConfig,
540: ","); //$NON-NLS-1$
541: if (configTokens.countTokens() == 3) {
542: Config toAdd = new Config(configTokens.nextToken()
543: .trim(), configTokens.nextToken().trim(),
544: configTokens.nextToken().trim());
545: if (toAdd.equals(Config.genericConfig()))
546: toAdd = Config.genericConfig();
547: configInfos.add(toAdd);
548: }
549: }
550: if (configInfos.size() == 0)
551: configInfos.add(Config.genericConfig());
552: configs = (Config[]) configInfos.toArray(new Config[configInfos
553: .size()]);
554: }
555: }
|