1 /******************************************************************************* 2 * Copyright (c) 2000, 2016 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Alena Laskavaia - Bug 453392 - No debug options help 14 *******************************************************************************/ 15 package org.eclipse.pde.internal.core; 16 17 import java.io.BufferedReader; 18 import java.io.File; 19 import java.io.FileInputStream; 20 import java.io.FileOutputStream; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.InputStreamReader; 24 import java.nio.charset.Charset; 25 import java.util.Enumeration; 26 import java.util.HashSet; 27 import java.util.Hashtable; 28 import java.util.Iterator; 29 import java.util.Map; 30 import java.util.Properties; 31 import java.util.zip.ZipEntry; 32 import java.util.zip.ZipFile; 33 import org.eclipse.core.runtime.IPath; 34 import org.eclipse.core.runtime.Path; 35 import org.eclipse.pde.core.plugin.IPluginModelBase; 36 import org.eclipse.pde.core.plugin.PluginRegistry; 37 38 public class TracingOptionsManager { 39 private Properties template; 40 TracingOptionsManager()41 public TracingOptionsManager() { 42 super(); 43 } 44 createTemplate()45 private Properties createTemplate() { 46 Properties temp = new Properties(); 47 IPluginModelBase[] models = PluginRegistry.getAllModels(); 48 for (IPluginModelBase model : models) { 49 addToTemplate(temp, model); 50 } 51 return temp; 52 } 53 addToTemplate(Properties temp, IPluginModelBase model)54 private void addToTemplate(Properties temp, IPluginModelBase model) { 55 Properties modelOptions = getOptions(model); 56 if (modelOptions != null) { 57 temp.putAll(modelOptions); 58 } 59 } 60 getTemplateTable(String pluginId)61 public synchronized Hashtable<String, Object> getTemplateTable(String pluginId) { 62 if (template == null) { 63 template = createTemplate(); 64 } 65 Hashtable<String, Object> defaults = new Hashtable<>(); 66 for (Enumeration<Object> keys = template.keys(); keys.hasMoreElements();) { 67 String key = keys.nextElement().toString(); 68 if (belongsTo(key, pluginId)) { 69 defaults.put(key, template.get(key)); 70 } 71 } 72 return defaults; 73 } 74 belongsTo(String option, String pluginId)75 private boolean belongsTo(String option, String pluginId) { 76 IPath path = new Path(option); 77 String firstSegment = path.segment(0); 78 return pluginId.equalsIgnoreCase(firstSegment); 79 } 80 getTracingOptions(Map<String, String> storedOptions)81 public Properties getTracingOptions(Map<String, String> storedOptions) { 82 // Start with the fresh template from plugins 83 Properties defaults = getTracingTemplateCopy(); 84 if (storedOptions != null) { 85 // Load stored values, but only for existing keys 86 Iterator<?> iter = storedOptions.keySet().iterator(); 87 while (iter.hasNext()) { 88 String key = iter.next().toString(); 89 if (defaults.containsKey(key)) { 90 defaults.setProperty(key, storedOptions.get(key)); 91 } 92 } 93 } 94 return defaults; 95 } 96 getTracingTemplateCopy()97 public synchronized Properties getTracingTemplateCopy() { 98 if (template == null) { 99 template = createTemplate(); 100 } 101 return (Properties) createTemplate().clone(); 102 } 103 isTraceable(IPluginModelBase model)104 public static boolean isTraceable(IPluginModelBase model) { 105 String location = model.getInstallLocation(); 106 if (location == null) { 107 return false; 108 } 109 110 File pluginLocation = new File(location); 111 if (pluginLocation.isDirectory()) { 112 return new File(pluginLocation, ICoreConstants.OPTIONS_FILENAME).exists(); 113 } 114 try (ZipFile jarFile = new ZipFile(pluginLocation, ZipFile.OPEN_READ)) { 115 ZipEntry manifestEntry = jarFile.getEntry(ICoreConstants.OPTIONS_FILENAME); 116 if (manifestEntry != null) { 117 try (InputStream stream = jarFile.getInputStream(manifestEntry)) { 118 return stream != null; 119 } 120 } 121 } catch (IOException e) { 122 } 123 return false; 124 } 125 reset()126 public synchronized void reset() { 127 template = null; 128 } 129 save(String fileName, Properties properties)130 private void save(String fileName, Properties properties) { 131 try (FileOutputStream stream = new FileOutputStream(fileName);) { 132 properties.store(stream, "Master Tracing Options"); //$NON-NLS-1$ 133 stream.flush(); 134 } catch (IOException e) { 135 PDECore.logException(e); 136 } 137 } 138 save(String filename, Map<String, String> map, HashSet<?> selected)139 public void save(String filename, Map<String, String> map, HashSet<?> selected) { 140 Properties properties = getTracingOptions(map); 141 for (Enumeration<?> keys = properties.keys(); keys.hasMoreElements();) { 142 String key = keys.nextElement().toString(); 143 Path path = new Path(key); 144 if (path.segmentCount() < 1 || !selected.contains(path.segment(0))) { 145 properties.remove(key); 146 } 147 } 148 save(filename, properties); 149 } 150 save(String filename, Map<String, String> map)151 public void save(String filename, Map<String, String> map) { 152 save(filename, getTracingOptions(map)); 153 } 154 getOptions(IPluginModelBase model)155 private Properties getOptions(IPluginModelBase model) { 156 String location = model.getInstallLocation(); 157 if (location == null) { 158 return null; 159 } 160 try { 161 File pluginLocation = new File(location); 162 Properties modelOptions = new Properties(); 163 if (pluginLocation.isDirectory()) { 164 File file = new File(pluginLocation, ICoreConstants.OPTIONS_FILENAME); 165 if (file.exists()) { 166 try (InputStream stream = new FileInputStream(file)) { 167 modelOptions.load(stream); 168 } 169 try (InputStream stream = new FileInputStream(file)) { 170 loadComments(stream, modelOptions); 171 } 172 return modelOptions; 173 } 174 } else { 175 try (ZipFile jarFile = new ZipFile(pluginLocation, ZipFile.OPEN_READ)) { 176 ZipEntry manifestEntry = jarFile.getEntry(ICoreConstants.OPTIONS_FILENAME); 177 if (manifestEntry != null) { 178 try (InputStream stream = jarFile.getInputStream(manifestEntry)) { 179 modelOptions.load(stream); 180 } 181 try (InputStream stream = jarFile.getInputStream(manifestEntry)) { 182 loadComments(stream, modelOptions); 183 } 184 return modelOptions; 185 } 186 } 187 } 188 } catch (IOException e) { 189 PDECore.logException(e); 190 } 191 return null; 192 } 193 194 /** 195 * Loads the comments from the properties files. This is simple version 196 * which won't cover 100% of the cases but hopefully cover 99%. It will find 197 * single or multiline comments starting with # (not !) and attach to the 198 * following property key by creating fake property with the key #key and 199 * value of the comment. Properties object which will receive these comments 200 * cannot be saved back properly without some special handling. It won't 201 * support cases when: key is split in multiple lines; key use escape 202 * characters; comment uses ! start char 203 */ loadComments(InputStream stream, Properties modelOptions)204 private void loadComments(InputStream stream, Properties modelOptions) throws IOException { 205 String prevComment = ""; //$NON-NLS-1$ 206 try (BufferedReader bufferedReader = new BufferedReader( 207 new InputStreamReader(stream, Charset.defaultCharset()))) { 208 String line; 209 while ((line = bufferedReader.readLine()) != null) { 210 if (line.startsWith("#") || line.trim().isEmpty()) { //$NON-NLS-1$ 211 prevComment += "\n" + line.trim(); //$NON-NLS-1$ 212 } else { 213 if (prevComment.trim().isEmpty()) { 214 continue; 215 } 216 217 int eq = line.indexOf('='); 218 if (eq >= 0) { 219 String key = line.substring(0, eq).trim(); 220 modelOptions.put("#" + key, prevComment); //$NON-NLS-1$ 221 } 222 prevComment = ""; //$NON-NLS-1$ 223 } 224 } 225 } 226 } 227 } 228