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.javascript.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.javascript.editing.JsFormatter;
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: JsFormatter formatter = new JsFormatter();
139:
140: try {
141: doc.atomicLock();
142: int lastOffset = edits.get(0).offset;
143: // I can just compute this by accumulating
144: Position lastPos = doc.createPosition(lastOffset,
145: Position.Bias.Forward);
146: int size = 0;
147:
148: // Apply edits in reverse order (to keep offsets accurate)
149: for (Edit edit : edits) {
150: if (edit.removeLen > 0) {
151: doc.remove(edit.offset, edit.removeLen);
152: size -= edit.removeLen;
153: }
154: if (edit.insertText != null) {
155: doc
156: .insertString(edit.offset, edit.insertText,
157: null);
158: size += edit.insertText.length();
159: int end = edit.offset + edit.insertText.length();
160: if (edit.getOffset() <= positionOffset
161: && end >= positionOffset) {
162: position = doc.createPosition(positionOffset); // Position of the comment
163: }
164: if (edit.format) {
165: formatter.reindent(doc, edit.offset, end);
166: }
167: }
168: }
169:
170: if (format) {
171: int firstOffset = edits.get(edits.size() - 1).offset;
172: lastOffset = lastPos.getOffset();
173: formatter.reindent(doc, firstOffset, lastOffset);
174: }
175: } catch (BadLocationException ble) {
176: Exceptions.printStackTrace(ble);
177: } finally {
178: doc.atomicUnlock();
179: }
180:
181: return position;
182: }
183:
184: public OffsetRange getRange() {
185: int minOffset = edits.get(0).offset;
186: int maxOffset = minOffset;
187: for (Edit edit : edits) {
188: if (edit.offset < minOffset) {
189: minOffset = edit.offset;
190: }
191: if (edit.offset > maxOffset) {
192: maxOffset = edit.offset;
193: }
194: }
195:
196: return new OffsetRange(minOffset, maxOffset);
197: }
198:
199: public int firstLine(BaseDocument doc) {
200: OffsetRange range = getRange();
201:
202: return NbDocument.findLineNumber((StyledDocument) doc, range
203: .getStart());
204: }
205: }
|