Ich habe mich heute einmal an Java versucht. Dabei will ich die Möglichkeiten regulärer Ausdrücke auch in LotusScript verwenden. Der kleine Helfer an meiner Seite heisst dabei java.util.regex.*.
Frage: Ist es ein Problem, wenn/dass das nur unter Notes 7 funktioniert?
Für Java1.3 (Notes 6) müßtest du andere open Source Regex Implementierungen verwenden (zwar unschöner, sollte aber gehen).
Sollte der Code auf einem Client laufen, ist das ok. Auf dem Server ist LS2J ein bischen gefährlich, wegen Memory Leak Problemen. Muß auf jeden Fall gut getestet werden. Es gab auch mal einen guten Blockeintrag.
Man möge mir vergeben; dies ist mein erster Versuchin Java etwas anderes zu bauen, als ein simples "Hallo Welt". Wahrscheinlich verstosse ich gegen so ziemlich jede Regel der Java Community.
Hey. Das ist hier
nicht die spanische Inquistion oder Freißlers Reichsgericht.
Genug des Vorgeplänkel, hier ist der Code der Klasse regexp:
Da funktioniert eine Menge. Nur wird in der Methode replaceAll alles mit ReplacedText ersetzt? Vielleicht müsste man da in die Klasse eine Map reinschiessen (oder generieren).
Hm. Leider gibts in Java1.4 noch nicht die split() Methode für Strings. Am saubersten wäre es wahrscheinlich jakarta commons lang einzubinden. Ich geh mir aber jetzt die entsprechenden Teile klauen und komme mit einem Vorschlag.
Disclaimer: So arbeite ich normalerweise nicht. Weiss noch nicht mal, ob das mit Apache2 licence erlaubt ist, da einfach Teile aus dem Source rauszukopieren.
Die intelligent geschriebenen Methoden mit den guten Kommentaren sind von apache-jarkarta (StringUtils in commonsLang). Das sieht vielleicht ein bischen abschreckend lang aus, aber das sind ja nur 2 service Methoden unter der Main Methode. Falls das brauchbar ist, kann ich dir sagen, wie du das umbaust und du direkt commons-lang-2-1.jar einbindest. Es geht hier um Wiederverwendbarkeit der Arbeit anderer.
Ich hab die Initialisierung des Patterns in die init() Methode gezogen. Da wird auch die ReplacerMap initiiert. Hab eine Testklasse geschrieben und poste die mit. Die für das Verständnis wichtigen Tests stehen aber auch in der public static void main (String args[])- Methode von der Regexp. Du kannst die Klasse also einfach starten (aus Eclipse oder mit java Regexp von der Kommandozeile). Da siehst du auch wie die Klasse benutzt werden kann.
a) immer die init Methode nach dem Konstruktor aufrufen (Mit LS2J sind Konstruktoren mit Parametern ein bischen unhandlich, deshalb die init.
Aus diesem String
<<supporter>>~Herr Supporter:<<Datum>>~19.03.1994:<<reqNumBer>>~42"
wird eine java.util.Map (ähnlich wie List in LotusScript) generiert. Das wird dann fürs replacement verwendet (mit m.group).
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Regexp {
private Map mapReplacers = new HashMap();
private String msgLastRunReplaceAll;
private Pattern p = null;
public void init(String inStrMapReplacers, String pattern) {
p = Pattern.compile(pattern);
String [] replacerPairs = split(inStrMapReplacers, ":");
for (int i=0; i < replacerPairs.length; i++) {
String[] replacerTokens = split(replacerPairs[i], "~");
if (replacerTokens.length != 2) {
throw new RuntimeException("The initializer String for mapReplacers is wrong. Should have 2 tokens separated by '~'. Position: " + replacerPairs[i]);
}
mapReplacers.put(replacerTokens[0], replacerTokens[1]);
}
}
public Map getMapReplacers () {
return mapReplacers;
}
public String getMsgLastRunReplaceAll() {
return msgLastRunReplaceAll;
}
public int count(String line) {
int i = 0;
Matcher m = p.matcher(line);
boolean result = m.find();
while (result) {
i++;
result = m.find();
}
return i; // returns number of matches found
}
public String replaceAll(String line) {
msgLastRunReplaceAll = "";
Matcher m = p.matcher(line);
StringBuffer sb = new StringBuffer();
boolean result = m.find();
while (result) {
String toReplace = m.group();
String replacer = (String) mapReplacers.get(toReplace);
if (replacer == null) {
String msg = "Cannot find replacer for " + toReplace + "\n";
msgLastRunReplaceAll = msgLastRunReplaceAll.length() == 0 ? msg : msgLastRunReplaceAll + "\n" + msg;
// logger.error("Cannot find replacer for " + toReplace);
m.appendReplacement(sb,toReplace);
} else {
m.appendReplacement(sb, replacer);
}
result = m.find();
}
m.appendTail(sb);
return sb.toString(); // returns the new string
}
// ############## some dirty ad hoc tests ###########################
public static void main (String args[]) {
// init some testvalues
Regexp cut = new Regexp();
String unformattedText = "\nLieber <<supporter>>,\n\n" +
"du hast heute <<Datum>> mal wieder vergessen, das Ticket ( <<reqNumBer>> ) zu bearbeiten!!\n\n" +
"Pöser Pursche, du. :-)";
String pattern= "<{2}(.[aA-zZ]+?)>{2}";
System.out.println("**test count()**");
cut.init("", pattern);
int res = cut.count(unformattedText);
System.out.println("result =" + res);
System.out.println("**testReplaceAllSuccess()**");
cut = new Regexp();
cut.init("<<supporter>>~Herr Supporter:<<Datum>>~19.03.1994:<<reqNumBer>>~42", pattern);
String resStr = cut.replaceAll(unformattedText);
System.out.println("result =" + resStr);
System.out.println("for problems in replacement::" + cut.getMsgLastRunReplaceAll());
System.out.println("**testReplaceWithUnknownToReplaceToken()**");
cut = new Regexp();
cut.init("<<supporter>>~Herr Supporter:<<Datum>>~19.03.1994:<<reqNumBer>>~42", pattern);
resStr = cut.replaceAll("<<thatDoesNotExist>>" + unformattedText);
System.out.println("result=" + resStr);
System.out.println("for problems in replacement::" + cut.getMsgLastRunReplaceAll());
}
//********************************************************************
// ############## apache.jakarta.commons ###########################
//********************************************************************
/**
* <p>Splits the provided text into an array, separators specified.
* This is an alternative to using StringTokenizer.</p>
*
* <p>The separator is not included in the returned String array.
* Adjacent separators are treated as one separator.
* For more control over the split use the StrTokenizer class.</p>
*
* <p>A <code>null</code> input String returns <code>null</code>.
* A <code>null</code> separatorChars splits on whitespace.</p>
*
* <pre>
* StringUtils.split(null, *) = null
* StringUtils.split("", *) = []
* StringUtils.split("abc def", null) = ["abc", "def"]
* StringUtils.split("abc def", " ") = ["abc", "def"]
* StringUtils.split("abc def", " ") = ["abc", "def"]
* StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
* </pre>
*
* @param str the String to parse, may be null
* @param separatorChars the characters used as the delimiters,
* <code>null</code> splits on whitespace
* @return an array of parsed Strings, <code>null</code> if null String input
*/
public static String[] split(String str, String separatorChars) {
return splitWorker(str, separatorChars, -1, false);
}
/**
* Performs the logic for the <code>split</code> and
* <code>splitPreserveAllTokens</code> methods that return a maximum array
* length.
*
* @param str the String to parse, may be <code>null</code>
* @param separatorChars the separate character
* @param max the maximum number of elements to include in the
* array. A zero or negative value implies no limit.
* @param preserveAllTokens if <code>true</code>, adjacent separators are
* treated as empty token separators; if <code>false</code>, adjacent
* separators are treated as one separator.
* @return an array of parsed Strings, <code>null</code> if null String input
*/
private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) {
// Performance tuned for 2.0 (JDK1.4)
// Direct code is quicker than StringTokenizer.
// Also, StringTokenizer uses isSpace() not isWhitespace()
if (str == null) {
return null;
}
int len = str.length();
if (len == 0) {
//return ArrayUtils.EMPTY_STRING_ARRAY;
return new String[0]; // to make it work: Axel Janssen
}
List list = new ArrayList();
int sizePlus1 = 1;
int i = 0, start = 0;
boolean match = false;
boolean lastMatch = false;
if (separatorChars == null) {
// Null separator means use whitespace
while (i < len) {
if (Character.isWhitespace(str.charAt(i))) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
} else {
lastMatch = false;
}
match = true;
i++;
}
} else if (separatorChars.length() == 1) {
// Optimise 1 character case
char sep = separatorChars.charAt(0);
while (i < len) {
if (str.charAt(i) == sep) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
} else {
lastMatch = false;
}
match = true;
i++;
}
} else {
// standard case
while (i < len) {
if (separatorChars.indexOf(str.charAt(i)) >= 0) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
} else {
lastMatch = false;
}
match = true;
i++;
}
}
if (match || (preserveAllTokens && lastMatch)) {
list.add(str.substring(start, i));
}
return (String[]) list.toArray(new String[list.size()]);
}
}
Testklasse (junit 3.8:
import java.util.HashMap;
import java.util.Map;
import junit.framework.TestCase;
/**
*
*/
/**
* @author Axel
*
*/
public class RegexpTest extends TestCase {
private String unformattedText;
private String pattern;
private Regexp cut = null;
/**
* @param arg0
*/
public RegexpTest(String arg0) {
super(arg0);
unformattedText = "\nLieber <<supporter>>,\n\n" +
"du hast heute <<Datum>> mal wieder vergessen, das Ticket ( <<reqNumBer>> ) zu bearbeiten!!\n\n" +
"Pöser Pursche, du. :-)";
pattern= "<{2}(.[aA-zZ]+?)>{2}";
}
/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
cut = new Regexp();
}
/* (non-Javadoc)
* @see junit.framework.TestCase#tearDown()
*/
protected void tearDown() throws Exception {
super.tearDown();
}
/**
* Test method for {@link Regexp#count(java.lang.String, java.lang.String)}.
*/
public void testCount() {
cut.init("", pattern);
int res = cut.count(unformattedText);
assertEquals("Its expected to find 3 occurrences.", 3, res);
}
/**
* Test method for {@link Regexp#replaceAll(java.lang.String, java.lang.String, java.lang.String)}.
*/
public void testReplaceAllSuccess() {
cut.init("<<supporter>>~Herr Supporter:<<Datum>>~19.03.1994:<<reqNumBer>>~42", pattern);
String res = cut.replaceAll(unformattedText);
System.out.println(res);
String resExpected = "\nLieber Herr Supporter,\n\n" +
"du hast heute 19.03.1994 mal wieder vergessen, das Ticket ( 42 ) zu bearbeiten!!\n\n" +
"Pöser Pursche, du. :-)";
assertEquals("Its expected for the method to return resExpected", resExpected, res);
assertEquals("", cut.getMsgLastRunReplaceAll());
}
public void testReplaceWithUnknownToReplaceToken() {
cut.init("<<supporter>>~Herr Supporter:<<Datum>>~19.03.1994:<<reqNumBer>>~42", pattern);
String res = cut.replaceAll("<<thatDoesNotExist>>" + unformattedText);
System.out.println(res);
String resExpected = "<<thatDoesNotExist>>\nLieber Herr Supporter,\n\n" +
"du hast heute 19.03.1994 mal wieder vergessen, das Ticket ( 42 ) zu bearbeiten!!\n\n" +
"Pöser Pursche, du. :-)";
assertEquals("Its expected for the method to return resExpected", resExpected, res);
assertEquals("Cannot find replacer for <<thatDoesNotExist>>\n",cut.getMsgLastRunReplaceAll() );
}
public void testInitMapReplacers() {
cut.init("<<supporter>>~Herr Supporter:<<Datum>>~19.03.1994:<<reqNumBer>>~42", pattern);
Map expectedMap = new HashMap();
expectedMap.put("<<supporter>>", "Herr Supporter");
expectedMap.put("<<Datum>>", "19.03.1994");
expectedMap.put("<<reqNumBer>>", "42");
assertEquals("map should equal expectedMp", expectedMap, cut.getMapReplacers());
}
}