| /******************************************************************************* |
| * Copyright (c) 2005, 2010 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.xml.ui.internal.validation; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.InputStream; |
| import java.io.UnsupportedEncodingException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; |
| import org.eclipse.wst.sse.core.StructuredModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| import org.eclipse.wst.sse.ui.internal.reconcile.validator.AnnotationInfo; |
| import org.eclipse.wst.sse.ui.internal.reconcile.validator.IncrementalReporter; |
| import org.eclipse.wst.validation.internal.core.IMessageAccess; |
| import org.eclipse.wst.validation.internal.core.ValidationException; |
| import org.eclipse.wst.validation.internal.provisional.core.IMessage; |
| import org.eclipse.wst.validation.internal.provisional.core.IProjectValidationContext; |
| import org.eclipse.wst.validation.internal.provisional.core.IReporter; |
| import org.eclipse.wst.validation.internal.provisional.core.IValidationContext; |
| import org.eclipse.wst.validation.internal.provisional.core.IValidator; |
| import org.eclipse.wst.validation.internal.provisional.core.IValidatorJob; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMText; |
| import org.eclipse.wst.xml.core.internal.validation.AnnotationMsg; |
| import org.eclipse.wst.xml.ui.internal.Logger; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| /** |
| * A DelegatingReconcileValidator calls its delegate validator to get a list |
| * of validation error IMessages. Using information in this IMessage the |
| * DelegatingReconcileValidator updates the IMessage with an offset and length |
| * to give a good range to be "squiggled" and adds the messages to the |
| * IReporter |
| * |
| * @author Mark Hutchinson |
| * |
| */ |
| public abstract class DelegatingSourceValidator implements IValidator { |
| // the selection strategies: |
| protected static final String ALL_ATTRIBUTES = "ALL_ATTRIBUTES"; //$NON-NLS-1$ |
| protected static final String ATTRIBUTE_NAME = "ATTRIBUTE_NAME"; //$NON-NLS-1$ |
| protected static final String ATTRIBUTE_VALUE = "ATTRIBUTE_VALUE"; //$NON-NLS-1$ |
| protected static final String START_TAG = "START_TAG"; //$NON-NLS-1$ |
| protected static final String TEXT = "TEXT"; //$NON-NLS-1$ |
| protected static final String FIRST_NON_WHITESPACE_TEXT = "FIRST_NON_WHITESPACE_TEXT"; //$NON-NLS-1$ |
| protected static final String TEXT_ENTITY_REFERENCE = "TEXT_ENTITY_REFERENCE"; //$NON-NLS-1$ |
| protected static final String VALUE_OF_ATTRIBUTE_WITH_GIVEN_VALUE = "VALUE_OF_ATTRIBUTE_WITH_GIVEN_VALUE"; //$NON-NLS-1$ |
| protected static final String END_TAG = "END_TAG"; //$NON-NLS-1$ |
| private static final String QUICKASSISTPROCESSOR = IQuickAssistProcessor.class.getName(); |
| |
| |
| |
| /** |
| * This constant specifies the attribute name that specifies the side of |
| * the 'start tag' that the validator has used to report an error. A |
| * validator may choose to report a message at the left (the start of the |
| * start tag)or at the right (the end of the start tag). When this |
| * attribute is not specified error ERROR_SIDE_LEFT is the default. |
| */ |
| protected static final String ERROR_SIDE = "ERROR_SIDE"; //$NON-NLS-1$ |
| |
| /** |
| * When the ERROR_SIDE attribute specifies the ERROR_SIDE_LEFT value it is |
| * assumed that the message specifies a location to the left of the start |
| * tag |
| */ |
| protected static final String ERROR_SIDE_LEFT = "ERROR_SIDE_LEFT"; //$NON-NLS-1$ |
| |
| /** |
| * When the ERROR_SIDE attribute specifies the ERROR_SIDE_RIGHT value it |
| * is assumed that the message specifies a location to the right of the |
| * start tag |
| */ |
| protected static final String ERROR_SIDE_RIGHT = "ERROR_SIDE_RIGHT"; //$NON-NLS-1$ |
| |
| protected static final String COLUMN_NUMBER_ATTRIBUTE = "columnNumber"; //$NON-NLS-1$ |
| protected static final String SQUIGGLE_SELECTION_STRATEGY_ATTRIBUTE = "squiggleSelectionStrategy"; //$NON-NLS-1$ |
| protected static final String SQUIGGLE_NAME_OR_VALUE_ATTRIBUTE = "squiggleNameOrValue"; //$NON-NLS-1$ |
| |
| public DelegatingSourceValidator() { |
| super(); // constructor |
| } |
| |
| public void cleanup(IReporter arg0) { // don't need to implement |
| } |
| |
| // My Implementation of IHelper |
| class MyHelper implements IProjectValidationContext { |
| InputStream inputStream; |
| |
| IFile file; |
| |
| public MyHelper(InputStream inputStream, IFile file) { |
| this.inputStream = inputStream; |
| this.file = file; |
| } |
| |
| public int getBuildKind() { |
| return 0; |
| } |
| |
| public Object loadModel(String symbolicName, Object[] parms) { |
| if (symbolicName.equals("getFile")) { //$NON-NLS-1$ |
| return file; |
| } |
| return null; |
| } |
| |
| public Object loadModel(String symbolicName) { |
| if (symbolicName.equals("inputStream")) { //$NON-NLS-1$ |
| return inputStream; |
| } |
| return null; |
| } |
| |
| public String[] getURIs() { |
| if (file != null) { |
| return new String[]{file.getFullPath().toString()}; |
| } |
| return new String[0]; |
| } |
| |
| public IProject getProject() { |
| if (file != null) { |
| return file.getProject(); |
| } |
| return null; |
| } |
| } |
| |
| // My Implementation of IReporter |
| class MyReporter implements IReporter { |
| List list = new ArrayList(); |
| |
| public MyReporter() { |
| super(); |
| } |
| |
| public void addMessage(IValidator origin, IMessage message) { |
| list.add(message); |
| } |
| |
| public void displaySubtask(IValidator validator, IMessage message) { |
| /* do not need to implement */ |
| } |
| |
| public IMessageAccess getMessageAccess() { |
| return null; |
| } |
| |
| public boolean isCancelled() { |
| return false; |
| } |
| |
| public void removeAllMessages(IValidator origin, Object object) { // do |
| /* do not need to implement */ |
| } |
| |
| public void removeAllMessages(IValidator origin) { |
| /* do not need to implement */ |
| } |
| |
| public void removeMessageSubset(IValidator validator, Object obj, String groupName) {// do |
| /* do not need to implement */ |
| } |
| |
| public List getMessages() { |
| return list; |
| } |
| } |
| |
| protected abstract IValidator getDelegateValidator(); |
| |
| /** |
| * Calls a delegate validator getting and updates it's list of |
| * ValidationMessages with a good squiggle offset and length. |
| * |
| * @param helper |
| * loads an object. |
| * @param reporter |
| * Is an instance of an IReporter interface, which is used for |
| * interaction with the user. |
| */ |
| public void validate(IValidationContext helper, IReporter reporter) throws ValidationException { |
| String[] delta = helper.getURIs(); |
| if (delta.length > 0) { |
| // get the file, model and document: |
| IFile file = getFile(delta[0]); |
| IDOMModel xmlModel = null; |
| if (file != null) |
| xmlModel = getModelForResource(file); |
| // some problem occurred, abort |
| if (xmlModel == null) |
| return; |
| |
| try { |
| IDOMDocument document = xmlModel.getDocument(); |
| |
| // store the text in a byte array; make a full copy to ease |
| // any threading problems |
| byte[] byteArray; |
| try { |
| byteArray = xmlModel.getStructuredDocument().get().getBytes("UTF-8"); |
| } |
| catch (UnsupportedEncodingException e) { |
| // Not likely to happen |
| byteArray = xmlModel.getStructuredDocument().get().getBytes(); |
| } |
| |
| if (isDelegateValidatorEnabled(file)) { |
| IValidator validator = getDelegateValidator(); |
| if (validator != null) { |
| // Validate the file: |
| IValidationContext vHelper = new MyHelper(new ByteArrayInputStream(byteArray), file); |
| MyReporter vReporter = new MyReporter(); |
| if (validator instanceof IValidatorJob) { |
| ((IValidatorJob) validator).validateInJob(vHelper, vReporter); |
| } |
| else { |
| validator.validate(vHelper, vReporter); |
| } |
| List messages = vReporter.list; |
| |
| // set the offset and length |
| updateValidationMessages(messages, document, reporter); |
| } |
| } |
| } |
| |
| |
| finally { |
| if (xmlModel != null) { |
| xmlModel.releaseFromRead(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * iterates through the messages and calculates a "better" offset and |
| * length |
| * |
| * @param messages - |
| * a List of IMessages |
| * @param document - |
| * the document |
| * @param reporter - |
| * the reporter the messages are to be added to |
| */ |
| protected void updateValidationMessages(List messages, IDOMDocument document, IReporter reporter) { |
| for (int i = 0; i < messages.size(); i++) { |
| IMessage message = (IMessage) messages.get(i); |
| try { |
| if (message.getAttribute(COLUMN_NUMBER_ATTRIBUTE) != null) { |
| int column = ((Integer) message.getAttribute(COLUMN_NUMBER_ATTRIBUTE)).intValue(); |
| if (message.getAttribute(AnnotationMsg.PROBMLEM_ID) != null && reporter instanceof IncrementalReporter){ |
| Integer problemId = (Integer)message.getAttribute(AnnotationMsg.PROBMLEM_ID); |
| MarkupQuickAssistProcessor processor = new MarkupQuickAssistProcessor(); |
| processor.setProblemId(problemId.intValue()); |
| message.setOffset(column); |
| Integer length = (Integer) message.getAttribute(AnnotationMsg.LENGTH); |
| message.setLength( length.intValue()); |
| Object attrValue = message.getAttribute(AnnotationMsg.ATTRVALUETEXT); |
| if (attrValue != null) |
| processor.setAdditionalFixInfo(attrValue); |
| else{ |
| Object attrValueNo = message.getAttribute(AnnotationMsg.ATTRVALUENO); |
| if (attrValueNo != null){ |
| int len = ((Integer)attrValueNo).intValue(); |
| Object[] objArray = new Object[len]; |
| for ( int j=0; j<len; j++){ |
| objArray[j] = message.getAttribute(AnnotationMsg.ATTRNO + j); |
| } |
| processor.setAdditionalFixInfo(objArray); |
| } |
| |
| } |
| message.setAttribute(QUICKASSISTPROCESSOR, processor); |
| AnnotationInfo info = new AnnotationInfo(message); |
| ((IncrementalReporter) reporter).addAnnotationInfo(this, info); |
| } |
| else { |
| String selectionStrategy = (String) message.getAttribute(SQUIGGLE_SELECTION_STRATEGY_ATTRIBUTE); |
| String nameOrValue = (String) message.getAttribute(SQUIGGLE_NAME_OR_VALUE_ATTRIBUTE); |
| |
| // convert the line and Column numbers to an offset: |
| int start = document.getStructuredDocument().getLineOffset(message.getLineNumber() - 1) + column - 1; |
| int[] result = computeStartAndEndLocation(start, selectionStrategy, getErrorSide(message), nameOrValue, document); |
| if (result != null) { |
| message.setOffset(result[0]); |
| message.setLength(result[1] - result[0]); |
| |
| reporter.addMessage(this, message); |
| } |
| } |
| } |
| |
| } |
| catch (BadLocationException e) { // this exception should not |
| // occur - it is thrown if |
| // trying to convert an |
| // invalid line number to and |
| // offset |
| } |
| |
| } |
| } |
| |
| /** |
| * @param delta |
| * the IFileDelta containing the file name to get |
| * @return the IFile |
| */ |
| public IFile getFile(String delta) { |
| IPath path = new Path(delta); |
| if (path.segmentCount() > 1) { |
| IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path); |
| if (file != null && file.exists()) |
| return file; |
| } |
| return null; |
| } |
| |
| /** |
| * |
| * @param file |
| * the file to get the model for |
| * @return the file's XMLModel or null |
| */ |
| protected IDOMModel getModelForResource(IFile file) { |
| IStructuredModel model = null; |
| IModelManager manager = StructuredModelManager.getModelManager(); |
| |
| try { |
| model = manager.getModelForRead(file); |
| // TODO.. HTML validator tries again to get a model a 2nd way |
| } |
| catch (Exception e) { |
| Logger.log(Logger.ERROR_DEBUG, file.getFullPath().toString(), e); |
| } |
| |
| if (model instanceof IDOMModel) |
| return (IDOMModel) model; |
| if (model != null) |
| model.releaseFromRead(); |
| return null; |
| } |
| |
| /** |
| * @deprecated use computeStartEndLocation(int startOffset, String |
| * errorMessage, String selectionStrategy, boolean leftError, |
| * String nameOrValue, IDOMDocument document) { |
| * |
| */ |
| protected int[] computeStartEndLocation(int startOffset, String errorMessage, String selectionStrategy, String nameOrValue, IDOMDocument document) { |
| return computeStartAndEndLocation(startOffset, selectionStrategy, ERROR_SIDE_RIGHT, nameOrValue, document); |
| } |
| |
| /** |
| * Calculates the "better" offsets. |
| * |
| * @param startOffset - |
| * the offset given by Xerces |
| * @param errorMessage - |
| * the Xerces error Message |
| * @param selectionStrategy - |
| * the selectionStrategy |
| * @param document - |
| * the document |
| * @return int[] - position 0 has the start offset of the squiggle range, |
| * position 1 has the endOffset |
| */ |
| /* |
| * The way the offsets is calculated is: - find the indexed region |
| * (element) closest to the given offset - if we are between two elements, |
| * choosing left or right element will depend on parameter 'errorSide' - |
| * based on the selectionStrategy choose the underlining strategy (eg |
| * START_TAG means underline the start tag of that element) - use |
| * information from nameOrValue and the DOM to get better offsets |
| * |
| */ |
| protected int[] computeStartAndEndLocation(int startOffset, String selectionStrategy, String errorSide, String nameOrValue, IDOMDocument document) { |
| try { |
| int startEndPositions[] = new int[2]; |
| |
| IndexedRegion region = document.getModel().getIndexedRegion(startOffset); |
| IndexedRegion prevRegion = document.getModel().getIndexedRegion(startOffset - 1); |
| |
| if (prevRegion != region) { |
| // if between two regions we use the 'errorSide' to understand |
| // which |
| // element is applicable. if we know the error has been |
| // reported to the |
| // right of the tag, then we can assume we need to step back |
| // to the previous |
| // region to land at the 'correct location. Otherwise assume |
| // we're |
| // exactly where we need to be. |
| if (ERROR_SIDE_LEFT.equals(errorSide)) { |
| region = prevRegion; |
| } |
| } |
| |
| // initialize start and end positions to be the start positions |
| // this means if the |
| // special case is not taken care of below the start and end |
| // offset are set to be |
| // the start of the region where the error was |
| if (region != null) { |
| startEndPositions[0] = region.getStartOffset(); |
| startEndPositions[1] = startEndPositions[0]; |
| } |
| else { // this will message will not get added to the IReporter |
| // since the length is 0 |
| startEndPositions[0] = 0; |
| startEndPositions[1] = 0; |
| } |
| if (region instanceof Node) { |
| Node node = (Node) region; |
| |
| if (START_TAG.equals(selectionStrategy)) {// then we want to |
| // underline the opening tag |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| IDOMElement element = (IDOMElement) node; |
| startEndPositions[0] = element.getStartOffset() + 1; |
| startEndPositions[1] = startEndPositions[0] + element.getTagName().length(); |
| } |
| } |
| else if (END_TAG.equals(selectionStrategy)) {// then we want to |
| // underline the end tag |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| IDOMElement element = (IDOMElement) node; |
| startEndPositions[0] = element.getEndStartOffset(); |
| startEndPositions[1] = element.getEndOffset(); |
| } |
| } |
| else if (ATTRIBUTE_NAME.equals(selectionStrategy)) { // in |
| // underline the attribute's name |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| IDOMElement element = (IDOMElement) node; |
| IDOMNode attributeNode = (IDOMNode) (element.getAttributeNode(nameOrValue)); |
| if (attributeNode != null) { |
| startEndPositions[0] = attributeNode.getStartOffset(); |
| startEndPositions[1] = attributeNode.getStartOffset() + nameOrValue.length(); |
| } |
| } |
| } |
| else if (ATTRIBUTE_VALUE.equals(selectionStrategy)) { |
| // underline the attribute's value |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| IDOMElement element = (IDOMElement) node; |
| IDOMAttr attributeNode = (IDOMAttr) (element.getAttributeNode(nameOrValue)); |
| if (attributeNode != null) { |
| startEndPositions[0] = attributeNode.getValueRegionStartOffset(); |
| String valueRegionText = attributeNode.getValueRegionText(); |
| int valueRegionLength = valueRegionText == null ? 0 : valueRegionText.length(); |
| startEndPositions[1] = startEndPositions[0] + valueRegionLength; |
| } |
| } |
| } |
| else if (ALL_ATTRIBUTES.equals(selectionStrategy)) { |
| // underline all attributes |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| IDOMElement element = (IDOMElement) node; |
| NamedNodeMap attributes = element.getAttributes(); |
| if (attributes != null) { |
| IDOMNode first = (IDOMNode) attributes.item(0); |
| IDOMNode last = (IDOMNode) attributes.item(attributes.getLength() - 1); |
| if ((first != null) && (last != null)) { |
| startEndPositions[0] = first.getStartOffset(); |
| startEndPositions[1] = last.getEndOffset(); |
| } |
| } |
| } |
| } |
| else if (TEXT.equals(selectionStrategy)) { |
| // underline the text between the tags |
| if (node.getNodeType() == Node.TEXT_NODE) { |
| IDOMText textNode = (IDOMText) node; |
| int start = textNode.getStartOffset(); |
| String value = textNode.getNodeValue(); |
| int index = 0; |
| char curChar = value.charAt(index); |
| // here we are finding start offset by skipping over |
| // whitespace: |
| while ((curChar == '\n') || (curChar == '\t') || (curChar == '\r') || (curChar == ' ')) { |
| curChar = value.charAt(index); |
| index++; |
| } |
| if (index > 0) { |
| index--; |
| |
| } |
| start = start + index; |
| startEndPositions[0] = start; |
| startEndPositions[1] = start + value.trim().length(); |
| } |
| else if (node.getNodeType() == Node.ELEMENT_NODE) { |
| IDOMElement element = (IDOMElement) node; |
| Node child = element.getFirstChild(); |
| if (child instanceof IDOMNode) { |
| IDOMNode xmlChild = ((IDOMNode) child); |
| startEndPositions[0] = xmlChild.getStartOffset(); |
| startEndPositions[1] = xmlChild.getEndOffset(); |
| } |
| } |
| } |
| else if (FIRST_NON_WHITESPACE_TEXT.equals(selectionStrategy)) { |
| // search through all child nodes and return range of |
| // first non-whitespace |
| // text node |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| NodeList nodes = node.getChildNodes(); |
| for (int i = 0; i < nodes.getLength(); i++) { |
| Node currentNode = nodes.item(i); |
| if (currentNode.getNodeType() == Node.TEXT_NODE) { |
| // TODO (Trung) I don't think we should call |
| // getNodeValue(), trim(), length() |
| // repeatedly. |
| // This is inefficient, to improve use local |
| // variables to store values. |
| IDOMText textNode = (IDOMText) currentNode; |
| if (textNode.getNodeValue().trim().length() > 0) { |
| String value = textNode.getNodeValue(); |
| int index = 0; |
| int start = textNode.getStartOffset(); |
| char curChar = value.charAt(index); |
| // here we are finding start offset by |
| // skipping over whitespace: |
| while ((curChar == '\n') || (curChar == '\t') || (curChar == '\r') || (curChar == ' ')) { |
| curChar = value.charAt(index); |
| index++; |
| } |
| if (index > 0) { |
| index--; |
| |
| } |
| start = start + index; |
| startEndPositions[0] = start; |
| startEndPositions[1] = start + value.trim().length(); |
| break; |
| } |
| } |
| |
| } |
| } |
| } |
| |
| else if (TEXT_ENTITY_REFERENCE.equals(selectionStrategy)) { |
| if (node.getNodeType() == Node.ENTITY_REFERENCE_NODE) { |
| startEndPositions[0] = region.getStartOffset(); |
| startEndPositions[1] = region.getEndOffset(); |
| } |
| else if (node.getNodeType() == Node.ELEMENT_NODE) { |
| /* |
| * In this case the undeclared entity might be in one |
| * of the attribute values. Search through the |
| * attributes to find the range of the undeclared |
| * entity. |
| */ |
| String entity = "&" + nameOrValue + ";"; //$NON-NLS-1$ //$NON-NLS-2$ |
| NamedNodeMap attributes = node.getAttributes(); |
| for (int i = 0; i < attributes.getLength(); i++) { |
| IDOMAttr attr = (IDOMAttr) attributes.item(i); |
| String nodeValue = attr.getNodeValue(); |
| int index = nodeValue.indexOf(entity); |
| if (index != -1) { |
| startEndPositions[0] = attr.getValueRegionStartOffset() + index + 1; |
| startEndPositions[1] = startEndPositions[0] + entity.length(); |
| } |
| } |
| } |
| |
| } |
| else if (VALUE_OF_ATTRIBUTE_WITH_GIVEN_VALUE.equals(selectionStrategy)) { |
| // TODO (Trung) do we really need this strategy ? |
| // If we know the name of the name of the attribute, we |
| // can retrieve its value. |
| // Hence, we can incoperate this strategy with |
| // ATTRIBUTE_VALUE ? |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| // here we will search through all attributes for the |
| // one with the |
| // with the value we want: |
| // TODO (Trung) I see a potential problem here. |
| // What happens when there is another attribute having |
| // the same value |
| // with this attribute's buggy value ? |
| // Need to solve when time permits. |
| NamedNodeMap attributes = node.getAttributes(); |
| for (int i = 0; i < attributes.getLength(); i++) { |
| IDOMAttr attr = (IDOMAttr) attributes.item(i); |
| String nodeValue = attr.getNodeValue().trim(); |
| if (nodeValue.equals(nameOrValue)) { |
| startEndPositions[0] = attr.getValueRegionStartOffset() + 1; |
| startEndPositions[1] = startEndPositions[0] + nodeValue.length(); |
| break; |
| } |
| } |
| } |
| } |
| } |
| return startEndPositions; |
| } |
| // catch (Exception e) { // e.printStackTrace(); |
| // } |
| finally { |
| } |
| // return null; |
| } |
| |
| /** |
| * Returns true if delegate validator is enabled based on Validation |
| * preferences |
| * |
| * @param file |
| * @return false if delegate validator is not enabled based on Validatoin |
| * preferences, true otherwise |
| */ |
| protected boolean isDelegateValidatorEnabled(IFile file) { |
| return true; |
| } |
| |
| protected String getErrorSide(IMessage message) { |
| // note that if the ERROR_SIDE is unspecified we return the default |
| // value ERROR_SIDE_LEFT |
| Object value = message.getAttribute(ERROR_SIDE); |
| return ERROR_SIDE_RIGHT.equals(value) ? ERROR_SIDE_RIGHT : ERROR_SIDE_LEFT; |
| } |
| } |