Možná si někteří z vás ještě vzpomenou, jak jsem kdysi psal o “mikro-knihovně” Re. Čas i myšlenky trochu uzrály, a tak tu máme nástupce, který jde ještě o krok dál. Mám zároveň radost, protože je to má první knihovna, která je dostupná i přímo z centrálního Maven repozitáře! Pojďme mrknout na to, co umí …
Úvod
Ještě než se do toho pustíme, zdrojové kódy Rojo najdete na githubu. Je vydána pod licencí GNU/GPL verze 3.0, takže návrhy či osobní snahy na zlepšení jsou vítány :-). Nažhavte tedy svá IDE, do POMu šoupněte tuhle dependency a můžeme začít.
<dependency> <groupId>com.svetylkovo</groupId> <artifactId>rojo</artifactId> <version>1.0.0</version> </dependency>
Letem světem
Ať vás nezatěžuji přílišným povídáním, nechme mluvit hlavně kód :-). Začněme definicí našeho beanu:
@Regex("(\\w+):(\\d+)") public class SimpleBean { @Group(1) private String name; @Group(2) private int count; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } }
Nad hlavičkou třídy anotace @Regex, pro určení samotného regulárního výrazu a pak @Group pro každý field třídy, aby bylo jasné, která grupa se bude mapovat na který field. Rojo zároveň umí automaticky mapovat naparsovaný String z dané grupy na cílový typ fieldu. Seznam podporovaných typů najdete v README na githubu. Dále může mít třída anotaci @Flags, pokud chcete používat flagy (ze třídy Pattern) a fieldy typu Date musí mít navíc anotaci @DateFormat, která určí, v jakém formátu datum chceme.
Nyní samotné matchování:
Optional<SimpleBean> bean = Rojo.of(SimpleBean.class) .match("a:1,b:2,c:3"); List<SimpleBean> list = Rojo.of(SimpleBean.class) .matchList("a:1,b:2,c:3"); Iterator<SimpleBean> iter = Rojo.of(SimpleBean.class) .matchIterator("a:1,b:2,c:3"); Stream<SimpleBean> stream = Rojo.of(SimpleBean.class) .matchStream("a:1,b:2,c:3");
Pokud chcete matchovat bez použití POJO objektů, pak Rojo po svém předchůdci Re převzal vše + přidal i něco navíc:
Optional<String> string = Rojo.find("[a-z]:2", "a:1,b:2,c:3"); Optional<Matcher> matcher = Rojo.findMatcher("[a-z]:2", "a:1,b:2,c:3"); List<String> list = Rojo.asList("[a-z]", "a:1,b:2,c:3"); List<Matcher> matcherList = Rojo.asMatcherList("[a-z]", "a:1,b:2,c:3"); Stream<String> stream = Rojo.asStream("[a-z]", "a:1,b:2,c:3"); Stream<Matcher> matcherStream = Rojo.asMatcherStream("[a-z]", "a:1,b:2,c:3"); Map<String, String> map = Rojo.asMap("([a-z]):(\\d)", "a:1,b:2,c:3"); String replaced = Rojo.replace("[a-z]", "a:1,b:2,c:3", String::toUpperCase); String replaced2 = Rojo.replaceMatcher("[a-z]", "a:1,b:2,c:3", m -> m.group().toUpperCase());
Zajímavou featurou je metoda asMap(), která v regulárním výrazu očekává právě 2 grupy, které vrátí jako key-value pár v podobě mapy. Sice tohle využijete zřídkakdy, ale někdy by se to hodit mohlo :-).
Optimalizace výkonu
Jelikož každá ze statických metod volaných přímo na třídě Rojo uvnitř vždy vytváří novou instanci třídy Pattern, při mnohačetném volání by mohlo dojít ke zvýšenému využití paměti a snížení celkového výkonu. Pro tyto případy je dobré si daný matcher uložit do proměnné a pak ho znovu-použít:
RojoBeanMatcher<SimpleBean> beanMatcher = Rojo.of(SimpleBean.class); SimpleBean bean = beanMatcher.match("a:1,b:2,c:3").get(); RojoMatcher matcher = Rojo.matcher("[a-z]"); List<String> list = matcher.asList("a:1,b:2,c:3");
Závěr
Nenapadá mě, co dodat :-). Snad jen, co byste dodali vy?