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.search;
043:
044: import java.awt.EventQueue;
045: import java.io.IOException;
046: import java.util.ArrayList;
047: import java.util.List;
048: import org.netbeans.api.progress.ProgressHandle;
049: import org.netbeans.api.progress.ProgressHandleFactory;
050: import org.netbeans.modules.search.MatchingObject.InvalidityStatus;
051: import org.openide.filesystems.FileAlreadyLockedException;
052: import org.openide.filesystems.FileLock;
053: import org.openide.util.NbBundle;
054: import org.openide.util.UserQuestionException;
055:
056: /**
057: * Task that checks validity of found files and then
058: * (if everything is valid) replaces the matching substrings
059: * with the replacement string/pattern.
060: *
061: * @author Tim Boudreau
062: * @author Marian Petras
063: */
064: final class ReplaceTask implements Runnable {
065:
066: /**
067: * maximum number of errors detected before replacing
068: * and displayed to the user
069: */
070: private static final int MAX_ERRORS_CHECKED = 20;
071:
072: private final MatchingObject[] matchingObjects;
073: private final ProgressHandle progressHandle;
074: private final List<String> problems;
075:
076: /** */
077: private ResultStatus resultStatus = null;
078:
079: enum ResultStatus {
080: SUCCESS, PRE_CHECK_FAILED, PROBLEMS_ENCOUNTERED
081: }
082:
083: /**
084: */
085: ReplaceTask(MatchingObject[] matchingObjects) {
086: this .matchingObjects = matchingObjects;
087:
088: problems = new ArrayList<String>(4);
089: progressHandle = ProgressHandleFactory.createHandle(NbBundle
090: .getMessage(getClass(), "LBL_Replacing")); //NOI18N
091: }
092:
093: /**
094: */
095: public void run() {
096: assert !EventQueue.isDispatchThread();
097:
098: progressHandle.start(matchingObjects.length * 2);
099: try {
100: replace();
101: assert resultStatus != null;
102: } finally {
103: progressHandle.finish();
104: }
105: }
106:
107: /**
108: */
109: private void replace() {
110: assert !EventQueue.isDispatchThread();
111:
112: checkForErrors();
113: if (resultStatus == null) { //the check passed
114: doReplace();
115: }
116: }
117:
118: /**
119: */
120: private void checkForErrors() {
121: assert !EventQueue.isDispatchThread();
122:
123: int errorsCount = 0;
124:
125: for (int i = 0; i < matchingObjects.length; i++) {
126: InvalidityStatus status = matchingObjects[i]
127: .checkValidity();
128: if (status != null) {
129: problems.add(status.getDescription(matchingObjects[i]
130: .getFile().getPath()));
131: if (++errorsCount > MAX_ERRORS_CHECKED) {
132: break;
133: }
134: }
135: }
136: if (!problems.isEmpty()) {
137: resultStatus = ResultStatus.PRE_CHECK_FAILED;
138: }
139: }
140:
141: /**
142: *
143: * @return list of strings describing problems that happened during
144: * the replace, or {@code null} if no problem happened
145: */
146: private void doReplace() {
147: assert !EventQueue.isDispatchThread();
148:
149: for (int i = 0; i < matchingObjects.length; i++) {
150: final MatchingObject obj = matchingObjects[i];
151:
152: progressHandle.progress(obj.getName(), i
153: + matchingObjects.length);
154: if (!obj.isSelected() || !obj.isValid()) {
155: continue;
156: }
157:
158: String invDescription = obj.getInvalidityDescription();
159: if (invDescription != null) {
160: problems.add(invDescription);
161: continue;
162: }
163:
164: String errMessage = null;
165: FileLock fileLock = null;
166: try {
167: fileLock = obj.lock();
168: MatchingObject.InvalidityStatus status = obj.replace();
169: if (status == null) {
170: obj.write(fileLock);
171: } else {
172: errMessage = status.getDescription(obj.getFile()
173: .getPath());
174: }
175: } catch (FileAlreadyLockedException ex) {
176: errMessage = createMsgFileLocked(obj);
177: } catch (UserQuestionException ex) {
178: errMessage = createMsgFileLocked(obj);
179: } catch (IOException ex) {
180: ex.printStackTrace(); //PENDING - ex.printStackTrace()?
181: errMessage = ex.getLocalizedMessage();
182: if (errMessage == null) {
183: errMessage = ex.getMessage();
184: }
185: } finally {
186: if (fileLock != null) {
187: fileLock.releaseLock();
188: }
189: }
190: if (errMessage != null) {
191: problems.add(errMessage);
192: }
193: }
194: resultStatus = problems.isEmpty() ? ResultStatus.SUCCESS
195: : ResultStatus.PROBLEMS_ENCOUNTERED;
196: }
197:
198: private static String createMsgFileLocked(MatchingObject matchingObj) {
199: return NbBundle.getMessage(ReplaceTask.class,
200: "MSG_cannot_access_file_already_locked", //NOI18N
201: matchingObj.getName());
202: }
203:
204: /**
205: *
206: * @see #getProblems()
207: */
208: ResultStatus getResultStatus() {
209: return resultStatus;
210: }
211:
212: /**
213: * Returns a list of problems encountered during the pre-check or
214: * during replacing. The type of problems (pre-check or replacing)
215: * can be determined from the results status returned by method
216: * {@link #getResultStatus()}.
217: *
218: * @return array of problems, or {@code null} if no problems have been
219: * encountered
220: * @see #getResultStatus()
221: */
222: String[] getProblems() {
223: return problems.isEmpty() ? null : problems
224: .toArray(new String[problems.size()]);
225: }
226:
227: }
|