1 /*******************************************************************************
2  * Copyright (c) 2005, 2008 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  *******************************************************************************/
14 package org.eclipse.ui.texteditor.rulers;
15 
16 import java.net.URL;
17 
18 import org.eclipse.core.runtime.Assert;
19 import org.eclipse.core.runtime.CoreException;
20 import org.eclipse.core.runtime.IConfigurationElement;
21 import org.eclipse.core.runtime.InvalidRegistryObjectException;
22 import org.eclipse.core.runtime.content.IContentType;
23 
24 import org.eclipse.jface.resource.ImageDescriptor;
25 
26 import org.eclipse.ui.IEditorInput;
27 import org.eclipse.ui.IWorkbenchPartSite;
28 import org.eclipse.ui.internal.texteditor.rulers.ExtensionPointHelper;
29 import org.eclipse.ui.internal.texteditor.rulers.RulerColumnMessages;
30 import org.eclipse.ui.internal.texteditor.rulers.RulerColumnPlacement;
31 import org.eclipse.ui.internal.texteditor.rulers.RulerColumnTarget;
32 
33 import org.eclipse.ui.texteditor.IDocumentProvider;
34 import org.eclipse.ui.texteditor.IDocumentProviderExtension4;
35 import org.eclipse.ui.texteditor.ITextEditor;
36 
37 
38 /**
39  * The description of an extension to the
40  * <code>org.eclipse.ui.workbench.texteditor.rulerColumns</code> extension point. Instances are
41  * immutable. Instances can be obtained from a {@link RulerColumnRegistry}.
42  *
43  * @since 3.3
44  * @noinstantiate This class is not intended to be instantiated by clients.
45  */
46 public final class RulerColumnDescriptor {
47 	/** The extension schema name of the class attribute. */
48 	private static final String CLASS= "class"; //$NON-NLS-1$
49 	/** The extension schema name of the id attribute. */
50 	private static final String ID= "id"; //$NON-NLS-1$
51 	/** The extension schema name of the optional name attribute. */
52 	private static final String NAME= "name"; //$NON-NLS-1$
53 	/** The extension schema name of the optional enabled attribute. */
54 	private static final String ENABLED= "enabled"; //$NON-NLS-1$
55 	/** The extension schema name of the optional icon attribute. */
56 	private static final String ICON= "icon"; //$NON-NLS-1$
57 	/** The extension schema name of the optional global attribute. */
58 	private static final String GLOBAL= "global"; //$NON-NLS-1$
59 	/** The extension schema name of the optional menu inclusion attribute. */
60 	private static final String INCLUDE_IN_MENU= "includeInMenu"; //$NON-NLS-1$
61 	/** The extension schema name of the targetEditor element. */
62 	private static final String TARGET_EDITOR= "targetEditor"; //$NON-NLS-1$
63 	/** The extension schema name of the targetContentType element. */
64 	private static final String TARGET_CONTENT_TYPE= "targetContentType"; //$NON-NLS-1$
65 	/** The extension schema name of the targetClass element. */
66 	private static final String TARGET_CLASS= "targetClass"; //$NON-NLS-1$
67 	/** The extension schema name of the placement element. */
68 	private static final String PLACEMENT= "placement"; //$NON-NLS-1$
69 
70 	/** The identifier of the extension. */
71 	private final String fId;
72 	/** The name of the extension, equal to the id if no name is given. */
73 	private final String fName;
74 	/** The icon descriptor. */
75 	private final ImageDescriptor fIcon;
76 	/** The configuration element of this extension. */
77 	private final IConfigurationElement fElement;
78 	/** The target specification of the ruler column contribution. */
79 	private final RulerColumnTarget fTarget;
80 	/** The placement specification of the ruler column contribution. */
81 	private final RulerColumnPlacement fRulerColumnPlacement;
82 	/** The default enablement setting of the ruler column contribution. */
83 	private final boolean fDefaultEnablement;
84 	/** The global setting of the ruler column contribution. */
85 	private final boolean fIsGlobal;
86 	/** The menu inclusion setting of the ruler column contribution. */
87 	private final boolean fIncludeInMenu;
88 
89 	/**
90 	 * Creates a new descriptor.
91 	 *
92 	 * @param element the configuration element to read
93 	 * @param registry the computer registry creating this descriptor
94 	 * @throws InvalidRegistryObjectException if the configuration element is no longer valid
95 	 * @throws CoreException if the configuration element does not conform to the extension point spec
96 	 */
RulerColumnDescriptor(IConfigurationElement element, RulerColumnRegistry registry)97 	RulerColumnDescriptor(IConfigurationElement element, RulerColumnRegistry registry) throws InvalidRegistryObjectException, CoreException {
98 		Assert.isLegal(registry != null);
99 		Assert.isLegal(element != null);
100 		fElement= element;
101 
102 		ExtensionPointHelper helper= new ExtensionPointHelper(element);
103 
104 		fId= helper.getNonNullAttribute(ID);
105 		fName= helper.getDefaultAttribute(NAME, fId);
106 		helper.getNonNullAttribute(CLASS); // just check validity
107 		URL iconURL= helper.getDefaultResourceURL(ICON, null);
108 		fIcon= iconURL == null ? null : ImageDescriptor.createFromURL(iconURL);
109 		fDefaultEnablement= helper.getDefaultAttribute(ENABLED, true);
110 		fIsGlobal= helper.getDefaultAttribute(GLOBAL, true);
111 		fIncludeInMenu= helper.getDefaultAttribute(INCLUDE_IN_MENU, true);
112 
113 		IConfigurationElement[] targetEditors= element.getChildren(TARGET_EDITOR);
114 		IConfigurationElement[] targetContentTypes= element.getChildren(TARGET_CONTENT_TYPE);
115 		IConfigurationElement[] targetClasses= element.getChildren(TARGET_CLASS);
116 
117 		if (targetContentTypes.length + targetEditors.length + targetClasses.length == 0) {
118 			helper.fail(RulerColumnMessages.RulerColumnDescriptor_missing_target_msg);
119 			fTarget= null; // dummy
120 		} else {
121 			RulerColumnTarget combined= null;
122 			for (IConfigurationElement targetEditor : targetEditors) {
123 				RulerColumnTarget target= RulerColumnTarget.createEditorIdTarget(new ExtensionPointHelper(targetEditor).getNonNullAttribute(ID));
124 				combined= RulerColumnTarget.createOrTarget(combined, target);
125 			}
126 			for (IConfigurationElement targetContentType : targetContentTypes) {
127 				RulerColumnTarget target= RulerColumnTarget.createContentTypeTarget(new ExtensionPointHelper(targetContentType).getNonNullAttribute(ID));
128 				combined= RulerColumnTarget.createOrTarget(combined, target);
129 			}
130 			for (IConfigurationElement targetClass : targetClasses) {
131 				RulerColumnTarget target= RulerColumnTarget.createClassTarget(new ExtensionPointHelper(targetClass).getNonNullAttribute(CLASS));
132 				combined= RulerColumnTarget.createOrTarget(combined, target);
133 			}
134 			fTarget= combined;
135 		}
136 
137 		IConfigurationElement[] placements= element.getChildren(PLACEMENT);
138 		switch (placements.length) {
139 			case 0:
140 				fRulerColumnPlacement= new RulerColumnPlacement();
141 				break;
142 			case 1:
143 				fRulerColumnPlacement= new RulerColumnPlacement(placements[0]);
144 				break;
145 			default:
146 				helper.fail(RulerColumnMessages.RulerColumnDescriptor_invalid_placement_msg);
147 				fRulerColumnPlacement= null; // dummy
148 				break;
149 		}
150 
151 		Assert.isTrue(fTarget != null);
152 		Assert.isTrue(fRulerColumnPlacement != null);
153 	}
154 
155 	/**
156 	 * Returns the identifier of the described extension.
157 	 *
158 	 * @return the identifier of the described extension
159 	 */
getId()160 	public String getId() {
161 		return fId;
162 	}
163 
164 	/**
165 	 * Returns the name of the described extension.
166 	 *
167 	 * @return the name of the described extension
168 	 */
getName()169 	public String getName() {
170 		return fName;
171 	}
172 
173 	/**
174 	 * Returns the image descriptor of the described extension, <code>null</code> if it does not
175 	 * have an image.
176 	 *
177 	 * @return the image descriptor of the described extension or <code>null</code> for no image
178 	 */
getIcon()179 	public ImageDescriptor getIcon() {
180 		return fIcon;
181 	}
182 
getTarget()183 	RulerColumnTarget getTarget() {
184 		return fTarget;
185 	}
186 
getPlacement()187 	RulerColumnPlacement getPlacement() {
188 		return fRulerColumnPlacement;
189 	}
190 
191 	/**
192 	 * Returns the default enablement of the described extension. Editors that support this
193 	 * contribution should typically enable the column by default.
194 	 *
195 	 * @return the default enablement of the described extension
196 	 */
getDefaultEnablement()197 	public boolean getDefaultEnablement() {
198 		return fDefaultEnablement;
199 	}
200 
201 	/**
202 	 * Returns the global property of the described extension. Changing the visibility of a column
203 	 * with the global property set to <code>true</code> should typically affect all matching
204 	 * editors. Changing the visibility of a column with the global property set to
205 	 * <code>false</code> should only affect the current editor.
206 	 *
207 	 * @return the global property of the described extension
208 	 */
isGlobal()209 	public boolean isGlobal() {
210 		return fIsGlobal;
211 	}
212 
213 	/**
214 	 * Returns the menu inclusion property of the described extension. A toggle menu entry should be
215 	 * inluded in the ruler context menu for columns with this property set to <code>true</code>.
216 	 *
217 	 * @return the menu inclusion property of the described extension
218 	 */
isIncludedInMenu()219 	public boolean isIncludedInMenu() {
220 		return fIncludeInMenu;
221 	}
222 
223 	/**
224 	 * Returns <code>true</code> if this contribution matches the passed editor, <code>false</code> if not.
225 	 *
226 	 * @param editor the editor to check
227 	 * @return <code>true</code> if this contribution targets the passed editor
228 	 */
matchesEditor(ITextEditor editor)229 	public boolean matchesEditor(ITextEditor editor) {
230 		Assert.isLegal(editor != null);
231 		RulerColumnTarget target= getTarget();
232 
233 		IWorkbenchPartSite site= editor.getSite();
234 		if (site != null && target.matchesEditorId(site.getId()))
235 			return true;
236 
237 		if (target.matchesClass(editor.getClass()))
238 			return true;
239 
240 		IContentType contentType= getContentType(editor);
241 		return contentType != null && target.matchesContentType(contentType);
242 
243 	}
244 
245 	/**
246 	 * Creates a {@link IContributedRulerColumn} instance as described by the receiver. This may load the contributing plug-in.
247 	 *
248 	 * @param editor the editor that loads the contributed column
249 	 * @return the instantiated column
250 	 * @throws CoreException as thrown by {@link IConfigurationElement#createExecutableExtension(String)}
251 	 * @throws InvalidRegistryObjectException as thrown by {@link IConfigurationElement#createExecutableExtension(String)}
252 	 */
createColumn(ITextEditor editor)253 	public IContributedRulerColumn createColumn(ITextEditor editor) throws CoreException, InvalidRegistryObjectException {
254 		Assert.isLegal(editor != null);
255 		IContributedRulerColumn column= (IContributedRulerColumn)fElement.createExecutableExtension(CLASS);
256 		column.setDescriptor(this);
257 		column.setEditor(editor);
258 		column.columnCreated();
259 		return column;
260 	}
261 
262 	@Override
toString()263 	public String toString() {
264 		return "RulerColumnDescriptor[name=" + getName() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
265 	}
266 
getConfigurationElement()267 	IConfigurationElement getConfigurationElement() {
268 		return fElement;
269 	}
270 
271 	@Override
hashCode()272 	public int hashCode() {
273 		final int prime= 31;
274 		int result= 1;
275 		result= prime * result + ((fId == null) ? 0 : fId.hashCode());
276 		return result;
277 	}
278 
279 	@Override
equals(Object obj)280 	public boolean equals(Object obj) {
281 		if (this == obj)
282 			return true;
283 		if (obj == null)
284 			return false;
285 		if (getClass() != obj.getClass())
286 			return false;
287 		final RulerColumnDescriptor other= (RulerColumnDescriptor) obj;
288 		if (fId == null) {
289 			if (other.fId != null)
290 				return false;
291 		} else if (!fId.equals(other.fId))
292 			return false;
293 		return true;
294 	}
295 
296 	/**
297 	 * Returns the content type of the editor's input, <code>null</code> if the editor input or
298 	 * the document provider is <code>null</code> or the content type cannot be determined.
299 	 *
300 	 * @param editor the editor to get the content type from
301 	 * @return the content type of the editor's input, <code>null</code> if it cannot be
302 	 *         determined
303 	 */
getContentType(ITextEditor editor)304 	private IContentType getContentType(ITextEditor editor) {
305 		IEditorInput input= editor.getEditorInput();
306 		if (input == null)
307 			return null;
308 		IDocumentProvider provider= editor.getDocumentProvider();
309 		if (provider instanceof IDocumentProviderExtension4) {
310 			IDocumentProviderExtension4 ext= (IDocumentProviderExtension4) provider;
311 			try {
312 				return ext.getContentType(input);
313 			} catch (CoreException x) {
314 				// ignore and return null;
315 			}
316 		}
317 		return null;
318 	}
319 
getContributor()320 	String getContributor() {
321 		try {
322 			return fElement.getContributor().getName();
323 		} catch (InvalidRegistryObjectException e) {
324 			return "unknown"; //$NON-NLS-1$
325 		}
326 	}
327 }
328