I wrote a class, which intends to work with regular expression (as a String or Pattern object) to get capturing groups of given regex, but it operate on regex. I wrote it to implement in my regex app and it is working quite well. I wanted to make it simple to implement, reusable, and as much as possible easy to read and understand, to make it like public API. I would be grateful if someone would review it from point of view of:readability - is it easy to grasp what given method is for?reusability - is it easy to use/implement for others? Are there some obviuos flaws I don't know, etc., maintaining - any visible bad practice for writing 'public' classes?hypothetical modification - is there something which could make implementing changes in class in future difficult for hypothetical user?I aware that this is small class (but still sorry for long code), so I will be grateful for any comments on above topics, as also for any comments about code. I added main() method, so it is easy to compile and test, also it shows basic methods:import java.util.*;import java.util.regex.Matcher;import java.util.regex.Pattern;/** * Tool for interpretation of the literal content of given regular expression. It performs analysis of capturing * abilities of this regex */public class Metamatcher {    private String pattern;    private TreeMap<Integer,Integer> groupsIndices;    private HashMap<String, Integer> namedGroupsIndex;    public Metamatcher(){        pattern = ;        namedGroupsIndex = new HashMap<String, Integer>();        groupsIndices = (TreeMap<Integer,Integer>)getGroups();    }    public Metamatcher(String pattern){        this.pattern = pattern;        namedGroupsIndex = new HashMap<String, Integer>();        groupsIndices = (TreeMap<Integer,Integer>)getGroups();    }    public Metamatcher(Pattern pattern){        this.pattern = pattern.toString();        namedGroupsIndex = new HashMap<String, Integer>();        groupsIndices = (TreeMap<Integer,Integer>)getGroups();    }    /**     * @param group ordinal number of group     * @return starting index of a fragment of pattern, which contain group capturing     */    public int start(int group){        List<Integer> indices = new ArrayList<Integer>(groupsIndices.keySet());        indices.add(0,0);        return indices.get(group);    }    /**     * @param name name of group     * @return starting index of a fragment of pattern, which contain named group capturing, or -1 if there is     * no such named group     */    public int start(String name){        return namedGroupsIndex.containsKey(name) ? start(namedGroupsIndex.get(name)) : -1;    }    /**     * @param group ordinal number of group     * @return ending index of a fragment of pattern, which contain group capturing     */    public int end(int group){        List<Integer> indices = new ArrayList<Integer>(groupsIndices.values());        indices.add(0,pattern.length());        return indices.get(group);    }    /**     * @param name name of group     * @return endig index of a fragment of pattern, which contain named group capturing, or -1 if there is no such     * named group     */    public int end(String name){        return namedGroupsIndex.containsKey(name) ? end(namedGroupsIndex.get(name)) : -1;    }    /**     * @param group ordinal number of group     * @return String object containing fragment of regular expression which capture given group     */    public String group(int group){        return pattern.substring(start(group), end(group));    }    public String group(String name){        return (namedGroupsIndex.containsKey(name) ? group(namedGroupsIndex.get(name)) : null);    }    /**     * @return number of capturing groups within given regular expression     */    public int groupCount(){        return groupsIndices.size();    }    /**     * @return information about Metamatcher object: group count and groups,     */    public String toString(){        StringBuilder result = new StringBuilder();        result.append(Groups count: )                .append(groupCount())                .append(\n);        for(int i = 0; i <= groupCount(); i++){            result.append(group()                    .append(i).append() )                    .append(start(i))                    .append(-)                    .append(end(i))                    .append(\t)                    .append(group(i))                    .append(\n);        }        return result.toString();    }    /**     * Changes the regular expression that this Metamatcher uses to a given String object     * @param pattern String consisting regular expression     * @return same object Metamatcher     */    public Metamatcher usePattern(String pattern){        this.pattern = pattern;        groupsIndices = (TreeMap<Integer,Integer>)getGroups();        return this;    }    /**     * Changes the regular expression that this Metamatcher uses to a regular expression used by given Pattern object     * @param pattern Pattern class object     */    public void usePattern(Pattern pattern){        this.pattern = pattern.toString();        groupsIndices = (TreeMap<Integer,Integer>)getGroups();    }    /**     * @return the regular expression from which this Metamatcher use.     */    public String getPattern(){        return pattern;    }    /**Returns start(key) and end(value) indices within Map object     * @return Map beginning and ending indices of captured groups of given regular expression     */    Map<Integer,Integer> getGroups(){        String copy = pattern;        Pattern pattern = Pattern.compile(\\([^\\(\\)]+\\));        Matcher matcher = pattern.matcher(copy);        Map<Integer,Integer> temp = new TreeMap<Integer,Integer>();        while(matcher.find()){            if(isCapturingGroup(matcher.group(0))){                temp.put(matcher.start(), matcher.end());            }            if(isNamedGroup(matcher.group(0))){                namedGroupsIndex.put(getNamedGroup(matcher.group(0)),matcher.start());            }            copy = copy.substring(0,matcher.start()) + replaceWithSpaces(matcher.group(0)) + copy.substring(matcher.end());            matcher.reset(copy);        }        return temp;    }    /**     * @param fragment of regular expression, enclosed by brackets     * @return true if given String consist regular expression which capture groups     */    boolean isCapturingGroup(String fragment){        return fragment.matches(((?<!\\\\)\\((?!\\?[<]?[:=!>])[^\\(\\)]+\\)));    }    /**     * @param fragment of regular expression, enclosed by brackets     * @return true if given String consist regular expression with capturing named groups     */    boolean isNamedGroup(String fragment){        return fragment.matches(\\(\\?<[A-Za-z0-9]+>[^)]+\\));    }    /**     * Extracts a name of named capturing group     * @param fragment of regular expression     * @return name of capturing group from within of given fragment     */    String getNamedGroup(String fragment){        Matcher matcher = Pattern.compile((?<=<)[a-zA-Z0-9]+?(?=>)).matcher(fragment);        matcher.find();        return matcher.group(0);    }    /**     * Provide a filler String composed of spaces, to replace part enclosed by brackets     * @param part String containing capturing group of regex, starting and ending with brackets,     * @return String composed of spaces (' '), with length of part object,     */    String replaceWithSpaces(String part){        String filler = ;        for(int i = 0; i < part.length(); i++){            filler +=  ;        }        return filler;    }    public static void main(String[] args){        Metamatcher matcher = new Metamatcher();        String[] patterns = {(a(b(c))d)(e(fg(h)ij)),                ^([_A-Za-z0-9-]+)(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+\n(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$,                \\((\\d+\\.\\d+)\\s(\\d+\\.\\d+),                (?<name>[a-z])+};        for(String pattern : patterns) {            matcher.usePattern(pattern);            System.out.println(matcher.toString());            System.out.println(matcher.groupCount() :  + matcher.groupCount());            System.out.println();        }        System.out.println(matcher.group() :  + matcher.group(name));    }}As I said above, class is for analyzing capturing groups within given regex. However for a short introduction, it has methods like:start(int group) - returns beginning index of group fragment withinregex,end(int group) - returns ending index of group fragment within regex,group(int group) - return String with fragment resposible formatching given group()usePatter(String patter) - for changing analyzed regex,patter - return String with used regex,etc.
Class for interpretation of capturing groups inside regex
java;beginner;regex
Before getting to your specific questions, let me just get a few nit-picky things out the way.At the very beginning, in the imports, you have this:import java.util.*;import java.util.regex.Matcher;import java.util.regex.Pattern;but why not just have:import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.TreeMap;import java.util.regex.Matcher;import java.util.regex.Pattern;Those are all of the dependencies in your program, and it's best to just write them out so everyone knows where each package is specifically. Many IDEs do this automatically now-a-days, so it's not hard to do.Your class's name is Metamatcher. Again, this is kind of picky, but the naming of class would look a bit better to developers if it was MetaMatcher. The name itself doesn't really describe what's happening in the class either. This area is up to you, but perhaps something like RegCount or RegexUtil would be better, based on how much you plan to expand its functionality.When writing the code out, please avoid the habit of something like this:public void HelloWorld(){ ...Just adding that extra space helps the visual appeal, and is kind of standard among JVM (and OOP) developers:public void HelloWorld() { ...Spacing can be good for some standard function calls in general as well, like with the for and while loops. I noticed that you didn't have this in a couple places.When documenting your code, the standard for JavaDocs looks something like this:/** Description of myMethod(int a, String b) *  * @param a         Description of a * @param b         Description of b * @return          Description of c */When done this way, the description will actually appear alongside the function's parameters, which is a big help in IDEs like Eclipse.Now, to actually attend to your questions:ReadabilityFor this one, most of my nit-picking should help clean up the code. As for what your code actually does, your documentation lists none of that. Yes, we get what the class is supposed to be used for and what the functions do, but apart from that we don't know what the code does solely from your documentation. If I were a newb, I wouldn't touch this. Instead, I'd most likely just scrounge through the Apache libraries to get what I need.ReusabilityAgain, this is kind of answered by my nit-picking and previous answer about readability. At the end of the day, if developer's can't tell the purpose of the program through anything other than spending time to chug down the raw code, they probably won't want to take the time to bother. This is why documentation has been under a heavy magnifying glass recently, because even though we all hate it, it's kind of a necessary evil.MaintainingI'm not quite sure what you mean specifically, but just taking what your question after this title is, that I can answer. You seem to be just fine in terms of design, just always remember your nomenclature though. Also, instead of doing this excessively in your constructors:namedGroupsIndex = new HashMap<String, Integer>();groupsIndices = (TreeMap<Integer,Integer>)getGroups();You can just put them directly at the variables, since you're only making a new instance of them:private TreeMap<Integer, Integer> groupsIndices = (TreeMap<Integer, Integer>) getGroups();private HashMap<String, Integer> namedGroupsIndex = new HashMap<String, Integer>();Note the spacing :)Go look at @Emz answer here, I'd highly recommend that.Hypothetical ModificationWhen you say hypothetical, you open the door to an endless world of opportunity. It all depends on what the program is designed to do. Only focus on what the program does, and how to do it better. If you have troubles on that specifically, then CR and SO will be of great help!I went ahead and fixed your replaceWithSpaces function, as you can use String.format to do what you want:/** * Provide a filler String composed of spaces, to replace part enclosed by * brackets *  * @param part *            String containing capturing group of regex, starting and *            ending with brackets, * @return String composed of spaces (' '), with length of part object, */String replaceWithSpaces(String part) {    return String.format(%1$ + part.length() + s, part);}This pads the string up to part.length to the right. Since your string is empty, it doesn't matter which direction the pad is going.Hope this helps!