001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * If you wish your version of this file to be governed by only the CDDL
025: * or only the GPL Version 2, indicate your decision by adding
026: * "[Contributor] elects to include this software in this distribution
027: * under the [CDDL or GPL Version 2] license." If you do not indicate a
028: * single choice of license, a recipient has the option to distribute
029: * your version of this file under either the CDDL, the GPL Version 2 or
030: * to extend the choice of license to its licensees as provided above.
031: * However, if you add GPL Version 2 code and therefore, elected the GPL
032: * Version 2 license, then the option applies only if the new code is
033: * made subject to such option by the copyright holder.
034: *
035: * Contributor(s):
036: *
037: * Portions Copyrighted 2007 Sun Microsystems, Inc.
038: */
039:
040: package org.netbeans.modules.ruby.hints.spi;
041:
042: import java.util.ArrayList;
043: import java.util.Collections;
044: import java.util.List;
045: import javax.swing.text.BadLocationException;
046: import javax.swing.text.Position;
047: import javax.swing.text.StyledDocument;
048: import org.netbeans.modules.gsf.api.OffsetRange;
049: import org.netbeans.editor.BaseDocument;
050: import org.netbeans.modules.ruby.Formatter;
051: import org.openide.text.NbDocument;
052: import org.openide.util.Exceptions;
053:
054: /**
055: * A list of edits to be made to a document. This should probably be combined with the many
056: * other similar abstractions in other classes; ModificationResult, Diff, etc.
057: *
058: * @todo Take out the offsetOrdinal number, and manage that on the edit list side
059: * (order of entry for duplicates should insert a new ordinal)
060: * @todo Make formatting more explicit; allow to add a "format" region edit. These must
061: * be sorted such that they don't overlap after edits and are all applied last.
062: *
063: * @author Tor Norbye
064: */
065: public class EditList {
066: private BaseDocument doc;
067: private List<Edit> edits;
068: private boolean format;
069:
070: public EditList(BaseDocument doc) {
071: this .doc = doc;
072: edits = new ArrayList<Edit>();
073: }
074:
075: @Override
076: public String toString() {
077: return "EditList(" + edits + ")";
078: }
079:
080: public EditList replace(int offset, int removeLen,
081: String insertText, boolean format, int offsetOrdinal) {
082: edits.add(new Edit(offset, removeLen, insertText, format,
083: offsetOrdinal));
084:
085: return this ;
086: }
087:
088: public void applyToDocument(BaseDocument otherDoc/*, boolean narrow*/) {
089: EditList newList = new EditList(otherDoc);
090: newList.format = format;
091: /*
092: if (narrow) {
093: OffsetRange range = getRange();
094: int start = range.getStart();
095: int lineno = NbDocument.findLineNumber((StyledDocument) otherDoc,start);
096: lineno = Math.max(0, lineno-3);
097: start = NbDocument.findLineOffset((StyledDocument) otherDoc,lineno);
098:
099: List newEdits = new ArrayList<Edit>(edits.size());
100: newList.edits = newEdits;
101: for (Edit edit : edits) {
102: newEdits.add(new Edit(edit.offset-start, edit.removeLen, edit.insertText, edit.format, edit.offsetOrdinal));
103: }
104: } else {
105: */
106: newList.edits = edits;
107: //}
108: newList.apply();
109: }
110:
111: public void apply() {
112: apply(-1);
113: }
114:
115: public void format() {
116: this .format = true;
117: }
118:
119: /** Apply the given list of edits in the current document. If positionOffset is a position
120: * within one of the regions, return a document Position that corresponds to it.
121: */
122: public Position apply(int positionOffset) {
123: if (edits.size() == 0) {
124: if (positionOffset >= 0) {
125: try {
126: return doc.createPosition(0);
127: } catch (BadLocationException ble) {
128: Exceptions.printStackTrace(ble);
129: }
130: }
131: return null;
132: }
133:
134: Position position = null;
135:
136: Collections.sort(edits);
137: Collections.reverse(edits);
138: Formatter formatter = new Formatter();
139:
140: try {
141: doc.atomicLock();
142: int lastOffset = edits.get(0).offset;
143: Position lastPos = doc.createPosition(lastOffset,
144: Position.Bias.Forward);
145:
146: // Apply edits in reverse order (to keep offsets accurate)
147: for (Edit edit : edits) {
148: if (edit.removeLen > 0) {
149: doc.remove(edit.offset, edit.removeLen);
150: }
151: if (edit.getInsertText() != null) {
152: doc
153: .insertString(edit.offset, edit.insertText,
154: null);
155: int end = edit.offset + edit.insertText.length();
156: if (edit.getOffset() <= positionOffset
157: && end >= positionOffset) {
158: position = doc.createPosition(positionOffset); // Position of the comment
159: }
160: if (edit.format) {
161: formatter.reindent(doc, edit.offset, end);
162: }
163: }
164: }
165:
166: if (format) {
167: int firstOffset = edits.get(edits.size() - 1).offset;
168: lastOffset = lastPos.getOffset();
169: formatter.reindent(doc, firstOffset, lastOffset);
170: }
171: } catch (BadLocationException ble) {
172: Exceptions.printStackTrace(ble);
173: } finally {
174: doc.atomicUnlock();
175: }
176:
177: return position;
178: }
179:
180: public OffsetRange getRange() {
181: int minOffset = edits.get(0).offset;
182: int maxOffset = minOffset;
183: for (Edit edit : edits) {
184: if (edit.offset < minOffset) {
185: minOffset = edit.offset;
186: }
187: if (edit.offset > maxOffset) {
188: maxOffset = edit.offset;
189: }
190: }
191:
192: return new OffsetRange(minOffset, maxOffset);
193: }
194:
195: public int firstLine(BaseDocument doc) {
196: OffsetRange range = getRange();
197:
198: return NbDocument.findLineNumber((StyledDocument) doc, range
199: .getStart());
200: }
201: }
|