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: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.web.jsf.refactoring;
043:
044: import java.io.*;
045: import java.nio.charset.Charset;
046: import java.util.*;
047: import javax.swing.text.BadLocationException;
048: import javax.swing.text.Document;
049: import org.netbeans.api.queries.FileEncodingQuery;
050: import org.netbeans.editor.BaseDocument;
051: import org.openide.cookies.EditorCookie;
052: import org.openide.filesystems.FileObject;
053: import org.openide.filesystems.FileUtil;
054: import org.openide.loaders.DataObject;
055: import org.openide.text.PositionRef;
056:
057: /**
058: * Calss for holding all modifications
059: *
060: * @author Petr Pisl, Dusan Balek
061: */
062: public final class Modifications {
063:
064: Map<FileObject, List<Difference>> diffs = new HashMap<FileObject, List<Difference>>();
065:
066: /** Creates a new instance of ModificationResult */
067: public Modifications() {
068:
069: }
070:
071: public void addDifference(FileObject fileObject,
072: Difference difference) {
073: List<Difference> differences = diffs.get(fileObject);
074:
075: if (differences == null) {
076: differences = new ArrayList<Difference>();
077: differences.add(difference);
078: diffs.put(fileObject, differences);
079: } else {
080: differences.add(difference);
081: }
082: }
083:
084: /**
085: * Once all of the changes have been collected, this method can be used
086: * to commit the changes to the source files
087: */
088: public void commit() throws IOException {
089: for (Map.Entry<FileObject, List<Difference>> me : diffs
090: .entrySet()) {
091: commit(me.getKey(), me.getValue(), null);
092: }
093: }
094:
095: protected void commit(final FileObject fileObject,
096: final List<Difference> differences, Writer outWriter)
097: throws IOException {
098: DataObject dataObj = DataObject.find(fileObject);
099: EditorCookie editorCookie = dataObj != null ? (EditorCookie) dataObj
100: .getCookie(EditorCookie.class)
101: : null;
102: // if editor cookie was found and user does not provided his own
103: // writer where he wants to see changes, commit the changes to
104: // found document.
105: if (editorCookie != null && outWriter == null) {
106: Document doc = editorCookie.getDocument();
107: if (doc != null) {
108: if (doc instanceof BaseDocument)
109: ((BaseDocument) doc).atomicLock();
110: try {
111: for (Difference diff : differences) {
112: if (diff.isExcluded())
113: continue;
114: try {
115: switch (diff.getKind()) {
116: case INSERT:
117: doc.insertString(
118: diff.getStartPosition()
119: .getOffset(), diff
120: .getNewText(), null);
121: break;
122: case REMOVE:
123: doc.remove(diff.getStartPosition()
124: .getOffset(), diff
125: .getEndPosition().getOffset()
126: - diff.getStartPosition()
127: .getOffset());
128: break;
129: case CHANGE:
130: doc.remove(diff.getStartPosition()
131: .getOffset(), diff
132: .getEndPosition().getOffset()
133: - diff.getStartPosition()
134: .getOffset());
135: doc.insertString(
136: diff.getStartPosition()
137: .getOffset(), diff
138: .getNewText(), null);
139: break;
140: }
141: } catch (BadLocationException ex) {
142: IOException ioe = new IOException();
143: ioe.initCause(ex);
144: throw ioe;
145: }
146: }
147: } finally {
148: if (doc instanceof BaseDocument)
149: ((BaseDocument) doc).atomicUnlock();
150: }
151: return;
152: }
153: }
154: InputStream ins = null;
155: ByteArrayOutputStream baos = null;
156: Reader inputReader = null;
157: try {
158: Charset encoding = FileEncodingQuery
159: .getEncoding(fileObject);
160: ins = fileObject.getInputStream();
161: baos = new ByteArrayOutputStream();
162: FileUtil.copy(ins, baos);
163:
164: ins.close();
165: ins = null;
166: byte[] arr = baos.toByteArray();
167: int arrLength = convertToLF(arr);
168: baos.close();
169: baos = null;
170: inputReader = new InputStreamReader(
171: new ByteArrayInputStream(arr, 0, arrLength),
172: encoding);
173: // initialize standard commit output stream, if user
174: // does not provide his own writer
175: if (outWriter == null) {
176: outWriter = new OutputStreamWriter(fileObject
177: .getOutputStream(), encoding);
178: }
179: int offset = 0;
180:
181: // need to be sure, that the differences will be sorted acocrding
182: // their start offset
183: Difference aDifferences[] = differences
184: .toArray(new Difference[differences.size()]);
185: Arrays.sort(aDifferences, new Comparator<Difference>() {
186:
187: public int compare(Difference diff1, Difference diff2) {
188: int result = 0;
189: if (diff1.getStartPosition().getOffset() < diff2
190: .getStartPosition().getOffset()) {
191: result = -1;
192: } else if (diff1.getStartPosition().getOffset() > diff2
193: .getStartPosition().getOffset()) {
194: result = 1;
195: }
196: return result;
197: }
198:
199: });
200:
201: for (Difference diff : aDifferences) {
202: if (diff.isExcluded())
203: continue;
204: int pos = diff.getStartPosition().getOffset();
205: int toread = pos - offset;
206: char[] buff = new char[toread];
207: int length;
208: int rcounter = 0;
209: while ((length = inputReader.read(buff, 0, toread
210: - rcounter)) > 0
211: && rcounter < toread) {
212: outWriter.write(buff, 0, length);
213: rcounter += length;
214: offset += length;
215: }
216: switch (diff.getKind()) {
217: case INSERT:
218: outWriter.write(diff.getNewText());
219: break;
220: case REMOVE:
221: int len = diff.getEndPosition().getOffset()
222: - diff.getStartPosition().getOffset();
223: inputReader.skip(len);
224: offset += len;
225: break;
226: case CHANGE:
227: len = diff.getEndPosition().getOffset()
228: - diff.getStartPosition().getOffset();
229: inputReader.skip(len);
230: offset += len;
231: outWriter.write(diff.getNewText());
232: break;
233: }
234: }
235: char[] buff = new char[1024];
236: int count;
237: while ((count = inputReader.read(buff)) > 0)
238: outWriter.write(buff, 0, count);
239: } finally {
240: if (ins != null)
241: ins.close();
242: if (baos != null)
243: baos.close();
244: if (inputReader != null)
245: inputReader.close();
246: if (outWriter != null)
247: outWriter.close();
248: }
249: }
250:
251: private int convertToLF(byte[] buff) {
252: int index = 0;
253: for (int i = 0; i < buff.length; i++) {
254: if (buff[i] != '\r') {
255: buff[index++] = buff[i];
256: }
257: }
258: return index;
259: }
260:
261: /**
262: * Returned string represents preview of resulting source. No difference
263: * really is applied. Respects {@code isExcluded()} flag of difference.
264: *
265: * @param there can be more resulting source, user has to specify
266: * which wants to preview.
267: * @return if changes are applied source looks like return string
268: */
269: public String getResultingSource(FileObject fileObject)
270: throws IOException {
271: assert fileObject != null : "Provided fileObject is null";
272: StringWriter writer = new StringWriter();
273: commit(fileObject, diffs.get(fileObject), writer);
274:
275: return writer.toString();
276: }
277:
278: public static final class Difference {
279: Kind kind;
280: PositionRef startPos;
281: PositionRef endPos;
282: String oldText;
283: String newText;
284: String description;
285: private boolean excluded;
286:
287: Difference(Kind kind, PositionRef startPos, PositionRef endPos,
288: String oldText, String newText, String description) {
289: this .kind = kind;
290: this .startPos = startPos;
291: this .endPos = endPos;
292: this .oldText = oldText;
293: this .newText = newText;
294: this .excluded = false;
295: this .description = description;
296: }
297:
298: public Kind getKind() {
299: return kind;
300: }
301:
302: public String getDesription() {
303: return description;
304: }
305:
306: public PositionRef getStartPosition() {
307: return startPos;
308: }
309:
310: public PositionRef getEndPosition() {
311: return endPos;
312: }
313:
314: public String getOldText() {
315: return oldText;
316: }
317:
318: public String getNewText() {
319: return newText;
320: }
321:
322: public boolean isExcluded() {
323: return excluded;
324: }
325:
326: public void setExclude(boolean exclude) {
327: this .excluded = exclude;
328: }
329:
330: public String toString() {
331: return kind + "<" + startPos.getOffset() + ", "
332: + endPos.getOffset() + ">: " + oldText + " -> "
333: + newText; //NOI18N
334: }
335:
336: public static enum Kind {
337: INSERT, REMOVE, CHANGE
338: }
339: }
340: }
|