duke@1: /* ohair@554: * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javac.jvm; duke@1: duke@1: import java.util.*; duke@1: duke@1: import com.sun.tools.javac.tree.*; duke@1: import com.sun.tools.javac.util.*; duke@1: import com.sun.tools.javac.util.List; duke@1: import com.sun.tools.javac.tree.JCTree.*; duke@1: duke@1: /** This class contains the CharacterRangeTable for some method duke@1: * and the hashtable for mapping trees or lists of trees to their duke@1: * ending positions. duke@1: * jjg@581: *

This is NOT part of any supported API. jjg@581: * If you write code that depends on this, you do so at your own risk. duke@1: * This code and its internal interfaces are subject to change or duke@1: * deletion without notice. duke@1: */ duke@1: public class CRTable duke@1: implements CRTFlags { duke@1: duke@1: private final boolean crtDebug = false; duke@1: duke@1: /** The list of CRTable entries. duke@1: */ duke@1: private ListBuffer entries = new ListBuffer(); duke@1: duke@1: /** The hashtable for source positions. duke@1: */ duke@1: private Map positions = new HashMap(); duke@1: duke@1: /** The hashtable for ending positions stored in the parser. duke@1: */ duke@1: private Map endPositions; duke@1: duke@1: /** The tree of the method this table is intended for. duke@1: * We should traverse this tree to get source ranges. duke@1: */ duke@1: JCTree.JCMethodDecl methodTree; duke@1: duke@1: /** Constructor duke@1: */ duke@1: public CRTable(JCTree.JCMethodDecl tree, Map endPositions) { duke@1: this.methodTree = tree; duke@1: this.endPositions = endPositions; duke@1: } duke@1: duke@1: /** Create a new CRTEntry and add it to the entries. duke@1: * @param tree The tree or the list of trees for which duke@1: * we are storing the code pointers. duke@1: * @param flags The set of flags designating type of the entry. duke@1: * @param startPc The starting code position. duke@1: * @param endPc The ending code position. duke@1: */ duke@1: public void put(Object tree, int flags, int startPc, int endPc) { duke@1: entries.append(new CRTEntry(tree, flags, startPc, endPc)); duke@1: } duke@1: duke@1: /** Compute source positions and write CRT to the databuf. duke@1: * @param databuf The buffer to write bytecodes to. duke@1: */ duke@1: public int writeCRT(ByteBuffer databuf, Position.LineMap lineMap, Log log) { duke@1: duke@1: int crtEntries = 0; duke@1: duke@1: // compute source positions for the method duke@1: new SourceComputer().csp(methodTree); duke@1: duke@1: for (List l = entries.toList(); l.nonEmpty(); l = l.tail) { duke@1: duke@1: CRTEntry entry = l.head; duke@1: duke@1: // eliminate entries that do not produce bytecodes: duke@1: // for example, empty blocks and statements duke@1: if (entry.startPc == entry.endPc) duke@1: continue; duke@1: duke@1: SourceRange pos = positions.get(entry.tree); duke@1: assert pos != null : "CRT: tree source positions are undefined"; duke@1: if ((pos.startPos == Position.NOPOS) || (pos.endPos == Position.NOPOS)) duke@1: continue; duke@1: duke@1: if (crtDebug) { duke@1: System.out.println("Tree: " + entry.tree + ", type:" + getTypes(entry.flags)); duke@1: System.out.print("Start: pos = " + pos.startPos + ", pc = " + entry.startPc); duke@1: } duke@1: duke@1: // encode startPos into line/column representation duke@1: int startPos = encodePosition(pos.startPos, lineMap, log); duke@1: if (startPos == Position.NOPOS) duke@1: continue; duke@1: duke@1: if (crtDebug) { duke@1: System.out.print("End: pos = " + pos.endPos + ", pc = " + (entry.endPc - 1)); duke@1: } duke@1: duke@1: // encode endPos into line/column representation duke@1: int endPos = encodePosition(pos.endPos, lineMap, log); duke@1: if (endPos == Position.NOPOS) duke@1: continue; duke@1: duke@1: // write attribute duke@1: databuf.appendChar(entry.startPc); duke@1: // 'endPc - 1' because endPc actually points to start of the next command duke@1: databuf.appendChar(entry.endPc - 1); duke@1: databuf.appendInt(startPos); duke@1: databuf.appendInt(endPos); duke@1: databuf.appendChar(entry.flags); duke@1: duke@1: crtEntries++; duke@1: } duke@1: duke@1: return crtEntries; duke@1: } duke@1: duke@1: /** Return the number of the entries. duke@1: */ duke@1: public int length() { duke@1: return entries.length(); duke@1: } duke@1: duke@1: /** Return string describing flags enabled. duke@1: */ duke@1: private String getTypes(int flags) { duke@1: String types = ""; duke@1: if ((flags & CRT_STATEMENT) != 0) types += " CRT_STATEMENT"; duke@1: if ((flags & CRT_BLOCK) != 0) types += " CRT_BLOCK"; duke@1: if ((flags & CRT_ASSIGNMENT) != 0) types += " CRT_ASSIGNMENT"; duke@1: if ((flags & CRT_FLOW_CONTROLLER) != 0) types += " CRT_FLOW_CONTROLLER"; duke@1: if ((flags & CRT_FLOW_TARGET) != 0) types += " CRT_FLOW_TARGET"; duke@1: if ((flags & CRT_INVOKE) != 0) types += " CRT_INVOKE"; duke@1: if ((flags & CRT_CREATE) != 0) types += " CRT_CREATE"; duke@1: if ((flags & CRT_BRANCH_TRUE) != 0) types += " CRT_BRANCH_TRUE"; duke@1: if ((flags & CRT_BRANCH_FALSE) != 0) types += " CRT_BRANCH_FALSE"; duke@1: return types; duke@1: } duke@1: duke@1: /** Source file positions in CRT are integers in the format: duke@1: * line-number << LINESHIFT + column-number duke@1: */ duke@1: private int encodePosition(int pos, Position.LineMap lineMap, Log log) { duke@1: int line = lineMap.getLineNumber(pos); duke@1: int col = lineMap.getColumnNumber(pos); duke@1: int new_pos = Position.encodePosition(line, col); duke@1: if (crtDebug) { duke@1: System.out.println(", line = " + line + ", column = " + col + duke@1: ", new_pos = " + new_pos); duke@1: } duke@1: if (new_pos == Position.NOPOS) duke@1: log.warning(pos, "position.overflow", line); duke@1: duke@1: return new_pos; duke@1: } duke@1: duke@1: /* ************************************************************************ duke@1: * Traversal methods duke@1: *************************************************************************/ duke@1: duke@1: /** duke@1: * This class contains methods to compute source positions for trees. duke@1: * Extends Tree.Visitor to traverse the abstract syntax tree. duke@1: */ duke@1: class SourceComputer extends JCTree.Visitor { duke@1: duke@1: /** The result of the tree traversal methods. duke@1: */ duke@1: SourceRange result; duke@1: duke@1: /** Visitor method: compute source positions for a single node. duke@1: */ duke@1: public SourceRange csp(JCTree tree) { duke@1: if (tree == null) return null; duke@1: tree.accept(this); duke@1: if (result != null) { duke@1: positions.put(tree, result); duke@1: } duke@1: return result; duke@1: } duke@1: duke@1: /** Visitor method: compute source positions for a list of nodes. duke@1: */ duke@1: public SourceRange csp(List trees) { duke@1: if ((trees == null) || !(trees.nonEmpty())) return null; duke@1: SourceRange list_sr = new SourceRange(); duke@1: for (List l = trees; l.nonEmpty(); l = l.tail) { duke@1: list_sr.mergeWith(csp(l.head)); duke@1: } duke@1: positions.put(trees, list_sr); duke@1: return list_sr; duke@1: } duke@1: duke@1: /** Visitor method: compute source positions for duke@1: * a list of case blocks of switch statements. duke@1: */ duke@1: public SourceRange cspCases(List trees) { duke@1: if ((trees == null) || !(trees.nonEmpty())) return null; duke@1: SourceRange list_sr = new SourceRange(); duke@1: for (List l = trees; l.nonEmpty(); l = l.tail) { duke@1: list_sr.mergeWith(csp(l.head)); duke@1: } duke@1: positions.put(trees, list_sr); duke@1: return list_sr; duke@1: } duke@1: duke@1: /** Visitor method: compute source positions for duke@1: * a list of catch clauses in try statements. duke@1: */ duke@1: public SourceRange cspCatchers(List trees) { duke@1: if ((trees == null) || !(trees.nonEmpty())) return null; duke@1: SourceRange list_sr = new SourceRange(); duke@1: for (List l = trees; l.nonEmpty(); l = l.tail) { duke@1: list_sr.mergeWith(csp(l.head)); duke@1: } duke@1: positions.put(trees, list_sr); duke@1: return list_sr; duke@1: } duke@1: duke@1: public void visitMethodDef(JCMethodDecl tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.body)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitVarDef(JCVariableDecl tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: csp(tree.vartype); duke@1: sr.mergeWith(csp(tree.init)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitSkip(JCSkip tree) { duke@1: // endPos is the same as startPos for the empty statement duke@1: SourceRange sr = new SourceRange(startPos(tree), startPos(tree)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitBlock(JCBlock tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: csp(tree.stats); // doesn't compare because block's ending position is defined duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitDoLoop(JCDoWhileLoop tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.body)); duke@1: sr.mergeWith(csp(tree.cond)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitWhileLoop(JCWhileLoop tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.cond)); duke@1: sr.mergeWith(csp(tree.body)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitForLoop(JCForLoop tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.init)); duke@1: sr.mergeWith(csp(tree.cond)); duke@1: sr.mergeWith(csp(tree.step)); duke@1: sr.mergeWith(csp(tree.body)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitForeachLoop(JCEnhancedForLoop tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.var)); duke@1: sr.mergeWith(csp(tree.expr)); duke@1: sr.mergeWith(csp(tree.body)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitLabelled(JCLabeledStatement tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.body)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitSwitch(JCSwitch tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.selector)); duke@1: sr.mergeWith(cspCases(tree.cases)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitCase(JCCase tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.pat)); duke@1: sr.mergeWith(csp(tree.stats)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitSynchronized(JCSynchronized tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.lock)); duke@1: sr.mergeWith(csp(tree.body)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitTry(JCTry tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); darcy@609: sr.mergeWith(csp(tree.resources)); duke@1: sr.mergeWith(csp(tree.body)); duke@1: sr.mergeWith(cspCatchers(tree.catchers)); duke@1: sr.mergeWith(csp(tree.finalizer)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitCatch(JCCatch tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.param)); duke@1: sr.mergeWith(csp(tree.body)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitConditional(JCConditional tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.cond)); duke@1: sr.mergeWith(csp(tree.truepart)); duke@1: sr.mergeWith(csp(tree.falsepart)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitIf(JCIf tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.cond)); duke@1: sr.mergeWith(csp(tree.thenpart)); duke@1: sr.mergeWith(csp(tree.elsepart)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitExec(JCExpressionStatement tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.expr)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitBreak(JCBreak tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitContinue(JCContinue tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitReturn(JCReturn tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.expr)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitThrow(JCThrow tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.expr)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitAssert(JCAssert tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.cond)); duke@1: sr.mergeWith(csp(tree.detail)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitApply(JCMethodInvocation tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.meth)); duke@1: sr.mergeWith(csp(tree.args)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitNewClass(JCNewClass tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.encl)); duke@1: sr.mergeWith(csp(tree.clazz)); duke@1: sr.mergeWith(csp(tree.args)); duke@1: sr.mergeWith(csp(tree.def)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitNewArray(JCNewArray tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.elemtype)); duke@1: sr.mergeWith(csp(tree.dims)); duke@1: sr.mergeWith(csp(tree.elems)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitParens(JCParens tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.expr)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitAssign(JCAssign tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.lhs)); duke@1: sr.mergeWith(csp(tree.rhs)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitAssignop(JCAssignOp tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.lhs)); duke@1: sr.mergeWith(csp(tree.rhs)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitUnary(JCUnary tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.arg)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitBinary(JCBinary tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.lhs)); duke@1: sr.mergeWith(csp(tree.rhs)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitTypeCast(JCTypeCast tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.clazz)); duke@1: sr.mergeWith(csp(tree.expr)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitTypeTest(JCInstanceOf tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.expr)); duke@1: sr.mergeWith(csp(tree.clazz)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitIndexed(JCArrayAccess tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.indexed)); duke@1: sr.mergeWith(csp(tree.index)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitSelect(JCFieldAccess tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.selected)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitIdent(JCIdent tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitLiteral(JCLiteral tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitTypeIdent(JCPrimitiveTypeTree tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitTypeArray(JCArrayTypeTree tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.elemtype)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitTypeApply(JCTypeApply tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.clazz)); duke@1: sr.mergeWith(csp(tree.arguments)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitTypeParameter(JCTypeParameter tree) { duke@1: SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); duke@1: sr.mergeWith(csp(tree.bounds)); duke@1: result = sr; duke@1: } duke@1: duke@1: public void visitWildcard(JCWildcard tree) { duke@1: result = null; duke@1: } duke@1: duke@1: public void visitErroneous(JCErroneous tree) { duke@1: result = null; duke@1: } duke@1: duke@1: public void visitTree(JCTree tree) { duke@1: assert false; duke@1: } duke@1: duke@1: /** The start position of given tree. duke@1: */ duke@1: public int startPos(JCTree tree) { duke@1: if (tree == null) return Position.NOPOS; duke@1: return tree.pos; duke@1: } duke@1: duke@1: /** The end position of given tree, if it has duke@1: * defined endpos, NOPOS otherwise. duke@1: */ duke@1: public int endPos(JCTree tree) { duke@1: if (tree == null) return Position.NOPOS; duke@1: if (tree.getTag() == JCTree.BLOCK) duke@1: return ((JCBlock) tree).endpos; duke@1: Integer endpos = endPositions.get(tree); duke@1: if (endpos != null) duke@1: return endpos.intValue(); duke@1: return Position.NOPOS; duke@1: } duke@1: } duke@1: duke@1: /** This class contains a CharacterRangeTableEntry. duke@1: */ duke@1: static class CRTEntry { duke@1: duke@1: /** A tree or a list of trees to obtain source positions. duke@1: */ duke@1: Object tree; duke@1: duke@1: /** The flags described in the CharacterRangeTable spec. duke@1: */ duke@1: int flags; duke@1: duke@1: /** The starting code position of this entry. duke@1: */ duke@1: int startPc; duke@1: duke@1: /** The ending code position of this entry. duke@1: */ duke@1: int endPc; duke@1: duke@1: /** Constructor */ duke@1: CRTEntry(Object tree, int flags, int startPc, int endPc) { duke@1: this.tree = tree; duke@1: this.flags = flags; duke@1: this.startPc = startPc; duke@1: this.endPc = endPc; duke@1: } duke@1: } duke@1: duke@1: duke@1: /** This class contains source positions duke@1: * for some tree or list of trees. duke@1: */ duke@1: static class SourceRange { duke@1: duke@1: /** The starting source position. duke@1: */ duke@1: int startPos; duke@1: duke@1: /** The ending source position. duke@1: */ duke@1: int endPos; duke@1: duke@1: /** Constructor */ duke@1: SourceRange() { duke@1: startPos = Position.NOPOS; duke@1: endPos = Position.NOPOS; duke@1: } duke@1: duke@1: /** Constructor */ duke@1: SourceRange(int startPos, int endPos) { duke@1: this.startPos = startPos; duke@1: this.endPos = endPos; duke@1: } duke@1: duke@1: /** Compare the starting and the ending positions duke@1: * of the source range and combines them assigning duke@1: * the widest range to this. duke@1: */ duke@1: SourceRange mergeWith(SourceRange sr) { duke@1: if (sr == null) return this; duke@1: if (startPos == Position.NOPOS) duke@1: startPos = sr.startPos; duke@1: else if (sr.startPos != Position.NOPOS) duke@1: startPos = (startPos < sr.startPos ? startPos : sr.startPos); duke@1: if (endPos == Position.NOPOS) duke@1: endPos = sr.endPos; duke@1: else if (sr.endPos != Position.NOPOS) duke@1: endPos = (endPos > sr.endPos ? endPos : sr.endPos); duke@1: return this; duke@1: } duke@1: } duke@1: duke@1: }