CellSpanControllerFactory.java |
1 /**************************************************************************** 2 * This demo file is part of yFiles for Java 2.14. 3 * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28, 4 * 72070 Tuebingen, Germany. All rights reserved. 5 * 6 * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution 7 * of demo files in source code or binary form, with or without 8 * modification, is not permitted. 9 * 10 * Owners of a valid software license for a yFiles for Java version that this 11 * demo is shipped with are allowed to use the demo source code as basis 12 * for their own yFiles for Java powered applications. Use of such programs is 13 * governed by the rights and conditions as set out in the yFiles for Java 14 * license agreement. 15 * 16 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 19 * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 ***************************************************************************/ 28 package demo.layout.hierarchic; 29 30 import demo.layout.hierarchic.CellSpanLayoutDemo.Cell; 31 import demo.layout.hierarchic.CellSpanLayoutDemo.CellColorManager; 32 import demo.layout.hierarchic.CellSpanLayoutDemo.Span; 33 34 import y.base.Node; 35 import y.base.NodeCursor; 36 import y.base.NodeList; 37 import y.geom.YInsets; 38 import y.view.EditMode; 39 import y.view.Graph2D; 40 import y.view.HitInfo; 41 import y.view.NodeRealizer; 42 import y.view.PopupMode; 43 import y.view.SelectionBoxMode; 44 import y.view.hierarchy.HierarchyManager; 45 import y.view.tabular.TableGroupNodeRealizer; 46 import y.view.tabular.TableGroupNodeRealizer.Column; 47 import y.view.tabular.TableGroupNodeRealizer.Row; 48 import y.view.tabular.TableGroupNodeRealizer.Table; 49 50 import java.awt.Color; 51 import java.awt.event.MouseEvent; 52 import java.awt.geom.Rectangle2D; 53 import java.util.ArrayList; 54 import java.util.Collection; 55 import javax.swing.JPopupMenu; 56 57 /** 58 * Provides custom user interaction for cell designer table nodes. 59 * 60 */ 61 class CellSpanControllerFactory { 62 /** 63 * Prevents instantiation of factory class. 64 */ 65 private CellSpanControllerFactory() { 66 } 67 68 69 /** 70 * Creates a new instance of {@link CellEditMode}. The returned 71 * instance will use {@link CellPopupMode} for custom context menus and 72 * {@link CellColorMode} for custom marquee selection behavior. 73 * @return an instance of {@link CellEditMode}. 74 */ 75 static EditMode newCellEditMode() { 76 final CellEditMode editMode = new CellEditMode(); 77 editMode.setPopupMode(new CellPopupMode()); 78 editMode.setSelectionBoxMode(new CellColorMode()); 79 return editMode; 80 } 81 82 /** 83 * Determines whether or not the <code>CTRL</code> key was pressed when 84 * the given mouse event was fired. 85 * @param e the mouse event to check. 86 * @return <code>true</code> if the <code>CTRL</code> key was pressed when 87 * the given mouse event was fired; <code>false</code> otherwise. 88 */ 89 static boolean isCtrlDown( final MouseEvent e ) { 90 final int mask = MouseEvent.CTRL_DOWN_MASK; 91 return e != null && (e.getModifiersEx() & mask) == mask; 92 } 93 94 95 /** 96 * Prevents edge creation while <code>CTRL</code> is pressed by triggering 97 * marquee selection instead and prevents creation of "free" nodes, that is 98 * nodes without a parent group/table node. 99 */ 100 private static final class CellEditMode extends EditMode { 101 /** 102 * Prevents edge creation while <code>CTRL</code> is pressed. 103 * With this customization, the selection box subordinate mode is triggered 104 * when dragging the mouse over a node while pressing <code>CTRL</code>. 105 * @param lastPress the last press event 106 * @param lastDrag the last drag event 107 * @return <code>false</code> if <code>CTRL</code> is pressed or 108 * the super implementation result if <code>CTRL</code> is not pressed. 109 * @see CellColorMode 110 */ 111 protected boolean isCreateEdgeGesture( 112 final MouseEvent lastPress, final MouseEvent lastDrag 113 ) { 114 if (isCtrlDown(lastPress) || isCtrlDown(lastDrag)) { 115 return false; 116 } else { 117 return super.isCreateEdgeGesture(lastPress, lastDrag); 118 } 119 } 120 121 /** 122 * Prevents node creation if the specified parent node is <code>null</code>. 123 * @param graph the graph which resided in the canvas 124 * @param x the x coordinate where the mouse was clicked 125 * @param y the y coordinate where the mouse was clicked 126 * @param parent the parent group node for the newly created node. 127 * If <code>null</code>, the new node will be a top level node. 128 * @return <code>null</code> if the specified parent node is 129 * <code>null</code> or the super implementation result if the specified 130 * parent node is not <code>null</code>. 131 */ 132 protected Node createNode( 133 final Graph2D graph, final double x, final double y, final Node parent 134 ) { 135 if (parent == null) { 136 return null; 137 } else { 138 return super.createNode(graph, x, y, parent); 139 } 140 } 141 } 142 143 /** 144 * Provides actions for adding and removing columns and rows in a table 145 * nodes. 146 */ 147 private static class CellPopupMode extends PopupMode { 148 /** 149 * Initializes a new <code>CellPopupMode</code> instance. 150 * This mode does not select elements when opening a context menu. 151 */ 152 CellPopupMode() { 153 setSelectSubject(false); 154 } 155 156 /** 157 * Returns the context menu to be opened by this mode. 158 * Overwritten to take the world (graph) coordinates of the triggering 159 * mouse event into account when populating the context menu for table 160 * nodes. 161 * @see #getNodePopup(y.base.Node, double, double) 162 */ 163 protected JPopupMenu getPopup( 164 final HitInfo hitInfo, 165 final double x, final double y, 166 final int popupType 167 ) { 168 if (POPUP_TYPE_NODE == popupType) { 169 return getNodePopup(hitInfo.getHitNode(), x, y); 170 } else { 171 return super.getPopup(hitInfo, x, y, popupType); 172 } 173 } 174 175 /** 176 * Returns the context menu for nodes. 177 * For table nodes, the context menu will provide actions for adding and 178 * removing columns and rows. 179 * For other (normal) nodes, the context menu will be empty (and will not 180 * be displayed). 181 */ 182 JPopupMenu getNodePopup( 183 final Node node, final double x, final double y 184 ) { 185 final JPopupMenu jpm = super.getNodePopup(node); 186 187 final Graph2D graph = getGraph2D(); 188 final NodeRealizer nr = graph.getRealizer(node); 189 if (nr instanceof TableGroupNodeRealizer) { 190 final TableGroupNodeRealizer tgnr = (TableGroupNodeRealizer) nr; 191 final Table table = tgnr.getTable(); 192 final Column col = table.columnAt(x, y); 193 if (col != null) { 194 final Row row = table.rowAt(x, y); 195 if (row != null) { 196 jpm.add(CellSpanActionFactory.newAddBefore(view, table, col)); 197 jpm.add(CellSpanActionFactory.newAddAfter(view, table, col)); 198 jpm.add(CellSpanActionFactory.newRemoveColumn(graph, table, col)); 199 jpm.addSeparator(); 200 jpm.add(CellSpanActionFactory.newAddBefore(view, table, row)); 201 jpm.add(CellSpanActionFactory.newAddAfter(view, table, row)); 202 jpm.add(CellSpanActionFactory.newRemoveRow(graph, table, row)); 203 } 204 } 205 } 206 207 return jpm; 208 } 209 } 210 211 /** 212 * Colors the background of table cells when pressing <code>CTRL</code> while 213 * using marquee selection. 214 * Removes the background color table cells when pressing <code>CTRL</code> 215 * and <code>ALT</code> while using marquee selection. 216 */ 217 private static final class CellColorMode extends SelectionBoxMode { 218 /** 219 * Handles marquee selection. 220 * Overwritten to check whether or not <code>CTRL</code> and 221 * <code>ALT</code> were pressed when ending the marquee selection 222 * to trigger background coloring instead of element selection. 223 * @param sb The position and size of the selection box. 224 * @param shiftMode <code>true</code> if shift was pressed when 225 */ 226 protected void selectionBoxAction( 227 final Rectangle2D.Double sb, final boolean shiftMode 228 ) { 229 final MouseEvent e = lastReleaseEvent; 230 if (isCtrlDown(e)) { 231 final int mask = MouseEvent.ALT_DOWN_MASK; 232 final boolean clear = (e.getModifiersEx() & mask) == mask; 233 234 final Graph2D graph = getGraph2D(); 235 final HierarchyManager hm = graph.getHierarchyManager(); 236 for (NodeCursor nc = hm.getChildren(null); nc.ok(); nc.next()) { 237 final Node node = nc.node(); 238 if (hm.isGroupNode(node)) { 239 final NodeRealizer nr = graph.getRealizer(node); 240 if (nr instanceof TableGroupNodeRealizer) { 241 setColor((TableGroupNodeRealizer) nr, sb, clear); 242 break; 243 } 244 } 245 } 246 graph.updateViews(); 247 } else { 248 super.selectionBoxAction(sb, shiftMode); 249 } 250 } 251 252 /** 253 * Sets a new background color for the table cells that intersect the 254 * specified selection box rectangle. 255 * @param tgnr the realizer holding the table structure. 256 * @param sb the selection box rectangle. 257 * @param clear if <code>true</code>, the background color of the cells 258 * in the selection box is cleared; otherwise an new background color is 259 * set. 260 */ 261 private void setColor( 262 final TableGroupNodeRealizer tgnr, final Rectangle2D sb, final boolean clear 263 ) { 264 final Table table = tgnr.getTable(); 265 266 // get a background color that was not yet used 267 final Color color = CellColorManager.getInstance(table).nextUnused(); 268 if (!clear && color == null) { 269 return; 270 } 271 272 273 // determine all cells that intersect the given selection box rectangle 274 // this code relies on the fact that CellSpanLayoutDemo does not provide 275 // a way to created columns in columns or rows in rows 276 final Rectangle2D.Double tmp = new Rectangle2D.Double(); 277 278 double x = tgnr.getX(); 279 final YInsets insets = table.getInsets(); 280 x += insets.left; 281 282 final ArrayList cells = new ArrayList(); 283 for (int i = 0, n = table.columnCount(); i < n; ++i) { 284 final Column col = table.getColumn(i); 285 final double w = col.getWidth(); 286 287 double y = tgnr.getY(); 288 y += insets.top; 289 for (int j = 0, m = table.rowCount(); j < m; ++j) { 290 final Row row = table.getRow(j); 291 final double h = row.getHeight(); 292 293 tmp.setFrame(x, y, w, h); 294 if (sb.intersects(tmp)) { 295 cells.add(new Cell(col, row)); 296 } 297 298 y += h; 299 } 300 x += w; 301 } 302 303 // now set or erase the background color for the cells 304 if (!cells.isEmpty()) { 305 getGraph2D().backupRealizers((new NodeList(tgnr.getNode())).nodes()); 306 if (clear) { 307 setColor(table, cells, null); 308 } else { 309 setColor(table, cells, color); 310 } 311 } 312 } 313 314 /** 315 * Sets the given background color for the given table cells. 316 * This method ensures that the resulting coloring defines valid 317 * rectangular cell spans. 318 * <p> 319 * E.g. suppose cells <code>(c1, r2)</code>, <code>(c2, r2)</code>, and 320 * <code>(c3, r2)</code> are colored red and the cells <code>(c2, r1)</code> 321 * and <code>(c2, r2)</code> should be newly colored blue, this method will 322 * erase the background color from cell <code>(c1, r2)</code> to prevent two 323 * disjoint red cell spans. 324 * </p><p> 325 * Alternatively, suppose cells <code>(c1, r1)</code>, 326 * <code>(c2, r1)</code>, <code>(c1, r2)</code>, and <code>(c2, r2)</code> 327 * are colored red and cell <code>(c2, r2)</code> should be newly colored 328 * blue, this method will erase the background color from cell 329 * <code>(c1, r2)</code> to prevent a non-rectangular red cell span. 330 * </p> 331 * @param table the table holding the cells whose background color is set. 332 * @param cells the cells whose background color is set. 333 * @param newColor the new background color. 334 */ 335 private void setColor( 336 final Table table, final Collection cells, final Color newColor 337 ) { 338 final Span newSpan = Span.span(cells); 339 340 final CellColorManager manager = CellColorManager.getInstance(table); 341 for (int i = newSpan.minCol, n = newSpan.maxCol + 1; i < n; ++i) { 342 for (int j = newSpan.minRow, m = newSpan.maxRow + 1; j < m; ++j) { 343 final Column col = table.getColumn(i); 344 final Row row = table.getRow(j); 345 final Color oldColor = manager.getCellColor(col, row); 346 // if the oldColor is null, cell (i,j) does not belong to another 347 // cell span and may be colored with no adverse effects 348 // otherwise, cell (i,j) belongs to a cell span that may need to 349 // be adjusted to remain valid 350 if (oldColor != null) { 351 final Span oldSpan = Span.find(table, col, row, oldColor); 352 if (newSpan.contains(oldSpan)) { 353 // replace the color of the entire span 354 manager.setCellColor(oldSpan, null); 355 } else { 356 // determine the cells whose background has to be erased 357 // for the old cell span to remain a valid 358 final Span cut = cut(newSpan, oldSpan); 359 manager.setCellColor(cut, null); 360 } 361 } 362 manager.setCellColor(col, row, newColor); 363 } 364 } 365 } 366 367 /** 368 * Determines the cells in <code>oldSpan</code> whose background 369 * has to be erased when coloring the cells in <code>newSpan</code> 370 * with a different background color. 371 */ 372 private static Span cut( final Span newSpan, final Span oldSpan ) { 373 if (oldSpan.contains(newSpan)) { 374 if (newSpan.contains(oldSpan)) { 375 return oldSpan; 376 } else { 377 // top cut 378 int minSize = size(newSpan, oldSpan, CUT_T); 379 Span min = newCut(newSpan, oldSpan, CUT_T); 380 // bottom cut 381 final int bSize = size(newSpan, oldSpan, CUT_B); 382 if (minSize > bSize) { 383 minSize = bSize; 384 min = newCut(newSpan, oldSpan, CUT_B); 385 } 386 // left cut 387 final int lSize = size(newSpan, oldSpan, CUT_L); 388 if (minSize > lSize) { 389 minSize = lSize; 390 min = newCut(newSpan, oldSpan, CUT_L); 391 } 392 // right cut 393 final int rSize = size(newSpan, oldSpan, CUT_R); 394 if (minSize > rSize) { 395 minSize = rSize; 396 min = newCut(newSpan, oldSpan, CUT_R); 397 } 398 399 return min; 400 } 401 } 402 403 404 // whether or not the new span starts vertically inside the old span 405 final boolean inT = oldSpan.minRow < newSpan.minRow; 406 // whether or not the new span ends vertically inside the old span 407 final boolean inB = newSpan.maxRow < oldSpan.maxRow; 408 // whether or not the new span starts horizontally inside the old span 409 final boolean inL = oldSpan.minCol < newSpan.minCol; 410 // whether or not the new span ends horizontally inside the old span 411 final boolean inR = newSpan.maxCol < oldSpan.maxCol; 412 // whether or not the new span is completely inside the old span's 413 // horizontal range 414 final boolean inH = inL && inR; 415 // whether or not the new span is completely inside the old span's 416 // vertical range 417 final boolean inV = inT && inB; 418 419 // whether or not the new span starts vertically outside the old span 420 final boolean outT = newSpan.minRow <= oldSpan.minRow; 421 // whether or not the new span ends vertically outside the old span 422 final boolean outB = oldSpan.maxRow <= newSpan.maxRow; 423 // whether or not the new span starts horizontally outside the old span 424 final boolean outL = newSpan.minCol <= oldSpan.minCol; 425 // whether or not the new span ends horizontally outside the old span 426 final boolean outR = oldSpan.maxCol <= newSpan.maxCol; 427 // whether or not the new span completely covers the old span's 428 // horizontal range 429 final boolean outH = outL && outR; 430 // whether or not the new span completely covers the old span's 431 // vertical range 432 final boolean outV = outT && outB; 433 434 // whether or not the new span starts outside and ends inside the old 435 // span's vertical range 436 final boolean cutT = outT && inB; 437 // whether or not the new span starts inside and ends outside the old 438 // span's vertical range 439 final boolean cutB = inT && outB; 440 // whether or not the new span starts outside and ends inside the old 441 // span's horizontal range 442 final boolean cutL = outL && inR; 443 // whether or not the new span starts inside and ends outside the old 444 // span's horizontal range 445 final boolean cutR = inL && outR; 446 447 448 // case 1: 449 // +-----------+ 450 // | | 451 // ---+-----------+--- 452 // ################### 453 // ---+-----------+--- 454 // | | 455 // +-----------+ 456 if (outH && inV) { 457 return newCut(newSpan, oldSpan, CUT_T, CUT_B); 458 } 459 460 // case 2: 461 // +-----------+ ##########| 462 // | | ##########| 463 // ---+-----+ | ##########+----+ 464 // #########| ##########| | 465 // ---+-----+ | ##########| | 466 // | | ##########| | 467 // +-----------+ ##########| | 468 // ##########+----+ 469 // ##########| 470 // ##########| 471 if (cutL && (inV || outV)) { 472 return newCut(newSpan, oldSpan, CUT_L); 473 } 474 475 // case 3: 476 // +-----------+ |########## 477 // | | |########## 478 // | +-----+--- +----+########## 479 // | |######### | |########## 480 // | +-----+--- | |########## 481 // | | | |########## 482 // +-----------+ | |########## 483 // +----+########## 484 // |########## 485 // |########## 486 if (cutR && (inV || outV)) { 487 return newCut(newSpan, oldSpan, CUT_R); 488 } 489 490 // case 4: 491 // |#| 492 // |#| 493 // +----+#+----+ 494 // | |#| | 495 // | |#| | 496 // | |#| | 497 // | |#| | 498 // +----+#+----+ 499 // |#| 500 // |#| 501 if (inH && outV) { 502 return newCut(newSpan, oldSpan, CUT_L, CUT_R); 503 } 504 505 // case 5: 506 // |#| ################### 507 // |#| ################### 508 // +----+#+----+ ################### 509 // | |#| | ################### 510 // | |#| | ################### 511 // | +-+ | ---+-----------+--- 512 // | | | | 513 // +-----------+ +-----------+ 514 // 515 // 516 if ((inH || outH) && cutT) { 517 return newCut(newSpan, oldSpan, CUT_T); 518 } 519 520 // case 6: 521 // 522 // 523 // +-----------+ +-----------+ 524 // | | | | 525 // | +-+ | ---+-----------+--- 526 // | |#| | ################### 527 // | |#| | ################### 528 // +----+#+----+ ################### 529 // |#| ################### 530 // |#| ################### 531 if ((inH || outH) && cutB) { 532 return newCut(newSpan, oldSpan, CUT_B); 533 } 534 535 536 // case 7: 537 // ##########| 538 // ##########| 539 // ##########+----+ 540 // ##########| | 541 // ##########| | 542 // ---+------+ | 543 // | | 544 // +-----------+ 545 // 546 // 547 if (cutL && cutT) { 548 return newCut(newSpan, oldSpan, CUT_T, CUT_L); 549 } 550 551 // case 8: 552 // |########## 553 // |########## 554 // +----+########## 555 // | |########## 556 // | |########## 557 // | +------+--- 558 // | | 559 // +-----------+ 560 // 561 // 562 if (cutR && cutT) { 563 return newCut(newSpan, oldSpan, CUT_T, CUT_R); 564 } 565 566 // case 9: 567 // 568 // 569 // +-----------+ 570 // | | 571 // | +------+--- 572 // | |########## 573 // | |########## 574 // +----+########## 575 // |########## 576 // |########## 577 if (cutR && cutB) { 578 return newCut(newSpan, oldSpan, CUT_B, CUT_R); 579 } 580 581 // case 10: 582 // 583 // 584 // +-----------+ 585 // | | 586 // ---+------+ | 587 // ##########| | 588 // ##########| | 589 // ##########+----+ 590 // ##########| 591 // ##########| 592 if (cutL && cutB) { 593 return newCut(newSpan, oldSpan, CUT_B, CUT_L); 594 } 595 596 597 // should never happen 598 return oldSpan; 599 } 600 601 602 /** Top cut. */ 603 private static final byte CUT_T = 1; 604 /** Bottom cut. */ 605 private static final byte CUT_B = 2; 606 /** Left cut. */ 607 private static final byte CUT_L = 3; 608 /** Right cut. */ 609 private static final byte CUT_R = 4; 610 611 /** 612 * Calculates the number of cells in a directional intersection of 613 * the two given cell spans. 614 * @param newSpan the new cell span to which the direction applies. 615 * @param oldSpan the old cell span. 616 * @param cut the direction of the intersection. Has to be one of 617 * <ul> 618 * <li>{@link #CUT_T},</li> 619 * <li>{@link #CUT_B},</li> 620 * <li>{@link #CUT_L}, or</li> 621 * <li>{@link #CUT_R}.</li> 622 * </ul> 623 * @return the number of cells in a directional intersection of 624 * the two given cell spans. 625 */ 626 private static int size( final Span newSpan, final Span oldSpan, final byte cut ) { 627 final int maxCol = CUT_L == cut ? newSpan.maxCol : oldSpan.maxCol; 628 final int minCol = CUT_R == cut ? newSpan.minCol : oldSpan.minCol; 629 final int maxRow = CUT_T == cut ? newSpan.maxRow : oldSpan.maxRow; 630 final int minRow = CUT_B == cut ? newSpan.minRow : oldSpan.minRow; 631 return (maxCol - minCol + 1) * (maxRow - minRow + 1); 632 } 633 634 /** 635 * Calculates the cells in a directional intersection of the two given cell 636 * spans. 637 * @param newSpan the new cell span to which the direction applies. 638 * @param oldSpan the old cell span. 639 * @param cut the direction of the intersection. Has to be one of 640 * <ul> 641 * <li>{@link #CUT_T},</li> 642 * <li>{@link #CUT_B},</li> 643 * <li>{@link #CUT_L}, or</li> 644 * <li>{@link #CUT_R}.</li> 645 * </ul> 646 * @return the cells in a directional intersection of the two given cell 647 * spans. 648 */ 649 private static Span newCut( final Span newSpan, final Span oldSpan, final byte cut ) { 650 final int maxCol = CUT_L == cut ? newSpan.maxCol : oldSpan.maxCol; 651 final int minCol = CUT_R == cut ? newSpan.minCol : oldSpan.minCol; 652 final int maxRow = CUT_T == cut ? newSpan.maxRow : oldSpan.maxRow; 653 final int minRow = CUT_B == cut ? newSpan.minRow : oldSpan.minRow; 654 return Span.manual(minCol, maxCol, minRow, maxRow); 655 } 656 657 /** 658 * Calculates the smaller of the two desired directional intersections 659 * of the two given cell spans. 660 * @param newSpan the new cell span to which the direction applies. 661 * @param oldSpan the old cell span. 662 * @param cut1 the first possible cut direction. Has to be one of 663 * {@link #CUT_T}, {@link #CUT_B}, {@link #CUT_L}, or {@link #CUT_R}. 664 * @param cut2 the second possible cut direction. Has to be one of 665 * {@link #CUT_T}, {@link #CUT_B}, {@link #CUT_L}, or {@link #CUT_R}. 666 * @return the smaller of the two desired directional intersections 667 * of the two given cell spans. 668 */ 669 private static Span newCut( 670 final Span newSpan, final Span oldSpan, 671 final byte cut1, final byte cut2 672 ) { 673 return size(newSpan, oldSpan, cut2) < size(newSpan, oldSpan, cut1) 674 ? newCut(newSpan, oldSpan, cut2) 675 : newCut(newSpan, oldSpan, cut1); 676 } 677 } 678 } 679