blob: 31fdfb8db05a0796db070a26cf1aee2d3f898564 [file] [log] [blame]
david_williamscfdb2cd2004-11-11 08:37:49 +00001/*******************************************************************************
2 * Copyright (c) 2001, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Jens Lukowski/Innoopract - initial renaming/restructuring
11 *
12 *******************************************************************************/
13package org.eclipse.wst.sse.ui.internal.reconcile.validator;
14
amywu7d8efb02006-04-13 04:08:07 +000015import java.lang.reflect.InvocationTargetException;
david_williamscfdb2cd2004-11-11 08:37:49 +000016import java.util.ArrayList;
17import java.util.Arrays;
18import java.util.HashMap;
pavery46c93e72005-02-08 16:07:33 +000019import java.util.HashSet;
david_williamscfdb2cd2004-11-11 08:37:49 +000020import java.util.Iterator;
21import java.util.List;
pavery46c93e72005-02-08 16:07:33 +000022import java.util.Set;
david_williamscfdb2cd2004-11-11 08:37:49 +000023
amywu7d8efb02006-04-13 04:08:07 +000024import org.eclipse.core.resources.IFile;
25import org.eclipse.core.resources.IProject;
26import org.eclipse.core.resources.ResourcesPlugin;
27import org.eclipse.core.runtime.IPath;
28import org.eclipse.core.runtime.Path;
pavery46c93e72005-02-08 16:07:33 +000029import org.eclipse.core.runtime.Platform;
30import org.eclipse.core.runtime.content.IContentType;
31import org.eclipse.core.runtime.content.IContentTypeManager;
david_williamscfdb2cd2004-11-11 08:37:49 +000032import org.eclipse.jface.text.IDocument;
33import org.eclipse.jface.text.ITypedRegion;
34import org.eclipse.jface.text.reconciler.DirtyRegion;
35import org.eclipse.jface.text.reconciler.IReconcileResult;
36import org.eclipse.jface.text.reconciler.IReconcileStep;
nitindf8e77632005-09-07 23:49:25 +000037import org.eclipse.jface.text.source.ISourceViewer;
amywu7d8efb02006-04-13 04:08:07 +000038import org.eclipse.wst.sse.core.StructuredModelManager;
39import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
paveryb0763c02005-10-17 15:55:28 +000040import org.eclipse.wst.sse.ui.internal.IReleasable;
amywu7d8efb02006-04-13 04:08:07 +000041import org.eclipse.wst.sse.ui.internal.Logger;
david_williamscfdb2cd2004-11-11 08:37:49 +000042import org.eclipse.wst.sse.ui.internal.reconcile.DocumentAdapter;
pavery1f1588d2006-02-15 20:44:54 +000043import org.eclipse.wst.sse.ui.internal.reconcile.ReconcileAnnotationKey;
paverydc8b3062005-10-24 20:43:42 +000044import org.eclipse.wst.sse.ui.internal.reconcile.StructuredTextReconcilingStrategy;
david_williamscfdb2cd2004-11-11 08:37:49 +000045import org.eclipse.wst.sse.ui.internal.reconcile.TemporaryAnnotation;
amywu7d8efb02006-04-13 04:08:07 +000046import org.eclipse.wst.validation.internal.ConfigurationManager;
47import org.eclipse.wst.validation.internal.ProjectConfiguration;
48import org.eclipse.wst.validation.internal.ValidationRegistryReader;
david_williams49d24fb2005-04-08 21:16:59 +000049import org.eclipse.wst.validation.internal.provisional.core.IValidator;
david_williamscfdb2cd2004-11-11 08:37:49 +000050
51
52/**
paveryf918eb22005-03-29 18:26:53 +000053 * Special validator strategy. Runs validator steps contributed via the
nitindb0445972006-02-16 22:19:20 +000054 * <code>org.eclipse.wst.sse.ui.extensions.sourcevalidation</code> extension
55 * point
david_williamscfdb2cd2004-11-11 08:37:49 +000056 *
57 * @author pavery
58 */
paverydc8b3062005-10-24 20:43:42 +000059public class ValidatorStrategy extends StructuredTextReconcilingStrategy {
nitindb0445972006-02-16 22:19:20 +000060
61 private String[] fContentTypeIds = null;
david_williamscfdb2cd2004-11-11 08:37:49 +000062 private List fMetaData = null;
nitindb0445972006-02-16 22:19:20 +000063 /** validator id (as declared in ext point) -> ReconcileStepForValidator * */
64 private HashMap fVidToVStepMap = null;
amywu7d8efb02006-04-13 04:08:07 +000065 private ProjectConfiguration fProjectConfiguration = null;
nitindb0445972006-02-16 22:19:20 +000066
67 /*
68 * List of ValidatorMetaDatas of total scope validators that have been run
69 * since beginProcessing() was called.
70 */
71 private List fTotalScopeValidatorsAlreadyRun;
david_williamscfdb2cd2004-11-11 08:37:49 +000072
nitindf8e77632005-09-07 23:49:25 +000073 public ValidatorStrategy(ISourceViewer sourceViewer, String contentType) {
74 super(sourceViewer);
david_williamscfdb2cd2004-11-11 08:37:49 +000075 fMetaData = new ArrayList();
pavery46c93e72005-02-08 16:07:33 +000076 fContentTypeIds = calculateParentContentTypeIds(contentType);
pavery55ae4472005-02-16 23:00:16 +000077 fVidToVStepMap = new HashMap();
david_williamscfdb2cd2004-11-11 08:37:49 +000078 }
79
nitindb0445972006-02-16 22:19:20 +000080 public void addValidatorMetaData(ValidatorMetaData vmd) {
david_williamscfdb2cd2004-11-11 08:37:49 +000081 fMetaData.add(vmd);
82 }
83
nitindb0445972006-02-16 22:19:20 +000084 public void beginProcessing() {
85 if (fTotalScopeValidatorsAlreadyRun == null)
86 fTotalScopeValidatorsAlreadyRun = new ArrayList();
87 }
88
89 /**
90 * The content type passed in should be the most specific one. TODO: This
91 * exact method is also in ValidatorMetaData. Should be in a common place.
92 *
93 * @param contentType
94 * @return
95 */
96 private String[] calculateParentContentTypeIds(String contentTypeId) {
97
98 Set parentTypes = new HashSet();
99
100 IContentTypeManager ctManager = Platform.getContentTypeManager();
101 IContentType ct = ctManager.getContentType(contentTypeId);
102 String id = contentTypeId;
103
104 while (ct != null && id != null) {
105
106 parentTypes.add(id);
107 ct = ctManager.getContentType(id);
108 if (ct != null) {
109 IContentType baseType = ct.getBaseType();
110 id = (baseType != null) ? baseType.getId() : null;
111 }
112 }
113 return (String[]) parentTypes.toArray(new String[parentTypes.size()]);
114 }
115
pavery3f791842006-02-15 21:32:25 +0000116 protected boolean canHandlePartition(String partitionType) {
david_williamscfdb2cd2004-11-11 08:37:49 +0000117 ValidatorMetaData vmd = null;
118 for (int i = 0; i < fMetaData.size(); i++) {
119 vmd = (ValidatorMetaData) fMetaData.get(i);
nitindb0445972006-02-16 22:19:20 +0000120 if (vmd.canHandlePartitionType(getContentTypeIds(), partitionType))
david_williamscfdb2cd2004-11-11 08:37:49 +0000121 return true;
122 }
123 return false;
124 }
nitindb0445972006-02-16 22:19:20 +0000125
126 protected boolean containsStep(IReconcileStep step) {
127 return fVidToVStepMap.containsValue(step);
128 }
david_williamscfdb2cd2004-11-11 08:37:49 +0000129
130 /**
david_williams4ad020f2005-04-18 08:00:30 +0000131 * @see org.eclipse.wst.sse.ui.internal.provisional.reconcile.AbstractStructuredTextReconcilingStrategy#createReconcileSteps()
david_williamscfdb2cd2004-11-11 08:37:49 +0000132 */
133 public void createReconcileSteps() {
134 // do nothing, steps are created
135 }
nitindb0445972006-02-16 22:19:20 +0000136
137 public void endProcessing() {
138 fTotalScopeValidatorsAlreadyRun.clear();
139 }
140
pavery1f1588d2006-02-15 20:44:54 +0000141 /**
142 * All content types on which this ValidatorStrategy can run
nitindb0445972006-02-16 22:19:20 +0000143 *
pavery1f1588d2006-02-15 20:44:54 +0000144 * @return
145 */
pavery46c93e72005-02-08 16:07:33 +0000146 public String[] getContentTypeIds() {
147 return fContentTypeIds;
david_williamscfdb2cd2004-11-11 08:37:49 +0000148 }
149
paverydfec4172005-11-09 21:42:30 +0000150 /**
nitindb0445972006-02-16 22:19:20 +0000151 * @param tr
152 * Partition of the region to reconcile.
153 * @param dr
154 * Dirty region representation of the typed region
paverydfec4172005-11-09 21:42:30 +0000155 */
pavery46c93e72005-02-08 16:07:33 +0000156 public void reconcile(ITypedRegion tr, DirtyRegion dr) {
nitindb0445972006-02-16 22:19:20 +0000157
158 if (isCanceled())
paverybaf7e522005-07-07 20:44:14 +0000159 return;
nitindb0445972006-02-16 22:19:20 +0000160
paverydfec4172005-11-09 21:42:30 +0000161 IDocument doc = getDocument();
david_williamscfdb2cd2004-11-11 08:37:49 +0000162 // for external files, this can be null
nitindb0445972006-02-16 22:19:20 +0000163 if (doc == null)
paverydfec4172005-11-09 21:42:30 +0000164 return;
nitindb0445972006-02-16 22:19:20 +0000165
paverydfec4172005-11-09 21:42:30 +0000166 String partitionType = tr.getType();
david_williamscfdb2cd2004-11-11 08:37:49 +0000167
nitindb0445972006-02-16 22:19:20 +0000168 ValidatorMetaData vmd = null;
169 List annotationsToAdd = new ArrayList();
david_williams5ce6d6c2006-05-28 23:58:12 +0000170 List stepsRanOnThisDirtyRegion = new ArrayList(1);
nitindb0445972006-02-16 22:19:20 +0000171 /*
172 * Loop through all of the relevant validator meta data to find
173 * supporting validators for this partition type. Don't check
174 * this.canHandlePartition() before-hand since it just loops through
175 * and calls vmd.canHandlePartitionType()...which we're already doing
176 * here anyway to find the right vmd.
177 */
178 for (int i = 0; i < fMetaData.size() && !isCanceled(); i++) {
179 vmd = (ValidatorMetaData) fMetaData.get(i);
180 if (vmd.canHandlePartitionType(getContentTypeIds(), partitionType)) {
amywu7d8efb02006-04-13 04:08:07 +0000181 /*
182 * Check if validator is enabled according to validation
183 * preferences before attempting to create/use it
184 */
185 if (isValidatorEnabled(vmd)) {
186 int validatorScope = vmd.getValidatorScope();
187 ReconcileStepForValidator validatorStep = null;
188 // get step for partition type
189 Object o = fVidToVStepMap.get(vmd.getValidatorId());
190 if (o != null) {
191 validatorStep = (ReconcileStepForValidator) o;
192 }
193 else {
194 // if doesn't exist, create one
195 IValidator validator = vmd.createValidator();
david_williamscfdb2cd2004-11-11 08:37:49 +0000196
amywu7d8efb02006-04-13 04:08:07 +0000197 validatorStep = new ReconcileStepForValidator(validator, validatorScope);
198 validatorStep.setInputModel(new DocumentAdapter(doc));
nitindb0445972006-02-16 22:19:20 +0000199
amywu7d8efb02006-04-13 04:08:07 +0000200 fVidToVStepMap.put(vmd.getValidatorId(), validatorStep);
201 }
nitindb0445972006-02-16 22:19:20 +0000202
amywu7d8efb02006-04-13 04:08:07 +0000203 if (!fTotalScopeValidatorsAlreadyRun.contains(vmd)) {
204 annotationsToAdd.addAll(Arrays.asList(validatorStep.reconcile(dr, dr)));
david_williams5ce6d6c2006-05-28 23:58:12 +0000205 stepsRanOnThisDirtyRegion.add(validatorStep);
nitindb0445972006-02-16 22:19:20 +0000206
amywu7d8efb02006-04-13 04:08:07 +0000207 if (validatorScope == ReconcileAnnotationKey.TOTAL) {
208 // mark this validator as "run"
209 fTotalScopeValidatorsAlreadyRun.add(vmd);
210 }
nitindb0445972006-02-16 22:19:20 +0000211 }
david_williamscfdb2cd2004-11-11 08:37:49 +0000212 }
213 }
nitindb0445972006-02-16 22:19:20 +0000214 }
215
david_williams5ce6d6c2006-05-28 23:58:12 +0000216 TemporaryAnnotation[] annotationsToRemove = getAnnotationsToRemove(dr, stepsRanOnThisDirtyRegion);
nitindb0445972006-02-16 22:19:20 +0000217 if (annotationsToRemove.length + annotationsToAdd.size() > 0)
218 smartProcess(annotationsToRemove, (IReconcileResult[]) annotationsToAdd.toArray(new IReconcileResult[annotationsToAdd.size()]));
219 }
220
221 public void release() {
222 super.release();
223 Iterator it = fVidToVStepMap.values().iterator();
224 IReconcileStep step = null;
225 while (it.hasNext()) {
226 step = (IReconcileStep) it.next();
227 if (step instanceof IReleasable)
228 ((IReleasable) step).release();
david_williamscfdb2cd2004-11-11 08:37:49 +0000229 }
230 }
231
nitindb0445972006-02-16 22:19:20 +0000232 /**
david_williamscfdb2cd2004-11-11 08:37:49 +0000233 * @see org.eclipse.wst.sse.ui.internal.reconcile.AbstractStructuredTextReconcilingStrategy#setDocument(org.eclipse.jface.text.IDocument)
234 */
235 public void setDocument(IDocument document) {
nitindb0445972006-02-16 22:19:20 +0000236
david_williamscfdb2cd2004-11-11 08:37:49 +0000237 super.setDocument(document);
nitindb0445972006-02-16 22:19:20 +0000238
pavery55ae4472005-02-16 23:00:16 +0000239 // validator steps are in "fVIdToVStepMap" (as opposed to fFirstStep >
david_williamscfdb2cd2004-11-11 08:37:49 +0000240 // next step etc...)
pavery55ae4472005-02-16 23:00:16 +0000241 Iterator it = fVidToVStepMap.values().iterator();
david_williamscfdb2cd2004-11-11 08:37:49 +0000242 IReconcileStep step = null;
243 while (it.hasNext()) {
244 step = (IReconcileStep) it.next();
245 step.setInputModel(new DocumentAdapter(document));
246 }
247 }
amywu7d8efb02006-04-13 04:08:07 +0000248
249 /**
250 * Checks if validator is enabled according to Validation preferences
251 *
252 * @param vmd
253 * @return
254 */
255 private boolean isValidatorEnabled(ValidatorMetaData vmd) {
amywue6620de2006-04-13 22:03:17 +0000256 boolean enabled = true;
amywu7d8efb02006-04-13 04:08:07 +0000257 ProjectConfiguration configuration = getProjectConfiguration();
258 org.eclipse.wst.validation.internal.ValidatorMetaData metadata = ValidationRegistryReader.getReader().getValidatorMetaData(vmd.getValidatorClass());
259 if (configuration != null && metadata != null) {
amywue6620de2006-04-13 22:03:17 +0000260 if (!configuration.isBuildEnabled(metadata) && !configuration.isManualEnabled(metadata))
261 enabled = false;
amywu7d8efb02006-04-13 04:08:07 +0000262 }
263 return enabled;
264 }
265
266 /**
267 * Gets current validation project configuration based on current project
268 * (which is based on current document)
269 *
270 * @return ProjectConfiguration
271 */
272 private ProjectConfiguration getProjectConfiguration() {
273 if (fProjectConfiguration == null) {
274 IFile file = getFile();
275 if (file != null) {
276 IProject project = file.getProject();
277 if (project != null) {
278 try {
279 fProjectConfiguration = ConfigurationManager.getManager().getProjectConfiguration(project);
280 }
281 catch (InvocationTargetException e) {
282 Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
283 }
284 }
285 }
286 }
287
288 return fProjectConfiguration;
289 }
290
291 /**
292 * Gets IFile from current document
293 *
nitind6f360012006-05-02 18:48:59 +0000294 * @return IFile the IFile, null if no such file exists
amywu7d8efb02006-04-13 04:08:07 +0000295 */
296 private IFile getFile() {
297 IStructuredModel model = null;
298 IFile file = null;
299 try {
300 model = StructuredModelManager.getModelManager().getExistingModelForRead(getDocument());
301 if (model != null) {
302 String baseLocation = model.getBaseLocation();
303 // The baseLocation may be a path on disk or relative to the
304 // workspace root. Don't translate on-disk paths to
305 // in-workspace resources.
306 IPath basePath = new Path(baseLocation);
nitind6f360012006-05-02 18:48:59 +0000307 if (basePath.segmentCount() > 1) {
amywu7d8efb02006-04-13 04:08:07 +0000308 file = ResourcesPlugin.getWorkspace().getRoot().getFile(basePath);
nitind6f360012006-05-02 18:48:59 +0000309 /*
310 * If the IFile doesn't exist, make sure it's not
311 * returned
312 */
313 if (!file.exists())
314 file = null;
amywu7d8efb02006-04-13 04:08:07 +0000315 }
316 }
317 }
318 finally {
319 if (model != null) {
320 model.releaseFromRead();
321 }
322 }
323 return file;
324 }
david_williamscfdb2cd2004-11-11 08:37:49 +0000325}