0001: /*
0002: * PluginJAR.java - Controls JAR loading and unloading
0003: * :tabSize=8:indentSize=8:noTabs=false:
0004: * :folding=explicit:collapseFolds=1:
0005: *
0006: * Copyright (C) 1999, 2004 Slava Pestov
0007: *
0008: * This program is free software; you can redistribute it and/or
0009: * modify it under the terms of the GNU General Public License
0010: * as published by the Free Software Foundation; either version 2
0011: * of the License, or any later version.
0012: *
0013: * This program is distributed in the hope that it will be useful,
0014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0016: * GNU General Public License for more details.
0017: *
0018: * You should have received a copy of the GNU General Public License
0019: * along with this program; if not, write to the Free Software
0020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0021: */
0022:
0023: package org.gjt.sp.jedit;
0024:
0025: //{{{ Imports
0026: import java.io.BufferedInputStream;
0027: import java.io.BufferedOutputStream;
0028: import java.io.DataInputStream;
0029: import java.io.DataOutputStream;
0030: import java.io.File;
0031: import java.io.FileInputStream;
0032: import java.io.FileNotFoundException;
0033: import java.io.FileOutputStream;
0034: import java.io.IOException;
0035: import java.io.InputStream;
0036: import java.lang.reflect.Modifier;
0037: import java.net.URL;
0038: import java.util.Enumeration;
0039: import java.util.LinkedHashSet;
0040: import java.util.LinkedList;
0041: import java.util.List;
0042: import java.util.Map;
0043: import java.util.Properties;
0044: import java.util.Set;
0045: import java.util.StringTokenizer;
0046: import java.util.zip.ZipEntry;
0047: import java.util.zip.ZipFile;
0048:
0049: import javax.swing.SwingUtilities;
0050:
0051: import org.gjt.sp.jedit.browser.VFSBrowser;
0052: import org.gjt.sp.jedit.buffer.DummyFoldHandler;
0053: import org.gjt.sp.jedit.buffer.FoldHandler;
0054: import org.gjt.sp.jedit.gui.DockableWindowFactory;
0055: import org.gjt.sp.jedit.msg.PluginUpdate;
0056: import org.gjt.sp.util.Log;
0057: import org.gjt.sp.util.StandardUtilities;
0058: import org.gjt.sp.util.IOUtilities;
0059:
0060: //}}}
0061:
0062: /**
0063: * Loads and unloads plugins.<p>
0064: *
0065: * <h3>JAR file contents</h3>
0066: *
0067: * When loading a plugin, jEdit looks for the following resources:
0068: *
0069: * <ul>
0070: * <li>A file named <code>actions.xml</code> defining plugin actions.
0071: * Only one such file per plugin is allowed. See {@link ActionSet} for
0072: * syntax.</li>
0073: * <li>A file named <code>browser.actions.xml</code> defining file system
0074: * browser actions.
0075: * Only one such file per plugin is allowed. See {@link ActionSet} for
0076: * syntax.</li>
0077: * <li>A file named <code>dockables.xml</code> defining dockable windows.
0078: * Only one such file per plugin is allowed. See {@link
0079: * org.gjt.sp.jedit.gui.DockableWindowManager} for
0080: * syntax.</li>
0081: * <li>A file named <code>services.xml</code> defining additional services
0082: * offered by the plugin, such as virtual file systems.
0083: * Only one such file per plugin is allowed. See {@link
0084: * org.gjt.sp.jedit.ServiceManager} for
0085: * syntax.</li>
0086: * <li>File with extension <code>.props</code> containing name/value pairs
0087: * separated by an equals sign.
0088: * A plugin can supply any number of property files. Property files are used
0089: * to define plugin men items, plugin option panes, as well as arbitriary
0090: * settings and strings used by the plugin. See {@link EditPlugin} for
0091: * information about properties used by jEdit. See
0092: * <code>java.util.Properties</code> for property file syntax.</li>
0093: * </ul>
0094: *
0095: * For a plugin to actually do something once it is resident in memory,
0096: * it must contain a class whose name ends with <code>Plugin</code>.
0097: * This class, known as the <i>plugin core class</i> must extend
0098: * {@link EditPlugin} and define a few required properties, otherwise it is
0099: * ignored.
0100: *
0101: * <h3>Dynamic and deferred loading</h3>
0102: *
0103: * Unlike in prior jEdit versions, jEdit 4.2 and later allow
0104: * plugins to be added and removed to the resident set at any time using
0105: * the {@link jEdit#addPluginJAR(String)} and
0106: * {@link jEdit#removePluginJAR(PluginJAR,boolean)} methods. Furthermore, the
0107: * plugin core class might not be loaded until the plugin is first used. See
0108: * {@link EditPlugin#start()} for a full description.
0109: *
0110: *
0111: * @see org.gjt.sp.jedit.jEdit#getProperty(String)
0112: * @see org.gjt.sp.jedit.jEdit#getPlugin(String)
0113: * @see org.gjt.sp.jedit.jEdit#getPlugins()
0114: * @see org.gjt.sp.jedit.jEdit#getPluginJAR(String)
0115: * @see org.gjt.sp.jedit.jEdit#getPluginJARs()
0116: * @see org.gjt.sp.jedit.jEdit#addPluginJAR(String)
0117: * @see org.gjt.sp.jedit.jEdit#removePluginJAR(PluginJAR,boolean)
0118: * @see org.gjt.sp.jedit.ActionSet
0119: * @see org.gjt.sp.jedit.gui.DockableWindowManager
0120: * @see org.gjt.sp.jedit.OptionPane
0121: * @see org.gjt.sp.jedit.PluginJAR
0122: * @see org.gjt.sp.jedit.ServiceManager
0123: *
0124: * @author Slava Pestov
0125: * @version $Id: PluginJAR.java 10832 2007-10-07 11:19:37Z kpouer $
0126: * @since jEdit 4.2pre1
0127: */
0128: public class PluginJAR {
0129: //{{{ Instance variables
0130: private final String path;
0131: private String cachePath;
0132: private final File file;
0133:
0134: private final JARClassLoader classLoader;
0135: private ZipFile zipFile;
0136: private Properties properties;
0137: private String[] classes;
0138: private ActionSet actions;
0139: private ActionSet browserActions;
0140: private EditPlugin plugin;
0141: private URL dockablesURI;
0142: private URL servicesURI;
0143: private boolean activated;
0144:
0145: // Lists of jarPaths
0146: private final Set<String> theseRequireMe = new LinkedHashSet<String>();
0147: /** The plugins that uses me as optional dependency. */
0148: private final Set<String> theseUseMe = new LinkedHashSet<String>();
0149: private final Set<String> weRequireThese = new LinkedHashSet<String>();
0150: private final Set<String> weUseThese = new LinkedHashSet<String>();
0151:
0152: //}}}
0153:
0154: //{{{ load(String jarPath, boolean activateDependentIfNecessary)
0155: /**
0156: * Loads a plugin, and its dependent plugins if necessary.
0157: *
0158: * @since jEdit 4.3pre7
0159: *
0160: */
0161: public static PluginJAR load(String path, boolean loadDependents) {
0162: PluginJAR jar = jEdit.getPluginJAR(path);
0163: if (jar != null && jar.getPlugin() != null) {
0164: return jar;
0165: }
0166: jEdit.addPluginJAR(path);
0167: jar = jEdit.getPluginJAR(path);
0168: String className = jar.getPlugin().getClassName();
0169: if (loadDependents) {
0170: Set<String> pluginLoadList = getDependencySet(className);
0171: for (String jarName : pluginLoadList) {
0172: String jarPath = findPlugin(jarName);
0173: load(jarPath, false);
0174: }
0175: }
0176: // Load extra jars that are part of this plugin
0177: String jars = jEdit
0178: .getProperty("plugin." + className + ".jars");
0179: if (jars != null) {
0180: String dir = MiscUtilities.getParentOfPath(path);
0181: StringTokenizer st = new StringTokenizer(jars);
0182: while (st.hasMoreTokens()) {
0183: String _jarPath = MiscUtilities.constructPath(dir, st
0184: .nextToken());
0185: PluginJAR _jar = jEdit.getPluginJAR(_jarPath);
0186: if (_jar == null) {
0187: jEdit.addPluginJAR(_jarPath);
0188: }
0189: }
0190: }
0191: jar.checkDependencies();
0192: jar.activatePluginIfNecessary();
0193: return jar;
0194: } // }}}
0195:
0196: //{{{ getPath() method
0197: /**
0198: * Returns the full path name of this plugin's JAR file.
0199: */
0200: public String getPath() {
0201: return path;
0202: } //}}}
0203:
0204: //{{{ findPlugin() method
0205: /**
0206: * Unlike getPlugin(), will return a PluginJAR that is not yet loaded,
0207: * given its classname.
0208: *
0209: * @param className a class name
0210: * @return the JARpath of the first PluginJAR it can find which contains this className,
0211: * or null if not found.
0212: * @since 4.3pre7
0213: */
0214: public static String findPlugin(String className) {
0215: EditPlugin ep = jEdit.getPlugin(className);
0216: if (ep != null)
0217: return ep.getPluginJAR().getPath();
0218:
0219: for (String JARpath : jEdit.getNotLoadedPluginJARs()) {
0220: PluginJAR pjar = new PluginJAR(new File(JARpath));
0221: if (pjar.containsClass(className)) {
0222: return JARpath;
0223: }
0224: }
0225: return null;
0226: } // }}}
0227:
0228: //{{{ containsClass() function
0229: /**
0230: * @param className a class name
0231: * @return true if this jar contains a class with that classname.
0232: * @since jedit 4.3pre7
0233: */
0234: boolean containsClass(String className) {
0235: try {
0236: getZipFile();
0237: } catch (IOException ioe) {
0238: throw new RuntimeException(ioe);
0239: }
0240: Enumeration itr = zipFile.entries();
0241: while (itr.hasMoreElements()) {
0242: String entry = itr.nextElement().toString();
0243: if (entry.endsWith(".class")) {
0244: String name = entry.substring(0, entry.length() - 6)
0245: .replace('/', '.');
0246: if (name.equals(className))
0247: return true;
0248: }
0249: }
0250: return false;
0251:
0252: } // }}}
0253:
0254: //{{{ getCachePath() method
0255: /**
0256: * Returns the full path name of this plugin's summary file.
0257: * The summary file is used to store certain information which allows
0258: * loading of the plugin's resources and core class to be deferred
0259: * until the plugin is first used. As long as a plugin is using the
0260: * jEdit 4.2 plugin API, no extra effort is required to take advantage
0261: * of the summary cache.
0262: */
0263: public String getCachePath() {
0264: return cachePath;
0265: } //}}}
0266:
0267: //{{{ getDependencySet() method
0268: /**
0269: *
0270: * @param className of a plugin that we wish to load
0271: * @return an ordered set of JARpaths that contains the
0272: * plugins that need to be (re)loaded, in the correct order.
0273: */
0274: public static Set<String> getDependencySet(String className) {
0275: String dep;
0276: Set<String> retval = new LinkedHashSet<String>();
0277: int i = 0;
0278: while ((dep = jEdit.getProperty("plugin." + className
0279: + ".depend." + i++)) != null) {
0280: PluginDepends pluginDepends;
0281: try {
0282: pluginDepends = getPluginDepends(dep);
0283: } catch (IllegalArgumentException e) {
0284: Log.log(Log.ERROR, PluginJAR.class, className
0285: + " has an invalid dependency: " + dep);
0286: continue;
0287: }
0288:
0289: if (pluginDepends.what.equals("plugin")) {
0290: int index2 = pluginDepends.arg.indexOf(' ');
0291: if (index2 == -1) {
0292: Log.log(Log.ERROR, PluginJAR.class, className
0293: + " has an invalid dependency: " + dep
0294: + " (version is missing)");
0295: continue;
0296: }
0297:
0298: String pluginName = pluginDepends.arg.substring(0,
0299: index2);
0300: String needVersion = pluginDepends.arg
0301: .substring(index2 + 1);
0302: //todo : check version ?
0303: Set<String> loadTheseFirst = getDependencySet(pluginName);
0304: loadTheseFirst.add(pluginName);
0305: loadTheseFirst.addAll(retval);
0306: retval = loadTheseFirst;
0307: }
0308: }
0309: return retval;
0310: } // }}}
0311:
0312: //{{{ getFile() method
0313: /**
0314: * Returns a file pointing to the plugin JAR.
0315: */
0316: public File getFile() {
0317: return file;
0318: } //}}}
0319:
0320: //{{{ getClassLoader() method
0321: /**
0322: * Returns the plugin's class loader.
0323: */
0324: public JARClassLoader getClassLoader() {
0325: return classLoader;
0326: } //}}}
0327:
0328: //{{{ getZipFile() method
0329: /**
0330: * Returns the plugin's JAR file, opening it if necessary.
0331: * @since jEdit 4.2pre1
0332: */
0333: public synchronized ZipFile getZipFile() throws IOException {
0334: if (zipFile == null) {
0335: Log.log(Log.DEBUG, this , "Opening " + path);
0336: zipFile = new ZipFile(path);
0337: }
0338: return zipFile;
0339: } //}}}
0340:
0341: //{{{ getActions() method
0342: /**
0343: * @deprecated Call getActionSet() instead
0344: */
0345: public ActionSet getActions() {
0346: return getActionSet();
0347: } //}}}
0348:
0349: //{{{ getActionSet() method
0350: /**
0351: * Returns the plugin's action set for the jEdit action context
0352: * {@link jEdit#getActionContext()}. These actions are loaded from
0353: * the <code>actions.xml</code> file; see {@link ActionSet}.
0354: *.
0355: * @since jEdit 4.2pre1
0356: */
0357: public ActionSet getActionSet() {
0358: return actions;
0359: } //}}}
0360:
0361: //{{{ getBrowserActionSet() method
0362: /**
0363: * Returns the plugin's action set for the file system browser action
0364: * context {@link
0365: * org.gjt.sp.jedit.browser.VFSBrowser#getActionContext()}.
0366: * These actions are loaded from
0367: * the <code>browser.actions.xml</code> file; see {@link ActionSet}.
0368: *.
0369: * @since jEdit 4.2pre1
0370: */
0371: public ActionSet getBrowserActionSet() {
0372: return browserActions;
0373: } //}}}
0374:
0375: //{{{ checkDependencies() method
0376: /**
0377: * Returns true if all dependencies are satisified, false otherwise.
0378: * Also if dependencies are not satisfied, the plugin is marked as
0379: * "broken".
0380: *
0381: */
0382: public boolean checkDependencies() {
0383: if (plugin == null)
0384: return true;
0385: int i = 0;
0386: boolean ok = true;
0387:
0388: String name = plugin.getClassName();
0389:
0390: String dep;
0391: while ((dep = jEdit.getProperty("plugin." + name + ".depend."
0392: + i++)) != null) {
0393: PluginDepends pluginDepends;
0394: try {
0395: pluginDepends = getPluginDepends(dep);
0396: } catch (IllegalArgumentException e) {
0397: Log.log(Log.ERROR, this , name + " has an invalid"
0398: + " dependency: " + dep);
0399: ok = false;
0400: continue;
0401: }
0402:
0403: if (pluginDepends.what.equals("jdk")) {
0404: if (!pluginDepends.optional
0405: && StandardUtilities.compareStrings(System
0406: .getProperty("java.version"),
0407: pluginDepends.arg, false) < 0) {
0408: String[] args = { pluginDepends.arg,
0409: System.getProperty("java.version") };
0410: jEdit.pluginError(path, "plugin-error.dep-jdk",
0411: args);
0412: ok = false;
0413: }
0414: } else if (pluginDepends.what.equals("jedit")) {
0415: if (pluginDepends.arg.length() != 11) {
0416: Log.log(Log.ERROR, this , "Invalid jEdit version"
0417: + " number: " + pluginDepends.arg);
0418: ok = false;
0419: }
0420:
0421: if (!pluginDepends.optional
0422: && StandardUtilities.compareStrings(jEdit
0423: .getBuild(), pluginDepends.arg, false) < 0) {
0424: String needs = MiscUtilities
0425: .buildToVersion(pluginDepends.arg);
0426: String[] args = { needs, jEdit.getVersion() };
0427: jEdit.pluginError(path, "plugin-error.dep-jedit",
0428: args);
0429: ok = false;
0430: }
0431: } else if (pluginDepends.what.equals("plugin")) {
0432: int index2 = pluginDepends.arg.indexOf(' ');
0433: if (index2 == -1) {
0434: Log.log(Log.ERROR, this , name
0435: + " has an invalid dependency: " + dep
0436: + " (version is missing)");
0437: ok = false;
0438: continue;
0439: }
0440:
0441: String pluginName = pluginDepends.arg.substring(0,
0442: index2);
0443: String needVersion = pluginDepends.arg
0444: .substring(index2 + 1);
0445: String currVersion = jEdit.getProperty("plugin."
0446: + pluginName + ".version");
0447:
0448: EditPlugin editPlugin = jEdit.getPlugin(pluginName,
0449: false);
0450: if (editPlugin == null) {
0451: if (!pluginDepends.optional) {
0452: String[] args = { needVersion, pluginName };
0453: jEdit.pluginError(path,
0454: "plugin-error.dep-plugin.no-version",
0455: args);
0456: ok = false;
0457: }
0458: } else if (StandardUtilities.compareStrings(
0459: currVersion, needVersion, false) < 0) {
0460: if (!pluginDepends.optional) {
0461: String[] args = { needVersion, pluginName,
0462: currVersion };
0463: jEdit.pluginError(path,
0464: "plugin-error.dep-plugin", args);
0465: ok = false;
0466: }
0467: } else if (editPlugin instanceof EditPlugin.Broken) {
0468: if (!pluginDepends.optional) {
0469: String[] args = { pluginName };
0470: jEdit.pluginError(path,
0471: "plugin-error.dep-plugin.broken", args);
0472: ok = false;
0473: }
0474: } else {
0475: PluginJAR jar = editPlugin.getPluginJAR();
0476: if (pluginDepends.optional) {
0477: jar.theseUseMe.add(path);
0478: weUseThese.add(jar.getPath());
0479: } else {
0480: jar.theseRequireMe.add(path);
0481: weRequireThese.add(jar.getPath());
0482: }
0483: }
0484: } else if (pluginDepends.what.equals("class")) {
0485: if (!pluginDepends.optional) {
0486: try {
0487: classLoader.loadClass(pluginDepends.arg, false);
0488: } catch (Exception e) {
0489: String[] args = { pluginDepends.arg };
0490: jEdit.pluginError(path,
0491: "plugin-error.dep-class", args);
0492: ok = false;
0493: }
0494: }
0495: } else {
0496: Log.log(Log.ERROR, this , name + " has unknown"
0497: + " dependency: " + dep);
0498: ok = false;
0499: }
0500: }
0501:
0502: // each JAR file listed in the plugin's jars property
0503: // needs to know that we need them
0504: String jars = jEdit.getProperty("plugin."
0505: + plugin.getClassName() + ".jars");
0506: if (jars != null) {
0507: String dir = MiscUtilities.getParentOfPath(path);
0508:
0509: StringTokenizer st = new StringTokenizer(jars);
0510: while (st.hasMoreTokens()) {
0511: String jarPath = MiscUtilities.constructPath(dir, st
0512: .nextToken());
0513: PluginJAR jar = jEdit.getPluginJAR(jarPath);
0514: if (jar == null) {
0515: String[] args = { jarPath };
0516: jEdit.pluginError(path, "plugin-error.missing-jar",
0517: args);
0518: ok = false;
0519: } else {
0520: weRequireThese.add(jarPath);
0521: jar.theseRequireMe.add(path);
0522: }
0523: }
0524: }
0525:
0526: if (!ok)
0527: breakPlugin();
0528:
0529: return ok;
0530: } //}}}
0531:
0532: //{{{ getRequiredJars() method
0533: /**
0534: * Returns the required jars of this plugin.
0535: *
0536: * @return the required jars of this plugin
0537: * @since jEdit 4.3pre12
0538: */
0539: public Set<String> getRequiredJars() {
0540: return weRequireThese;
0541: } //}}}
0542:
0543: //{{{ getPluginDepends() method
0544: private static PluginDepends getPluginDepends(String dep)
0545: throws IllegalArgumentException {
0546: boolean optional;
0547: if (dep.startsWith("optional ")) {
0548: optional = true;
0549: dep = dep.substring("optional ".length());
0550: } else {
0551: optional = false;
0552: }
0553:
0554: int index = dep.indexOf(' ');
0555: if (index == -1)
0556: throw new IllegalArgumentException("wrong dependency");
0557:
0558: String what = dep.substring(0, index);
0559: String arg = dep.substring(index + 1);
0560: PluginDepends depends = new PluginDepends();
0561: depends.what = what;
0562: depends.arg = arg;
0563: depends.optional = optional;
0564: return depends;
0565: } //}}}
0566:
0567: //{{{ PluginDepends class
0568: private static class PluginDepends {
0569: String what;
0570: String arg;
0571: boolean optional;
0572: } //}}}
0573:
0574: //{{{ transitiveClosure()
0575: /**
0576: * If plugin A is needed by B, and B is needed by C, we want to
0577: * tell the user that A is needed by B and C when they try to
0578: * unload A.
0579: *
0580: * @param dependents a set of plugins which we wish to disable
0581: * @param listModel a set of plugins which will be affected, and will need
0582: * to be disabled also.
0583: */
0584: public static void transitiveClosure(String[] dependents,
0585: List<String> listModel) {
0586: for (int i = 0; i < dependents.length; i++) {
0587: String jarPath = dependents[i];
0588: if (!listModel.contains(jarPath)) {
0589: listModel.add(jarPath);
0590: PluginJAR jar = jEdit.getPluginJAR(jarPath);
0591: transitiveClosure(jar.getDependentPlugins(), listModel);
0592: }
0593: }
0594: } //}}}
0595:
0596: //{{{ getDependentPlugins() method
0597: public String[] getDependentPlugins() {
0598: return theseRequireMe
0599: .toArray(new String[theseRequireMe.size()]);
0600: } //}}}
0601:
0602: //{{{ getPlugin() method
0603: /**
0604: * Returns the plugin core class for this JAR file. Note that if the
0605: * plugin has not been activated, this will return an instance of
0606: * {@link EditPlugin.Deferred}. If you need the actual plugin core
0607: * class instance, call {@link #activatePlugin()} first.
0608: * If the plugin is not yet loaded, returns null
0609: *
0610: * @since jEdit 4.2pre1
0611: */
0612: public EditPlugin getPlugin() {
0613: return plugin;
0614: } //}}}
0615:
0616: //{{{ activatePlugin() method
0617: /**
0618: * Loads the plugin core class. Does nothing if the plugin core class
0619: * has already been loaded. This method might be called on startup,
0620: * depending on what properties are set. See {@link EditPlugin#start()}.
0621: * This method is thread-safe.
0622: *
0623: * @since jEdit 4.2pre1
0624: */
0625: public void activatePlugin() {
0626: synchronized (this ) {
0627: if (activated) {
0628: // recursive call
0629: return;
0630: }
0631:
0632: activated = true;
0633: }
0634:
0635: if (!(plugin instanceof EditPlugin.Deferred)) {
0636: return;
0637: }
0638:
0639: String className = plugin.getClassName();
0640:
0641: try {
0642: Class clazz = classLoader.loadClass(className, false);
0643: int modifiers = clazz.getModifiers();
0644: if (Modifier.isInterface(modifiers)
0645: || Modifier.isAbstract(modifiers)
0646: || !EditPlugin.class.isAssignableFrom(clazz)) {
0647: Log.log(Log.ERROR, this ,
0648: "Plugin has properties but does not extend EditPlugin: "
0649: + className);
0650: breakPlugin();
0651: return;
0652: }
0653:
0654: plugin = (EditPlugin) clazz.newInstance();
0655: plugin.jar = this ;
0656: } catch (Throwable t) {
0657: breakPlugin();
0658:
0659: Log.log(Log.ERROR, this , "Error while starting plugin "
0660: + className);
0661: Log.log(Log.ERROR, this , t);
0662: String[] args = { t.toString() };
0663: jEdit.pluginError(path, "plugin-error.start-error", args);
0664:
0665: return;
0666: }
0667:
0668: if (jEdit.isMainThread()
0669: || SwingUtilities.isEventDispatchThread()) {
0670: startPlugin();
0671: } else {
0672: // for thread safety
0673: startPluginLater();
0674: }
0675:
0676: EditBus.send(new PluginUpdate(this , PluginUpdate.ACTIVATED,
0677: false));
0678: } //}}}
0679:
0680: //{{{ activateIfNecessary() method
0681: /**
0682: * Should be called after a new plugin is installed.
0683: * @since jEdit 4.2pre2
0684: */
0685: public void activatePluginIfNecessary() {
0686: String filename = MiscUtilities.getFileName(getPath());
0687: jEdit.setBooleanProperty("plugin-blacklist." + filename, false);
0688: if (!(plugin instanceof EditPlugin.Deferred && plugin != null)) {
0689: return;
0690: }
0691:
0692: String className = plugin.getClassName();
0693:
0694: // default for plugins that don't specify this property (ie,
0695: // 4.1-style plugins) is to load them on startup
0696: String activate = jEdit.getProperty("plugin." + className
0697: + ".activate");
0698:
0699: if (activate == null) {
0700: // 4.1 plugin
0701: if (!jEdit.isMainThread()) {
0702: breakPlugin();
0703:
0704: jEdit.pluginError(path, "plugin-error.not-42", null);
0705: } else {
0706: activatePlugin();
0707: }
0708: } else {
0709: // 4.2 plugin
0710:
0711: // if at least one property listed here is true,
0712: // load the plugin
0713: boolean load = false;
0714:
0715: StringTokenizer st = new StringTokenizer(activate);
0716: while (st.hasMoreTokens()) {
0717: String prop = st.nextToken();
0718: boolean value = jEdit.getBooleanProperty(prop);
0719: if (value) {
0720: Log.log(Log.DEBUG, this , "Activating " + className
0721: + " because of " + prop);
0722: load = true;
0723: break;
0724: }
0725: }
0726:
0727: if (load) {
0728: activatePlugin();
0729: }
0730: }
0731: } //}}}
0732:
0733: //{{{ deactivatePlugin() method
0734: /**
0735: * Unloads the plugin core class. Does nothing if the plugin core class
0736: * has not been loaded.
0737: * This method can only be called from the AWT event dispatch thread!
0738: * @see EditPlugin#stop()
0739: *
0740: * @since jEdit 4.2pre3
0741: */
0742: public void deactivatePlugin(boolean exit) {
0743: if (!activated)
0744: return;
0745:
0746: if (!exit) {
0747: // buffers retain a reference to the fold handler in
0748: // question... and the easiest way to handle fold
0749: // handler unloading is this...
0750: Buffer buffer = jEdit.getFirstBuffer();
0751: while (buffer != null) {
0752: if (buffer.getFoldHandler() != null
0753: && buffer.getFoldHandler().getClass()
0754: .getClassLoader() == classLoader) {
0755: buffer.setFoldHandler(new DummyFoldHandler());
0756: }
0757: buffer = buffer.getNext();
0758: }
0759: }
0760:
0761: if (plugin != null && !(plugin instanceof EditPlugin.Broken)) {
0762: if (plugin instanceof EBPlugin)
0763: EditBus.removeFromBus((EBPlugin) plugin);
0764:
0765: try {
0766: plugin.stop();
0767: } catch (Throwable t) {
0768: Log.log(Log.ERROR, this , "Error while "
0769: + "stopping plugin:");
0770: Log.log(Log.ERROR, this , t);
0771: }
0772:
0773: plugin = new EditPlugin.Deferred(this , plugin
0774: .getClassName());
0775:
0776: EditBus.send(new PluginUpdate(this ,
0777: PluginUpdate.DEACTIVATED, exit));
0778:
0779: if (!exit) {
0780: // see if this is a 4.1-style plugin
0781: String activate = jEdit.getProperty("plugin."
0782: + plugin.getClassName() + ".activate");
0783:
0784: if (activate == null) {
0785: breakPlugin();
0786: jEdit
0787: .pluginError(path, "plugin-error.not-42",
0788: null);
0789: }
0790: }
0791: }
0792:
0793: activated = false;
0794: } //}}}
0795:
0796: //{{{ getDockablesURI() method
0797: /**
0798: * Returns the location of the plugin's
0799: * <code>dockables.xml</code> file.
0800: * @since jEdit 4.2pre1
0801: */
0802: public URL getDockablesURI() {
0803: return dockablesURI;
0804: } //}}}
0805:
0806: //{{{ getServicesURI() method
0807: /**
0808: * Returns the location of the plugin's
0809: * <code>services.xml</code> file.
0810: * @since jEdit 4.2pre1
0811: */
0812: public URL getServicesURI() {
0813: return servicesURI;
0814: } //}}}
0815:
0816: //{{{ toString() method
0817: public String toString() {
0818: if (plugin == null)
0819: return path;
0820: else
0821: return path + ",class=" + plugin.getClassName();
0822: } //}}}
0823:
0824: //{{{ Package-private members
0825:
0826: //{{{ Static methods
0827:
0828: //{{{ getPluginCache() method
0829: public static PluginCacheEntry getPluginCache(PluginJAR plugin) {
0830: String jarCachePath = plugin.getCachePath();
0831: if (jarCachePath == null)
0832: return null;
0833:
0834: DataInputStream din = null;
0835: try {
0836: PluginCacheEntry cache = new PluginCacheEntry();
0837: cache.plugin = plugin;
0838: cache.modTime = plugin.getFile().lastModified();
0839: din = new DataInputStream(new BufferedInputStream(
0840: new FileInputStream(jarCachePath)));
0841: if (cache.read(din))
0842: return cache;
0843: else {
0844: // returns false with outdated cache
0845: return null;
0846: }
0847: } catch (FileNotFoundException fnf) {
0848: return null;
0849: } catch (IOException io) {
0850: Log.log(Log.ERROR, PluginJAR.class, io);
0851: return null;
0852: } finally {
0853: IOUtilities.closeQuietly(din);
0854: }
0855: } //}}}
0856:
0857: //{{{ setPluginCache() method
0858: static void setPluginCache(PluginJAR plugin, PluginCacheEntry cache) {
0859: String jarCachePath = plugin.getCachePath();
0860: if (jarCachePath == null)
0861: return;
0862:
0863: Log.log(Log.DEBUG, PluginJAR.class, "Writing " + jarCachePath);
0864:
0865: DataOutputStream dout = null;
0866: try {
0867: dout = new DataOutputStream(new BufferedOutputStream(
0868: new FileOutputStream(jarCachePath)));
0869: cache.write(dout);
0870: dout.close();
0871: } catch (IOException io) {
0872: Log.log(Log.ERROR, PluginJAR.class, io);
0873: IOUtilities.closeQuietly(dout);
0874: new File(jarCachePath).delete();
0875: }
0876: } //}}}
0877:
0878: //}}}
0879:
0880: //{{{ PluginJAR constructor
0881: /**
0882: * Creates a PluginJAR object which is not necessarily loaded, but can be later.
0883: * @see #load(String, boolean)
0884: */
0885: public PluginJAR(File file) {
0886: this .path = file.getPath();
0887: String jarCacheDir = jEdit.getJARCacheDirectory();
0888: if (jarCacheDir != null) {
0889: cachePath = MiscUtilities.constructPath(jarCacheDir, file
0890: .getName()
0891: + ".summary");
0892: }
0893: this .file = file;
0894: classLoader = new JARClassLoader(this );
0895: actions = new ActionSet();
0896: } //}}}
0897:
0898: //{{{ init() method
0899: void init() {
0900: PluginCacheEntry cache = getPluginCache(this );
0901: if (cache != null) {
0902: loadCache(cache);
0903: classLoader.activate();
0904: } else {
0905: try {
0906: cache = generateCache();
0907: if (cache != null) {
0908: setPluginCache(this , cache);
0909: classLoader.activate();
0910: }
0911: } catch (IOException io) {
0912: Log.log(Log.ERROR, this , "Cannot load" + " plugin "
0913: + path);
0914: Log.log(Log.ERROR, this , io);
0915:
0916: String[] args = { io.toString() };
0917: jEdit
0918: .pluginError(path, "plugin-error.load-error",
0919: args);
0920:
0921: uninit(false);
0922: }
0923: }
0924: } //}}}
0925:
0926: //{{{ uninit() method
0927: void uninit(boolean exit) {
0928: deactivatePlugin(exit);
0929:
0930: if (!exit) {
0931: for (String path : weRequireThese) {
0932: PluginJAR jar = jEdit.getPluginJAR(path);
0933: if (jar != null)
0934: jar.theseRequireMe.remove(this .path);
0935: }
0936:
0937: for (String path : weUseThese) {
0938: PluginJAR jar = jEdit.getPluginJAR(path);
0939: if (jar != null)
0940: jar.theseUseMe.remove(this .path);
0941: }
0942:
0943: classLoader.deactivate();
0944: BeanShell.resetClassManager();
0945:
0946: if (actions != null)
0947: jEdit.removeActionSet(actions);
0948: if (browserActions != null)
0949: VFSBrowser.getActionContext().removeActionSet(
0950: browserActions);
0951:
0952: DockableWindowFactory.getInstance().unloadDockableWindows(
0953: this );
0954: ServiceManager.unloadServices(this );
0955:
0956: jEdit.removePluginProps(properties);
0957:
0958: try {
0959: if (zipFile != null) {
0960: zipFile.close();
0961: zipFile = null;
0962: }
0963: } catch (IOException io) {
0964: Log.log(Log.ERROR, this , io);
0965: }
0966: }
0967: } //}}}
0968:
0969: //{{{ getClasses() method
0970: String[] getClasses() {
0971: return classes;
0972: } //}}}
0973:
0974: //}}}
0975:
0976: //{{{ Private members
0977:
0978: //{{{ actionsPresentButNotCoreClass() method
0979: private void actionsPresentButNotCoreClass() {
0980: Log.log(Log.WARNING, this , getPath()
0981: + " has an actions.xml but no plugin core class");
0982: actions.setLabel("MISSING PLUGIN CORE CLASS");
0983: } //}}}
0984:
0985: //{{{ loadCache() method
0986: private void loadCache(PluginCacheEntry cache) {
0987: classes = cache.classes;
0988:
0989: /* this should be before dockables are initialized */
0990: if (cache.cachedProperties != null) {
0991: properties = cache.cachedProperties;
0992: jEdit.addPluginProps(cache.cachedProperties);
0993: }
0994:
0995: if (cache.actionsURI != null && cache.cachedActionNames != null) {
0996: actions = new ActionSet(this , cache.cachedActionNames,
0997: cache.cachedActionToggleFlags, cache.actionsURI);
0998: }
0999:
1000: if (cache.browserActionsURI != null
1001: && cache.cachedBrowserActionNames != null) {
1002: browserActions = new ActionSet(this ,
1003: cache.cachedBrowserActionNames,
1004: cache.cachedBrowserActionToggleFlags,
1005: cache.browserActionsURI);
1006: VFSBrowser.getActionContext().addActionSet(browserActions);
1007: }
1008:
1009: if (cache.dockablesURI != null
1010: && cache.cachedDockableNames != null
1011: && cache.cachedDockableActionFlags != null
1012: && cache.cachedDockableMovableFlags != null) {
1013: dockablesURI = cache.dockablesURI;
1014: DockableWindowFactory.getInstance().cacheDockableWindows(
1015: this , cache.cachedDockableNames,
1016: cache.cachedDockableActionFlags,
1017: cache.cachedDockableMovableFlags);
1018: }
1019:
1020: if (actions.size() != 0)
1021: jEdit.addActionSet(actions);
1022:
1023: if (cache.servicesURI != null && cache.cachedServices != null) {
1024: servicesURI = cache.servicesURI;
1025: for (int i = 0; i < cache.cachedServices.length; i++) {
1026: ServiceManager.Descriptor d = cache.cachedServices[i];
1027: ServiceManager.registerService(d);
1028: }
1029: }
1030:
1031: if (cache.pluginClass != null) {
1032: // Check if a plugin with the same name
1033: // is already loaded
1034: if (jEdit.getPlugin(cache.pluginClass) != null) {
1035: jEdit.pluginError(path, "plugin-error.already-loaded",
1036: null);
1037: uninit(false);
1038: } else {
1039: String label = jEdit.getProperty("plugin."
1040: + cache.pluginClass + ".name");
1041: actions.setLabel(jEdit.getProperty("action-set.plugin",
1042: new String[] { label }));
1043: plugin = new EditPlugin.Deferred(this ,
1044: cache.pluginClass);
1045: }
1046: } else {
1047: if (actions.size() != 0)
1048: actionsPresentButNotCoreClass();
1049: }
1050: } //}}}
1051:
1052: //{{{ generateCache() method
1053: public PluginCacheEntry generateCache() throws IOException {
1054: properties = new Properties();
1055:
1056: List<String> classes = new LinkedList<String>();
1057:
1058: ZipFile zipFile = getZipFile();
1059:
1060: List<String> plugins = new LinkedList<String>();
1061:
1062: PluginCacheEntry cache = new PluginCacheEntry();
1063: cache.modTime = file.lastModified();
1064: cache.cachedProperties = new Properties();
1065:
1066: Enumeration<? extends ZipEntry> entries = zipFile.entries();
1067: while (entries.hasMoreElements()) {
1068: ZipEntry entry = entries.nextElement();
1069: String name = entry.getName();
1070: String lname = name.toLowerCase();
1071: if (lname.equals("actions.xml")) {
1072: cache.actionsURI = classLoader.getResource(name);
1073: } else if (lname.equals("browser.actions.xml")) {
1074: cache.browserActionsURI = classLoader.getResource(name);
1075: } else if (lname.equals("dockables.xml")) {
1076: dockablesURI = classLoader.getResource(name);
1077: cache.dockablesURI = dockablesURI;
1078: } else if (lname.equals("services.xml")) {
1079: servicesURI = classLoader.getResource(name);
1080: cache.servicesURI = servicesURI;
1081: } else if (lname.endsWith(".props")) {
1082: InputStream in = classLoader.getResourceAsStream(name);
1083: properties.load(in);
1084: in.close();
1085: } else if (name.endsWith(".class")) {
1086: String className = MiscUtilities.fileToClass(name);
1087: if (className.endsWith("Plugin")) {
1088: plugins.add(className);
1089: }
1090: classes.add(className);
1091: }
1092: }
1093:
1094: cache.cachedProperties = properties;
1095: jEdit.addPluginProps(properties);
1096:
1097: this .classes = cache.classes = classes
1098: .toArray(new String[classes.size()]);
1099:
1100: String label = null;
1101:
1102: for (String className : plugins) {
1103: String _label = jEdit.getProperty("plugin." + className
1104: + ".name");
1105: String version = jEdit.getProperty("plugin." + className
1106: + ".version");
1107: if (_label == null || version == null) {
1108: Log.log(Log.WARNING, this , "Ignoring: " + className);
1109: } else {
1110: cache.pluginClass = className;
1111:
1112: // Check if a plugin with the same name
1113: // is already loaded
1114: if (jEdit.getPlugin(className) != null) {
1115: jEdit.pluginError(path,
1116: "plugin-error.already-loaded", null);
1117: return null;
1118: }
1119: plugin = new EditPlugin.Deferred(this , className);
1120: label = _label;
1121:
1122: break;
1123: }
1124: }
1125:
1126: if (cache.actionsURI != null) {
1127: actions = new ActionSet(this , null, null, cache.actionsURI);
1128: actions.load();
1129: cache.cachedActionNames = actions.getCacheableActionNames();
1130: cache.cachedActionToggleFlags = new boolean[cache.cachedActionNames.length];
1131: for (int i = 0; i < cache.cachedActionNames.length; i++) {
1132: cache.cachedActionToggleFlags[i] = jEdit
1133: .getBooleanProperty(cache.cachedActionNames[i]
1134: + ".toggle");
1135: }
1136: }
1137:
1138: if (cache.browserActionsURI != null) {
1139: browserActions = new ActionSet(this , null, null,
1140: cache.browserActionsURI);
1141: browserActions.load();
1142: VFSBrowser.getActionContext().addActionSet(browserActions);
1143: cache.cachedBrowserActionNames = browserActions
1144: .getCacheableActionNames();
1145: cache.cachedBrowserActionToggleFlags = new boolean[cache.cachedBrowserActionNames.length];
1146: for (int i = 0; i < cache.cachedBrowserActionNames.length; i++) {
1147: cache.cachedBrowserActionToggleFlags[i] = jEdit
1148: .getBooleanProperty(cache.cachedBrowserActionNames[i]
1149: + ".toggle");
1150: }
1151: }
1152:
1153: if (dockablesURI != null) {
1154: DockableWindowFactory.getInstance().loadDockableWindows(
1155: this , dockablesURI, cache);
1156: }
1157:
1158: if (actions.size() != 0) {
1159: if (label != null) {
1160: actions.setLabel(jEdit.getProperty("action-set.plugin",
1161: new String[] { label }));
1162: } else
1163: actionsPresentButNotCoreClass();
1164:
1165: jEdit.addActionSet(actions);
1166: }
1167:
1168: if (servicesURI != null) {
1169: ServiceManager.loadServices(this , servicesURI, cache);
1170: }
1171:
1172: return cache;
1173: } //}}}
1174:
1175: //{{{ startPlugin() method
1176: private void startPlugin() {
1177: try {
1178: plugin.start();
1179: } catch (Throwable t) {
1180: breakPlugin();
1181:
1182: Log.log(Log.ERROR, PluginJAR.this ,
1183: "Error while starting plugin "
1184: + plugin.getClassName());
1185: Log.log(Log.ERROR, PluginJAR.this , t);
1186: String[] args = { t.toString() };
1187: jEdit.pluginError(path, "plugin-error.start-error", args);
1188: }
1189:
1190: if (plugin instanceof EBPlugin) {
1191: if (jEdit.getProperty("plugin." + plugin.getClassName()
1192: + ".activate") == null) {
1193: // old plugins expected jEdit 4.1-style
1194: // behavior, where a PropertiesChanged
1195: // was sent after plugins were started
1196: ((EBComponent) plugin)
1197: .handleMessage(new org.gjt.sp.jedit.msg.PropertiesChanged(
1198: null));
1199: }
1200: EditBus.addToBus((EBPlugin) plugin);
1201: }
1202:
1203: // buffers retain a reference to the fold handler in
1204: // question... and the easiest way to handle fold
1205: // handler loading is this...
1206: Buffer buffer = jEdit.getFirstBuffer();
1207: while (buffer != null) {
1208: FoldHandler handler = FoldHandler.getFoldHandler(buffer
1209: .getStringProperty("folding"));
1210: // == null before loaded
1211: if (buffer.getFoldHandler() != null && handler != null
1212: && handler != buffer.getFoldHandler()) {
1213: buffer.setFoldHandler(handler);
1214: }
1215: buffer = buffer.getNext();
1216: }
1217: } //}}}
1218:
1219: //{{{ startPluginLater() method
1220: private void startPluginLater() {
1221: SwingUtilities.invokeLater(new Runnable() {
1222: public void run() {
1223: if (!activated)
1224: return;
1225:
1226: startPlugin();
1227: }
1228: });
1229: } //}}}
1230:
1231: //{{{ breakPlugin() method
1232: private void breakPlugin() {
1233: plugin = new EditPlugin.Broken(this , plugin.getClassName());
1234:
1235: // remove action sets, dockables, etc so that user doesn't
1236: // see the broken plugin
1237: uninit(false);
1238: // but we want properties to hang around
1239: jEdit.addPluginProps(properties);
1240: } //}}}
1241:
1242: //}}}
1243:
1244: //{{{ PluginCacheEntry class
1245: /**
1246: * Used by the <code>DockableWindowManager</code> and
1247: * <code>ServiceManager</code> to handle caching.
1248: * @since jEdit 4.2pre1
1249: */
1250: public static class PluginCacheEntry {
1251: public static final int MAGIC = 0xB7A2E421;
1252:
1253: //{{{ Instance variables
1254: public PluginJAR plugin;
1255: public long modTime;
1256:
1257: public String[] classes;
1258: public URL actionsURI;
1259: public String[] cachedActionNames;
1260: public boolean[] cachedActionToggleFlags;
1261: public URL browserActionsURI;
1262: public String[] cachedBrowserActionNames;
1263: public boolean[] cachedBrowserActionToggleFlags;
1264: public URL dockablesURI;
1265: public String[] cachedDockableNames;
1266: public boolean[] cachedDockableActionFlags;
1267: public boolean[] cachedDockableMovableFlags;
1268: public URL servicesURI;
1269: ServiceManager.Descriptor[] cachedServices;
1270:
1271: public Properties cachedProperties;
1272: public String pluginClass;
1273:
1274: //}}}
1275:
1276: /* read() and write() must be kept perfectly in sync...
1277: * its a very simple file format. doing it this way is
1278: * faster than serializing since serialization calls
1279: * reflection, etc. */
1280:
1281: //{{{ read() method
1282: public boolean read(DataInputStream din) throws IOException {
1283: int cacheMagic = din.readInt();
1284: if (cacheMagic != MAGIC)
1285: return false;
1286:
1287: String cacheBuild = readString(din);
1288: if (!cacheBuild.equals(jEdit.getBuild()))
1289: return false;
1290:
1291: long cacheModTime = din.readLong();
1292: if (cacheModTime != modTime)
1293: return false;
1294:
1295: actionsURI = readURI(din);
1296: cachedActionNames = readStringArray(din);
1297: cachedActionToggleFlags = readBooleanArray(din);
1298:
1299: browserActionsURI = readURI(din);
1300: cachedBrowserActionNames = readStringArray(din);
1301: cachedBrowserActionToggleFlags = readBooleanArray(din);
1302:
1303: dockablesURI = readURI(din);
1304: cachedDockableNames = readStringArray(din);
1305: cachedDockableActionFlags = readBooleanArray(din);
1306: cachedDockableMovableFlags = readBooleanArray(din);
1307:
1308: servicesURI = readURI(din);
1309: int len = din.readInt();
1310: if (len == 0)
1311: cachedServices = null;
1312: else {
1313: cachedServices = new ServiceManager.Descriptor[len];
1314: for (int i = 0; i < len; i++) {
1315: ServiceManager.Descriptor d = new ServiceManager.Descriptor(
1316: readString(din), readString(din), null,
1317: plugin);
1318: cachedServices[i] = d;
1319: }
1320: }
1321:
1322: classes = readStringArray(din);
1323:
1324: cachedProperties = readMap(din);
1325:
1326: pluginClass = readString(din);
1327:
1328: return true;
1329: } //}}}
1330:
1331: //{{{ write() method
1332: public void write(DataOutputStream dout) throws IOException {
1333: dout.writeInt(MAGIC);
1334: writeString(dout, jEdit.getBuild());
1335:
1336: dout.writeLong(modTime);
1337:
1338: writeString(dout, actionsURI);
1339: writeStringArray(dout, cachedActionNames);
1340: writeBooleanArray(dout, cachedActionToggleFlags);
1341:
1342: writeString(dout, browserActionsURI);
1343: writeStringArray(dout, cachedBrowserActionNames);
1344: writeBooleanArray(dout, cachedBrowserActionToggleFlags);
1345:
1346: writeString(dout, dockablesURI);
1347: writeStringArray(dout, cachedDockableNames);
1348: writeBooleanArray(dout, cachedDockableActionFlags);
1349: writeBooleanArray(dout, cachedDockableMovableFlags);
1350:
1351: writeString(dout, servicesURI);
1352: if (cachedServices == null)
1353: dout.writeInt(0);
1354: else {
1355: dout.writeInt(cachedServices.length);
1356: for (int i = 0; i < cachedServices.length; i++) {
1357: writeString(dout, cachedServices[i].clazz);
1358: writeString(dout, cachedServices[i].name);
1359: }
1360: }
1361:
1362: writeStringArray(dout, classes);
1363:
1364: writeMap(dout, cachedProperties);
1365:
1366: writeString(dout, pluginClass);
1367: } //}}}
1368:
1369: //{{{ Private members
1370:
1371: //{{{ readString() method
1372: private static String readString(DataInputStream din)
1373: throws IOException {
1374: int len = din.readInt();
1375: if (len == 0)
1376: return null;
1377: char[] str = new char[len];
1378: for (int i = 0; i < len; i++)
1379: str[i] = din.readChar();
1380: return new String(str);
1381: } //}}}
1382:
1383: //{{{ readURI() method
1384: private static URL readURI(DataInputStream din)
1385: throws IOException {
1386: String str = readString(din);
1387: if (str == null)
1388: return null;
1389: else
1390: return new URL(str);
1391: } //}}}
1392:
1393: //{{{ readStringArray() method
1394: private static String[] readStringArray(DataInputStream din)
1395: throws IOException {
1396: int len = din.readInt();
1397: if (len == 0)
1398: return null;
1399: String[] str = new String[len];
1400: for (int i = 0; i < len; i++) {
1401: str[i] = readString(din);
1402: }
1403: return str;
1404: } //}}}
1405:
1406: //{{{ readBooleanArray() method
1407: private static boolean[] readBooleanArray(DataInputStream din)
1408: throws IOException {
1409: int len = din.readInt();
1410: if (len == 0)
1411: return null;
1412: boolean[] bools = new boolean[len];
1413: for (int i = 0; i < len; i++) {
1414: bools[i] = din.readBoolean();
1415: }
1416: return bools;
1417: } //}}}
1418:
1419: //{{{ readMap() method
1420: private static Properties readMap(DataInputStream din)
1421: throws IOException {
1422: Properties returnValue = new Properties();
1423: int count = din.readInt();
1424: for (int i = 0; i < count; i++) {
1425: String key = readString(din);
1426: String value = readString(din);
1427: if (value == null)
1428: value = "";
1429: returnValue.put(key, value);
1430: }
1431: return returnValue;
1432: } //}}}
1433:
1434: //{{{ writeString() method
1435: private static void writeString(DataOutputStream dout,
1436: Object obj) throws IOException {
1437: if (obj == null) {
1438: dout.writeInt(0);
1439: } else {
1440: String str = obj.toString();
1441: dout.writeInt(str.length());
1442: dout.writeChars(str);
1443: }
1444: } //}}}
1445:
1446: //{{{ writeStringArray() method
1447: private static void writeStringArray(DataOutputStream dout,
1448: String[] str) throws IOException {
1449: if (str == null) {
1450: dout.writeInt(0);
1451: } else {
1452: dout.writeInt(str.length);
1453: for (int i = 0; i < str.length; i++) {
1454: writeString(dout, str[i]);
1455: }
1456: }
1457: } //}}}
1458:
1459: //{{{ writeBooleanArray() method
1460: private static void writeBooleanArray(DataOutputStream dout,
1461: boolean[] bools) throws IOException {
1462: if (bools == null) {
1463: dout.writeInt(0);
1464: } else {
1465: dout.writeInt(bools.length);
1466: for (int i = 0; i < bools.length; i++) {
1467: dout.writeBoolean(bools[i]);
1468: }
1469: }
1470: } //}}}
1471:
1472: //{{{ writeMap() method
1473: private static void writeMap(DataOutputStream dout, Map map)
1474: throws IOException {
1475: dout.writeInt(map.size());
1476: Set<Map.Entry<Object, Object>> set = map.entrySet();
1477: for (Map.Entry<Object, Object> entry : set) {
1478: writeString(dout, entry.getKey());
1479: writeString(dout, entry.getValue());
1480: }
1481: } //}}}
1482:
1483: //}}}
1484: } //}}}
1485: }
|