001: /*******************************************************************************
002: * Copyright (c) 2000, 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.texteditor;
011:
012: import java.util.ArrayList;
013: import java.util.HashMap;
014: import java.util.HashSet;
015: import java.util.Hashtable;
016: import java.util.Iterator;
017: import java.util.List;
018: import java.util.Map;
019: import java.util.Set;
020: import java.util.StringTokenizer;
021:
022: import org.osgi.framework.Bundle;
023:
024: import org.eclipse.core.resources.IFile;
025: import org.eclipse.core.runtime.CoreException;
026: import org.eclipse.core.runtime.IConfigurationElement;
027: import org.eclipse.core.runtime.IExtensionPoint;
028: import org.eclipse.core.runtime.ILog;
029: import org.eclipse.core.runtime.IStatus;
030: import org.eclipse.core.runtime.Platform;
031: import org.eclipse.core.runtime.Status;
032:
033: import org.eclipse.ui.editors.text.EditorsUI;
034:
035: import org.eclipse.ui.IEditorInput;
036: import org.eclipse.ui.PlatformUI;
037: import org.eclipse.ui.internal.editors.text.NLSUtility;
038:
039: /**
040: * This registry manages shared document providers. Document
041: * providers are specified in <code>plugin.xml</code> either
042: * per name extension or per editor input type. A name extension
043: * rule always overrules an editor input type rule. Editor input
044: * type rules follow the same rules <code>IAdapterManager</code>
045: * used to find object adapters.
046: *
047: * @see org.eclipse.core.runtime.IAdapterManager
048: */
049: public class DocumentProviderRegistry {
050:
051: /** The registry singleton. */
052: private static DocumentProviderRegistry fgRegistry;
053:
054: /**
055: * Returns the standard document provider registry.
056: *
057: * @return the default document provider registry
058: */
059: public static DocumentProviderRegistry getDefault() {
060: if (fgRegistry == null)
061: fgRegistry = new DocumentProviderRegistry();
062: return fgRegistry;
063: }
064:
065: /** The mapping between name extensions and configuration elements. */
066: private Map fExtensionMapping = new HashMap();
067: /** The mapping between editor input type names and configuration elements. */
068: private Map fInputTypeMapping = new HashMap();
069: /** The mapping between configuration elements and instantiated document providers. */
070: private Map fInstances = new HashMap();
071:
072: /**
073: * Creates a new document provider registry and initializes it with the information
074: * found in the plug-in registry.
075: */
076: private DocumentProviderRegistry() {
077: initialize();
078: }
079:
080: /**
081: * Reads the comma-separated value of the given configuration element
082: * for the given attribute name and remembers the configuration element
083: * in the given map under the individual tokens of the attribute value.
084: *
085: * @param map the map
086: * @param element the configuration element
087: * @param attributeName the attribute name
088: */
089: private void read(Map map, IConfigurationElement element,
090: String attributeName) {
091: String value = element.getAttribute(attributeName);
092: if (value != null) {
093: StringTokenizer tokenizer = new StringTokenizer(value, ","); //$NON-NLS-1$
094: while (tokenizer.hasMoreTokens()) {
095: String token = tokenizer.nextToken().trim();
096:
097: Set s = (Set) map.get(token);
098: if (s == null) {
099: s = new HashSet();
100: map.put(token, s);
101: }
102: s.add(element);
103: }
104: }
105: }
106:
107: /**
108: * Initializes the document provider registry. It retrieves all implementers of the <code>documentProviders</code>
109: * extension point and remembers those implementers based on the name extensions and the editor input
110: * types they are for.
111: */
112: private void initialize() {
113:
114: IExtensionPoint extensionPoint;
115: extensionPoint = Platform.getExtensionRegistry()
116: .getExtensionPoint(EditorsUI.PLUGIN_ID,
117: "documentProviders"); //$NON-NLS-1$
118:
119: if (extensionPoint == null) {
120: String msg = NLSUtility
121: .format(
122: TextEditorMessages.DocumentProviderRegistry_error_extension_point_not_found,
123: PlatformUI.PLUGIN_ID);
124: Bundle bundle = Platform.getBundle(PlatformUI.PLUGIN_ID);
125: ILog log = Platform.getLog(bundle);
126: log.log(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
127: IStatus.OK, msg, null));
128: return;
129: }
130:
131: IConfigurationElement[] elements = extensionPoint
132: .getConfigurationElements();
133: for (int i = 0; i < elements.length; i++) {
134: read(fExtensionMapping, elements[i], "extensions"); //$NON-NLS-1$
135: read(fInputTypeMapping, elements[i], "inputTypes"); //$NON-NLS-1$
136: }
137: }
138:
139: /**
140: * Returns the document provider for the given configuration element.
141: * If there is no instantiated document provider remembered for this
142: * element, a new document provider is created and put into the cache.
143: *
144: * @param entry the configuration element
145: * @return the document provider for the given entry
146: */
147: private IDocumentProvider getDocumentProvider(
148: IConfigurationElement entry) {
149: IDocumentProvider provider = (IDocumentProvider) fInstances
150: .get(entry);
151: if (provider == null) {
152: try {
153: provider = (IDocumentProvider) entry
154: .createExecutableExtension("class"); //$NON-NLS-1$
155: fInstances.put(entry, provider);
156: } catch (CoreException x) {
157: }
158: }
159: return provider;
160: }
161:
162: /**
163: * Returns the first enumerated element of the given set.
164: *
165: * @param set the set
166: * @return the first configuration element in the set or <code>null</code> if none
167: */
168: private IConfigurationElement selectConfigurationElement(Set set) {
169: if (set != null && !set.isEmpty()) {
170: Iterator e = set.iterator();
171: return (IConfigurationElement) e.next();
172: }
173: return null;
174: }
175:
176: /**
177: * Returns a shared document provider for the given name extension.
178: *
179: * @param extension the name extension to be used for lookup
180: * @return the shared document provider or <code>null</code>
181: */
182: public IDocumentProvider getDocumentProvider(String extension) {
183:
184: Set set = (Set) fExtensionMapping.get(extension);
185: if (set != null) {
186: IConfigurationElement entry = selectConfigurationElement(set);
187: return getDocumentProvider(entry);
188: }
189: return null;
190: }
191:
192: /**
193: * Computes the class hierarchy of the given type. The type is
194: * part of the computed hierarchy.
195: *
196: * @param type the type
197: * @return a list containing the super class hierarchy
198: */
199: private List computeClassList(Class type) {
200:
201: List result = new ArrayList();
202:
203: Class c = type;
204: while (c != null) {
205: result.add(c);
206: c = c.getSuperclass();
207: }
208:
209: return result;
210: }
211:
212: /**
213: * Computes the list of all interfaces for the given list of
214: * classes. The interface lists of the given classes are
215: * concatenated.
216: *
217: * @param classes a list of {@link java.lang.Class} objects
218: * @return a list with elements of type <code>Class</code>
219: */
220: private List computeInterfaceList(List classes) {
221:
222: List result = new ArrayList(4);
223: Hashtable visited = new Hashtable(4);
224:
225: Iterator e = classes.iterator();
226: while (e.hasNext()) {
227: Class c = (Class) e.next();
228: computeInterfaceList(c.getInterfaces(), result, visited);
229: }
230:
231: return result;
232: }
233:
234: /**
235: * Computes the list of all interfaces of the given list of interfaces,
236: * taking a depth-first approach.
237: *
238: * @param interfaces an array of {@link java.lang.Class} objects denoting interfaces
239: * @param result the result list
240: * @param visited map of visited interfaces
241: */
242: private void computeInterfaceList(Class[] interfaces, List result,
243: Hashtable visited) {
244:
245: List toBeVisited = new ArrayList(interfaces.length);
246:
247: for (int i = 0; i < interfaces.length; i++) {
248: Class iface = interfaces[i];
249: if (visited.get(iface) == null) {
250: visited.put(iface, iface);
251: result.add(iface);
252: toBeVisited.add(iface);
253: }
254: }
255:
256: Iterator e = toBeVisited.iterator();
257: while (e.hasNext()) {
258: Class iface = (Class) e.next();
259: computeInterfaceList(iface.getInterfaces(), result, visited);
260: }
261: }
262:
263: /**
264: * Returns the configuration elements for the first class in the list
265: * of given classes for which configuration elements have been remembered.
266: *
267: * @param classes a list of {@link java.lang.Class} objects
268: * @return an input type mapping or <code>null</code>
269: */
270: private Object getFirstInputTypeMapping(List classes) {
271: Iterator e = classes.iterator();
272: while (e.hasNext()) {
273: Class c = (Class) e.next();
274: Object mapping = fInputTypeMapping.get(c.getName());
275: if (mapping != null)
276: return mapping;
277: }
278: return null;
279: }
280:
281: /**
282: * Returns the appropriate configuration element for the given type. If
283: * there is no configuration element for the type's name, first the list of
284: * super classes is searched, and if not successful the list of all interfaces.
285: *
286: * @param type a {@link java.lang.Class} object
287: * @return an input type mapping or <code>null</code>
288: */
289: private Object findInputTypeMapping(Class type) {
290:
291: if (type == null)
292: return null;
293:
294: Object mapping = fInputTypeMapping.get(type.getName());
295: if (mapping != null)
296: return mapping;
297:
298: List classList = computeClassList(type);
299: mapping = getFirstInputTypeMapping(classList);
300: if (mapping != null)
301: return mapping;
302:
303: return getFirstInputTypeMapping(computeInterfaceList(classList));
304: }
305:
306: /**
307: * Returns the shared document for the type of the given editor input.
308: *
309: * @param editorInput the input for whose type the provider is looked up
310: * @return the shared document provider
311: */
312: public IDocumentProvider getDocumentProvider(
313: IEditorInput editorInput) {
314:
315: IDocumentProvider provider = null;
316:
317: IFile file = (IFile) editorInput.getAdapter(IFile.class);
318: if (file != null)
319: provider = getDocumentProvider(file.getFileExtension());
320:
321: if (provider == null) {
322: Set set = (Set) findInputTypeMapping(editorInput.getClass());
323: if (set != null) {
324: IConfigurationElement entry = selectConfigurationElement(set);
325: provider = getDocumentProvider(entry);
326: }
327: }
328:
329: return provider;
330: }
331: }
|