001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.apisupport.project.suite;
043:
044: import java.io.File;
045: import java.io.FileInputStream;
046: import java.io.FileOutputStream;
047: import java.io.IOException;
048: import java.io.InputStream;
049: import java.io.OutputStream;
050: import java.net.MalformedURLException;
051: import java.net.URL;
052: import java.util.Collections;
053: import java.util.HashMap;
054: import java.util.HashSet;
055: import java.util.Iterator;
056: import java.util.Map;
057: import java.util.Set;
058: import java.util.jar.JarEntry;
059: import java.util.jar.JarFile;
060: import org.netbeans.modules.apisupport.project.ManifestManager;
061: import org.netbeans.modules.apisupport.project.ui.customizer.SuiteProperties;
062: import org.netbeans.modules.apisupport.project.universe.ModuleEntry;
063: import org.netbeans.modules.apisupport.project.universe.ModuleList;
064: import org.netbeans.modules.apisupport.project.universe.NbPlatform;
065: import org.netbeans.spi.project.support.ant.EditableProperties;
066: import org.netbeans.spi.project.support.ant.PropertyUtils;
067: import org.openide.filesystems.FileUtil;
068: import org.openide.util.Utilities;
069:
070: /**
071: * Provide set of helper methods for branding purposes.
072: * @author Radek Matous
073: */
074: public final class BrandingSupport {
075:
076: private final SuiteProject suiteProject;
077: private final SuiteProperties suiteProperties;
078: private Set<ModuleEntry> brandedModules;
079: private Set<BundleKey> brandedBundleKeys;
080: private Set<BrandedFile> brandedFiles;
081:
082: private NbPlatform platform;
083: private final File brandingDir;
084:
085: public static final String BRANDING_DIR_PROPERTY = "branding.dir"; // NOI18N
086: private static final String BUNDLE_NAME = "Bundle.properties"; //NOI18N
087:
088: public static BrandingSupport getInstance(
089: final SuiteProperties suiteProperties) throws IOException {
090: return new BrandingSupport(suiteProperties);
091: }
092:
093: private BrandingSupport(final SuiteProperties suiteProperties)
094: throws IOException {
095: this .suiteProperties = suiteProperties;
096: this .suiteProject = suiteProperties.getProject();
097: File suiteDir = suiteProject.getProjectDirectoryFile();
098: assert suiteDir != null && suiteDir.exists();
099: brandingDir = new File(suiteDir, getNameOfBrandingFolder());//NOI18N
100: init();
101: }
102:
103: /**
104: * @return the project directory beneath which everything in the project lies
105: */
106: public File getProjectDirectory() {
107: return suiteProject.getProjectDirectoryFile();
108: }
109:
110: /**
111: * @return the top-level branding directory
112: */
113: public File getBrandingRoot() {
114: return new File(getProjectDirectory(),
115: getNameOfBrandingFolder());
116: }
117:
118: /**
119: * @return the branding directory for NetBeans module represented as
120: * <code>ModuleEntry</code>
121: */
122: public File getModuleEntryDirectory(ModuleEntry mEntry) {
123: String relativePath;
124: relativePath = PropertyUtils.relativizeFile(mEntry
125: .getClusterDirectory(), mEntry.getJarLocation());
126: return new File(getBrandingRoot(), relativePath);
127: }
128:
129: /**
130: * @return the file representing localizing bundle for NetBeans module
131: */
132: public File getLocalizingBundle(final ModuleEntry mEntry) {
133: ManifestManager mfm = ManifestManager.getInstanceFromJAR(mEntry
134: .getJarLocation());
135: File bundle = null;
136: if (mfm != null) {
137: String bundlePath = mfm.getLocalizingBundle();
138: if (bundlePath != null) {
139: bundle = new File(getModuleEntryDirectory(mEntry),
140: bundlePath);
141: }
142: }
143: return bundle;
144: }
145:
146: public boolean isBranded(final BundleKey key) {
147: boolean retval = getBrandedBundleKeys().contains(key);
148: return retval;
149:
150: }
151:
152: public boolean isBranded(final BrandedFile bFile) {
153: return getBrandedFiles().contains(bFile);
154: }
155:
156: /**
157: * @return true if NetBeans module is already branded
158: */
159: public boolean isBranded(final ModuleEntry entry) {
160: boolean retval = getBrandedModules().contains(entry);
161: assert (retval == getModuleEntryDirectory(entry).exists());
162: return retval;
163: }
164:
165: public Set<ModuleEntry> getBrandedModules() {
166: return brandedModules;
167: }
168:
169: public Set<BundleKey> getBrandedBundleKeys() {
170: return brandedBundleKeys;
171: }
172:
173: public Set getBrandedFiles() {
174: return brandedFiles;
175: }
176:
177: public Set<BundleKey> getLocalizingBundleKeys(
178: final String moduleCodeNameBase, final Set<String> keys) {
179: ModuleEntry foundEntry = getModuleEntry(moduleCodeNameBase);
180: return (foundEntry != null) ? getLocalizingBundleKeys(
181: foundEntry, keys) : null;
182: }
183:
184: public Set<BundleKey> getLocalizingBundleKeys(
185: final ModuleEntry moduleEntry, final Set<String> keys) {
186: Set<BundleKey> retval = new HashSet<BundleKey>();
187: for (Iterator<BundleKey> it = getBrandedBundleKeys().iterator(); it
188: .hasNext()
189: && retval.size() != keys.size();) {
190: BundleKey bKey = it.next();
191: if (keys.contains(bKey.getKey())) {
192: retval.add(bKey);
193: }
194: }
195:
196: if (retval.size() != keys.size()) {
197: loadLocalizedBundlesFromPlatform(moduleEntry, keys, retval);
198: }
199: return (retval.size() != keys.size()) ? null : retval;
200: }
201:
202: public BrandedFile getBrandedFile(final String moduleCodeNameBase,
203: final String entryPath) {
204: ModuleEntry foundEntry = getModuleEntry(moduleCodeNameBase);
205: return (foundEntry != null) ? getBrandedFile(foundEntry,
206: entryPath) : null;
207: }
208:
209: public BrandedFile getBrandedFile(final ModuleEntry moduleEntry,
210: final String entryPath) {
211: BrandedFile retval = null;
212: try {
213: retval = new BrandedFile(moduleEntry, entryPath);
214: for (Iterator it = getBrandedFiles().iterator(); it
215: .hasNext();) {
216: BrandedFile bFile = (BrandedFile) it.next();
217:
218: if (retval.equals(bFile)) {
219: retval = bFile;
220:
221: }
222: }
223: } catch (MalformedURLException ex) {
224: retval = null;
225: }
226: return retval;
227: }
228:
229: public BundleKey getBundleKey(final String moduleCodeNameBase,
230: final String bundleEntry, final String key) {
231: Set<BundleKey> keys = getBundleKeys(moduleCodeNameBase,
232: bundleEntry, Collections.singleton(key));
233: return (keys == null) ? null : (BrandingSupport.BundleKey) keys
234: .toArray()[0];
235: }
236:
237: public Set<BundleKey> getBundleKeys(
238: final String moduleCodeNameBase, final String bundleEntry,
239: final Set<String> keys) {
240: ModuleEntry foundEntry = getModuleEntry(moduleCodeNameBase);
241: return (foundEntry != null) ? getBundleKeys(foundEntry,
242: bundleEntry, keys) : null;
243: }
244:
245: public Set<BundleKey> getBundleKeys(final ModuleEntry moduleEntry,
246: final String bundleEntry, final Set<String> keys) {
247: Set<BundleKey> retval = new HashSet<BundleKey>();
248: for (Iterator<BundleKey> it = getBrandedBundleKeys().iterator(); it
249: .hasNext()
250: && retval.size() != keys.size();) {
251: BundleKey bKey = it.next();
252: if (keys.contains(bKey.getKey())) {
253: retval.add(bKey);
254: }
255: }
256:
257: if (retval.size() != keys.size()) {
258: try {
259: loadLocalizedBundlesFromPlatform(moduleEntry,
260: bundleEntry, keys, retval);
261: } catch (IOException ex) {
262: //ex.printStackTrace();
263: throw new IllegalStateException();
264: }
265: }
266:
267: return (retval.size() != keys.size()) ? null : retval;
268: }
269:
270: private ModuleEntry getModuleEntry(final String moduleCodeNameBase) {
271: NbPlatform p = getActivePlatform();
272: return p != null ? p.getModule(moduleCodeNameBase) : null;
273: }
274:
275: private NbPlatform getActivePlatform() {
276: NbPlatform retval = suiteProperties.getActivePlatform();
277: if (retval != null) {
278: return retval;
279: } else {
280: return NbPlatform.getDefaultPlatform();
281: }
282: }
283:
284: public void brandFile(final BrandedFile bFile) throws IOException {
285: if (!bFile.isModified())
286: return;
287:
288: File target = bFile.getFileLocation();
289: if (!target.exists()) {
290: target.getParentFile().mkdirs();
291: target.createNewFile();
292: }
293:
294: assert target.exists();
295:
296: InputStream is = null;
297: OutputStream os = null;
298: try {
299: is = bFile.getBrandingSource().openStream();
300: os = new FileOutputStream(target);
301: FileUtil.copy(is, os);
302: } finally {
303: if (is != null) {
304: is.close();
305: }
306:
307: if (os != null) {
308: os.close();
309: }
310:
311: brandedFiles.add(bFile);
312: bFile.modified = false;
313: }
314: }
315:
316: public void brandFile(final BrandedFile bFile,
317: final Runnable saveTask) throws IOException {
318: if (!bFile.isModified())
319: return;
320:
321: saveTask.run();
322: brandedFiles.add(bFile);
323: bFile.modified = false;
324: }
325:
326: public void brandBundleKey(final BundleKey bundleKey)
327: throws IOException {
328: if (bundleKey == null) {
329: return;
330: }
331: brandBundleKeys(Collections.singleton(bundleKey));
332: }
333:
334: public void brandBundleKeys(final Set<BundleKey> bundleKeys)
335: throws IOException {
336: init();
337: Map<File, EditableProperties> mentryToEditProp = new HashMap<File, EditableProperties>();
338: for (BundleKey bKey : bundleKeys) {
339: if (bKey.isModified()) {
340: EditableProperties ep = mentryToEditProp.get(bKey
341: .getBrandingBundle());
342: if (ep == null) {
343: File bundle = bKey.getBrandingBundle();
344: if (!bundle.exists()) {
345: bundle.getParentFile().mkdirs();
346: bundle.createNewFile();
347: }
348: ep = getEditableProperties(bundle);
349: mentryToEditProp.put(bKey.getBrandingBundle(), ep);
350: }
351: ep.setProperty(bKey.getKey(), bKey.getValue());
352: }
353: }
354:
355: for (Map.Entry<File, EditableProperties> entry : mentryToEditProp
356: .entrySet()) {
357: File bundle = entry.getKey();
358: assert bundle.exists();
359: storeEditableProperties(entry.getValue(), bundle);
360: for (BundleKey bKey : bundleKeys) {
361: File bundle2 = bKey.getBrandingBundle();
362: if (bundle2.equals(bundle)) {
363: brandedBundleKeys.add(bKey);
364: bKey.modified = false;
365: brandedModules.add(bKey.getModuleEntry());
366: }
367: }
368: }
369: }
370:
371: private void init() throws IOException {
372: NbPlatform newPlatform = getActivePlatform();
373: if (newPlatform == null) {
374: return;
375: }
376:
377: if (brandedModules == null || !newPlatform.equals(platform)) {
378: brandedModules = new HashSet<ModuleEntry>();
379: brandedBundleKeys = new HashSet<BundleKey>();
380: brandedFiles = new HashSet<BrandedFile>();
381: platform = newPlatform;
382:
383: if (brandingDir.exists()) {
384: assert brandingDir.isDirectory();
385: scanModulesInBrandingDir(brandingDir, platform
386: .getModules());
387: }
388: }
389: }
390:
391: private void scanModulesInBrandingDir(final File srcDir,
392: final ModuleEntry[] platformModules) throws IOException {
393: if (srcDir.getName().endsWith(".jar")) {//NOI18N
394: ModuleEntry foundEntry = null;
395: for (ModuleEntry platformModule : platformModules) {
396: if (isBrandingForModuleEntry(srcDir, platformModule)) {
397: scanBrandedFiles(srcDir, platformModule);
398:
399: foundEntry = platformModule;
400: break;
401: }
402: }
403: if (foundEntry != null) {
404: brandedModules.add(foundEntry);
405: }
406: } else {
407: String[] kids = srcDir.list();
408: assert (kids != null);
409:
410: for (String kidName : kids) {
411: File kid = new File(srcDir, kidName);
412: if (!kid.isDirectory()) {
413: continue;
414: }
415: scanModulesInBrandingDir(kid, platformModules);
416: }
417: }
418: }
419:
420: private void scanBrandedFiles(final File srcDir,
421: final ModuleEntry mEntry) throws IOException {
422: String[] kids = srcDir.list();
423: assert (kids != null);
424:
425: for (String kidName : kids) {
426: File kid = new File(srcDir, kidName);
427: if (!kid.isDirectory()) {
428: if (kid.getName().endsWith(BUNDLE_NAME)) {
429: loadBundleKeys(mEntry, kid);
430: } else {
431: loadBrandedFiles(mEntry, kid);
432: }
433:
434: continue;
435: }
436: scanBrandedFiles(kid, mEntry);
437: }
438: }
439:
440: private void loadBundleKeys(final ModuleEntry mEntry,
441: final File bundle) throws IOException {
442: EditableProperties p = getEditableProperties(bundle);
443: for (Map.Entry<String, String> entry : p.entrySet()) {
444: brandedBundleKeys.add(new BundleKey(mEntry, bundle, entry
445: .getKey(), entry.getValue()));
446: }
447: }
448:
449: private void loadBrandedFiles(final ModuleEntry mEntry,
450: final File file) throws IOException {
451: String entryPath = PropertyUtils.relativizeFile(
452: getModuleEntryDirectory(mEntry), file);
453: BrandedFile bf = new BrandedFile(mEntry, file.toURI().toURL(),
454: entryPath);
455: brandedFiles.add(bf);
456: }
457:
458: private static EditableProperties getEditableProperties(
459: final File bundle) throws IOException {
460: EditableProperties p = new EditableProperties(true);
461: InputStream is = new FileInputStream(bundle);
462: try {
463: p.load(is);
464: } finally {
465: is.close();
466: }
467: return p;
468: }
469:
470: private static void storeEditableProperties(
471: final EditableProperties p, final File bundle)
472: throws IOException {
473: OutputStream os = new FileOutputStream(bundle);
474: try {
475: p.store(os);
476: } finally {
477: os.close();
478: }
479: }
480:
481: private void loadLocalizedBundlesFromPlatform(
482: final ModuleEntry moduleEntry, final Set<String> keys,
483: final Set<BundleKey> bundleKeys) {
484: EditableProperties p = ModuleList.loadBundleInfo(
485: moduleEntry.getSourceLocation()).toEditableProperties();
486: for (String key : p.keySet()) {
487: if (keys.contains(key)) {
488: String value = p.getProperty(key);
489: bundleKeys.add(new BundleKey(moduleEntry, key, value));
490: }
491: }
492: }
493:
494: private void loadLocalizedBundlesFromPlatform(
495: final ModuleEntry moduleEntry, final String bundleEntry,
496: final Set<String> keys, final Set<BundleKey> bundleKeys)
497: throws IOException {
498: EditableProperties p = new EditableProperties();
499: JarFile module = new JarFile(moduleEntry.getJarLocation());
500: JarEntry je = module.getJarEntry(bundleEntry);
501: InputStream is = module.getInputStream(je);
502: File bundle = new File(getModuleEntryDirectory(moduleEntry),
503: bundleEntry);
504: try {
505:
506: p.load(is);
507: } finally {
508: is.close();
509: }
510: for (String key : p.keySet()) {
511: if (keys.contains(key)) {
512: String value = p.getProperty(key);
513: bundleKeys.add(new BundleKey(moduleEntry, bundle, key,
514: value));
515: }
516: }
517: }
518:
519: private boolean isBrandingForModuleEntry(final File srcDir,
520: final ModuleEntry mEntry) {
521: boolean retval = mEntry.getJarLocation().getName().equals(
522: srcDir.getName());
523: if (retval) {
524: String relPath1 = PropertyUtils.relativizeFile(mEntry
525: .getClusterDirectory(), mEntry.getJarLocation()
526: .getParentFile());
527: String relPath2 = PropertyUtils.relativizeFile(brandingDir,
528: srcDir.getParentFile());
529:
530: retval = relPath1.equals(relPath2);
531: }
532: return retval;
533: }
534:
535: public final class BundleKey {
536: private final File brandingBundle;
537: private final ModuleEntry moduleEntry;
538: private final String key;
539: private String value;
540: private boolean modified = false;
541:
542: private BundleKey(final ModuleEntry moduleEntry,
543: final File brandingBundle, final String key,
544: final String value) {
545: this .moduleEntry = moduleEntry;
546: this .key = key;
547: this .value = value;
548: this .brandingBundle = brandingBundle;
549: }
550:
551: private BundleKey(final ModuleEntry mEntry, final String key,
552: final String value) {
553: this (mEntry, getLocalizingBundle(mEntry), key, value);
554: }
555:
556: public ModuleEntry getModuleEntry() {
557: return moduleEntry;
558: }
559:
560: public String getKey() {
561: return key;
562: }
563:
564: public String getValue() {
565: return value;
566: }
567:
568: public void setValue(final String value) {
569: if (!this .value.equals(value)) {
570: modified = true;
571: }
572: this .value = value;
573: }
574:
575: @Override
576: public boolean equals(Object obj) {
577: boolean retval = false;
578:
579: if (obj instanceof BundleKey) {
580: BundleKey bKey = (BundleKey) obj;
581: retval = getKey().equals(bKey.getKey())
582: && getModuleEntry().equals(
583: bKey.getModuleEntry())
584: && getBrandingBundle().equals(
585: bKey.getBrandingBundle());
586: }
587:
588: return retval;
589: }
590:
591: @Override
592: public int hashCode() {
593: return 0;
594: }
595:
596: boolean isModified() {
597: return modified;
598: }
599:
600: public File getBrandingBundle() {
601: return brandingBundle;
602: }
603:
604: }
605:
606: public class BrandedFile {
607: private final ModuleEntry moduleEntry;
608: private final String entryPath;
609: private URL brandingSource;
610: private boolean modified = false;
611:
612: private BrandedFile(final ModuleEntry moduleEntry,
613: final String entry) throws MalformedURLException {
614: this (moduleEntry, null, entry);
615: }
616:
617: private BrandedFile(final ModuleEntry moduleEntry,
618: final URL source, final String entry)
619: throws MalformedURLException {
620: this .moduleEntry = moduleEntry;
621: this .entryPath = entry;
622: if (source == null) {
623: brandingSource = moduleEntry.getJarLocation().toURI()
624: .toURL();
625: brandingSource = new URL("jar:" + brandingSource + "!/"
626: + entryPath); // NOI18N
627: } else {
628: brandingSource = source;
629: }
630:
631: }
632:
633: public ModuleEntry getModuleEntry() {
634: return moduleEntry;
635: }
636:
637: public String getEntryPath() {
638: return entryPath;
639: }
640:
641: public File getFileLocation() {
642: return new File(getModuleEntryDirectory(getModuleEntry()),
643: getEntryPath());
644: }
645:
646: public URL getBrandingSource() {
647: return brandingSource;
648: }
649:
650: public void setBrandingSource(URL brandingSource) {
651: if (!Utilities.compareObjects(brandingSource,
652: this .brandingSource)) {
653: modified = true;
654: }
655: this .brandingSource = brandingSource;
656: }
657:
658: public void setBrandingSource(File brandingFile)
659: throws MalformedURLException {
660: setBrandingSource(brandingFile.toURI().toURL());
661: }
662:
663: public boolean isModified() {
664: return modified;
665: }
666:
667: @Override
668: public boolean equals(Object obj) {
669: boolean retval = false;
670:
671: if (obj instanceof BrandedFile) {
672: BrandedFile bFile = (BrandedFile) obj;
673: retval = getModuleEntry()
674: .equals(bFile.getModuleEntry())
675: && getFileLocation().equals(
676: bFile.getFileLocation());
677: }
678:
679: //if ()
680: return retval;
681: }
682:
683: @Override
684: public int hashCode() {
685: return 0;
686: }
687:
688: }
689:
690: public String getNameOfBrandingFolder() {
691: String f = suiteProject.getEvaluator().getProperty(
692: BRANDING_DIR_PROPERTY);
693: if (f == null) { // #125160
694: f = "branding"; // NOI18N
695: }
696: return f;
697: }
698:
699: }
|