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-2006 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.cnd.refactoring.support;
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.netbeans.modules.cnd.api.model.CsmObject;
052: import org.netbeans.modules.cnd.api.model.CsmProject;
053: import org.netbeans.modules.cnd.api.model.CsmUID;
054: import org.openide.cookies.EditorCookie;
055: import org.openide.filesystems.FileObject;
056: import org.openide.filesystems.FileUtil;
057: import org.openide.loaders.DataObject;
058: import org.openide.text.PositionRef;
059:
060: /**
061: * based on org.netbeans.api.java.source.ModificationResult
062: *
063: * Class that collects changes for one project
064: *
065: * @author Dusan Balek
066: * @author Vladimir Voskresensky
067: */
068: public final class ModificationResult {
069:
070: private final CsmProject project;
071: Map<FileObject, List<Difference>> diffs = new HashMap<FileObject, List<Difference>>();
072:
073: /** Creates a new instance of ModificationResult */
074: public ModificationResult(CsmProject project) {
075: this .project = project;
076: }
077:
078: // API of the class --------------------------------------------------------
079:
080: public void addDifference(FileObject fo, Difference diff) {
081: List<Difference> foDiffs = diffs.get(fo);
082: if (foDiffs == null) {
083: foDiffs = new LinkedList<Difference>();
084: diffs.put(fo, foDiffs);
085: }
086: foDiffs.add(diff);
087: }
088:
089: public Set<? extends FileObject> getModifiedFileObjects() {
090: return diffs.keySet();
091: }
092:
093: public List<? extends Difference> getDifferences(FileObject fo) {
094: return diffs.get(fo);
095: }
096:
097: /**
098: * Once all of the changes have been collected, this method can be used
099: * to commit the changes to the source files
100: */
101: public void commit() throws IOException {
102: try {
103: for (Map.Entry<FileObject, List<Difference>> me : diffs
104: .entrySet()) {
105: commit(me.getKey(), me.getValue(), null);
106: }
107: } finally {
108: if (this .project != null) {
109: // need to reparse project
110: this .project.waitParse();
111: }
112: }
113: }
114:
115: private void commit(final FileObject fo,
116: final List<Difference> differences, Writer out)
117: throws IOException {
118: DataObject dObj = DataObject.find(fo);
119: EditorCookie ec = dObj != null ? dObj
120: .getCookie(org.openide.cookies.EditorCookie.class)
121: : null;
122: // if editor cookie was found and user does not provided his own
123: // writer where he wants to see changes, commit the changes to
124: // found document.
125: if (ec != null && out == null) {
126: Document doc = ec.getDocument();
127: if (doc != null) {
128: if (doc instanceof BaseDocument)
129: ((BaseDocument) doc).atomicLock();
130: try {
131: for (Difference diff : differences) {
132: if (diff.isExcluded())
133: continue;
134: try {
135: switch (diff.getKind()) {
136: case INSERT:
137: doc.insertString(
138: diff.getStartPosition()
139: .getOffset(), diff
140: .getNewText(), null);
141: break;
142: case REMOVE:
143: doc.remove(diff.getStartPosition()
144: .getOffset(), diff
145: .getEndPosition().getOffset()
146: - diff.getStartPosition()
147: .getOffset());
148: break;
149: case CHANGE:
150: doc.remove(diff.getStartPosition()
151: .getOffset(), diff
152: .getEndPosition().getOffset()
153: - diff.getStartPosition()
154: .getOffset());
155: doc.insertString(
156: diff.getStartPosition()
157: .getOffset(), diff
158: .getNewText(), null);
159: break;
160: }
161: } catch (BadLocationException ex) {
162: IOException ioe = new IOException();
163: ioe.initCause(ex);
164: throw ioe;
165: }
166: }
167: } finally {
168: if (doc instanceof BaseDocument)
169: ((BaseDocument) doc).atomicUnlock();
170: }
171: return;
172: }
173: }
174: InputStream ins = null;
175: ByteArrayOutputStream baos = null;
176: Reader in = null;
177: try {
178: Charset encoding = FileEncodingQuery.getEncoding(fo);
179: ins = fo.getInputStream();
180: baos = new ByteArrayOutputStream();
181: FileUtil.copy(ins, baos);
182:
183: ins.close();
184: ins = null;
185: byte[] arr = baos.toByteArray();
186: int arrLength = convertToLF(arr);
187: baos.close();
188: baos = null;
189: in = new InputStreamReader(new ByteArrayInputStream(arr, 0,
190: arrLength), encoding);
191: // initialize standard commit output stream, if user
192: // does not provide his own writer
193: if (out == null) {
194: out = new OutputStreamWriter(fo.getOutputStream(),
195: encoding);
196: }
197: int offset = 0;
198: for (Difference diff : differences) {
199: if (diff.isExcluded())
200: continue;
201: int pos = diff.getStartPosition().getOffset();
202: int toread = pos - offset;
203: char[] buff = new char[toread];
204: int n;
205: int rc = 0;
206: while ((n = in.read(buff, 0, toread - rc)) > 0
207: && rc < toread) {
208: out.write(buff, 0, n);
209: rc += n;
210: offset += n;
211: }
212: switch (diff.getKind()) {
213: case INSERT:
214: out.write(diff.getNewText());
215: break;
216: case REMOVE:
217: int len = diff.getEndPosition().getOffset()
218: - diff.getStartPosition().getOffset();
219: in.skip(len);
220: offset += len;
221: break;
222: case CHANGE:
223: len = diff.getEndPosition().getOffset()
224: - diff.getStartPosition().getOffset();
225: in.skip(len);
226: offset += len;
227: out.write(diff.getNewText());
228: break;
229: }
230: }
231: char[] buff = new char[1024];
232: int n;
233: while ((n = in.read(buff)) > 0)
234: out.write(buff, 0, n);
235: } finally {
236: if (ins != null)
237: ins.close();
238: if (baos != null)
239: baos.close();
240: if (in != null)
241: in.close();
242: if (out != null)
243: out.close();
244: }
245: }
246:
247: private int convertToLF(byte[] buff) {
248: int j = 0;
249: for (int i = 0; i < buff.length; i++) {
250: if (buff[i] != '\r') {
251: buff[j++] = buff[i];
252: }
253: }
254: return j;
255: }
256:
257: /**
258: * Returned string represents preview of resulting source. No difference
259: * really is applied. Respects {@code isExcluded()} flag of difference.
260: *
261: * @param there can be more resulting source, user has to specify
262: * which wants to preview.
263: * @return if changes are applied source looks like return string
264: */
265: public String getResultingSource(FileObject fileObject)
266: throws IOException {
267: assert fileObject != null : "Provided fileObject is null";
268: StringWriter writer = new StringWriter();
269: commit(fileObject, diffs.get(fileObject), writer);
270:
271: return writer.toString();
272: }
273:
274: public Set<File> getNewFiles() {
275: Set<File> newFiles = new HashSet<File>();
276: for (List<Difference> ds : diffs.values()) {
277: for (Difference d : ds) {
278: if (d.getKind() == Difference.Kind.CREATE) {
279: //newFiles.add(new File(((CreateChange) d).getFileObject().toUri()));
280: }
281: }
282: }
283: return newFiles;
284: }
285:
286: public static final class Difference {
287: Kind kind;
288: PositionRef startPos;
289: PositionRef endPos;
290: String oldText;
291: String newText;
292: String description;
293: private boolean excluded;
294: private final CsmUID<CsmObject> this Object;
295:
296: public Difference(Kind kind, CsmObject ref,
297: PositionRef startPos, PositionRef endPos,
298: String oldText, String newText, String description) {
299: this .kind = kind;
300: this .startPos = startPos;
301: this .endPos = endPos;
302: this .oldText = oldText;
303: this .newText = newText;
304: this .description = description;
305: this .excluded = false;
306: this .this Object = CsmRefactoringUtils.getHandler(ref);
307: }
308:
309: public CsmObject getReferenceObject() {
310: return this Object.getObject();
311: }
312:
313: public Kind getKind() {
314: return kind;
315: }
316:
317: public PositionRef getStartPosition() {
318: return startPos;
319: }
320:
321: public PositionRef getEndPosition() {
322: return endPos;
323: }
324:
325: public String getOldText() {
326: return oldText;
327: }
328:
329: public String getNewText() {
330: return newText;
331: }
332:
333: public boolean isExcluded() {
334: return excluded;
335: }
336:
337: public void exclude(boolean b) {
338: excluded = b;
339: }
340:
341: @Override
342: public String toString() {
343: return kind + "<" + startPos.getOffset() + ", "
344: + endPos.getOffset() + ">: " + oldText + " -> "
345: + newText; // NOI18N
346: }
347:
348: public String getDescription() {
349: return description;
350: }
351:
352: public static enum Kind {
353: INSERT, REMOVE, CHANGE, CREATE
354: }
355: }
356: }
|