All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xdef.sys.SPosition Maven / Gradle / Ivy

There is a newer version: 42.2.13
Show newest version
package org.xdef.sys;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/** Source position.
 * @author  Vaclav Trojan
 */
public class SPosition {
	/** Actual buffer position to source buffer. */
	private int _bufIndex;
	/** Line number. */
	private long _line;
	/** Relative file position to the beginning of source of start of line. */
	private long _startLine;
	/** Relative position of parsed text to the beginning of source text. */
	private long _filePos;
	/** File name or URL of source data (or null). Note that PublicId can be
	 appended to sysId as "$pubid[" ... "]".*/
	private String _sysId;
	/** list of replacements in the source buffer. */
	private List _modificationInfo;

	/** Creates a new empty instance of SPosition. */
	public SPosition() {}

	/** Creates a new instance of SPosition as copy of given position.
	 * @param line line number.
	 * @param column column number.
	 * @param sysId system ID or null.
	 * @param pubId public ID or null.
	 */
	public SPosition(final int line,
		final int column,
		final String sysId,
		final String pubId) {
		_bufIndex = column > 0 ? column - 1 : 0;
		_line = line == 0 ? 0 : line < 0 ? Integer.MAX_VALUE - line : line;
		_sysId = pubId == null ? sysId : sysId == null
			? "$pubid[" + pubId + ']' : (sysId + "$pubid[" + pubId + ']');
//		_modificationInfo = null; _filePos = _startLine = 0L; //Java makes it!
	}

	/** Creates a new instance of SPosition as copy of given position.
	 * @param bufIndex buffer position.
	 * @param line line number.
	 * @param startLine file position of the last start of line.
	 * @param filePos file position of the start of buffer.
	 * @param rawSysId the string with raw form of sysId and putId.
	 */
	public SPosition(final int bufIndex,
		final long line,
		final long startLine,
		final long filePos,
		final String rawSysId) {
		_bufIndex = bufIndex;
		_line = line;
		_startLine = startLine;
		_filePos = filePos;
		_sysId = rawSysId;
//		_modificationInfo = null; //Java makes it!
	}

	/** Creates a new instance of SPosition as copy of given position.
	 * @param spos The SPosition.
	 */
	public SPosition(final SPosition spos) {
		if (spos != null) {
			_bufIndex = spos._bufIndex;
			_line = spos._line;
			_startLine = spos._startLine;
			_filePos = spos._filePos;
			_sysId = spos._sysId;
			if (spos._modificationInfo != null) {
				_modificationInfo = spos.cloneModificationInfo();
			}
		}
	}

	/** Set position to this object.
	 * @param line line number.
	 * @param column column number.
	 * @param sysId system ID or null.
	 * @param pubId public ID or null.
	 */
	public final void setPosition(final int line,
		final int column,
		final String sysId,
		final String pubId) {
		_bufIndex = column > 0 ? column - 1 : 0;
		_line = line == 0 ? 0 : line < 0 ? Integer.MAX_VALUE - line : line;
		_sysId = pubId == null ? sysId : sysId == null
			? "$pubid[" + pubId + ']' : (sysId + "$pubid[" + pubId + ']');
		_filePos = 0L;
		_startLine = 0L;
		_modificationInfo = null;
	}

	public final void setNewLine() {
		_line++;
		_startLine = _filePos + _bufIndex + 1;
	}

	/** Set position from other position.
	 * @param spos other position.
	 */
	public final void setPosition(final SPosition spos) {
		_bufIndex = spos._bufIndex;
		_line = spos._line;
		_sysId = spos._sysId;
		_filePos = spos._filePos;
		_startLine = spos._startLine;
		cloneModificationInfo(spos);
	}

	/** Clone ModificationInfo.
	 * @return a clone of ModificationInfo.
	 */
	private List cloneModificationInfo() {
		int n;
		if (_modificationInfo == null || (n = _modificationInfo.size()) == 0) {
			return null;
		}
		// Note can't just copy!
		List result = new ArrayList<>(n);
		for (int i = 0; i < n; i++) {
			result.add(new Replacement(_modificationInfo.get(i)));
		}
		return result;
	}

	/** Get buffer index.
	 * @return buffer index.
	 */
	public final int getIndex() {return _bufIndex;}

	/** Set buffer index. Note it can not be final - in StringParser override.
	 * @param pos buffer index.
	 */
	public void setIndex(final int pos) {_bufIndex = pos;}

	/** Increase buffer index (this method is used only in StringParser).
	 * @return increased index.
	 */
	protected final int incIndex() {return ++_bufIndex;}

	/** Get relative position to the beginning of start of line.
	 * @return relative position to the beginning of start of line.
	 */
	public final long getStartLine() {return _startLine;}

	/** Set relative position to the beginning of start of line.
	 * @param startLine relative position to the beginning of start of line.
	 */
	public final void setStartLine(final long startLine) {_startLine=startLine;}

	/** Get relative file position of parsed text.
	 * @return relative file position of parsed text.
	 */
	public final long getFilePos(){return _filePos;}

	/** Set relative file position of parsed text.
	 * @param filePos Relative file position of parsed text.
	 */
	public final void setFilePos(final long filePos){_filePos = filePos;}

	/** Get file name or URL of source data (or null). Note that public id can
	 * be appended to sysId as "$pubid["... public ..."]".
	 * @return sysId the file name or URL of source data (or null).
	 */
	public final String getSysId() {return _sysId;}

	/** Set file name or URL of source data (or null). Note that public id can
	 * be appended to sysId as "$pubid["... public ..."]".
	 * @param sysId the file name or URL of source data (or null).
	 */
	public final void setSysId(final String sysId) {_sysId = sysId;}

	/** Return the line number where the current document event ends. Lines are
	 * delimited by line ends (0x0D). Warning: The return value from the method
	 * is intended only as an approximation for the sake of diagnostics.
	 * If possible, the parser should provide the line position of the first
	 * character after the text associated with the document event. The first
	 * line is line 1.
	 * @return The line number, or -1 if none is available.
	 */
	public final long getLineNumber() { return _line; }

	/** Set the line number.
	 * @param x line number, or -1 if none is available.
	 */
	public final void setLineNumber(long x) { _line = x; }

	/** Return the column number where the current document event ends. This is
	 * one-based number of Java char values since the last line end. Warning:
	 * The return value from the method is intended only as an approximation for
	 * the sake of diagnostics.
	 * If possible, the parser should provide the line position of the first
	 * character after the text associated with the document event. The first
	 * column in each line is column 1.
	 * @return The column number, or -1 if none is available.
	 */
	public final long getColumnNumber() {
		return _filePos + _bufIndex - _startLine + 1;
	}

	/** Set the column number.
	 * @param x column number, or -1 if none is available.
	 */
	public final void setColumnNumber(long x) {
		if (x > 0) {
			_bufIndex = (int) (_startLine - _filePos - 1 + x);
		}
	}

	/** Return the system identifier for the current document. The return value
	 * is the system identifier (the name of source data) of the document. If
	 * the system identifier is a URL, the parser must resolve it fully before
	 * passing it to the application. For example, a file name must always be
	 * provided as a file:... URL, and other kinds of relative URI are also
	 * resolved against their bases.
	 * @return A string containing the system identifier, or null if none is
	 * available.
	 */
	public final String getSystemId() {
		int ndx;
		return _sysId != null && (ndx = _sysId.indexOf("$pubid[")) >= 0 &&
			_sysId.endsWith("]") ? _sysId.substring(0, ndx) : _sysId;
	}

	/** Return the public identifier for the current document.
	 * @return A string containing the public identifier, or null if none is
	 * available.
	 */
	public final String getPublicId() {
		int ndx;
		return _sysId != null && (ndx = _sysId.indexOf("$pubid[")) >= 0 &&
			_sysId.endsWith("]") ?
			_sysId.substring(ndx + 7, _sysId.length() - 1) :  null;
	}

	/** Get position related to the start of source data.
	 * @return The position.
	 */
	public final long getSourcePosition() { return _filePos + _bufIndex; }

	/** Return the position corrected to original buffer (before modifications).
	 * Position modifications are resolved (the result does not contain them).
	 * @return the corrected position object.
	 */
	public final SPosition correctPosition() {
		return _modificationInfo == null ? this : correctPosition(_bufIndex);
	}

	/** Return the position corrected to original buffer (before modifications).
	 * Position modifications are resolved (the result does not contain them).
	 * @param pos Relative position in actual buffer.
	 * @return the position in original buffer.
	 */
	public final SPosition correctPosition(final int pos) {
		int n;
		Replacement lastItem;
		if (_modificationInfo == null || (n = _modificationInfo.size()) == 0
			|| pos < (lastItem = _modificationInfo.get(0))._bufIndex) {
			return new SPosition(pos, _line, _startLine, _filePos, _sysId);
		}
		// find Replacement with the lisne equal or greater then in the position
		int m = -1;
		for (int i = 0; i < n; i++) {
			Replacement item = _modificationInfo.get(i);
			if (item._line >= _line || item._fixed) {
				lastItem = item;
				m = i;
				break;
			}
		}
		if (m >= 0 && lastItem._bufIndex < pos) {
			// find the last replacement with index smaller then in the position
			for (int i = m + 1; i < n; i++) {
				Replacement item = _modificationInfo.get(i);
				if (item._bufIndex > pos) {
					break;
				}
				lastItem = item;
			}
		}
		return new SPosition((lastItem._fixed) // return corrected position
			? - lastItem._diff : pos - lastItem._bufIndex - lastItem._diff,
			lastItem._line,
			lastItem._startLine,
			lastItem._startLine,
			lastItem._sysId);
	}

	/** Create position information to report.
	 * @param report The report object.
	 * @param pos buffer position.
	 */
	public final void genPositionInfo(final int pos, final Report report) {
		correctPosition(pos).genPositionInfo(report);
	}

	/** Create position information to report.
	 * @param report The report object.
	 */
	public final void genPositionInfo(final Report report) {
		String text = report.getText();
		if (text == null) {
			text = Report.getReportText(report.getMsgID(), "eng");
			if (text == null) {
				text = "";
			}
		}
		String sysId;
		String modification = report.getModification();
		if (modification == null) {
			modification = (getSourcePosition() < 0 ?
				"": "&{pos}" + getSourcePosition()) +
				(_line <= 0 ? "" : "&{line}" + _line) +
				(_line <= 0 || getColumnNumber() < 0 ?
					"" : "&{column}" + getColumnNumber()) +
				((sysId = getSystemId()) == null || sysId.isEmpty() ?
					"" : "&{sysId}" + sysId);
			if (modification.contains("&{line}")
				&& !text.contains("&{#SYS000}") && !text.contains("&{line}")) {
				text += "&{line}{; line=}";
			}
			if (modification.contains("&{column}")
				&&!text.contains("&{#SYS000}") && !text.contains("&{column}")) {
				text += "&{column}{; column=}";
			}
			if (modification.contains("&{sysId}")
				&& !text.contains("&{#SYS000}") && !text.contains("&{sysId}")) {
				text += "&{sysId}{; source='}{'}";
			}
			report.setModification(modification);
		} else {
			if (!modification.contains("&{line}")	&& _line > 0) {
				if (!text.contains("&{line}") && !text.contains("&{#SYS000}")) {
					text += "&{line}{; line=}";
				}
				modification += "&{line}" + _line;
			}
			if (_line > 0 && !modification.contains("&{column}") &&
				getColumnNumber() > 0) {
				if (!text.contains("&{column}")&& !text.contains("&{#SYS000}")){
					text += "&{column}{; column=}";
				}
				modification += "&{column}" + getColumnNumber();
			}
			if (!modification.contains("&{sysId}") &&
				(sysId = getSystemId()) != null && sysId.length() > 0) {
				if (!text.contains("&{sysId}") && !text.contains("&{#SYS000}")){
					text += "&{sysId}{; source='}{'}";
				}
				modification += "&{sysId}" + sysId;
			}
			if (!modification.contains("&{pos}") && getSourcePosition() >= 0) {
				if (!text.contains("&{pos}")) {
					text += "&{pos}{; pos=}";
				}
				modification += "&{pos}" + getSourcePosition();
			}
			if (modification.contains("&{xpath}") &&
				!text.contains("&{xpath}") && !text.contains("&{#SYS000}")) {
				text += "&{xpath}{; xpath=}";
			}
			if (modification.contains("&{xdpos}") &&
				!text.contains("&{xdpos}") && !text.contains("&{#SYS000}")) {
				text += "&{xdpos}{; X-position=}";
			}
		}
		report.setText(text);
		report.setModification(modification);
	}

	/** Put report with position information to the report writer.
	 * If report writer is null and report type is ERROR or FATAL
	 * is thrown the SRuntimeException created from the report.
	 * @param pos The source buffer position.
	 * @param report Report to be sent to reporter or thrown.
	 * @param reportWriter Report writer or null.
	 * @throws SRuntimeException if report writer is null and report type
	 * is ERROR or FATAL.
	 */
	public final void putReport(final int pos,
		final Report report,
		final ReportWriter reportWriter) throws SRuntimeException {
		SPosition p = correctPosition(pos);
		p.genPositionInfo(report);
		if (reportWriter == null) {
			if (report.getType() == Report.WARNING) {
				return;
			}
			throw new SRuntimeException(report);
		}
		reportWriter.putReport(report);
		if (report.getType() != Report.FATAL) {
			return;
		}
		reportWriter.checkAndThrowErrors();
	}

	/** Put report with position information to the report writer.
	 * If report writer is null and report type is ERROR or FATAL
	 * is thrown the SRuntimeException created from the report.
	 * @param report Report to be sent to reporter or thrown.
	 * @param reportWriter Report writer or null.
	 * @throws SRuntimeException if report writer is null and report type
	 * is ERROR or FATAL.
	 */
	public final void putReport(final Report report,
		final ReportWriter reportWriter) throws SRuntimeException {
		correctPosition().genPositionInfo(report);
		if (reportWriter == null) {
			if (report.getType() == Report.WARNING) {
				return;
			}
			throw new SRuntimeException(report);
		}
		reportWriter.putReport(report);
		if (report.getType() != Report.FATAL) {
			return;
		}
		reportWriter.checkAndThrowErrors();
	}

	@Override
	/** Check if some object is equal to this position.
	 * @param obj Object to be compared.
	 * @return true if the argument is considered as the same
	 * position as this one position; otherwise return false.
	 */
	public final boolean equals(final Object obj) {
		if (obj == null || !(obj instanceof SPosition)) {
			return false;
		}
		SPosition pos = (SPosition) obj;
		if (_sysId != null) {
			if (!_sysId.equals(pos._sysId)) {
				return false;
			}
		}  else if (pos._sysId != null) {
			return false;
		}
		return pos._filePos + pos._bufIndex == _filePos + _bufIndex;
	}

	@Override
	public int hashCode() {
		int hash = 79 * 7 + _bufIndex;
		hash = 79 * hash + (_sysId != null ? _sysId.hashCode() : 0);
		return 79 * hash + (int) (_filePos ^ (_filePos >>> 32));
	}

	/** Compares this position with the specified object for order. Returns a
	 * negative integer, zero, or a positive integer as this position is less
	 * than, equal to, or greater than the specified object.

*
The compare algorithm checks: *
1. source name *
2. line *
2. column *
This helps to sort messages according to source position. * @param pos the SPosition to be compared. * @return -1, zero, or +1 as this position * is less than, equal to, or greater than the specified object. * @throws ClassCastException if the specified object's type prevents it * from being compared to this Object. */ public int compareTo(final SPosition pos) { if (_sysId != null) { if (!_sysId.equals(pos._sysId)) { return _sysId.compareTo(pos._sysId); } else if (pos._sysId != null) { return -1; } } long fpos1 = pos._filePos + pos._bufIndex; long fpos = _filePos + _bufIndex; return fpos1 == fpos ? 0 : fpos1 < fpos ? 1 : -1; } /** Add line position. All line positions MUST be set at the end of actual * position modifications list. * @param pos relative position in the buffer. * @param line line number. * @param startLine position of actual line in the file. * @param diff offset difference, * @param fixed switch if position is fixed. */ public final void addPos(final int pos, final long line, final long startLine, final int diff, final boolean fixed) { if (_modificationInfo == null) { //first replacement _modificationInfo = new ArrayList<>(); } _modificationInfo.add( new Replacement(pos, diff, line, startLine, fixed, _sysId)); } /** Add line position. All line positions MUST be set at the end of actual * position modifications list. * @param pos The position of line in the buffer. * @param line line number. * @param startLine position of actual line in the file. */ public final void addLine(final int pos, final long line, final long startLine) { if (_modificationInfo == null) { //first replacement _modificationInfo = new ArrayList<>(); } _modificationInfo.add( new Replacement(pos, 0, line, startLine, false, _sysId)); } /** Add position to internal list of positions. (This method is not public, * it is used only in SBuffer). * @param pos position in buffer. * @param spos source position object. * @param len length of text in buffer described by this position. * @param fixed if true then internal position is not printed. */ final void appendPos(final int pos, final SPosition spos, final int len, final boolean fixed) { if (_modificationInfo == null) { _modificationInfo = new ArrayList<>(); } int diff = (int) (spos._startLine + spos._bufIndex - spos._filePos); _modificationInfo.add(new Replacement(pos, diff, spos._line, spos._startLine, fixed, spos._sysId)); int n; if (spos._modificationInfo != null && (n = spos._modificationInfo.size()) > 0) { for (int i = 0; i < n; i++) { Replacement item = spos._modificationInfo.get(i); _modificationInfo.add(new Replacement( pos + item._bufIndex - diff, diff, item._line, item._startLine, fixed, item._sysId)); } } } /** Update list of modifications when given interval is changed to * the new length. (This method is not public, it is used only in * StringParser). * @param pos The buffer position of modification. * @param length The length of modified original. * @param newLength The length of modification. * @param fixed if true one position will be used for whole range. */ public final void updatePositions(final int pos, final int length, final int newLength, final boolean fixed) { int diff = length - newLength; int newEndPos = pos + newLength; int endPos = pos + length; // The replacement may be a modification of existing source // (newPos == null) or it may be considered as the independent // part (newPos != null). Overlapnig replacements are considered // as the part of the first replacement. // There are 5 situations of replacement position: // 1. There is no replacement block yet -> add first replacement // 2. is between two existing or before the first one -> insert before // 3. covers whole area between the existing ones -> join and/or extend // 4. extends the existing one -> update // 5. is after the last one -> add to the end of list // 6. text is added to the end of source buffer int n; //number of items in replacements if (_modificationInfo == null) { //first replacement _modificationInfo = new ArrayList<>(); n = 0; // empty list } else { n = _modificationInfo.size(); // number of items in the list } if (n == 0) { // create first replacement - situation 0 _modificationInfo.add(new Replacement( pos, 0, _line, _startLine, true, _sysId)); _modificationInfo.add(new Replacement( pos + newLength, diff, _line, _startLine, true, _sysId)); return; } Replacement item = null; for (int i = 0; i < n; i++) { Replacement lastItem = item; item = _modificationInfo.get(i); Replacement nitem; if (item._bufIndex > pos) { if (endPos <= item._bufIndex) { // insert before an item if (i == 0) { // insert before the first item _modificationInfo.add(0, new Replacement(pos, 0, _line, _startLine, true, _sysId)); _modificationInfo.add(1, new Replacement(pos+newLength, diff, _line, _startLine, true, _sysId)); return; } else { // insert between lastItem and item _modificationInfo.add(i, new Replacement(pos, lastItem._bufIndex - pos, lastItem._line, lastItem._startLine, true, lastItem._sysId)); } if (endPos < item._bufIndex) { _modificationInfo.add(++i, new Replacement(newEndPos, lastItem._bufIndex - endPos, lastItem._line, lastItem._startLine, lastItem._fixed, lastItem._sysId)); } i++; n++; while (i < n) { nitem = _modificationInfo.get(i); nitem._bufIndex -= diff; if (nitem._startLine == lastItem._startLine) { nitem._diff += diff; } i++; } return; } else { _modificationInfo.add(0, new Replacement( pos, 0, item._line, item._startLine, true,item._sysId)); n++; i++; //update all following items while (i < n) { nitem = _modificationInfo.get(i); if (endPos < nitem._bufIndex) { _modificationInfo.remove(i); n--; } else { if (endPos == item._bufIndex) { } else { _modificationInfo.add(i, new Replacement(newEndPos, diff, item._line, item._startLine, true, item._sysId)); } break; } } while (i < n) { nitem = _modificationInfo.get(i); nitem._bufIndex -= diff; if (item._startLine == nitem._startLine) { nitem._diff += diff; } i++; } return; } } } _modificationInfo.add(new Replacement(pos, 0, item._line, item._startLine, true, item._sysId)); _modificationInfo.add(new Replacement(pos, diff, item._line, item._startLine, true, item._sysId)); } /** Copy modification information from given position to this position. * (raw copy). (This method is not public, it is used only in StringParser * and StreamParser). * @param pos position from which copy should be copied. */ final void copyModificationInfo(final SPosition pos) { _modificationInfo = pos._modificationInfo; } /** Set clone of modification information from given position to this * position. (This method is not public, it is used only in StringParser * and StreamParser). * @param pos position from which copy should be copied. */ final void cloneModificationInfo(final SPosition pos) { _modificationInfo = pos.cloneModificationInfo(); } /** Clear modification information (used only internally). */ final void clearModificationInfo() { _modificationInfo = null; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Line: ").append(String.valueOf(_line)); sb.append(", column: ").append(String.valueOf(getColumnNumber())); sb.append(", position: ").append(String.valueOf(getSourcePosition())); sb.append(", startLine: ").append(String.valueOf(_startLine)); if (_sysId != null) { sb.append(", source: ").append(_sysId); } if (_modificationInfo == null || _modificationInfo.isEmpty()) { return sb.toString(); } sb.append("; replacements:\n"); for (int i = 0; i < _modificationInfo.size(); i++) { sb.append("[").append(i <= 9 ? "0"+i : String.valueOf(i)). append("] "); sb.append(_modificationInfo.get(i)); } return sb.toString(); } public final void writeObj(final SObjectWriter w) throws IOException { w.writeInt(_bufIndex); w.writeLong(_line); w.writeLong(_startLine); w.writeLong(_filePos); w.writeString(_sysId); int len = _modificationInfo == null ? 0 : _modificationInfo.size(); w.writeLength(len); for (int i = 0; i < len; i++) { Replacement item = _modificationInfo.get(i); w.writeInt(item._bufIndex); w.writeInt(item._diff); w.writeLong(item._line); w.writeLong(item._startLine); w.writeBoolean(item._fixed); w.writeString(item._sysId); } } public static final SPosition readObj(final SObjectReader r) throws IOException { SPosition x = new SPosition(); x._bufIndex = r.readInt(); x._line = r.readLong(); x._startLine = r.readLong(); x._filePos = r.readLong(); x._sysId = r.readString(); int len = r.readLength(); if (len > 0) { x._modificationInfo = new ArrayList<>(len); for (int i = 0; i < len; i++) { int pos = r.readInt(); int diff = r.readInt(); long line = r.readLong(); long startLine = r.readLong(); boolean fixed = r.readBoolean(); String sysId = r.readString(); x._modificationInfo.add(new Replacement( pos, diff, line, startLine, fixed, sysId)); } } return x; } /** The object with modification information. */ private final static class Replacement { /** Start position of replacement. */ private int _bufIndex; /** Difference to start line position. */ private int _diff; /** Original line number. */ private final long _line; /** Original start line position. */ private final long _startLine; /** If true use this position for whole range. */ private final boolean _fixed; /** System ID. */ private final String _sysId; /** create new Replacement object including a position. * @param bufIndex position of replacement. * @param diff difference to start line position. * @param line line number. * @param startLine starting position of line. * @param fixed if true one position will be used for whole range. * @param sPosition source position of replacement. */ Replacement(final int bufIndex, final int diff, final long line, final long startLine, final boolean fixed, final String sysId) { _bufIndex = bufIndex; _diff = diff; _line = line; _startLine = startLine; _fixed = fixed; _sysId = sysId; } /** Create new Replacement object as copy of given argument. * @param r The replacement to be cloned. */ private Replacement(final Replacement r) { _bufIndex = r._bufIndex; _diff = r._diff; _line = r._line; _startLine = r._startLine; _fixed = r._fixed; _sysId = r._sysId; } @Override /** Get printable format of source position. */ public String toString() { return "bufIdex" + _bufIndex + ", line:" + _line + ", diff:" + _diff + ", startLine:" + _startLine + ", fixed:" + _fixed + ", sysId:" + _sysId; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy