001: /*
002: * DataStoreReplacer.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.storage;
013:
014: import java.util.regex.Matcher;
015: import java.util.regex.Pattern;
016: import java.util.regex.PatternSyntaxException;
017: import workbench.gui.editor.SearchAndReplace;
018: import workbench.log.LogMgr;
019: import workbench.util.ConverterException;
020: import workbench.util.SqlUtil;
021: import workbench.util.StringUtil;
022:
023: /**
024: * @author support@sql-workbench.net
025: */
026: public class DataStoreReplacer {
027: private DataStore client;
028: private Position lastFoundPosition;
029: private Pattern lastPattern;
030: private String currentReplacementValue;
031: private int[] selectedRows;
032: private int lastSelectedRowIndex;
033: private String lastCriteria;
034: private boolean isRegexSearch;
035:
036: public DataStoreReplacer() {
037:
038: }
039:
040: /**
041: * Create a new DataStoreReplacer for the given DataStore.
042: * The datastore is not checked if it is updateable!
043: *
044: * @param store the DataStore to search and replace in
045: */
046: public DataStoreReplacer(DataStore store) {
047: setDataStore(store);
048: }
049:
050: public void setDataStore(DataStore ds) {
051: this .client = ds;
052: this .reset();
053: }
054:
055: /**
056: * Limit all search and replace actions to the selected rows.
057: * To reset search & replace in the selected rows, setSelecteRows()
058: * has to be called again with a null value
059: * @param rows the selected rows to be searched, null to reset row selection
060: */
061: public void setSelectedRows(int[] rows) {
062: this .selectedRows = rows;
063: this .lastSelectedRowIndex = 0;
064: }
065:
066: public Position getLastFoundPosition() {
067: return this .lastFoundPosition;
068: }
069:
070: public String getLastCriteria() {
071: return this .lastCriteria;
072: }
073:
074: /**
075: * Find the given text in the datastore.
076: *
077: * @param text the text to search for
078: * @param ignoreCase if true, search is case-insesitive
079: * @param wholeWord if true, only text in word bounderies is found
080: * @param useRegex treat the text as a regular expression
081: * @return the position where the text was found
082: *
083: * @see workbench.gui.editor.SearchAndReplace#getSearchExpression(String, boolean, boolean, boolean)
084: */
085: public Position find(String text, boolean ignoreCase,
086: boolean wholeWord, boolean useRegex)
087: throws PatternSyntaxException {
088: lastCriteria = text;
089: lastFoundPosition = Position.NO_POSITION;
090: lastSelectedRowIndex = 0;
091: if (StringUtil.isEmptyString(text))
092: return Position.NO_POSITION;
093: this .isRegexSearch = useRegex;
094: String expression = SearchAndReplace.getSearchExpression(text,
095: ignoreCase, wholeWord, useRegex);
096: Pattern p = null;
097: try {
098: p = Pattern.compile(expression);
099: } catch (PatternSyntaxException e) {
100: LogMgr.logError("DataStoreReplacer.find()",
101: "Error compiling search pattern", e);
102: throw e;
103: }
104: return findPattern(p);
105: }
106:
107: /**
108: * Find the next occurance of the search string.
109: * This returns NO_POSITION if find(String, boolean) has not
110: * been called before.
111: * @return the position of the next occurance
112: */
113: public Position findNext() {
114: if (lastPattern == null) {
115: reset();
116: return lastFoundPosition;
117: }
118: return findPattern(lastPattern);
119: }
120:
121: public void reset() {
122: lastPattern = null;
123: lastCriteria = null;
124: currentReplacementValue = null;
125: lastSelectedRowIndex = 0;
126: lastFoundPosition = Position.NO_POSITION;
127: }
128:
129: private Position findPattern(Pattern p) {
130: int startRow = 0;
131: int startCol = 0;
132:
133: int rowCount = this .client.getRowCount();
134: int colCount = this .client.getColumnCount();
135:
136: if (this .lastFoundPosition.isValid()) {
137: startRow = this .lastFoundPosition.getRow();
138: startCol = this .lastFoundPosition.getColumn() + 1;
139: if (startCol >= colCount) {
140: startCol = 0;
141: startRow++;
142: }
143: }
144:
145: this .lastPattern = p;
146:
147: int startIndex = startRow;
148:
149: if (this .selectedRows != null) {
150: startIndex = this .lastSelectedRowIndex;
151: rowCount = this .selectedRows.length;
152: }
153:
154: if (startIndex < 0)
155: startIndex = 0;
156:
157: for (int index = startIndex; index < rowCount; index++) {
158: int row = index;
159: if (selectedRows != null) {
160: this .lastSelectedRowIndex = index;
161: row = this .selectedRows[index];
162: }
163:
164: for (int col = startCol; col < colCount; col++) {
165: int type = client.getColumnType(col);
166: if (SqlUtil.isBlobType(type))
167: continue;
168: String colValue = client.getValueAsString(row, col);
169: if (StringUtil.isEmptyString(colValue))
170: continue;
171: Matcher m = p.matcher(colValue);
172: if (m.find()) {
173: this .lastFoundPosition = new Position(row, col);
174: return this .lastFoundPosition;
175: }
176: }
177: startCol = 0;
178: }
179:
180: return Position.NO_POSITION;
181: }
182:
183: /**
184: * Replace all occurances of a value with the given replacement value.
185: *
186: * @param text the value to search for
187: * @param replacement the replacement value
188: * @param rows if not null search and replace is only done in these rows
189: * @param ignoreCase should the search pattern be applied case-insensitive
190: * @param wholeWord if true, only whole words are found
191: * @param useRegex if true, expression is treated as a regular expression
192: *
193: * @return the number of occurances replaced
194: * @see workbench.gui.editor.SearchAndReplace#getSearchExpression(String, boolean, boolean, boolean)
195: * @see workbench.gui.editor.SearchAndReplace#fixSpecialReplacementChars(String)
196: */
197: public int replaceAll(String text, String replacement, int[] rows,
198: boolean ignoreCase, boolean wholeWord, boolean useRegex)
199: throws ConverterException, PatternSyntaxException {
200: reset();
201: String expression = SearchAndReplace.getSearchExpression(text,
202: ignoreCase, wholeWord, useRegex);
203:
204: this .isRegexSearch = useRegex;
205:
206: currentReplacementValue = SearchAndReplace
207: .fixSpecialReplacementChars(replacement, isRegexSearch);
208:
209: int replaced = 0;
210: Pattern p = Pattern.compile(expression);
211:
212: this .setSelectedRows(rows);
213:
214: Position pos = findPattern(p);
215:
216: while (pos.isValid()) {
217: replaceValueAt(pos, this .currentReplacementValue,
218: this .lastPattern);
219: replaced++;
220: pos = findNext();
221: }
222: return replaced;
223: }
224:
225: public boolean replaceCurrent(String replacement)
226: throws ConverterException {
227: if (this .lastFoundPosition == null)
228: return false;
229:
230: if (this .lastFoundPosition.isValid()) {
231: currentReplacementValue = SearchAndReplace
232: .fixSpecialReplacementChars(replacement,
233: isRegexSearch);
234: replaceValueAt(lastFoundPosition,
235: this .currentReplacementValue, this .lastPattern);
236: return true;
237: }
238: return false;
239: }
240:
241: private void replaceValueAt(Position pos, String replacement,
242: Pattern p) throws ConverterException {
243: String value = this .client.getValueAsString(pos.getRow(), pos
244: .getColumn());
245: if (!StringUtil.isEmptyString(value)) {
246: Matcher m = p.matcher(value);
247: String newValue = m.replaceAll(replacement);
248: try {
249: client.setInputValue(pos.getRow(), pos.getColumn(),
250: newValue);
251: } catch (ConverterException e) {
252: LogMgr.logError("DataStoreReplacer.replaceAll()",
253: "Could not convert the replacement data", e);
254: throw e;
255: }
256: }
257: }
258: }
|