blob: 931336d5f3cfa7ed8a153f6b2347d86a5c91286b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.search;
import java.awt.EventQueue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.modules.search.MatchingObject.InvalidityStatus;
import org.netbeans.modules.search.ui.BasicReplaceResultsPanel;
import org.openide.filesystems.FileAlreadyLockedException;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
import org.openide.util.UserQuestionException;
/**
* Task that checks validity of found files and then
* (if everything is valid) replaces the matching substrings
* with the replacement string/pattern.
*
* @author Tim Boudreau
* @author Marian Petras
*/
public final class ReplaceTask implements Runnable {
/**
* maximum number of errors detected before replacing
* and displayed to the user
*/
private static final int MAX_ERRORS_CHECKED = 20;
private final List<MatchingObject> matchingObjects;
private final ProgressHandle progressHandle;
private final List<String> problems;
private final BasicReplaceResultsPanel panel;
/** */
private ResultStatus resultStatus = null;
enum ResultStatus {
SUCCESS,
PRE_CHECK_FAILED,
PROBLEMS_ENCOUNTERED
}
/**
*/
public ReplaceTask(List<MatchingObject> matchingObjects,
BasicReplaceResultsPanel panel) {
this.matchingObjects = matchingObjects;
this.panel = panel;
problems = new ArrayList<String>(4);
progressHandle = ProgressHandleFactory.createHandle(
NbBundle.getMessage(getClass(), "LBL_Replacing")); //NOI18N
}
/**
*/
public void run() {
assert !EventQueue.isDispatchThread();
progressHandle.start(matchingObjects.size() * 2);
try {
replace();
assert resultStatus != null;
} finally {
progressHandle.finish();
}
}
/**
*/
private void replace() {
assert !EventQueue.isDispatchThread();
checkForErrors();
if (resultStatus == null) { //the check passed
FileUtil.runAtomicAction(new Runnable() {
public void run() {
doReplace();
}
});
}
}
/**
*/
private void checkForErrors() {
assert !EventQueue.isDispatchThread();
int errorsCount = 0;
for(MatchingObject mo: matchingObjects) {
InvalidityStatus status = mo.checkValidity();
if (status != null) {
problems.add(status.getDescription(mo.getFileObject().getPath()));
if (++errorsCount > MAX_ERRORS_CHECKED) {
break;
}
}
}
if (!problems.isEmpty()) {
resultStatus = ResultStatus.PRE_CHECK_FAILED;
}
}
/**
*
* @return list of strings describing problems that happened during
* the replace, or {@code null} if no problem happened
*/
private void doReplace() {
assert !EventQueue.isDispatchThread();
int i = 0;
int moSize = matchingObjects.size();
for(final MatchingObject obj: matchingObjects) {
int workunit = moSize + i++;
progressHandle.progress(obj.getName(), workunit);
if (!obj.isSelected() || !obj.isValid()) {
continue;
}
String invDescription = obj.getInvalidityDescription();
if (invDescription != null) {
problems.add(invDescription);
continue;
}
String errMessage = null;
FileLock fileLock = null;
try {
fileLock = obj.lock();
MatchingObject.InvalidityStatus status = obj.replace();
if (status == null) {
obj.write(fileLock);
} else {
errMessage = status.getDescription(obj.getFileObject().getPath());
}
} catch (FileAlreadyLockedException ex) {
errMessage = createMsgFileLocked(obj);
} catch (UserQuestionException ex) {
errMessage = createMsgFileLocked(obj);
} catch (IOException ex) {
ex.printStackTrace(); //PENDING - ex.printStackTrace()?
errMessage = ex.getLocalizedMessage();
if (errMessage == null) {
errMessage = ex.getMessage();
}
} finally {
if (fileLock != null) {
fileLock.releaseLock();
}
}
if (errMessage != null) {
problems.add(errMessage);
}
}
resultStatus = problems.isEmpty() ? ResultStatus.SUCCESS
: ResultStatus.PROBLEMS_ENCOUNTERED;
}
private static String createMsgFileLocked(MatchingObject matchingObj) {
return NbBundle.getMessage(
ReplaceTask.class,
"MSG_cannot_access_file_already_locked", //NOI18N
matchingObj.getName());
}
/**
*
* @see #getProblems()
*/
ResultStatus getResultStatus() {
return resultStatus;
}
/**
* Returns a list of problems encountered during the pre-check or
* during replacing. The type of problems (pre-check or replacing)
* can be determined from the results status returned by method
* {@link #getResultStatus()}.
*
* @return array of problems, or {@code null} if no problems have been
* encountered
* @see #getResultStatus()
*/
String[] getProblems() {
return problems.isEmpty()
? null
: problems.toArray(new String[problems.size()]);
}
BasicReplaceResultsPanel getPanel() {
return panel;
}
}