001: /*******************************************************************************
002: * Copyright (c) 2005, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.services;
011:
012: import java.util.ArrayList;
013: import java.util.Collection;
014: import java.util.List;
015:
016: import org.eclipse.core.commands.Command;
017: import org.eclipse.core.commands.IParameter;
018: import org.eclipse.core.commands.Parameterization;
019: import org.eclipse.core.commands.ParameterizedCommand;
020: import org.eclipse.core.commands.common.NotDefinedException;
021: import org.eclipse.core.runtime.IStatus;
022: import org.eclipse.core.runtime.Status;
023: import org.eclipse.jface.preference.IPreferenceStore;
024: import org.eclipse.jface.util.IPropertyChangeListener;
025: import org.eclipse.jface.util.PropertyChangeEvent;
026: import org.eclipse.ui.IMemento;
027: import org.eclipse.ui.commands.ICommandService;
028: import org.eclipse.ui.internal.WorkbenchPlugin;
029: import org.eclipse.ui.internal.util.Util;
030:
031: /**
032: * <p>
033: * A manager for items parsed from the preference store. This attaches a
034: * listener to the registry after the first read, and monitors the preference
035: * for changes from that point on. When {@link #dispose()} is called, the
036: * listener is detached.
037: * </p>
038: * <p>
039: * This class is only intended for internal use within the
040: * <code>org.eclipse.ui.workbench</code> plug-in.
041: * </p>
042: *
043: * @since 3.2
044: */
045: public abstract class PreferencePersistence extends RegistryPersistence {
046:
047: /**
048: * Inserts the given element into the indexed two-dimensional array in the
049: * array at the index. The array is grown as necessary.
050: *
051: * @param elementToAdd
052: * The element to add to the indexed array; may be
053: * <code>null</code>
054: * @param indexedArray
055: * The two-dimensional array that is indexed by element type;
056: * must not be <code>null</code>.
057: * @param index
058: * The index at which the element should be added; must be a
059: * valid index.
060: * @param currentCount
061: * The current number of items in the array at the index.
062: */
063: protected static final void addElementToIndexedArray(
064: final IMemento elementToAdd,
065: final IMemento[][] indexedArray, final int index,
066: final int currentCount) {
067: final IMemento[] elements;
068: if (currentCount == 0) {
069: elements = new IMemento[1];
070: indexedArray[index] = elements;
071: } else {
072: if (currentCount >= indexedArray[index].length) {
073: final IMemento[] copy = new IMemento[indexedArray[index].length * 2];
074: System.arraycopy(indexedArray[index], 0, copy, 0,
075: currentCount);
076: elements = copy;
077: indexedArray[index] = elements;
078: } else {
079: elements = indexedArray[index];
080: }
081: }
082: elements[currentCount] = elementToAdd;
083: }
084:
085: /**
086: * Adds a warning to be logged at some later point in time.
087: *
088: * @param warningsToLog
089: * The collection of warnings to be logged; must not be
090: * <code>null</code>.
091: * @param message
092: * The mesaage to log; must not be <code>null</code>.
093: */
094: protected static final void addWarning(final List warningsToLog,
095: final String message) {
096: addWarning(warningsToLog, message, null, null, null);
097: }
098:
099: /**
100: * Adds a warning to be logged at some later point in time. This logs the
101: * identifier of the item.
102: *
103: * @param warningsToLog
104: * The collection of warnings to be logged; must not be
105: * <code>null</code>.
106: * @param message
107: * The mesaage to log; must not be <code>null</code>.
108: * @param id
109: * The identifier of the item for which a warning is being
110: * logged; may be <code>null</code>.
111: */
112: protected static final void addWarning(final List warningsToLog,
113: final String message, final String id) {
114: addWarning(warningsToLog, message, id, null, null);
115: }
116:
117: /**
118: * Adds a warning to be logged at some later point in time. This logs the
119: * identifier of the item, as well as an extra attribute.
120: *
121: * @param warningsToLog
122: * The collection of warnings to be logged; must not be
123: * <code>null</code>.
124: * @param message
125: * The mesaage to log; must not be <code>null</code>.
126: * @param id
127: * The identifier of the item for which a warning is being
128: * logged; may be <code>null</code>.
129: * @param extraAttributeName
130: * The name of extra attribute to be logged; may be
131: * <code>null</code>.
132: * @param extraAttributeValue
133: * The value of the extra attribute to be logged; may be
134: * <code>null</code>.
135: */
136: protected static final void addWarning(final List warningsToLog,
137: final String message, final String id,
138: final String extraAttributeName,
139: final String extraAttributeValue) {
140: String statusMessage = message;
141: if (id != null) {
142: statusMessage = statusMessage + ": id='" + id + '\''; //$NON-NLS-1$
143: }
144: if (extraAttributeName != null) {
145: if (id != null) {
146: statusMessage = statusMessage + ',';
147: } else {
148: statusMessage = statusMessage + ':';
149: }
150: statusMessage = statusMessage + ' ' + extraAttributeName
151: + "='" //$NON-NLS-1$
152: + extraAttributeValue + '\'';
153: }
154:
155: final IStatus status = new Status(IStatus.WARNING,
156: WorkbenchPlugin.PI_WORKBENCH, 0, statusMessage, null);
157: warningsToLog.add(status);
158: }
159:
160: /**
161: * Reads a boolean attribute from a memnto.
162: *
163: * @param memento
164: * The memento from which to read the attribute; must not be
165: * <code>null</code>.
166: * @param attribute
167: * The attribute to read; must not be <code>null</code>.
168: * @param defaultValue
169: * The default boolean value.
170: * @return The attribute's value; may be <code>null</code> if none.
171: */
172: protected static final boolean readBoolean(final IMemento memento,
173: final String attribute, final boolean defaultValue) {
174: final String value = memento.getString(attribute);
175: if (value == null) {
176: return defaultValue;
177: }
178:
179: if (defaultValue) {
180: return !value.equalsIgnoreCase("false"); //$NON-NLS-1$
181: }
182:
183: return !value.equalsIgnoreCase("true"); //$NON-NLS-1$
184: }
185:
186: /**
187: * Reads an optional attribute from a memento. This converts zero-length
188: * strings into <code>null</code>.
189: *
190: * @param memento
191: * The memento from which to read the attribute; must not be
192: * <code>null</code>.
193: * @param attribute
194: * The attribute to read; must not be <code>null</code>.
195: * @return The attribute's value; may be <code>null</code> if none.
196: */
197: protected static final String readOptional(final IMemento memento,
198: final String attribute) {
199: String value = memento.getString(attribute);
200: if ((value != null) && (value.length() == 0)) {
201: value = null;
202: }
203:
204: return value;
205: }
206:
207: /**
208: * Reads the parameterized command from a parent memento. This is used to
209: * read the parameter sub-elements from a key element, as well as the
210: * command id. Each parameter is guaranteed to be valid. If invalid
211: * parameters are found, then a warning status will be appended to the
212: * <code>warningsToLog</code> list. The command id is required, or a
213: * warning will be logged.
214: *
215: * @param memento
216: * The memento from which the parameters should be read; must not
217: * be <code>null</code>.
218: * @param commandService
219: * The service providing commands for the workbench; must not be
220: * <code>null</code>.
221: * @param warningsToLog
222: * The list of warnings found during parsing. Warnings found will
223: * parsing the parameters will be appended to this list. This
224: * value must not be <code>null</code>.
225: * @param message
226: * The message to print if the command identifier is not present;
227: * must not be <code>null</code>.
228: * @return The array of parameters found for this configuration element;
229: * <code>null</code> if none can be found.
230: */
231: protected static final ParameterizedCommand readParameterizedCommand(
232: final IMemento memento,
233: final ICommandService commandService,
234: final List warningsToLog, final String message,
235: final String id) {
236: final String commandId = readRequired(memento, ATT_COMMAND_ID,
237: warningsToLog, message, id);
238: if (commandId == null) {
239: return null;
240: }
241:
242: final Command command = commandService.getCommand(commandId);
243: final ParameterizedCommand parameterizedCommand = readParameters(
244: memento, warningsToLog, command);
245:
246: return parameterizedCommand;
247: }
248:
249: /**
250: * Reads the parameters from a parent memento. This is used to read the
251: * parameter sub-elements from a key element. Each parameter is guaranteed
252: * to be valid. If invalid parameters are found, then a warning status will
253: * be appended to the <code>warningsToLog</code> list.
254: *
255: * @param memento
256: * The memento from which the parameters should be read; must not
257: * be <code>null</code>.
258: * @param warningsToLog
259: * The list of warnings found during parsing. Warnings found will
260: * parsing the parameters will be appended to this list. This
261: * value must not be <code>null</code>.
262: * @param command
263: * The command around which the parameterization should be
264: * created; must not be <code>null</code>.
265: * @return The array of parameters found for this memento; <code>null</code>
266: * if none can be found.
267: */
268: protected static final ParameterizedCommand readParameters(
269: final IMemento memento, final List warningsToLog,
270: final Command command) {
271: final IMemento[] parameterMementos = memento
272: .getChildren(TAG_PARAMETER);
273: if ((parameterMementos == null)
274: || (parameterMementos.length == 0)) {
275: return new ParameterizedCommand(command, null);
276: }
277:
278: final Collection parameters = new ArrayList();
279: for (int i = 0; i < parameterMementos.length; i++) {
280: final IMemento parameterMemento = parameterMementos[i];
281:
282: // Read out the id.
283: final String id = parameterMemento.getString(ATT_ID);
284: if ((id == null) || (id.length() == 0)) {
285: // The name should never be null. This is invalid.
286: addWarning(warningsToLog, "Parameters need a name"); //$NON-NLS-1$
287: continue;
288: }
289:
290: // Find the parameter on the command.
291: IParameter parameter = null;
292: try {
293: final IParameter[] commandParameters = command
294: .getParameters();
295: if (parameters != null) {
296: for (int j = 0; j < commandParameters.length; j++) {
297: final IParameter currentParameter = commandParameters[j];
298: if (Util.equals(currentParameter.getId(), id)) {
299: parameter = currentParameter;
300: break;
301: }
302: }
303:
304: }
305: } catch (final NotDefinedException e) {
306: // This should not happen.
307: }
308: if (parameter == null) {
309: // The name should never be null. This is invalid.
310: addWarning(warningsToLog,
311: "Could not find a matching parameter", id); //$NON-NLS-1$
312: continue;
313: }
314:
315: // Read out the value.
316: final String value = parameterMemento.getString(ATT_VALUE);
317: if ((value == null) || (value.length() == 0)) {
318: // The name should never be null. This is invalid.
319: addWarning(warningsToLog, "Parameters need a value", id); //$NON-NLS-1$
320: continue;
321: }
322:
323: parameters.add(new Parameterization(parameter, value));
324: }
325:
326: if (parameters.isEmpty()) {
327: return new ParameterizedCommand(command, null);
328: }
329:
330: return new ParameterizedCommand(
331: command,
332: (Parameterization[]) parameters
333: .toArray(new Parameterization[parameters.size()]));
334: }
335:
336: /**
337: * Reads a required attribute from the memento.
338: *
339: * @param memento
340: * The memento from which to read; must not be <code>null</code>.
341: * @param attribute
342: * The attribute to read; must not be <code>null</code>.
343: * @param warningsToLog
344: * The list of warnings; must not be <code>null</code>.
345: * @param message
346: * The warning message to use if the attribute is missing; must
347: * not be <code>null</code>.
348: * @return The required attribute; may be <code>null</code> if missing.
349: */
350: protected static final String readRequired(final IMemento memento,
351: final String attribute, final List warningsToLog,
352: final String message) {
353: return readRequired(memento, attribute, warningsToLog, message,
354: null);
355: }
356:
357: /**
358: * Reads a required attribute from the memento. This logs the identifier of
359: * the item if this required element cannot be found.
360: *
361: * @param memento
362: * The memento from which to read; must not be <code>null</code>.
363: * @param attribute
364: * The attribute to read; must not be <code>null</code>.
365: * @param warningsToLog
366: * The list of warnings; must not be <code>null</code>.
367: * @param message
368: * The warning message to use if the attribute is missing; must
369: * not be <code>null</code>.
370: * @param id
371: * The identifier of the element for which this is a required
372: * attribute; may be <code>null</code>.
373: * @return The required attribute; may be <code>null</code> if missing.
374: */
375: protected static final String readRequired(final IMemento memento,
376: final String attribute, final List warningsToLog,
377: final String message, final String id) {
378: final String value = memento.getString(attribute);
379: if ((value == null) || (value.length() == 0)) {
380: addWarning(warningsToLog, message, id);
381: return null;
382: }
383:
384: return value;
385: }
386:
387: /**
388: * Whether the preference and registry change listeners have been attached
389: * yet.
390: */
391: protected boolean preferenceListenerAttached = false;
392:
393: /**
394: * The registry change listener for this class.
395: */
396: private final IPropertyChangeListener preferenceChangeListener;
397:
398: /**
399: * Detaches the preference change listener from the registry.
400: */
401: public final void dispose() {
402: super .dispose();
403:
404: final IPreferenceStore store = WorkbenchPlugin.getDefault()
405: .getPreferenceStore();
406: store.removePropertyChangeListener(preferenceChangeListener);
407: }
408:
409: /**
410: * Checks whether the preference change could affect this persistence class.
411: *
412: * @param event
413: * The event indicating the preference change; must not be
414: * <code>null</code>.
415: * @return <code>true</code> if the persistence instance is affected by
416: * this change; <code>false</code> otherwise.
417: */
418: protected abstract boolean isChangeImportant(
419: final PropertyChangeEvent event);
420:
421: /**
422: * Reads the various elements from the registry. Subclasses should extend,
423: * but must not override.
424: */
425: protected void read() {
426: super .read();
427:
428: if (!preferenceListenerAttached) {
429: final IPreferenceStore store = WorkbenchPlugin.getDefault()
430: .getPreferenceStore();
431: store.addPropertyChangeListener(preferenceChangeListener);
432: }
433: }
434:
435: /**
436: * Constructs a new instance of {@link PreferencePersistence}. A preference
437: * change listener is created.
438: */
439: protected PreferencePersistence() {
440: super ();
441:
442: preferenceChangeListener = new IPropertyChangeListener() {
443: public final void propertyChange(
444: final PropertyChangeEvent event) {
445: if (isChangeImportant(event)) {
446: read();
447: }
448: }
449: };
450: }
451: }
|