1 | /* |
2 | * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. |
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | * |
5 | * This code is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License version 2 only, as |
7 | * published by the Free Software Foundation. Sun designates this |
8 | * particular file as subject to the "Classpath" exception as provided |
9 | * by Sun in the LICENSE file that accompanied this code. |
10 | * |
11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | * version 2 for more details (a copy is included in the LICENSE file that |
15 | * accompanied this code). |
16 | * |
17 | * You should have received a copy of the GNU General Public License version |
18 | * 2 along with this work; if not, write to the Free Software Foundation, |
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
20 | * |
21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
23 | * have any questions. |
24 | */ |
25 | |
26 | package com.sun.tools.javac.jvm; |
27 | |
28 | import java.util.*; |
29 | |
30 | import com.sun.tools.javac.tree.*; |
31 | import com.sun.tools.javac.util.*; |
32 | import com.sun.tools.javac.util.List; |
33 | import com.sun.tools.javac.tree.JCTree.*; |
34 | |
35 | /** This class contains the CharacterRangeTable for some method |
36 | * and the hashtable for mapping trees or lists of trees to their |
37 | * ending positions. |
38 | * |
39 | * <p><b>This is NOT part of any API supported by Sun Microsystems. If |
40 | * you write code that depends on this, you do so at your own risk. |
41 | * This code and its internal interfaces are subject to change or |
42 | * deletion without notice.</b> |
43 | */ |
44 | public class CRTable |
45 | implements CRTFlags { |
46 | |
47 | private final boolean crtDebug = false; |
48 | |
49 | /** The list of CRTable entries. |
50 | */ |
51 | private ListBuffer<CRTEntry> entries = new ListBuffer<CRTEntry>(); |
52 | |
53 | /** The hashtable for source positions. |
54 | */ |
55 | private Map<Object,SourceRange> positions = new HashMap<Object,SourceRange>(); |
56 | |
57 | /** The hashtable for ending positions stored in the parser. |
58 | */ |
59 | private Map<JCTree, Integer> endPositions; |
60 | |
61 | /** The tree of the method this table is intended for. |
62 | * We should traverse this tree to get source ranges. |
63 | */ |
64 | JCTree.JCMethodDecl methodTree; |
65 | |
66 | /** Constructor |
67 | */ |
68 | public CRTable(JCTree.JCMethodDecl tree, Map<JCTree, Integer> endPositions) { |
69 | this.methodTree = tree; |
70 | this.endPositions = endPositions; |
71 | } |
72 | |
73 | /** Create a new CRTEntry and add it to the entries. |
74 | * @param tree The tree or the list of trees for which |
75 | * we are storing the code pointers. |
76 | * @param flags The set of flags designating type of the entry. |
77 | * @param startPc The starting code position. |
78 | * @param endPc The ending code position. |
79 | */ |
80 | public void put(Object tree, int flags, int startPc, int endPc) { |
81 | entries.append(new CRTEntry(tree, flags, startPc, endPc)); |
82 | } |
83 | |
84 | /** Compute source positions and write CRT to the databuf. |
85 | * @param databuf The buffer to write bytecodes to. |
86 | */ |
87 | public int writeCRT(ByteBuffer databuf, Position.LineMap lineMap, Log log) { |
88 | |
89 | int crtEntries = 0; |
90 | |
91 | // compute source positions for the method |
92 | new SourceComputer().csp(methodTree); |
93 | |
94 | for (List<CRTEntry> l = entries.toList(); l.nonEmpty(); l = l.tail) { |
95 | |
96 | CRTEntry entry = l.head; |
97 | |
98 | // eliminate entries that do not produce bytecodes: |
99 | // for example, empty blocks and statements |
100 | if (entry.startPc == entry.endPc) |
101 | continue; |
102 | |
103 | SourceRange pos = positions.get(entry.tree); |
104 | assert pos != null : "CRT: tree source positions are undefined"; |
105 | if ((pos.startPos == Position.NOPOS) || (pos.endPos == Position.NOPOS)) |
106 | continue; |
107 | |
108 | if (crtDebug) { |
109 | System.out.println("Tree: " + entry.tree + ", type:" + getTypes(entry.flags)); |
110 | System.out.print("Start: pos = " + pos.startPos + ", pc = " + entry.startPc); |
111 | } |
112 | |
113 | // encode startPos into line/column representation |
114 | int startPos = encodePosition(pos.startPos, lineMap, log); |
115 | if (startPos == Position.NOPOS) |
116 | continue; |
117 | |
118 | if (crtDebug) { |
119 | System.out.print("End: pos = " + pos.endPos + ", pc = " + (entry.endPc - 1)); |
120 | } |
121 | |
122 | // encode endPos into line/column representation |
123 | int endPos = encodePosition(pos.endPos, lineMap, log); |
124 | if (endPos == Position.NOPOS) |
125 | continue; |
126 | |
127 | // write attribute |
128 | databuf.appendChar(entry.startPc); |
129 | // 'endPc - 1' because endPc actually points to start of the next command |
130 | databuf.appendChar(entry.endPc - 1); |
131 | databuf.appendInt(startPos); |
132 | databuf.appendInt(endPos); |
133 | databuf.appendChar(entry.flags); |
134 | |
135 | crtEntries++; |
136 | } |
137 | |
138 | return crtEntries; |
139 | } |
140 | |
141 | /** Return the number of the entries. |
142 | */ |
143 | public int length() { |
144 | return entries.length(); |
145 | } |
146 | |
147 | /** Return string describing flags enabled. |
148 | */ |
149 | private String getTypes(int flags) { |
150 | String types = ""; |
151 | if ((flags & CRT_STATEMENT) != 0) types += " CRT_STATEMENT"; |
152 | if ((flags & CRT_BLOCK) != 0) types += " CRT_BLOCK"; |
153 | if ((flags & CRT_ASSIGNMENT) != 0) types += " CRT_ASSIGNMENT"; |
154 | if ((flags & CRT_FLOW_CONTROLLER) != 0) types += " CRT_FLOW_CONTROLLER"; |
155 | if ((flags & CRT_FLOW_TARGET) != 0) types += " CRT_FLOW_TARGET"; |
156 | if ((flags & CRT_INVOKE) != 0) types += " CRT_INVOKE"; |
157 | if ((flags & CRT_CREATE) != 0) types += " CRT_CREATE"; |
158 | if ((flags & CRT_BRANCH_TRUE) != 0) types += " CRT_BRANCH_TRUE"; |
159 | if ((flags & CRT_BRANCH_FALSE) != 0) types += " CRT_BRANCH_FALSE"; |
160 | return types; |
161 | } |
162 | |
163 | /** Source file positions in CRT are integers in the format: |
164 | * line-number << LINESHIFT + column-number |
165 | */ |
166 | private int encodePosition(int pos, Position.LineMap lineMap, Log log) { |
167 | int line = lineMap.getLineNumber(pos); |
168 | int col = lineMap.getColumnNumber(pos); |
169 | int new_pos = Position.encodePosition(line, col); |
170 | if (crtDebug) { |
171 | System.out.println(", line = " + line + ", column = " + col + |
172 | ", new_pos = " + new_pos); |
173 | } |
174 | if (new_pos == Position.NOPOS) |
175 | log.warning(pos, "position.overflow", line); |
176 | |
177 | return new_pos; |
178 | } |
179 | |
180 | /* ************************************************************************ |
181 | * Traversal methods |
182 | *************************************************************************/ |
183 | |
184 | /** |
185 | * This class contains methods to compute source positions for trees. |
186 | * Extends Tree.Visitor to traverse the abstract syntax tree. |
187 | */ |
188 | class SourceComputer extends JCTree.Visitor { |
189 | |
190 | /** The result of the tree traversal methods. |
191 | */ |
192 | SourceRange result; |
193 | |
194 | /** Visitor method: compute source positions for a single node. |
195 | */ |
196 | public SourceRange csp(JCTree tree) { |
197 | if (tree == null) return null; |
198 | tree.accept(this); |
199 | if (result != null) { |
200 | positions.put(tree, result); |
201 | } |
202 | return result; |
203 | } |
204 | |
205 | /** Visitor method: compute source positions for a list of nodes. |
206 | */ |
207 | public SourceRange csp(List<? extends JCTree> trees) { |
208 | if ((trees == null) || !(trees.nonEmpty())) return null; |
209 | SourceRange list_sr = new SourceRange(); |
210 | for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) { |
211 | list_sr.mergeWith(csp(l.head)); |
212 | } |
213 | positions.put(trees, list_sr); |
214 | return list_sr; |
215 | } |
216 | |
217 | /** Visitor method: compute source positions for |
218 | * a list of case blocks of switch statements. |
219 | */ |
220 | public SourceRange cspCases(List<JCCase> trees) { |
221 | if ((trees == null) || !(trees.nonEmpty())) return null; |
222 | SourceRange list_sr = new SourceRange(); |
223 | for (List<JCCase> l = trees; l.nonEmpty(); l = l.tail) { |
224 | list_sr.mergeWith(csp(l.head)); |
225 | } |
226 | positions.put(trees, list_sr); |
227 | return list_sr; |
228 | } |
229 | |
230 | /** Visitor method: compute source positions for |
231 | * a list of catch clauses in try statements. |
232 | */ |
233 | public SourceRange cspCatchers(List<JCCatch> trees) { |
234 | if ((trees == null) || !(trees.nonEmpty())) return null; |
235 | SourceRange list_sr = new SourceRange(); |
236 | for (List<JCCatch> l = trees; l.nonEmpty(); l = l.tail) { |
237 | list_sr.mergeWith(csp(l.head)); |
238 | } |
239 | positions.put(trees, list_sr); |
240 | return list_sr; |
241 | } |
242 | |
243 | public void visitMethodDef(JCMethodDecl tree) { |
244 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
245 | sr.mergeWith(csp(tree.body)); |
246 | result = sr; |
247 | } |
248 | |
249 | public void visitVarDef(JCVariableDecl tree) { |
250 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
251 | csp(tree.vartype); |
252 | sr.mergeWith(csp(tree.init)); |
253 | result = sr; |
254 | } |
255 | |
256 | public void visitSkip(JCSkip tree) { |
257 | // endPos is the same as startPos for the empty statement |
258 | SourceRange sr = new SourceRange(startPos(tree), startPos(tree)); |
259 | result = sr; |
260 | } |
261 | |
262 | public void visitBlock(JCBlock tree) { |
263 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
264 | csp(tree.stats); // doesn't compare because block's ending position is defined |
265 | result = sr; |
266 | } |
267 | |
268 | public void visitDoLoop(JCDoWhileLoop tree) { |
269 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
270 | sr.mergeWith(csp(tree.body)); |
271 | sr.mergeWith(csp(tree.cond)); |
272 | result = sr; |
273 | } |
274 | |
275 | public void visitWhileLoop(JCWhileLoop tree) { |
276 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
277 | sr.mergeWith(csp(tree.cond)); |
278 | sr.mergeWith(csp(tree.body)); |
279 | result = sr; |
280 | } |
281 | |
282 | public void visitForLoop(JCForLoop tree) { |
283 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
284 | sr.mergeWith(csp(tree.init)); |
285 | sr.mergeWith(csp(tree.cond)); |
286 | sr.mergeWith(csp(tree.step)); |
287 | sr.mergeWith(csp(tree.body)); |
288 | result = sr; |
289 | } |
290 | |
291 | public void visitForeachLoop(JCEnhancedForLoop tree) { |
292 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
293 | sr.mergeWith(csp(tree.var)); |
294 | sr.mergeWith(csp(tree.expr)); |
295 | sr.mergeWith(csp(tree.body)); |
296 | result = sr; |
297 | } |
298 | |
299 | public void visitLabelled(JCLabeledStatement tree) { |
300 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
301 | sr.mergeWith(csp(tree.body)); |
302 | result = sr; |
303 | } |
304 | |
305 | public void visitSwitch(JCSwitch tree) { |
306 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
307 | sr.mergeWith(csp(tree.selector)); |
308 | sr.mergeWith(cspCases(tree.cases)); |
309 | result = sr; |
310 | } |
311 | |
312 | public void visitCase(JCCase tree) { |
313 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
314 | sr.mergeWith(csp(tree.pat)); |
315 | sr.mergeWith(csp(tree.stats)); |
316 | result = sr; |
317 | } |
318 | |
319 | public void visitSynchronized(JCSynchronized tree) { |
320 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
321 | sr.mergeWith(csp(tree.lock)); |
322 | sr.mergeWith(csp(tree.body)); |
323 | result = sr; |
324 | } |
325 | |
326 | public void visitTry(JCTry tree) { |
327 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
328 | sr.mergeWith(csp(tree.body)); |
329 | sr.mergeWith(cspCatchers(tree.catchers)); |
330 | sr.mergeWith(csp(tree.finalizer)); |
331 | result = sr; |
332 | } |
333 | |
334 | public void visitCatch(JCCatch tree) { |
335 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
336 | sr.mergeWith(csp(tree.param)); |
337 | sr.mergeWith(csp(tree.body)); |
338 | result = sr; |
339 | } |
340 | |
341 | public void visitConditional(JCConditional tree) { |
342 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
343 | sr.mergeWith(csp(tree.cond)); |
344 | sr.mergeWith(csp(tree.truepart)); |
345 | sr.mergeWith(csp(tree.falsepart)); |
346 | result = sr; |
347 | } |
348 | |
349 | public void visitIf(JCIf tree) { |
350 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
351 | sr.mergeWith(csp(tree.cond)); |
352 | sr.mergeWith(csp(tree.thenpart)); |
353 | sr.mergeWith(csp(tree.elsepart)); |
354 | result = sr; |
355 | } |
356 | |
357 | public void visitExec(JCExpressionStatement tree) { |
358 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
359 | sr.mergeWith(csp(tree.expr)); |
360 | result = sr; |
361 | } |
362 | |
363 | public void visitBreak(JCBreak tree) { |
364 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
365 | result = sr; |
366 | } |
367 | |
368 | public void visitContinue(JCContinue tree) { |
369 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
370 | result = sr; |
371 | } |
372 | |
373 | public void visitReturn(JCReturn tree) { |
374 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
375 | sr.mergeWith(csp(tree.expr)); |
376 | result = sr; |
377 | } |
378 | |
379 | public void visitThrow(JCThrow tree) { |
380 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
381 | sr.mergeWith(csp(tree.expr)); |
382 | result = sr; |
383 | } |
384 | |
385 | public void visitAssert(JCAssert tree) { |
386 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
387 | sr.mergeWith(csp(tree.cond)); |
388 | sr.mergeWith(csp(tree.detail)); |
389 | result = sr; |
390 | } |
391 | |
392 | public void visitApply(JCMethodInvocation tree) { |
393 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
394 | sr.mergeWith(csp(tree.meth)); |
395 | sr.mergeWith(csp(tree.args)); |
396 | result = sr; |
397 | } |
398 | |
399 | public void visitNewClass(JCNewClass tree) { |
400 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
401 | sr.mergeWith(csp(tree.encl)); |
402 | sr.mergeWith(csp(tree.clazz)); |
403 | sr.mergeWith(csp(tree.args)); |
404 | sr.mergeWith(csp(tree.def)); |
405 | result = sr; |
406 | } |
407 | |
408 | public void visitNewArray(JCNewArray tree) { |
409 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
410 | sr.mergeWith(csp(tree.elemtype)); |
411 | sr.mergeWith(csp(tree.dims)); |
412 | sr.mergeWith(csp(tree.elems)); |
413 | result = sr; |
414 | } |
415 | |
416 | public void visitParens(JCParens tree) { |
417 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
418 | sr.mergeWith(csp(tree.expr)); |
419 | result = sr; |
420 | } |
421 | |
422 | public void visitAssign(JCAssign tree) { |
423 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
424 | sr.mergeWith(csp(tree.lhs)); |
425 | sr.mergeWith(csp(tree.rhs)); |
426 | result = sr; |
427 | } |
428 | |
429 | public void visitAssignop(JCAssignOp tree) { |
430 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
431 | sr.mergeWith(csp(tree.lhs)); |
432 | sr.mergeWith(csp(tree.rhs)); |
433 | result = sr; |
434 | } |
435 | |
436 | public void visitUnary(JCUnary tree) { |
437 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
438 | sr.mergeWith(csp(tree.arg)); |
439 | result = sr; |
440 | } |
441 | |
442 | public void visitBinary(JCBinary tree) { |
443 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
444 | sr.mergeWith(csp(tree.lhs)); |
445 | sr.mergeWith(csp(tree.rhs)); |
446 | result = sr; |
447 | } |
448 | |
449 | public void visitTypeCast(JCTypeCast tree) { |
450 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
451 | sr.mergeWith(csp(tree.clazz)); |
452 | sr.mergeWith(csp(tree.expr)); |
453 | result = sr; |
454 | } |
455 | |
456 | public void visitTypeTest(JCInstanceOf tree) { |
457 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
458 | sr.mergeWith(csp(tree.expr)); |
459 | sr.mergeWith(csp(tree.clazz)); |
460 | result = sr; |
461 | } |
462 | |
463 | public void visitIndexed(JCArrayAccess tree) { |
464 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
465 | sr.mergeWith(csp(tree.indexed)); |
466 | sr.mergeWith(csp(tree.index)); |
467 | result = sr; |
468 | } |
469 | |
470 | public void visitSelect(JCFieldAccess tree) { |
471 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
472 | sr.mergeWith(csp(tree.selected)); |
473 | result = sr; |
474 | } |
475 | |
476 | public void visitIdent(JCIdent tree) { |
477 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
478 | result = sr; |
479 | } |
480 | |
481 | public void visitLiteral(JCLiteral tree) { |
482 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
483 | result = sr; |
484 | } |
485 | |
486 | public void visitTypeIdent(JCPrimitiveTypeTree tree) { |
487 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
488 | result = sr; |
489 | } |
490 | |
491 | public void visitTypeArray(JCArrayTypeTree tree) { |
492 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
493 | sr.mergeWith(csp(tree.elemtype)); |
494 | result = sr; |
495 | } |
496 | |
497 | public void visitTypeApply(JCTypeApply tree) { |
498 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
499 | sr.mergeWith(csp(tree.clazz)); |
500 | sr.mergeWith(csp(tree.arguments)); |
501 | result = sr; |
502 | } |
503 | |
504 | public void visitTypeParameter(JCTypeParameter tree) { |
505 | SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); |
506 | sr.mergeWith(csp(tree.bounds)); |
507 | result = sr; |
508 | } |
509 | |
510 | public void visitWildcard(JCWildcard tree) { |
511 | result = null; |
512 | } |
513 | |
514 | public void visitErroneous(JCErroneous tree) { |
515 | result = null; |
516 | } |
517 | |
518 | public void visitTree(JCTree tree) { |
519 | assert false; |
520 | } |
521 | |
522 | /** The start position of given tree. |
523 | */ |
524 | public int startPos(JCTree tree) { |
525 | if (tree == null) return Position.NOPOS; |
526 | return tree.pos; |
527 | } |
528 | |
529 | /** The end position of given tree, if it has |
530 | * defined endpos, NOPOS otherwise. |
531 | */ |
532 | public int endPos(JCTree tree) { |
533 | if (tree == null) return Position.NOPOS; |
534 | if (tree.getTag() == JCTree.BLOCK) |
535 | return ((JCBlock) tree).endpos; |
536 | Integer endpos = endPositions.get(tree); |
537 | if (endpos != null) |
538 | return endpos.intValue(); |
539 | return Position.NOPOS; |
540 | } |
541 | } |
542 | |
543 | /** This class contains a CharacterRangeTableEntry. |
544 | */ |
545 | static class CRTEntry { |
546 | |
547 | /** A tree or a list of trees to obtain source positions. |
548 | */ |
549 | Object tree; |
550 | |
551 | /** The flags described in the CharacterRangeTable spec. |
552 | */ |
553 | int flags; |
554 | |
555 | /** The starting code position of this entry. |
556 | */ |
557 | int startPc; |
558 | |
559 | /** The ending code position of this entry. |
560 | */ |
561 | int endPc; |
562 | |
563 | /** Constructor */ |
564 | CRTEntry(Object tree, int flags, int startPc, int endPc) { |
565 | this.tree = tree; |
566 | this.flags = flags; |
567 | this.startPc = startPc; |
568 | this.endPc = endPc; |
569 | } |
570 | } |
571 | |
572 | |
573 | /** This class contains source positions |
574 | * for some tree or list of trees. |
575 | */ |
576 | static class SourceRange { |
577 | |
578 | /** The starting source position. |
579 | */ |
580 | int startPos; |
581 | |
582 | /** The ending source position. |
583 | */ |
584 | int endPos; |
585 | |
586 | /** Constructor */ |
587 | SourceRange() { |
588 | startPos = Position.NOPOS; |
589 | endPos = Position.NOPOS; |
590 | } |
591 | |
592 | /** Constructor */ |
593 | SourceRange(int startPos, int endPos) { |
594 | this.startPos = startPos; |
595 | this.endPos = endPos; |
596 | } |
597 | |
598 | /** Compare the starting and the ending positions |
599 | * of the source range and combines them assigning |
600 | * the widest range to this. |
601 | */ |
602 | SourceRange mergeWith(SourceRange sr) { |
603 | if (sr == null) return this; |
604 | if (startPos == Position.NOPOS) |
605 | startPos = sr.startPos; |
606 | else if (sr.startPos != Position.NOPOS) |
607 | startPos = (startPos < sr.startPos ? startPos : sr.startPos); |
608 | if (endPos == Position.NOPOS) |
609 | endPos = sr.endPos; |
610 | else if (sr.endPos != Position.NOPOS) |
611 | endPos = (endPos > sr.endPos ? endPos : sr.endPos); |
612 | return this; |
613 | } |
614 | } |
615 | |
616 | } |