/* * Zettelkasten - nach Luhmann * Copyright (C) 2001-2015 by Daniel Lüdecke (http://www.danielluedecke.de) * * Homepage: http://zettelkasten.danielluedecke.de * * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, see <http://www.gnu.org/licenses/>. * * * Dieses Programm ist freie Software. Sie können es unter den Bedingungen der GNU * General Public License, wie von der Free Software Foundation veröffentlicht, weitergeben * und/oder modifizieren, entweder gemäß Version 3 der Lizenz oder (wenn Sie möchten) * jeder späteren Version. * * Die Veröffentlichung dieses Programms erfolgt in der Hoffnung, daß es Ihnen von Nutzen sein * wird, aber OHNE IRGENDEINE GARANTIE, sogar ohne die implizite Garantie der MARKTREIFE oder * der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. Details finden Sie in der * GNU General Public License. * * Sie sollten ein Exemplar der GNU General Public License zusammen mit diesem Programm * erhalten haben. Falls nicht, siehe <http://www.gnu.org/licenses/>. */ package de.danielluedecke.zettelkasten.tasks; import de.danielluedecke.zettelkasten.database.Daten; import de.danielluedecke.zettelkasten.database.TasksData; import de.danielluedecke.zettelkasten.util.Constants; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JOptionPane; /** * * @author danielludecke */ public class ReplaceTask extends org.jdesktop.application.Task<Object, Void> { private final Daten dataObj; private final TasksData taskinfo; private String findTerm; private String replaceTerm; private int[] replaceEntries; private final int replaceWhere; private final boolean wholeword; private final boolean matchcase; private final boolean regex; private final javax.swing.JDialog parentDialog; private final javax.swing.JLabel msgLabel; /** * get the strings for file descriptions from the resource map */ private final org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(de.danielluedecke.zettelkasten.ZettelkastenApp.class). getContext().getResourceMap(ReplaceTask.class); /** * * @param app * @param parent * @param label * @param d * @param fs a string containing the find-term that should be replaced * @param rs a string containing the replace-term that replaces the find-term {@code fs} * @param re an integer array containing the entry-numbers of those entries where * the search should be applied to. use {@code null} to search and replace in all entries. * @param w where the search should be applied to, i.e. search within content, keywords, authors etc. * @param ww pass true, if the search should find whole words only * @param mc whether the search is case sensitive (true) or not (false) * @param rex whether the find-term {@code fs} is a regular expression (true) or not... */ ReplaceTask(org.jdesktop.application.Application app, javax.swing.JDialog parent, javax.swing.JLabel label, TasksData td, Daten d, String fs, String rs, int[] re, int w, boolean ww, boolean mc, boolean rex) { // Runs on the EDT. Copy GUI state that // doInBackground() depends on from parameters // to ImportFileTask fields, here. super(app); dataObj=d; taskinfo = td; findTerm=fs; replaceTerm=rs; replaceEntries=re; replaceWhere=w; wholeword=ww; matchcase=mc; regex=rex; parentDialog = parent; msgLabel = label; // init status text msgLabel.setText(resourceMap.getString("msg1")); } @Override protected Object doInBackground() { // here we initiate variables that count the amount of changes in each // element, so after the replacement is done we can tell the user where // and how many replacements have been made int changeCounterTitles = 0; int changeCounterContent = 0; int changeCounterRemarks = 0; int changeCounterLinks = 0; int changeCounterAuthors = 0; int changeCounterKeywords = 0; int totalreplacements; // when we pass "null" as parameter for the entry-numbers of those entries // where the search should be applied to, we assume that we want to search // through the whole data. thus, we fill the arrays with all entrynumbers now... if (null==replaceEntries) { replaceEntries = new int[dataObj.getCount(Daten.ZKNCOUNT)]; // go through all entries for (int cnt=1; cnt<=dataObj.getCount(Daten.ZKNCOUNT);cnt++) replaceEntries[cnt-1] = cnt; } // when we have *no* regular expression for the replace-request, // quote/escape reg-ex-chars in replacement term if (!regex && !replaceTerm.isEmpty()) replaceTerm = Matcher.quoteReplacement(replaceTerm); // check whether the findterm is a regular expression or not. // if not, prepare findterm with wholeword and matchcase-regex if (!regex && !findTerm.isEmpty()) { // escape chars, so they are not recognized as regex. findTerm = Pattern.quote(findTerm); // when we have a whole-word-find&replace, surround findTerm with // the regular expression that indicates word beginning and ending (i.e. whole word) if (wholeword) findTerm = "\\b"+findTerm+"\\b"; // when the find & replace is *not* case-sensitive, set regular expression // to ignore the case... if (!matchcase) findTerm = "(?i)"+findTerm; // the final findterm now might look like this: // "(?i)\\b<findterm>\\b", in case we ignore case and have whole word search } // here we check whether we should find and replace in entries // later, below, we check whether we have to replace in lists, like // the keyword- or author-list if ((replaceWhere&Constants.SEARCH_CONTENT)!=0 || (replaceWhere&Constants.SEARCH_TITLE)!=0 || (replaceWhere&Constants.SEARCH_LINKS)!=0 || (replaceWhere&Constants.SEARCH_REMARKS)!=0) { // go through all entries... for (int cnt=0; cnt<replaceEntries.length; cnt++) { // // check whether we have to replace titles // if ((replaceWhere&Constants.SEARCH_TITLE)!=0) { // get the entry's title... String oldtitle = dataObj.getZettelTitle(replaceEntries[cnt]); // check whether the findterm is empty. if so, we want to // replace an empty title-element with the replaceterm if (findTerm.isEmpty()) { // when both findterm and title are empty, set replace-term // as new title and increase our counter... if (oldtitle.isEmpty()) { dataObj.setZettelTitle(replaceEntries[cnt], replaceTerm); changeCounterTitles++; } } // else replace replace the findterm within the current title (stored in "dummy") // with the replace-term else if (!oldtitle.isEmpty()) { // replace findterm in old title String newtitle = oldtitle.replaceAll(findTerm, replaceTerm); // check whether we have any changes at all... if (!oldtitle.equals(newtitle)) { // if yes, set new title dataObj.setZettelTitle(replaceEntries[cnt], newtitle); // and increase our counter changeCounterTitles++; } } } // // check whether we have to replace content // // check whether the findterm is empty. if so, do nothing, because we cannot // have an empty entry-content if ((replaceWhere&Constants.SEARCH_CONTENT)!=0 && !findTerm.isEmpty()) { // get the entry's content... String oldcontent = dataObj.getZettelContent(replaceEntries[cnt]); // and replace the findterms. String newcontent = oldcontent.replaceAll(findTerm, replaceTerm); // check whether we have any changes at all // and check that the new content is not empty! if (!oldcontent.equals(newcontent) && !newcontent.isEmpty()) { // if yes, set new content dataObj.setZettelContent(replaceEntries[cnt], newcontent, false); // and increase our counter changeCounterContent++; } } // // check whether we have to replace remarks // if ((replaceWhere&Constants.SEARCH_REMARKS)!=0) { // get the entry's remarks... String oldremarks = dataObj.getRemarks(replaceEntries[cnt]); // check whether the findterm is empty. if so, we want to // replace an empty remarks-element with the replaceterm if (findTerm.isEmpty()) { // when both findterm and remarks are empty, set replace-term // as new remarks and increase our counter... if (oldremarks.isEmpty()) { if (dataObj.setRemarks(replaceEntries[cnt], replaceTerm)) changeCounterRemarks++; } } // else replace replace the findterm within the current remarks (stored in "oldremarks") // with the replace-term else if (!oldremarks.isEmpty()) { // replace findterm in old remarks String newremarks = oldremarks.replaceAll(findTerm, replaceTerm); // check whether we have any changes at all... if (!oldremarks.equals(newremarks)) { // if yes, set new remarks if (dataObj.setRemarks(replaceEntries[cnt], newremarks)) { // and increase our counter changeCounterRemarks++; } } } } // // check whether we have to replace attachments // // we cannot have empty attachment-elements, so we // only proceed here when we have any findterms if ((replaceWhere&Constants.SEARCH_LINKS)!=0 && !findTerm.isEmpty()) { // get a string array with all entry's attachments String[] att = dataObj.getAttachmentsAsString(replaceEntries[cnt], false); // here we check whether we have made any changes at all... boolean changesmade = false; // this list stores all new attachment-values ArrayList<String> newarray = new ArrayList<>(); // go through all possible attachments for (String oldatt : att) { // replace findterm in old attachments String newatt = oldatt.replaceAll(findTerm, replaceTerm); // check whether we have any changes at all. // if yes, change indicator-variable... if (!oldatt.equals(newatt)) changesmade = true; // add newattachment to the linked list, but only // if it's not empty... if (!newatt.isEmpty()) newarray.add(newatt); } // now check whether we had any changes to the attachments // at all... if (changesmade) { // set new attachments dataObj.setAttachments(replaceEntries[cnt], newarray.toArray(new String[newarray.size()])); // and increase our counter changeCounterLinks++; } } // update progress bar setProgress(cnt,0,replaceEntries.length); } } // here we check whether we should find and replace in the keyword-list. // be careful when replacing empty replaceterms: check // whether keyword is not empty - else don't change keyword! // since we don't have empty keywords, we only need to proceed // when we have a find term. if ((replaceWhere&Constants.SEARCH_KEYWORDS)!=0 && !findTerm.isEmpty()) { // go through all keywords for (int cnt=1; cnt<=dataObj.getCount(Daten.KWCOUNT); cnt++) { // retrieve old keyword String oldkw = dataObj.getKeyword(cnt); // replace findterm in old keyword String newkw = oldkw.replaceAll(findTerm, replaceTerm); // if we have any changes, and new keyword is not empty... if (!oldkw.equals(newkw) && !newkw.isEmpty()) { // check whether new keyword (replace term) already exists if (dataObj.getKeywordPosition(newkw, false)!=-1) { // the new name for keyword already exists, so we can offer to merge // the keywords here. in fact, this is an easy find/replace-routine, since the // old keyword is replaced by the existing one, when we merge them. // create a JOptionPane with yes/no/cancel options int option = JOptionPane.showConfirmDialog(null, resourceMap.getString("mergeKeywordMsg", newkw), resourceMap.getString("mergeKeywordTitle"), JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE); // if no merge is requested, leave method if (JOptionPane.NO_OPTION == option) continue; // merge the keywords by opening a dialog with a background task dataObj.mergeKeywords(oldkw, newkw); // and increase our counter changeCounterKeywords++; } else { // ...change keyword dataObj.setKeyword(cnt, newkw); // and increase our counter changeCounterKeywords++; } } } } // here we check whether we should find and replace in the author-list. // be careful when replacing empty replaceterms: check // whether author is not empty - else don't change author! // since we don't have empty authors, we only need to proceed // when we have a find term. if ((replaceWhere&Constants.SEARCH_AUTHOR)!=0 && !findTerm.isEmpty()) { // go through all authors for (int cnt=1; cnt<=dataObj.getCount(Daten.AUCOUNT); cnt++) { // retrieve old author String oldau = dataObj.getAuthor(cnt); // replace findterm in old author String newau = oldau.replaceAll(findTerm, replaceTerm); // if we have any changes, and new author is not empty... if (!oldau.equals(newau) && !newau.isEmpty()) { // check whether new author (replace term) already exists if (dataObj.getAuthorPosition(newau)!=-1) { // the new name for author already exists, so we can offer to merge // the authors here. in fact, this is an easy find/replace-routine, since the // old author is replaced by the existing one, when we merge them. // create a JOptionPane with yes/no/cancel options int option = JOptionPane.showConfirmDialog(null, resourceMap.getString("mergeAuthorMsg"), resourceMap.getString("mergeAuthorTitle"), JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE); // if no merge is requested, leave method if (JOptionPane.NO_OPTION == option ) continue; // merge authors dataObj.mergeAuthors(oldau, newau); // and increase our counter changeCounterAuthors++; } else { // ...change author dataObj.setAuthor(cnt, newau); // and increase our counter changeCounterAuthors++; } } } } // calculate total replacements made totalreplacements = changeCounterTitles+ changeCounterContent+ changeCounterRemarks+ changeCounterLinks+ changeCounterAuthors+ changeCounterKeywords; // prepare string builder for result message StringBuilder replacemsg = new StringBuilder(""); // check whether we have any replacements at all... // if yes, totalreplacements would be greater than zero if (totalreplacements>0) { // now check which replace-domain had any changes - this is indicated by the related counter-variable, which // must have a value greater than zero. if we have changes, retrieve the message-string from the // resourcemap and create the "replaced message" // in some casess, we also set the up-to-date-indicator for particular tables to false, // so a table update is made. if (changeCounterTitles>0) { replacemsg.append(resourceMap.getString("replacedInTitles",String.valueOf(changeCounterTitles))); dataObj.setTitlelistUpToDate(false); } if (changeCounterContent>0) replacemsg.append(resourceMap.getString("replacedInContents",String.valueOf(changeCounterContent))); if (changeCounterRemarks>0) replacemsg.append(resourceMap.getString("replacedInRemarks",String.valueOf(changeCounterRemarks))); if (changeCounterLinks>0) { replacemsg.append(resourceMap.getString("replacedInAttachments",String.valueOf(changeCounterLinks))); dataObj.setAttachmentlistUpToDate(false); } if (changeCounterKeywords>0) { replacemsg.append(resourceMap.getString("replacedInKeywords",String.valueOf(changeCounterKeywords))); dataObj.setKeywordlistUpToDate(false); } if (changeCounterAuthors>0) { replacemsg.append(resourceMap.getString("replacedInAuthors",String.valueOf(changeCounterAuthors))); dataObj.setAuthorlistUpToDate(false); } } // now create the "final" message taskinfo.setReplaceMessage((totalreplacements>0) ? resourceMap.getString("replacedMsg", replacemsg.toString()) : resourceMap.getString("noReplacementsMsg")); // and store replacement-count taskinfo.setReplaceCount(totalreplacements); return null; // return your result } @Override protected void succeeded(Object result) { // Runs on the EDT. Update the GUI based on // the result computed by doInBackground(). } @Override protected void finished() { super.finished(); // and close window parentDialog.dispose(); parentDialog.setVisible(false); } }