views:

80

answers:

5

I am trying to create a simple regex kind of thing for my own use where a character in a string can either be an exact match or one in among a set of characters belonging to a group (e.g., Group CAPS will have all uppercase characters - 'A', 'B', etc.). Therefore a regex such as - CAPS p p l e - should match 'Apple' or a regex such as - DIGIT DIGIT . DIGIT - should match 76.3.

I am trying to store this regex pattern in an array {CAPS, 'p', 'p', 'l', 'e'}. Now, Java will allow me to declare an array of type char[]/Character[] or Group[] , where Group is a class I have constructed that represents such groups of characters. If I need to have a hybrid array to be able to store above mentioned patterns, what other options do I have other than declaring an array of type Object[] ?

I really don't want to be dealing with an array of type Object[].

+3  A: 

You could define a common superclass (interface), with subclasses for both normal characters and character classes/groups.

Péter Török
The caveat is that if one of the types you put into the array is a primitive type, you have to use the corresponding wrapper type to make the typing work. And that probably means that the common superclass (i.e. the array element type) is `Object`, and that you need to use type casts and `instanceof` to use the elements. (@Peter - feel free to incorporate this in your answer)
Stephen C
@Stephen, my idea was to simply define a class which implements the specific interface and contains the primitive value as a member. Obviously, I was not clear enough about this in my answer - thanks for pointing this out.
Péter Török
A: 

You can't define an array with elements from one of two unrelated types.

You can define your own supertype (or interface) from which you extend two classes, one that wraps a character, the other your Group.

Or, perhaps easier, just uses a Object[] and wrap that with a class that has the intelligence to put/get the correct types.

leonbloy
+1  A: 

If you want a hybrid array or collection, you are going to need to abstract char/Character and Group behind a common interface or supertype. Given that char is a primitive and Character is a final class, writing an Interface and wrapping char/Character is going to be your best bet.

I'd suggest making Group an Interface (possibly renaming it) and then create two classes that implement Group - one that wraps a char or Character (let's call it, SingleChar), and a concrete Group (let's call it GroupImpl, for now) that serves the purpose of the original group. Create an array of Group[] and manipulate each instance of Group through your common interface.

whaley
A: 

char[] is untyped and maybe not the best thing to interact with.

Group[] is not really very descriptive of what it does.

Perhaps you could have an interface with the method "match" and stick a bunch of objects that implement this interface in there.

So one of these objects might be instantiated "new MatchChar('a')" while another might be "new MatchType(MatchType.CAPS)".

The problem with this is that it's not very easy to instantiate--it's verbose. I would use a method (Say inside "Match" which could be a superclass of MatchType and MatchChar) like getMatches that would take a string and return your array, completely generated.

I like that your "Language" is generally more explicit than Regex, so what about something like this: "[DIGIT][DIGIT].[DIGIT]" or "[CAPS]pple"--plus the obligatory "[[]" and "[]]" to match braces.

Then your method just looks for matching [] in the string, creates the correct MatchGroup objects, turns the remaining letters into MatchChar objects and returns them to the caller.

Parsing a string like this is pretty easy, in fact StringTokenizer would have a ball with it.

Bill K
A: 

You can employ the Composite design pattern to model this situation. This pattern is useful where you need to treat an object type as well as the collection of the objects of that type in the same way. You can also use Chain of Responsibility pattern along with it.

Basically you would define classes like this (lets say that you need a matches() method):

class Pattern {
    IPatternComponent[] pattern;
    boolean matches(String str) {
        for(int i = 0; i < str.length; i++) {
            if(!pattern[i].matches(str.charAt(i)))
                return false;
        }
        return true;
    }
}

interface IPatternComponent {
    boolean matches(char ch);
}

class Simple implements IPatternComponent {
    char ch;
    boolean matches(char ch) {
        return (this.ch == ch);
    }
}

class Group implements IPatternComponent {
    Set<IPatternComponent> components;
    boolean matches(char charToMatch) {
        for(IPatternComponent comp : components) {
            if(comp.matches(charToMatch)
                return true;
        }
        return false;
    }
}

Taking the DIGIT DIGIT . DIGIT example:

  • The whole pattern will be represented by an instance of Pattern class.
  • The instance will store the individual parts in its internal array- {DIGIT, DIGIT, ., DIGIT}.
  • . will be an instance of Simple.
  • DIGIT will be an instance of Group, which will store 1,2,3,4,5,6,7,8,9,0 in its internal set
  • Each element of the set will be represented by an instance of Simple.
  • matches() will work using chain of responsibility - delegating the responsibility down the chain.

Using this design, you can also easily define ALPHANUM as a Group whose internal set comprises of ALPHABETS and DIGIT and matches() will still work.

Note: I coded the code here directly, so it may have typos or small errors. I omitted the constructors for better readability.

Samit G.
thanks very much.
euphoria83