| /******************************************************************************* |
| * Copyright (c) 2001, 2006 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.core.internal.validation; |
| |
| import java.io.BufferedReader; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.net.ConnectException; |
| import java.net.URL; |
| import java.net.UnknownHostException; |
| import java.util.ArrayList; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.apache.xerces.impl.XMLErrorReporter; |
| import org.apache.xerces.parsers.StandardParserConfiguration; |
| import org.apache.xerces.xni.Augmentations; |
| import org.apache.xerces.xni.NamespaceContext; |
| import org.apache.xerces.xni.QName; |
| import org.apache.xerces.xni.XMLAttributes; |
| import org.apache.xerces.xni.XMLLocator; |
| import org.apache.xerces.xni.XMLResourceIdentifier; |
| import org.apache.xerces.xni.XNIException; |
| import org.apache.xerces.xni.parser.XMLEntityResolver; |
| import org.apache.xerces.xni.parser.XMLInputSource; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolver; |
| import org.eclipse.wst.xml.core.internal.validation.core.LazyURLInputStream; |
| import org.eclipse.wst.xml.core.internal.validation.core.logging.LoggerFactory; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| import org.xml.sax.XMLReader; |
| import org.xml.sax.ext.DeclHandler; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| /** |
| * This class performs validation using a xerces sax parser. |
| * Here's a quick overview of the details : |
| * - an ErrorHandler is used to collect errors into a list (so they may be displayed by the UI) |
| * - an EntityResolver is used along with the xerces "external-schemaLocation" property to implement XML Catalog support |
| */ |
| public class XMLValidator |
| { |
| protected URIResolver uriResolver = null; |
| protected Hashtable ingoredErrorKeyTable = new Hashtable(); |
| protected Set adjustLocationErrorKeySet = new TreeSet(); |
| |
| protected static final String IGNORE_ALWAYS = "IGNORE_ALWAYS"; //$NON-NLS-1$ |
| protected static final String IGNORE_IF_DTD_WITHOUT_ELEMENT_DECL = "IGNORE_IF_DTD_WITHOUT_ELEMENT_DECL"; //$NON-NLS-1$ |
| protected static final String PREMATURE_EOF = "PrematureEOF"; //$NON-NLS-1$ |
| protected static final String ROOT_ELEMENT_TYPE_MUST_MATCH_DOCTYPEDECL = "RootElementTypeMustMatchDoctypedecl"; //$NON-NLS-1$ |
| protected static final String MSG_ELEMENT_NOT_DECLARED = "MSG_ELEMENT_NOT_DECLARED"; //$NON-NLS-1$ |
| |
| // WTP XML validator specific key. |
| protected static final String NO_GRAMMAR_FOUND = "NO_GRAMMAR_FOUND"; //$NON-NLS-1$ |
| |
| private static final String FILE_NOT_FOUND_KEY = "FILE_NOT_FOUND"; //$NON-NLS-1$ |
| |
| /** |
| * Constructor. |
| */ |
| public XMLValidator() |
| { |
| // Here we add some error keys that we need to filter out when we're validation |
| // against a DTD without any element declarations. |
| ingoredErrorKeyTable.put(PREMATURE_EOF, IGNORE_ALWAYS); |
| ingoredErrorKeyTable.put(ROOT_ELEMENT_TYPE_MUST_MATCH_DOCTYPEDECL, IGNORE_IF_DTD_WITHOUT_ELEMENT_DECL); |
| ingoredErrorKeyTable.put(MSG_ELEMENT_NOT_DECLARED, IGNORE_IF_DTD_WITHOUT_ELEMENT_DECL); |
| |
| // Here we add some error keys that we need to adjust the location information for. |
| // The location information will be adjusted to place the message on the line of the starting |
| // element instead of on the line of the closing element. |
| adjustLocationErrorKeySet.add("MSG_CONTENT_INVALID"); |
| adjustLocationErrorKeySet.add("MSG_CONTENT_INCOMPLETE"); |
| adjustLocationErrorKeySet.add("cvc-complex-type.2.4.b"); |
| adjustLocationErrorKeySet.add("cvc-complex-type.2.3"); |
| } |
| |
| /** |
| * Set the URI Resolver to use. |
| * |
| * @param uriResolver The URI Resolver to use. |
| */ |
| public void setURIResolver(URIResolver uriResolver) |
| { |
| this.uriResolver = uriResolver; |
| //entityResolver = new MyEntityResolver(uriResolver); |
| } |
| |
| |
| /** |
| * Create an XML Reader. |
| * |
| * @return The newly created XML reader or null if unsuccessful. |
| * @throws Exception |
| */ |
| protected XMLReader createXMLReader(final XMLValidationInfo valinfo, XMLEntityResolver entityResolver) throws Exception |
| { |
| XMLReader reader = null; |
| // move to Xerces-2... add the contextClassLoader stuff |
| ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader(); |
| try |
| { |
| Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); |
| StandardParserConfiguration configuration = new MyStandardParserConfiguration(valinfo); |
| reader = new org.apache.xerces.parsers.SAXParser(configuration) |
| { |
| private XMLLocator locator = null; |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.parsers.AbstractSAXParser#startDocument(org.apache.xerces.xni.XMLLocator, java.lang.String, org.apache.xerces.xni.NamespaceContext, org.apache.xerces.xni.Augmentations) |
| */ |
| public void startDocument(org.apache.xerces.xni.XMLLocator theLocator, java.lang.String encoding, NamespaceContext nscontext, org.apache.xerces.xni.Augmentations augs) |
| { |
| locator = theLocator; |
| valinfo.setXMLLocator(theLocator); |
| super.startDocument(theLocator, encoding, nscontext, augs); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.parsers.AbstractSAXParser#startElement(org.apache.xerces.xni.QName, org.apache.xerces.xni.XMLAttributes, org.apache.xerces.xni.Augmentations) |
| */ |
| public void startElement(QName arg0, XMLAttributes arg1, Augmentations arg2) throws XNIException |
| { |
| valinfo.getStartElementLocations().push(new LocationCoordinate(locator.getLineNumber(), locator.getColumnNumber())); |
| super.startElement(arg0, arg1, arg2); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.parsers.AbstractSAXParser#endElement(org.apache.xerces.xni.QName, org.apache.xerces.xni.Augmentations) |
| */ |
| public void endElement(QName arg0, Augmentations arg1) throws XNIException { |
| super.endElement(arg0, arg1); |
| valinfo.getStartElementLocations().pop(); |
| } |
| }; |
| |
| reader.setFeature("http://apache.org/xml/features/continue-after-fatal-error", false); //$NON-NLS-1$ |
| reader.setFeature("http://xml.org/sax/features/namespace-prefixes", valinfo.isNamespaceEncountered()); //$NON-NLS-1$ |
| reader.setFeature("http://xml.org/sax/features/namespaces", valinfo.isNamespaceEncountered()); //$NON-NLS-1$ |
| reader.setFeature("http://xml.org/sax/features/validation", valinfo.isGrammarEncountered()); //$NON-NLS-1$ |
| reader.setFeature("http://apache.org/xml/features/validation/schema", valinfo.isGrammarEncountered()); //$NON-NLS-1$ |
| reader.setContentHandler(new DefaultHandler() |
| { |
| public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { |
| valinfo.getErrorCustomizationManager().startElement(uri, localName); |
| } |
| |
| public void endElement(String uri, String localName, String qName) throws SAXException { |
| valinfo.getErrorCustomizationManager().endElement(uri, localName); |
| } |
| }); |
| |
| // MH make sure validation works even when a customer entityResolver is note set (i.e. via setURIResolver()) |
| if (entityResolver != null) |
| { |
| reader.setProperty("http://apache.org/xml/properties/internal/entity-resolver", entityResolver); //$NON-NLS-1$ |
| } |
| reader.setProperty("http://xml.org/sax/properties/declaration-handler", new MyDeclHandler()); //$NON-NLS-1$ |
| } |
| catch(Exception e) |
| { |
| //TODO: log error message; |
| //e.printStackTrace(); |
| } |
| finally |
| { |
| Thread.currentThread().setContextClassLoader(prevClassLoader); |
| } |
| return reader; |
| } |
| |
| /** |
| * Validate the file located at the given URI. |
| * |
| * @param uri The URI of the file to validate. |
| * @return Returns an XML validation report. |
| */ |
| public XMLValidationReport validate(String uri) |
| { |
| return validate(uri, null, new XMLValidationConfiguration()); |
| } |
| |
| final String createStringForInputStream(InputStream inputStream) |
| { |
| // Here we are reading the file and storing to a stringbuffer. |
| StringBuffer fileString = new StringBuffer(); |
| try |
| { |
| InputStreamReader inputReader = new InputStreamReader(inputStream, "UTF-8"); |
| BufferedReader reader = new BufferedReader(inputReader); |
| char[] chars = new char[1024]; |
| int numberRead = reader.read(chars); |
| while (numberRead != -1) |
| { |
| fileString.append(chars, 0, numberRead); |
| numberRead = reader.read(chars); |
| } |
| } |
| catch (Exception e) |
| { |
| //TODO: log error message |
| //e.printStackTrace(); |
| } |
| return fileString.toString(); |
| } |
| /** |
| * Validate the inputStream |
| * |
| * @param uri The URI of the file to validate. |
| * @param the inputStream of the file to validate |
| * @return Returns an XML validation report. |
| */ |
| public XMLValidationReport validate(String uri, InputStream inputStream) |
| { |
| return validate(uri, inputStream, new XMLValidationConfiguration()); |
| } |
| /** |
| * Validate the inputStream |
| * |
| * @param uri |
| * The URI of the file to validate. |
| * @param inputstream |
| * The inputStream of the file to validate |
| * @param configuration |
| * A configuration for this validation session. |
| * @return |
| * Returns an XML validation report. |
| */ |
| public XMLValidationReport validate(String uri, InputStream inputStream, XMLValidationConfiguration configuration) |
| { |
| Reader reader1 = null; // Used for the preparse. |
| Reader reader2 = null; // Used for validation parse. |
| |
| if (inputStream != null) |
| { |
| String string = createStringForInputStream(inputStream); |
| reader1 = new StringReader(string); |
| reader2 = new StringReader(string); |
| } |
| |
| XMLValidationInfo valinfo = new XMLValidationInfo(uri); |
| MyEntityResolver entityResolver = new MyEntityResolver(uriResolver); |
| ValidatorHelper helper = new ValidatorHelper(); |
| try |
| { |
| helper.computeValidationInformation(uri, reader1, uriResolver); |
| valinfo.setDTDEncountered(helper.isDTDEncountered); |
| valinfo.setElementDeclarationCount(helper.numDTDElements); |
| valinfo.setNamespaceEncountered(helper.isNamespaceEncountered); |
| valinfo.setGrammarEncountered(helper.isGrammarEncountered); |
| |
| XMLReader reader = createXMLReader(valinfo, entityResolver); |
| XMLErrorHandler errorhandler = new XMLErrorHandler(valinfo); |
| reader.setErrorHandler(errorhandler); |
| |
| InputSource inputSource = new InputSource(uri); |
| inputSource.setCharacterStream(reader2); |
| reader.parse(inputSource); |
| if(configuration.getFeature(XMLValidationConfiguration.WARN_NO_GRAMMAR) && |
| valinfo.isValid() && !helper.isGrammarEncountered) |
| { |
| valinfo.addWarning(XMLValidationMessages._WARN_NO_GRAMMAR, 1, 0, uri, NO_GRAMMAR_FOUND, null); |
| } |
| } |
| catch (SAXParseException saxParseException) |
| { |
| // These errors are caught by the error handler. |
| //addValidationMessage(valinfo, saxParseException); |
| } |
| catch (IOException ioException) |
| { |
| addValidationMessage(valinfo, ioException); |
| } |
| catch (Exception exception) |
| { |
| LoggerFactory.getLoggerInstance().logError(exception.getLocalizedMessage(), exception); |
| } |
| |
| |
| return valinfo; |
| |
| } |
| |
| /** |
| * Add a validation message to the specified list. |
| * |
| * @param valinfo The validation info object to add the error to. |
| * @param exception The exception that contains the validation information. |
| */ |
| protected void addValidationMessage(XMLValidationInfo valinfo, IOException exception) |
| { |
| String validationMessageStr = exception.getMessage(); |
| Throwable cause = exception.getCause() != null ? exception.getCause() : exception; |
| while(validationMessageStr == null && cause != null){ |
| String localizedMessage = cause.getLocalizedMessage(); |
| cause = cause.getCause(); |
| if(cause == null && localizedMessage != null ) |
| { |
| validationMessageStr = localizedMessage; |
| } |
| } |
| |
| if (validationMessageStr != null) |
| { |
| if (cause instanceof FileNotFoundException) |
| { |
| validationMessageStr = NLS.bind(XMLValidationMessages._UI_PROBLEMS_VALIDATING_FILE_NOT_FOUND, new Object [] { validationMessageStr }); |
| } |
| else if (cause instanceof UnknownHostException) |
| { |
| validationMessageStr = NLS.bind(XMLValidationMessages._UI_PROBLEMS_VALIDATING_UNKNOWN_HOST, new Object [] { validationMessageStr }); |
| } |
| else if(cause instanceof ConnectException) |
| { |
| validationMessageStr = XMLValidationMessages._UI_PROBLEMS_CONNECTION_REFUSED; |
| } |
| } |
| |
| if (validationMessageStr != null) |
| { |
| XMLLocator locator = valinfo.getXMLLocator(); |
| valinfo.addWarning(validationMessageStr, locator != null ? locator.getLineNumber() : 1, locator != null ? locator.getColumnNumber() : 0, valinfo.getFileURI(), FILE_NOT_FOUND_KEY, null); |
| } |
| } |
| |
| /** |
| * Add a validation message to the specified list. |
| * |
| * @param valinfo The validation info object to add the error to. |
| * @param exception The exception that contains the validation information. |
| */ |
| protected void addValidationMessage(XMLValidationInfo valinfo, SAXParseException exception) |
| { |
| if (exception.getMessage() != null) |
| { |
| valinfo.addError(exception.getLocalizedMessage(), exception.getLineNumber(), exception.getColumnNumber(), exception.getSystemId()); |
| } |
| } |
| |
| |
| /** |
| * A custom entity resolver that uses the URI resolver specified to resolve entities. |
| */ |
| protected class MyEntityResolver implements XMLEntityResolver |
| { |
| private URIResolver uriResolver; |
| |
| |
| /** |
| * Constructor. |
| * |
| * @param uriResolver The URI resolver to use with this entity resolver. |
| */ |
| public MyEntityResolver(URIResolver uriResolver) |
| { |
| this.uriResolver = uriResolver; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.xni.parser.XMLEntityResolver#resolveEntity(org.apache.xerces.xni.XMLResourceIdentifier) |
| */ |
| public XMLInputSource resolveEntity(XMLResourceIdentifier rid) throws XNIException, IOException |
| { |
| try |
| { |
| return _internalResolveEntity(uriResolver, rid); |
| } |
| catch(IOException e) |
| { |
| //e.printStackTrace(); |
| } |
| return null; |
| } |
| } |
| |
| // cs : I've refactored the common SAX based resolution code into this method for use by other validators |
| // (i.e. XML Schema, WSDL etc). The other approach is maintain a copy for each validator that has |
| // identical code. In any case we should strive to ensure that the validators perform resolution consistently. |
| public static XMLInputSource _internalResolveEntity(URIResolver uriResolver, XMLResourceIdentifier rid) throws IOException |
| { |
| XMLInputSource is = null; |
| |
| if (uriResolver != null) |
| { |
| String id = rid.getPublicId(); |
| if(id == null) |
| { |
| id = rid.getNamespace(); |
| } |
| |
| String location = null; |
| if (id != null || rid.getLiteralSystemId() != null) |
| { |
| location = uriResolver.resolve(rid.getBaseSystemId(), id, rid.getLiteralSystemId()); |
| } |
| |
| if (location != null) |
| { |
| String physical = uriResolver.resolvePhysicalLocation(rid.getBaseSystemId(), id, location); |
| is = new XMLInputSource(rid.getPublicId(), location, location); |
| |
| // This block checks that the file exists. If it doesn't we need to throw |
| // an exception so Xerces will report an error. note: This may not be |
| // necessary with all versions of Xerces but has specifically been |
| // experienced with the version included in IBM's 1.4.2 JDK. |
| InputStream isTemp = null; |
| try |
| { |
| isTemp = new URL(physical).openStream(); |
| } |
| finally |
| { |
| if(isTemp != null) |
| { |
| isTemp.close(); |
| } |
| } |
| is.setByteStream(new LazyURLInputStream(physical)); |
| } |
| } |
| return is; |
| } |
| |
| /** |
| * An error handler to catch errors encountered while parsing the XML document. |
| */ |
| protected class XMLErrorHandler implements org.xml.sax.ErrorHandler |
| { |
| |
| private final int ERROR = 0; |
| private final int WARNING = 1; |
| private XMLValidationInfo valinfo; |
| |
| /** |
| * Constructor. |
| * |
| * @param valinfo The XML validation info object that will hold the validation messages. |
| */ |
| public XMLErrorHandler(XMLValidationInfo valinfo) |
| { |
| this.valinfo = valinfo; |
| } |
| |
| /** |
| * Add a validation message with the given severity. |
| * |
| * @param exception The exception that contains the message. |
| * @param severity The severity of the message. |
| */ |
| |
| protected void addValidationMessage(SAXParseException exception, int severity) |
| { |
| if(exception.getSystemId() != null) |
| { |
| int lineNumber = exception.getLineNumber(); |
| int columnNumber = exception.getColumnNumber(); |
| |
| // For the following three errors the line number will be modified to use that of the start |
| // tag instead of the end tag. |
| String currentErrorKey = valinfo.currentErrorKey; |
| if (currentErrorKey != null && adjustLocationErrorKeySet.contains(currentErrorKey)) |
| { |
| LocationCoordinate adjustedCoordinates = (LocationCoordinate)valinfo.getStartElementLocations().peek(); |
| lineNumber = adjustedCoordinates.getLineNumber(); |
| columnNumber = adjustedCoordinates.getColumnNumner(); |
| } |
| |
| if(severity == WARNING) |
| { |
| valinfo.addWarning(exception.getLocalizedMessage(), lineNumber, columnNumber, exception.getSystemId()); |
| } |
| else |
| { |
| valinfo.addError(exception.getLocalizedMessage(), lineNumber, columnNumber, exception.getSystemId(), valinfo.getCurrentErrorKey(), valinfo.getMessageArguments()); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) |
| */ |
| public void error(SAXParseException exception) throws SAXException |
| { |
| addValidationMessage(exception, ERROR); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) |
| */ |
| public void fatalError(SAXParseException exception) throws SAXException |
| { |
| addValidationMessage(exception, ERROR); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) |
| */ |
| public void warning(SAXParseException exception) throws SAXException |
| { |
| addValidationMessage(exception, WARNING); |
| } |
| } |
| |
| /** |
| * This class is used to count the elementDecls that are encountered in a DTD. |
| */ |
| protected class MyDeclHandler implements DeclHandler |
| { |
| |
| /** |
| * Constructor. |
| * |
| * @param valinfo The XMLValidationInfo object that will count the declarations. |
| */ |
| public MyDeclHandler() |
| { |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ext.DeclHandler#attributeDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) |
| */ |
| public void attributeDecl(String eName, String aName, String type, String valueDefault, String value) |
| { |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ext.DeclHandler#elementDecl(java.lang.String, java.lang.String) |
| */ |
| public void elementDecl(String name, String model) |
| { |
| //valinfo.increaseElementDeclarationCount(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(java.lang.String, java.lang.String, java.lang.String) |
| */ |
| public void externalEntityDecl(String name, String publicId, String systemId) |
| { |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ext.DeclHandler#internalEntityDecl(java.lang.String, java.lang.String) |
| */ |
| public void internalEntityDecl(String name, String value) |
| { |
| } |
| } |
| |
| /** |
| * A StandardParserConfiguration that creates an error reporter which can ignore |
| * DTD error messages for DTD's with no elements defined. |
| */ |
| |
| protected class MyStandardParserConfiguration extends StandardParserConfiguration |
| { |
| XMLValidationInfo valinfo = null; |
| List reportedExceptions = new ArrayList(); |
| |
| /** |
| * Constructor. |
| * |
| * @param valinfo The XMLValidationInfo object to use. |
| */ |
| public MyStandardParserConfiguration(XMLValidationInfo valinfo) |
| { |
| this.valinfo = valinfo; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.parsers.DTDConfiguration#createErrorReporter() |
| */ |
| protected XMLErrorReporter createErrorReporter() |
| { |
| return new XMLErrorReporter() |
| { |
| /* (non-Javadoc) |
| * @see org.apache.xerces.impl.XMLErrorReporter#reportError(java.lang.String, java.lang.String, java.lang.Object[], short) |
| */ |
| public void reportError(String domain, String key, Object[] arguments, short severity) throws XNIException |
| { |
| boolean reportError = true; |
| valinfo.setCurrentErrorKey(key); |
| valinfo.setMessageArguments(arguments); |
| String ignoreCondition = (String)ingoredErrorKeyTable.get(key); |
| if (ignoreCondition != null) |
| { |
| if (ignoreCondition.equals(XMLValidator.IGNORE_IF_DTD_WITHOUT_ELEMENT_DECL)) |
| { |
| boolean isDTDWithoutElementDeclarationEncountered = valinfo.isDTDWithoutElementDeclarationEncountered(); |
| reportError = !isDTDWithoutElementDeclarationEncountered; |
| } |
| else |
| { |
| reportError = false; |
| } |
| } |
| if ("schema_reference.4".equals(key) && arguments.length > 0) //$NON-NLS-1$ |
| { |
| Object location = arguments[0]; |
| if (location != null) |
| { |
| if(reportedExceptions.contains(location)) |
| { |
| reportError = false; |
| } |
| else |
| { |
| reportedExceptions.add(location); |
| } |
| } |
| } |
| if (reportError) |
| { |
| super.reportError(domain, key, arguments, severity); |
| valinfo.getErrorCustomizationManager().considerReportedError(valinfo, key, arguments); |
| } |
| } |
| }; |
| } |
| } |
| |
| /** |
| * A line and column number coordinate. |
| */ |
| protected class LocationCoordinate |
| { |
| private int lineNo = -1; |
| private int columnNo = -1; |
| |
| public LocationCoordinate(int lineNumber, int columnNumber) |
| { |
| this.lineNo = lineNumber; |
| this.columnNo = columnNumber; |
| } |
| |
| public int getLineNumber() |
| { |
| return this.lineNo; |
| } |
| |
| public int getColumnNumner() |
| { |
| return this.columnNo; |
| } |
| } |
| } |