blob: 756566a153b0b0516a97cd5e91c4118a72b4903f [file] [log] [blame]
david_williams49d24fb2005-04-08 21:16:59 +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 *******************************************************************************/
david_williams96643dd2005-04-13 04:35:24 +000011package org.eclipse.wst.html.internal.validation;
paverye6107e62005-02-23 18:01:10 +000012
13import java.io.IOException;
14import java.io.UnsupportedEncodingException;
15
paverya0e2eae2005-10-20 20:24:29 +000016import org.eclipse.core.filebuffers.ITextFileBuffer;
paverye6107e62005-02-23 18:01:10 +000017import org.eclipse.core.resources.IContainer;
18import org.eclipse.core.resources.IFile;
19import org.eclipse.core.resources.IProject;
20import org.eclipse.core.resources.IResource;
21import org.eclipse.core.resources.ResourcesPlugin;
22import org.eclipse.core.runtime.CoreException;
david_williamse6ed7322005-10-10 17:32:24 +000023import org.eclipse.core.runtime.IPath;
paverye6107e62005-02-23 18:01:10 +000024import org.eclipse.core.runtime.NullProgressMonitor;
25import org.eclipse.core.runtime.OperationCanceledException;
26import org.eclipse.core.runtime.Path;
david_williamse6ed7322005-10-10 17:32:24 +000027import org.eclipse.core.runtime.Platform;
nitinda06daf82005-11-01 22:42:33 +000028import org.eclipse.core.runtime.content.IContentDescription;
david_williamse6ed7322005-10-10 17:32:24 +000029import org.eclipse.core.runtime.content.IContentType;
30import org.eclipse.core.runtime.content.IContentTypeManager;
paverya0e2eae2005-10-20 20:24:29 +000031import org.eclipse.jface.text.IDocument;
32import org.eclipse.jface.text.IRegion;
nitinde83b1a42005-06-22 21:22:20 +000033import org.eclipse.osgi.util.NLS;
david_williams56777022005-04-11 06:21:55 +000034import org.eclipse.wst.html.core.internal.validate.HTMLValidationAdapterFactory;
david_williams3a2ab2d2005-04-11 16:32:45 +000035import org.eclipse.wst.html.ui.internal.HTMLUIMessages;
paverya0e2eae2005-10-20 20:24:29 +000036import org.eclipse.wst.sse.core.internal.FileBufferModelManager;
david_williams4ad020f2005-04-18 08:00:30 +000037import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
38import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory;
paverya0e2eae2005-10-20 20:24:29 +000039import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
david_williams4ad020f2005-04-18 08:00:30 +000040import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
paverya0e2eae2005-10-20 20:24:29 +000041import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
david_williams4ad020f2005-04-18 08:00:30 +000042import org.eclipse.wst.sse.core.internal.provisional.StructuredModelManager;
david_williams2aecf082005-04-13 05:03:21 +000043import org.eclipse.wst.sse.core.internal.util.URIResolver;
david_williams3bcfe392005-04-13 04:49:36 +000044import org.eclipse.wst.sse.core.internal.validate.ValidationAdapter;
paverya0e2eae2005-10-20 20:24:29 +000045import org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator;
vbhadrir0b14da32005-04-18 15:40:15 +000046import org.eclipse.wst.validation.internal.core.Message;
vbhadrir271d28a2005-04-12 19:36:05 +000047import org.eclipse.wst.validation.internal.operations.IWorkbenchContext;
paverye6107e62005-02-23 18:01:10 +000048import org.eclipse.wst.validation.internal.operations.WorkbenchReporter;
david_williams49d24fb2005-04-08 21:16:59 +000049import org.eclipse.wst.validation.internal.provisional.core.IMessage;
50import org.eclipse.wst.validation.internal.provisional.core.IReporter;
51import org.eclipse.wst.validation.internal.provisional.core.IValidationContext;
52import org.eclipse.wst.validation.internal.provisional.core.IValidator;
david_williams646a5e22005-04-02 07:16:27 +000053import org.eclipse.wst.xml.core.internal.document.DocumentTypeAdapter;
david_williams4ad020f2005-04-18 08:00:30 +000054import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
55import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
paverydc8b3062005-10-24 20:43:42 +000056import org.w3c.dom.Text;
paverye6107e62005-02-23 18:01:10 +000057
paverya0e2eae2005-10-20 20:24:29 +000058public class HTMLValidator implements IValidator, ISourceValidator {
nitind73e76412005-11-10 01:50:19 +000059 private static final String ORG_ECLIPSE_JST_JSP_CORE_JSPSOURCE = "org.eclipse.jst.jsp.core.jspsource"; //$NON-NLS-1$
60 private static final String ORG_ECLIPSE_WST_HTML_CORE_HTMLSOURCE = "org.eclipse.wst.html.core.htmlsource"; //$NON-NLS-1$
david_williamse6ed7322005-10-10 17:32:24 +000061
nitind165498a2005-07-12 18:47:49 +000062 static boolean shouldValidate(IFile file) {
63 IResource resource = file;
64 do {
65 if (resource.isDerived() || resource.isTeamPrivateMember() || !resource.isAccessible() || (resource.getName().charAt(0) == '.' && resource.getType() == IResource.FOLDER)) {
66 return false;
67 }
68 resource = resource.getParent();
69 }
70 while ((resource.getType() & IResource.PROJECT) == 0);
71 return true;
72 }
73
paverya0e2eae2005-10-20 20:24:29 +000074 private IDocument fDocument;
75
paverye6107e62005-02-23 18:01:10 +000076 public HTMLValidator() {
77 super();
78 }
79
80 /**
81 */
82 public void cleanup(IReporter reporter) {
83 // nothing to do
84 }
85
86 /**
87 */
david_williamsc39caaf2005-04-05 06:07:16 +000088 protected IDOMModel getModel(IProject project, IFile file) {
paverye6107e62005-02-23 18:01:10 +000089 if (project == null || file == null)
90 return null;
91 if (!file.exists())
92 return null;
93 if (!canHandle(file))
94 return null;
95
96 IStructuredModel model = null;
97 IModelManager manager = StructuredModelManager.getModelManager();
98
99 try {
100 try {
101 model = manager.getModelForRead(file);
102 }
103 catch (UnsupportedEncodingException ex) {
david_williams49d24fb2005-04-08 21:16:59 +0000104 // retry ignoring META charset for invalid META charset
105 // specification
paverye6107e62005-02-23 18:01:10 +0000106 // recreate input stream, because it is already partially read
107 model = manager.getModelForRead(file, new String(), null);
108 }
109 }
110 catch (UnsupportedEncodingException ex) {
111 }
112 catch (IOException ex) {
113 }
114 catch (CoreException ex) {
115 }
116
117 if (model == null)
118 return null;
david_williamsc39caaf2005-04-05 06:07:16 +0000119 if (!(model instanceof IDOMModel)) {
paverye6107e62005-02-23 18:01:10 +0000120 releaseModel(model);
121 return null;
122 }
david_williamsc39caaf2005-04-05 06:07:16 +0000123 return (IDOMModel) model;
paverye6107e62005-02-23 18:01:10 +0000124 }
125
126 /**
127 */
david_williamsc39caaf2005-04-05 06:07:16 +0000128 protected HTMLValidationReporter getReporter(IReporter reporter, IFile file, IDOMModel model) {
paverye6107e62005-02-23 18:01:10 +0000129 return new HTMLValidationReporter(this, reporter, file, model);
130 }
131
132 /**
133 * Check file extension to validate
134 */
135 private boolean canHandle(IFile file) {
david_williamse6ed7322005-10-10 17:32:24 +0000136 boolean result = false;
137 if (file != null) {
138 try {
139 IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
140
nitinda06daf82005-11-01 22:42:33 +0000141 IContentDescription contentDescription = file.getContentDescription();
david_williamse6ed7322005-10-10 17:32:24 +0000142 IContentType htmlContentType = contentTypeManager.getContentType(ORG_ECLIPSE_WST_HTML_CORE_HTMLSOURCE);
nitinda06daf82005-11-01 22:42:33 +0000143 if (contentDescription != null) {
144 IContentType fileContentType = contentDescription.getContentType();
145
146 if (htmlContentType != null) {
147 if (fileContentType.isKindOf(htmlContentType)) {
148 result = true;
149 }
150 else {
151 // ISSUE: here's a little "backwards" dependancy.
152 // there should be a "JSPEMBEDDEDHTML validator"
153 // contributed by JSP plugin.
154 IContentType jspContentType = contentTypeManager.getContentType(ORG_ECLIPSE_JST_JSP_CORE_JSPSOURCE);
155 if (jspContentType != null) {
156 result = fileContentType.isKindOf(jspContentType);
157 }
david_williamse6ed7322005-10-10 17:32:24 +0000158 }
159 }
160 }
nitinda06daf82005-11-01 22:42:33 +0000161 else if (htmlContentType != null) {
162 result = htmlContentType.isAssociatedWith(file.getName());
163 }
david_williamse6ed7322005-10-10 17:32:24 +0000164 }
165 catch (CoreException e) {
166 // should be rare, but will ignore to avoid logging "encoding
167 // exceptions" and the like here.
168 // Logger.logException(e);
169 }
170 }
171 return result;
paverye6107e62005-02-23 18:01:10 +0000172 }
173
174 /**
175 */
david_williamsc39caaf2005-04-05 06:07:16 +0000176 private boolean hasHTMLFeature(IDOMDocument document) {
paverye6107e62005-02-23 18:01:10 +0000177 DocumentTypeAdapter adapter = (DocumentTypeAdapter) document.getAdapterFor(DocumentTypeAdapter.class);
178 if (adapter == null)
179 return false;
180 return adapter.hasFeature("HTML");//$NON-NLS-1$
181 }
182
183 /**
184 */
185 protected void releaseModel(IStructuredModel model) {
186 if (model != null)
187 model.releaseFromRead();
188 }
189
190 /**
191 */
vbhadrir271d28a2005-04-12 19:36:05 +0000192 public void validate(IValidationContext helper, IReporter reporter) {
paverye6107e62005-02-23 18:01:10 +0000193 if (helper == null)
194 return;
195 if ((reporter != null) && (reporter.isCancelled() == true)) {
196 throw new OperationCanceledException();
197 }
vbhadrir271d28a2005-04-12 19:36:05 +0000198 String[] deltaArray = helper.getURIs();
paverye6107e62005-02-23 18:01:10 +0000199 if (deltaArray != null && deltaArray.length > 0) {
vbhadrir271d28a2005-04-12 19:36:05 +0000200 validateDelta(helper, reporter);
paverye6107e62005-02-23 18:01:10 +0000201 }
202 else {
vbhadrir271d28a2005-04-12 19:36:05 +0000203 validateFull(helper, reporter);
paverye6107e62005-02-23 18:01:10 +0000204 }
205 }
paverya0e2eae2005-10-20 20:24:29 +0000206
207 /**
208 * This validate call is for the ISourceValidator partial document validation approach
209 * @param dirtyRegion
210 * @param helper
211 * @param reporter
212 * @see org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator
213 */
214 public void validate(IRegion dirtyRegion, IValidationContext helper, IReporter reporter) {
215
216 if (helper == null || fDocument == null)
217 return;
218
219 if ((reporter != null) && (reporter.isCancelled() == true)) {
220 throw new OperationCanceledException();
221 }
222
223 IStructuredModel model = StructuredModelManager.getModelManager().getExistingModelForRead(fDocument);
224 if (model == null)
225 return; // error
226
227 try {
228
229 IDOMDocument document = null;
230 if(model instanceof IDOMModel) {
231 document = ((IDOMModel)model).getDocument();
232 }
paverye6107e62005-02-23 18:01:10 +0000233
paverya0e2eae2005-10-20 20:24:29 +0000234 if (document == null || !hasHTMLFeature(document))
235 return ; // ignore
236
237 ITextFileBuffer fb = FileBufferModelManager.getInstance().getBuffer(fDocument);
238 if(fb == null)
239 return;
240
241 IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(fb.getLocation());
242 if(file == null || !file.exists())
243 return;
244
paverydc8b3062005-10-24 20:43:42 +0000245 // this will be the wrong region if it's Text (instead of Element)
246 // we don't know how to validate Text
paverya0e2eae2005-10-20 20:24:29 +0000247 IndexedRegion ir = model.getIndexedRegion(dirtyRegion.getOffset());
paverydc8b3062005-10-24 20:43:42 +0000248 if(ir instanceof Text) {
249 while(ir != null && ir instanceof Text) {
250 // it's assumed that this gets the IndexedRegion to
251 // the right of the end offset
252 ir = model.getIndexedRegion(ir.getEndOffset());
253 }
254 }
255
paverya0e2eae2005-10-20 20:24:29 +0000256 if(ir instanceof INodeNotifier) {
257
258 INodeAdapterFactory factory = HTMLValidationAdapterFactory.getInstance();
259 ValidationAdapter adapter = (ValidationAdapter) factory.adapt((INodeNotifier)ir);
260 if (adapter == null)
261 return; // error
262
263 if (reporter != null) {
264 HTMLValidationReporter rep = null;
265 rep = getReporter(reporter, file, (IDOMModel)model);
266 rep.clear();
267 adapter.setReporter(rep);
268
nitind73e76412005-11-10 01:50:19 +0000269 String fileName = ""; //$NON-NLS-1$
paverya0e2eae2005-10-20 20:24:29 +0000270 IPath filePath = file.getFullPath();
271 if (filePath != null) {
272 fileName = filePath.toString();
273 }
274 String args[] = new String[]{fileName};
275
276 Message mess = new LocalizedMessage(IMessage.LOW_SEVERITY, NLS.bind(HTMLUIMessages.MESSAGE_HTML_VALIDATION_MESSAGE_UI_, args));
277 mess.setParams(args);
278 reporter.displaySubtask(this, mess);
279 }
280 adapter.validate(ir);
281 }
282 }
283 finally {
284 if(model != null)
285 model.releaseFromRead();
286 }
287 }
288
289 /**
290 * @see org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator
291 */
292 public void connect(IDocument document) {
293 fDocument = document;
294 }
295 /**
296 * @see org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator
297 */
298 public void disconnect(IDocument document) {
299 // don't need to do anything
300 }
301
paverye6107e62005-02-23 18:01:10 +0000302 /**
303 */
david_williamsc39caaf2005-04-05 06:07:16 +0000304 protected HTMLValidationResult validate(IDOMModel model, IFile file) {
paverye6107e62005-02-23 18:01:10 +0000305 IProject prj = null;
306 if (file != null) {
307 prj = file.getProject();
308 }
david_williams49d24fb2005-04-08 21:16:59 +0000309 if ((prj == null) && (model != null)) {
paverye6107e62005-02-23 18:01:10 +0000310 URIResolver res = model.getResolver();
311 if (res != null) {
312 prj = res.getProject();
313 }
314 }
315 final WorkbenchReporter reporter = new WorkbenchReporter(prj, new NullProgressMonitor());
david_williams49d24fb2005-04-08 21:16:59 +0000316 return validate(reporter, file, model);
paverye6107e62005-02-23 18:01:10 +0000317 }
318
319 /**
320 */
david_williamsc39caaf2005-04-05 06:07:16 +0000321 private HTMLValidationResult validate(IReporter reporter, IFile file, IDOMModel model) {
paverye6107e62005-02-23 18:01:10 +0000322 if (file == null || model == null)
323 return null; // error
david_williamsc39caaf2005-04-05 06:07:16 +0000324 IDOMDocument document = model.getDocument();
paverye6107e62005-02-23 18:01:10 +0000325 if (document == null)
326 return null; // error
327 if (!hasHTMLFeature(document))
328 return null; // ignore
329
david_williams285ee2d2005-04-10 18:21:21 +0000330 INodeAdapterFactory factory = HTMLValidationAdapterFactory.getInstance();
paverye6107e62005-02-23 18:01:10 +0000331 ValidationAdapter adapter = (ValidationAdapter) factory.adapt(document);
332 if (adapter == null)
333 return null; // error
334
335 HTMLValidationReporter rep = getReporter(reporter, file, model);
336 rep.clear();
337 adapter.setReporter(rep);
338 if (reporter != null) {
nitind73e76412005-11-10 01:50:19 +0000339 String fileName = ""; //$NON-NLS-1$
david_williamse6ed7322005-10-10 17:32:24 +0000340 IPath filePath = file.getFullPath();
341 if (filePath != null) {
342 fileName = filePath.toString();
343 }
344 String args[] = new String[]{fileName};
david_williams49d24fb2005-04-08 21:16:59 +0000345
346 // Message mess = new Message("HTMLValidation", //$NON-NLS-1$
347 // SeverityEnum.LOW_SEVERITY,
348 // "MESSAGE_HTML_VALIDATION_MESSAGE_UI_", //$NON-NLS-1$
349 // args);
nitinde83b1a42005-06-22 21:22:20 +0000350 Message mess = new LocalizedMessage(IMessage.LOW_SEVERITY, NLS.bind(HTMLUIMessages.MESSAGE_HTML_VALIDATION_MESSAGE_UI_, args));
paverye6107e62005-02-23 18:01:10 +0000351 mess.setParams(args);
352 reporter.displaySubtask(this, mess);
353 }
354 adapter.validate(document);
355 return rep.getResult();
356 }
357
358 /**
359 */
david_williams43271672005-04-01 05:19:26 +0000360 private void validateContainer(IValidationContext helper, IReporter reporter, IContainer container) {
paverye6107e62005-02-23 18:01:10 +0000361 try {
362 IResource[] resourceArray = container.members(false);
363 for (int i = 0; i < resourceArray.length; i++) {
364 IResource resource = resourceArray[i];
365 if (resource == null)
366 continue;
367 if (resource instanceof IFile) {
368 validateFile(helper, reporter, (IFile) resource);
369 }
370 else if (resource instanceof IContainer) {
371 validateContainer(helper, reporter, (IContainer) resource);
372 }
373 }
374 }
375 catch (CoreException ex) {
376 }
377 }
378
379 /**
380 */
vbhadrir271d28a2005-04-12 19:36:05 +0000381 private void validateDelta(IValidationContext helper, IReporter reporter) {
382 String[] deltaArray = helper.getURIs();
paverye6107e62005-02-23 18:01:10 +0000383 for (int i = 0; i < deltaArray.length; i++) {
vbhadrir271d28a2005-04-12 19:36:05 +0000384 String delta = deltaArray[i];
385 if (delta == null)
paverye6107e62005-02-23 18:01:10 +0000386 continue;
387 IResource resource = getResource(delta);
388 if (resource == null || !(resource instanceof IFile))
389 continue;
390 validateFile(helper, reporter, (IFile) resource);
391 }
392 }
393
394 /**
395 */
david_williams43271672005-04-01 05:19:26 +0000396 private void validateFile(IValidationContext helper, IReporter reporter, IFile file) {
paverye6107e62005-02-23 18:01:10 +0000397 if ((reporter != null) && (reporter.isCancelled() == true)) {
398 throw new OperationCanceledException();
399 }
nitind165498a2005-07-12 18:47:49 +0000400 if (!shouldValidate(file)) {
401 return;
402 }
david_williamsc39caaf2005-04-05 06:07:16 +0000403 IDOMModel model = getModel(file.getProject(), file);
paverye6107e62005-02-23 18:01:10 +0000404 if (model == null)
405 return;
406
407 try {
408 validate(reporter, file, model);
409 }
410 finally {
411 releaseModel(model);
412 }
413 }
414
415 /**
416 */
vbhadrir271d28a2005-04-12 19:36:05 +0000417 private void validateFull(IValidationContext helper, IReporter reporter) {
paverye6107e62005-02-23 18:01:10 +0000418 IProject project = null;
vbhadrir271d28a2005-04-12 19:36:05 +0000419 String[] fileDelta = helper.getURIs();
420 if (helper instanceof IWorkbenchContext) {
421 IWorkbenchContext wbHelper = (IWorkbenchContext) helper;
paverye6107e62005-02-23 18:01:10 +0000422 project = wbHelper.getProject();
423 }
424 else {
425 // won't work for project validation (b/c nothing in file delta)
426 project = getResource(fileDelta[0]).getProject();
427 }
428 if (project == null)
429 return;
430 validateContainer(helper, reporter, project);
431 }
432
david_williams49d24fb2005-04-08 21:16:59 +0000433 /*
paverye6107e62005-02-23 18:01:10 +0000434 * added to get rid or dependency on IWorkbenchHelper
david_williams49d24fb2005-04-08 21:16:59 +0000435 *
paverye6107e62005-02-23 18:01:10 +0000436 */
vbhadrir271d28a2005-04-12 19:36:05 +0000437 public IResource getResource(String delta) {
david_williamse6ed7322005-10-10 17:32:24 +0000438 return ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(delta));
paverye6107e62005-02-23 18:01:10 +0000439 }
440}