1 11 package org.eclipse.ltk.core.refactoring; 12 13 import java.util.ArrayList ; 14 import java.util.Arrays ; 15 import java.util.HashMap ; 16 import java.util.HashSet ; 17 import java.util.Iterator ; 18 import java.util.LinkedList ; 19 import java.util.List ; 20 import java.util.Map ; 21 import java.util.Set ; 22 23 import org.eclipse.text.edits.InsertEdit; 24 import org.eclipse.text.edits.MalformedTreeException; 25 import org.eclipse.text.edits.MultiTextEdit; 26 import org.eclipse.text.edits.ReplaceEdit; 27 import org.eclipse.text.edits.TextEdit; 28 import org.eclipse.text.edits.TextEditCopier; 29 import org.eclipse.text.edits.TextEditGroup; 30 import org.eclipse.text.edits.TextEditProcessor; 31 import org.eclipse.text.edits.UndoEdit; 32 33 import org.eclipse.core.runtime.Assert; 34 import org.eclipse.core.runtime.CoreException; 35 import org.eclipse.core.runtime.IPath; 36 import org.eclipse.core.runtime.IProgressMonitor; 37 import org.eclipse.core.runtime.NullProgressMonitor; 38 import org.eclipse.core.runtime.OperationCanceledException; 39 import org.eclipse.core.runtime.SubProgressMonitor; 40 41 import org.eclipse.core.filebuffers.FileBuffers; 42 import org.eclipse.core.filebuffers.ITextFileBuffer; 43 import org.eclipse.core.filebuffers.ITextFileBufferManager; 44 import org.eclipse.core.filebuffers.LocationKind; 45 46 import org.eclipse.core.resources.IFile; 47 48 import org.eclipse.jface.text.BadLocationException; 49 import org.eclipse.jface.text.BadPositionCategoryException; 50 import org.eclipse.jface.text.Document; 51 import org.eclipse.jface.text.DocumentEvent; 52 import org.eclipse.jface.text.DocumentRewriteSession; 53 import org.eclipse.jface.text.DocumentRewriteSessionType; 54 import org.eclipse.jface.text.IDocument; 55 import org.eclipse.jface.text.IDocumentExtension4; 56 import org.eclipse.jface.text.IDocumentListener; 57 import org.eclipse.jface.text.IPositionUpdater; 58 import org.eclipse.jface.text.IRegion; 59 import org.eclipse.jface.text.Position; 60 import org.eclipse.jface.text.Region; 61 62 import org.eclipse.ltk.internal.core.refactoring.BufferValidationState; 63 import org.eclipse.ltk.internal.core.refactoring.Changes; 64 import org.eclipse.ltk.internal.core.refactoring.ContentStamps; 65 import org.eclipse.ltk.internal.core.refactoring.MultiStateUndoChange; 66 import org.eclipse.ltk.internal.core.refactoring.NonDeletingPositionUpdater; 67 import org.eclipse.ltk.internal.core.refactoring.RefactoringCorePlugin; 68 69 87 public class MultiStateTextFileChange extends TextEditBasedChange { 88 89 private static final class ComposableBufferChange { 90 91 private TextEdit fEdit; 92 93 private List fGroups; 94 95 private final TextEdit getEdit() { 96 return fEdit; 97 } 98 99 private final List getGroups() { 100 return fGroups; 101 } 102 103 private final void setEdit(final TextEdit edit) { 104 Assert.isNotNull(edit); 105 106 fEdit= edit; 107 } 108 109 private final void setGroups(final List groups) { 110 Assert.isNotNull(groups); 111 112 fGroups= groups; 113 } 114 } 115 116 private static final class ComposableBufferChangeGroup extends TextEditBasedChangeGroup { 117 118 private final Set fEdits= new HashSet (); 119 120 private ComposableBufferChangeGroup(final MultiStateTextFileChange change, final TextEditGroup group) { 121 super(change, group); 122 123 final TextEdit[] edits= group.getTextEdits(); 124 for (int index= 0; index < edits.length; index++) 125 cacheEdit(edits[index]); 126 } 127 128 private final void cacheEdit(final TextEdit edit) { 129 fEdits.add(edit); 130 131 final TextEdit[] edits= edit.getChildren(); 132 for (int index= 0; index < edits.length; index++) 133 cacheEdit(edits[index]); 134 } 135 136 private final boolean containsEdit(final TextEdit edit) { 137 return fEdits.contains(edit); 138 } 139 140 private final Set getCachedEdits() { 141 return fEdits; 142 } 143 } 144 145 private static final class ComposableEditPosition extends Position { 146 147 private String fText; 148 149 private final String getText() { 150 return fText; 151 } 152 153 private final void setText(final String text) { 154 Assert.isNotNull(text); 155 156 fText= text; 157 } 158 } 159 160 private static final class ComposableUndoEdit { 161 162 private ComposableBufferChangeGroup fGroup; 163 164 private TextEdit fOriginal; 165 166 private ReplaceEdit fUndo; 167 168 private final ComposableBufferChangeGroup getGroup() { 169 return fGroup; 170 } 171 172 private final TextEdit getOriginal() { 173 return fOriginal; 174 } 175 176 private final String getOriginalText() { 177 if (fOriginal instanceof ReplaceEdit) { 178 return ((ReplaceEdit) getOriginal()).getText(); 179 } else if (fOriginal instanceof InsertEdit) { 180 return ((InsertEdit) getOriginal()).getText(); 181 } 182 return ""; } 184 185 private final ReplaceEdit getUndo() { 186 return fUndo; 187 } 188 189 private final void setGroup(final ComposableBufferChangeGroup group) { 190 Assert.isNotNull(group); 191 192 fGroup= group; 193 } 194 195 private final void setOriginal(final TextEdit edit) { 196 fOriginal= edit; 197 } 198 199 private final void setUndo(final ReplaceEdit undo) { 200 Assert.isNotNull(undo); 201 202 fUndo= undo; 203 } 204 } 205 206 207 private static final String COMPOSABLE_POSITION_CATEGORY= "ComposableEditPositionCategory_" + System.currentTimeMillis(); 209 210 private static final String MARKER_POSITION_CATEGORY= "MarkerPositionCategory_" + System.currentTimeMillis(); 212 213 private ITextFileBuffer fBuffer; 214 215 216 private String fCachedString; 217 218 222 private final ArrayList fChanges= new ArrayList (4); 223 224 225 private ContentStamp fContentStamp; 226 227 228 private TextEditCopier fCopier; 229 230 231 private int fCount; 232 233 234 private boolean fDirty; 235 236 237 private IFile fFile; 238 239 240 private int fSaveMode= TextFileChange.KEEP_SAVE_STATE; 241 242 243 private BufferValidationState fValidationState; 244 245 256 public MultiStateTextFileChange(final String name, final IFile file) { 257 super(name); 258 259 Assert.isNotNull(file); 260 fFile= file; 261 262 setTextType("txt"); } 264 265 273 private IDocument acquireDocument(final IProgressMonitor monitor) throws CoreException { 274 if (fCount > 0) 275 return fBuffer.getDocument(); 276 277 final ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager(); 278 final IPath path= fFile.getFullPath(); 279 280 manager.connect(path, LocationKind.IFILE, monitor); 281 fCount++; 282 283 fBuffer= manager.getTextFileBuffer(path, LocationKind.IFILE); 284 285 final IDocument document= fBuffer.getDocument(); 286 fContentStamp= ContentStamps.get(fFile, document); 287 288 return document; 289 } 290 291 302 public final void addChange(final TextChange change) { 303 Assert.isNotNull(change); 304 305 final ComposableBufferChange result= new ComposableBufferChange(); 306 result.setEdit(change.getEdit()); 307 308 final TextEditBasedChangeGroup[] groups= change.getChangeGroups(); 309 final List list= new ArrayList (groups.length); 310 311 for (int index= 0; index < groups.length; index++) { 312 313 final TextEditBasedChangeGroup group= new ComposableBufferChangeGroup(this, groups[index].getTextEditGroup()); 314 list.add(group); 315 316 addChangeGroup(group); 317 } 318 result.setGroups(list); 319 fChanges.add(result); 320 } 321 322 private TextEditProcessor createTextEditProcessor(ComposableBufferChange change, IDocument document, int flags, boolean preview) { 324 List excludes= new ArrayList (0); 325 for (final Iterator iterator= change.getGroups().iterator(); iterator.hasNext();) { 326 TextEditBasedChangeGroup group= (TextEditBasedChangeGroup) iterator.next(); 327 if (!group.isEnabled()) 328 excludes.addAll(Arrays.asList(group.getTextEdits())); 329 } 330 331 if (preview) { 332 fCopier= new TextEditCopier(change.getEdit()); 333 TextEdit copiedEdit= fCopier.perform(); 334 boolean keep= getKeepPreviewEdits(); 335 if (keep) 336 flags= flags | TextEdit.UPDATE_REGIONS; 337 LocalTextEditProcessor result= new LocalTextEditProcessor(document, copiedEdit, flags); 338 result.setExcludes(mapEdits((TextEdit[]) excludes.toArray(new TextEdit[excludes.size()]), fCopier)); 339 if (!keep) 340 fCopier= null; 341 return result; 342 } else { 343 LocalTextEditProcessor result= new LocalTextEditProcessor(document, change.getEdit(), flags | TextEdit.UPDATE_REGIONS); 344 result.setExcludes((TextEdit[]) excludes.toArray(new TextEdit[excludes.size()])); 345 return result; 346 } 347 } 348 349 362 private ReplaceEdit createUndoEdit(final IDocument document, final int offset, final int length, final String text) { 363 String currentText= null; 364 try { 365 currentText= document.get(offset, length); 366 } catch (BadLocationException cannotHappen) { 367 } 369 370 if (fCachedString != null && fCachedString.equals(currentText)) 371 currentText= fCachedString; 372 else 373 fCachedString= currentText; 374 375 return new ReplaceEdit(offset, text != null ? text.length() : 0, currentText); 376 } 377 378 381 public final void dispose() { 382 fValidationState.dispose(); 383 } 384 385 388 public final String getCurrentContent(final IProgressMonitor monitor) throws CoreException { 389 return getCurrentDocument(monitor).get(); 390 } 391 392 395 public final String getCurrentContent(final IRegion region, final boolean expand, final int surround, final IProgressMonitor monitor) throws CoreException { 396 Assert.isNotNull(region); 397 Assert.isTrue(surround >= 0); 398 final IDocument document= getCurrentDocument(monitor); 399 Assert.isTrue(document.getLength() >= region.getOffset() + region.getLength()); 400 return getContent(document, region, expand, surround); 401 } 402 403 416 public final IDocument getCurrentDocument(IProgressMonitor monitor) throws CoreException { 417 if (monitor == null) 418 monitor= new NullProgressMonitor(); 419 IDocument result= null; 420 monitor.beginTask("", 2); try { 422 result= acquireDocument(new SubProgressMonitor(monitor, 1)); 423 } finally { 424 if (result != null) 425 releaseDocument(result, new SubProgressMonitor(monitor, 1)); 426 } 427 monitor.done(); 428 if (result == null) 429 result= new Document(); 430 return result; 431 } 432 433 436 public final Object getModifiedElement() { 437 return fFile; 438 } 439 440 443 public final String getPreviewContent(final TextEditBasedChangeGroup[] groups, final IRegion region, final boolean expand, final int surround, final IProgressMonitor monitor) throws CoreException { 444 445 final Set cachedGroups= new HashSet (Arrays.asList(groups)); 446 final IDocument document= new Document(getCurrentDocument(monitor).get()); 447 448 final Position range= new Position(region.getOffset(), region.getLength()); 450 try { 451 452 ComposableBufferChange change= null; 453 454 final TextEditBasedChangeGroup[] changedGroups= getChangeGroups(); 455 456 LinkedList compositeUndo= new LinkedList (); 457 for (int index= 0; index < fChanges.size(); index++) { 458 change= (ComposableBufferChange) fChanges.get(index); 459 460 TextEdit copy= null; 461 try { 462 fCopier= new TextEditCopier(change.getEdit()); 464 copy= fCopier.perform(); 465 466 final Map originalMap= new HashMap (); 468 for (final Iterator outer= change.getGroups().iterator(); outer.hasNext();) { 469 470 final ComposableBufferChangeGroup group= (ComposableBufferChangeGroup) outer.next(); 471 for (final Iterator inner= group.getCachedEdits().iterator(); inner.hasNext();) { 472 473 final TextEdit originalEdit= (TextEdit) inner.next(); 474 final TextEdit copiedEdit= fCopier.getCopy(originalEdit); 475 476 if (copiedEdit != null) 477 originalMap.put(copiedEdit, originalEdit); 478 else 479 RefactoringCorePlugin.logErrorMessage("Could not find a copy for the indexed text edit " + originalEdit.toString()); } 481 } 482 483 final ComposableBufferChangeGroup[] currentGroup= { null}; 484 final TextEdit[] currentEdit= { null}; 485 486 final TextEditProcessor processor= new TextEditProcessor(document, copy, TextEdit.NONE) { 489 490 protected final boolean considerEdit(final TextEdit edit) { 491 492 final TextEdit originalEdit= (TextEdit) originalMap.get(edit); 493 if (originalEdit != null) { 494 495 currentEdit[0]= originalEdit; 496 497 boolean found= false; 498 for (int offset= 0; offset < changedGroups.length && !found; offset++) { 499 500 final ComposableBufferChangeGroup group= (ComposableBufferChangeGroup) changedGroups[offset]; 501 if (group.containsEdit(originalEdit)) { 502 503 currentGroup[0]= group; 504 found= true; 505 } 506 } 507 if (!found) 508 currentGroup[0]= null; 509 510 } else if (!(edit instanceof MultiTextEdit)) { 511 RefactoringCorePlugin.logErrorMessage("Could not find the original of the copied text edit " + edit.toString()); } 513 return true; 514 } 515 }; 516 517 final LinkedList eventUndos= new LinkedList (); 518 519 final IDocumentListener listener= new IDocumentListener() { 522 523 public final void documentAboutToBeChanged(final DocumentEvent event) { 524 final ComposableUndoEdit edit= new ComposableUndoEdit(); 525 526 edit.setGroup(currentGroup[0]); 527 edit.setOriginal(currentEdit[0]); 528 edit.setUndo(createUndoEdit(document, event.getOffset(), event.getLength(), event.getText())); 529 530 eventUndos.addFirst(edit); 531 } 532 533 public final void documentChanged(final DocumentEvent event) { 534 } 536 }; 537 538 try { 539 document.addDocumentListener(listener); 541 processor.performEdits(); 542 } finally { 543 document.removeDocumentListener(listener); 544 } 545 546 compositeUndo.addFirst(eventUndos); 547 548 } finally { 549 fCopier= null; 550 } 551 } 552 553 final IPositionUpdater positionUpdater= new IPositionUpdater() { 554 555 public final void update(final DocumentEvent event) { 556 557 final int eventOffset= event.getOffset(); 558 final int eventLength= event.getLength(); 559 final int eventOldEndOffset= eventOffset + eventLength; 560 final String eventText= event.getText(); 561 final int eventNewLength= eventText == null ? 0 : eventText.length(); 562 final int eventNewEndOffset= eventOffset + eventNewLength; 563 final int deltaLength= eventNewLength - eventLength; 564 565 try { 566 567 final Position[] positions= event.getDocument().getPositions(COMPOSABLE_POSITION_CATEGORY); 568 for (int index= 0; index < positions.length; index++) { 569 570 final Position position= positions[index]; 571 if (position.isDeleted()) 572 continue; 573 574 final int offset= position.getOffset(); 575 final int length= position.getLength(); 576 final int end= offset + length; 577 578 if (offset > eventOldEndOffset) { 579 position.setOffset(offset + deltaLength); 581 } else if (end < eventOffset) { 582 } else if (offset == eventOffset) { 585 } else if (offset <= eventOffset && end >= eventOldEndOffset) { 587 position.setLength(length + deltaLength); 591 } else if (offset < eventOffset) { 592 position.setLength(eventNewEndOffset - offset); 596 } else if (end > eventOldEndOffset) { 597 position.setOffset(eventOffset); 603 int deleted= eventOldEndOffset - offset; 604 position.setLength(length - deleted + eventNewLength); 605 } else { 606 int newOffset= Math.min(offset, eventNewEndOffset); 611 int newEndOffset= Math.min(end, eventNewEndOffset); 612 position.setOffset(newOffset); 613 position.setLength(newEndOffset - newOffset); 614 } 615 } 616 } catch (BadPositionCategoryException exception) { 617 } 619 } 620 }; 621 622 try { 623 624 document.addPositionCategory(COMPOSABLE_POSITION_CATEGORY); 625 document.addPositionUpdater(positionUpdater); 626 627 final LinkedList undoQueue= new LinkedList (); 633 for (final Iterator outer= compositeUndo.iterator(); outer.hasNext();) { 634 for (final Iterator inner= ((List ) outer.next()).iterator(); inner.hasNext();) { 635 636 final ComposableUndoEdit edit= (ComposableUndoEdit) inner.next(); 637 final ReplaceEdit undo= edit.getUndo(); 638 639 final int offset= undo.getOffset(); 640 final int length= undo.getLength(); 641 final String text= undo.getText(); 642 643 ComposableEditPosition position= new ComposableEditPosition(); 644 if (cachedGroups.contains(edit.getGroup())) { 645 646 if (text == null || text.equals("")) { position.offset= offset; 648 if (length != 0) { 649 position.length= 0; 652 position.setText(edit.getOriginalText()); 653 } else 654 RefactoringCorePlugin.logErrorMessage("Dubious undo edit found: " + undo.toString()); 656 } else if (length == 0) { 657 position.offset= offset; 658 position.setText(""); position.length= text.length(); 662 } else { 663 position.offset= offset; 665 position.length= length; 666 position.setText(edit.getOriginalText()); 667 } 668 669 document.addPosition(COMPOSABLE_POSITION_CATEGORY, position); 670 } 671 672 position= new ComposableEditPosition(); 673 position.offset= undo.getOffset(); 674 position.length= undo.getLength(); 675 position.setText(undo.getText()); 676 677 undoQueue.add(position); 678 } 679 680 for (final Iterator iterator= undoQueue.iterator(); iterator.hasNext();) { 681 final ComposableEditPosition position= (ComposableEditPosition) iterator.next(); 682 683 document.replace(position.offset, position.length, position.getText()); 684 iterator.remove(); 685 } 686 } 687 688 final IPositionUpdater markerUpdater= new NonDeletingPositionUpdater(MARKER_POSITION_CATEGORY); 690 691 try { 692 693 final Position[] positions= document.getPositions(COMPOSABLE_POSITION_CATEGORY); 694 document.addPositionCategory(MARKER_POSITION_CATEGORY); 695 document.addPositionUpdater(markerUpdater); 696 document.addPosition(MARKER_POSITION_CATEGORY, range); 697 698 for (int index= 0; index < positions.length; index++) { 699 final ComposableEditPosition position= (ComposableEditPosition) positions[index]; 700 701 document.replace(position.offset, position.length, position.getText() != null ? position.getText() : ""); } 703 704 } catch (BadPositionCategoryException exception) { 705 RefactoringCorePlugin.log(exception); 706 } finally { 707 document.removePositionUpdater(markerUpdater); 708 try { 709 document.removePosition(MARKER_POSITION_CATEGORY, range); 710 document.removePositionCategory(MARKER_POSITION_CATEGORY); 711 } catch (BadPositionCategoryException exception) { 712 } 714 } 715 } catch (BadPositionCategoryException exception) { 716 RefactoringCorePlugin.log(exception); 717 } finally { 718 document.removePositionUpdater(positionUpdater); 719 try { 720 document.removePositionCategory(COMPOSABLE_POSITION_CATEGORY); 721 } catch (BadPositionCategoryException exception) { 722 RefactoringCorePlugin.log(exception); 723 } 724 } 725 726 return getContent(document, new Region(range.offset, range.length), expand, surround); 727 728 } catch (MalformedTreeException exception) { 729 RefactoringCorePlugin.log(exception); 730 } catch (BadLocationException exception) { 731 RefactoringCorePlugin.log(exception); 732 } 733 return getPreviewDocument(monitor).get(); 734 } 735 736 739 public final String getPreviewContent(final IProgressMonitor monitor) throws CoreException { 740 return getPreviewDocument(monitor).get(); 741 } 742 743 753 public final IDocument getPreviewDocument(IProgressMonitor monitor) throws CoreException { 754 if (monitor == null) 755 monitor= new NullProgressMonitor(); 756 757 IDocument result= null; 758 IDocument document= null; 759 DocumentRewriteSession session= null; 760 761 try { 762 763 document= acquireDocument(new SubProgressMonitor(monitor, 1)); 764 if (document != null) { 765 result= new Document(document.get()); 766 767 if (result instanceof IDocumentExtension4) 768 session= ((IDocumentExtension4) result).startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED); 769 770 performChanges(result, null, true); 771 } 772 773 } catch (BadLocationException exception) { 774 throw Changes.asCoreException(exception); 775 } finally { 776 if (document != null) { 777 try { 778 if (session != null && result != null) 779 ((IDocumentExtension4) result).stopRewriteSession(session); 780 } finally { 781 releaseDocument(document, new SubProgressMonitor(monitor, 1)); 782 } 783 } 784 monitor.done(); 785 } 786 if (result == null) 787 result= new Document(); 788 return result; 789 } 790 791 796 public final int getSaveMode() { 797 return fSaveMode; 798 } 799 800 803 public final void initializeValidationData(final IProgressMonitor monitor) { 804 monitor.beginTask("", 1); fValidationState= BufferValidationState.create(fFile); 806 monitor.worked(1); 807 } 808 809 812 public final RefactoringStatus isValid(final IProgressMonitor monitor) throws CoreException, OperationCanceledException { 813 monitor.beginTask("", 1); 815 final ITextFileBuffer buffer= FileBuffers.getTextFileBufferManager().getTextFileBuffer(fFile.getFullPath(), LocationKind.IFILE); 816 fDirty= buffer != null && buffer.isDirty(); 817 818 final RefactoringStatus status= fValidationState.isValid(needsSaving()); 819 if (needsSaving()) { 820 status.merge(Changes.validateModifiesFiles(new IFile[] { fFile})); 821 } else { 822 status.merge(Changes.checkInSync(new IFile[] { fFile})); 824 } 825 monitor.worked(1); 826 return status; 827 } 828 829 835 public final boolean needsSaving() { 836 return (fSaveMode & TextFileChange.FORCE_SAVE) != 0 || (!fDirty && (fSaveMode & TextFileChange.KEEP_SAVE_STATE) != 0); 837 } 838 839 842 public final Change perform(final IProgressMonitor monitor) throws CoreException { 843 monitor.beginTask("", 3); 845 IDocument document= null; 846 DocumentRewriteSession session= null; 847 848 try { 849 document= acquireDocument(new SubProgressMonitor(monitor, 1)); 850 if (document instanceof IDocumentExtension4) 851 session= ((IDocumentExtension4) document).startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED); 852 853 final LinkedList undoList= new LinkedList (); 854 performChanges(document, undoList, false); 855 856 if (needsSaving()) 857 fBuffer.commit(new SubProgressMonitor(monitor, 1), false); 858 859 return new MultiStateUndoChange(getName(), fFile, (UndoEdit[]) undoList.toArray(new UndoEdit[undoList.size()]), fContentStamp, fSaveMode); 860 861 } catch (BadLocationException exception) { 862 throw Changes.asCoreException(exception); 863 } finally { 864 if (document != null) { 865 try { 866 if (session != null) 867 ((IDocumentExtension4) document).stopRewriteSession(session); 868 } finally { 869 releaseDocument(document, new SubProgressMonitor(monitor, 1)); 870 } 871 } 872 monitor.done(); 873 } 874 } 875 876 889 private void performChanges(final IDocument document, final LinkedList undoList, final boolean preview) throws BadLocationException { 890 891 for (final Iterator iterator= fChanges.iterator(); iterator.hasNext();) { 892 final ComposableBufferChange change= (ComposableBufferChange) iterator.next(); 893 894 final UndoEdit edit= createTextEditProcessor(change, document, undoList != null ? TextEdit.CREATE_UNDO : TextEdit.NONE, preview).performEdits(); 895 if (undoList != null) 896 undoList.addFirst(edit); 897 } 898 } 899 900 909 private void releaseDocument(final IDocument document, final IProgressMonitor monitor) throws CoreException { 910 Assert.isTrue(fCount > 0); 911 912 if (fCount == 1) 913 FileBuffers.getTextFileBufferManager().disconnect(fFile.getFullPath(), LocationKind.IFILE, monitor); 914 915 fCount--; 916 } 917 918 921 public final void setKeepPreviewEdits(final boolean keep) { 922 super.setKeepPreviewEdits(keep); 923 924 if (!keep) 925 fCopier= null; 926 } 927 928 934 public final void setSaveMode(final int mode) { 935 fSaveMode= mode; 936 } 937 } 938 | Popular Tags |