Java 8, how can I implement a switch statement using threads?

I have an imgui.ini text file containing:

 [Debug] Pos=7,79 Size=507,392 Collapsed=0 [ImGui Demo] Pos=320,5 Size=550,680 Collapsed=0 

There is always Pos , Size and Collapsed for each "element", and I need to read them.

I would like to use, if possible, java 8 threads.

Is it possible to simulate the behavior of a switch statement?

  try (Stream<String> stream = Files.lines(Paths.get(context.io.iniFilename))) { ... /* switch(string) { case "Pos": settings.pos = value; break; case "Size": settings.size = value; break; case "Collapsed": settings.collapsed = value; break; } */ } catch (IOException e) { } } 
+6
source share
4 answers

The best way to parse such a file (without using dedicated third-party libraries) is through the regex API and its external Scanner class. Unfortunately, the best operations for its implementation through the Stream API are currently missing. Namely, Matcher.results() and Scanner.findAll(…) do not exist yet. Therefore, if we do not want to wait for Java 9, we must create similar methods for the Java 8-compatible solution:

 public static Stream<MatchResult> findAll(Scanner s, Pattern pattern) { return StreamSupport.stream(new Spliterators.AbstractSpliterator<MatchResult>( 1000, Spliterator.ORDERED|Spliterator.NONNULL) { public boolean tryAdvance(Consumer<? super MatchResult> action) { if(s.findWithinHorizon(pattern, 0)!=null) { action.accept(s.match()); return true; } else return false; } }, false); } public static Stream<MatchResult> results(Matcher m) { return StreamSupport.stream(new Spliterators.AbstractSpliterator<MatchResult>( m.regionEnd()-m.regionStart(), Spliterator.ORDERED|Spliterator.NONNULL) { public boolean tryAdvance(Consumer<? super MatchResult> action) { if(m.find()) { action.accept(m.toMatchResult()); return true; } else return false; } }, false); } 

Using methods with similar semantics allows you to replace their use with standard API methods as soon as Java 9 is released and becomes commonplace.

Using these two operations, you can parse your file using

 Pattern groupPattern=Pattern.compile("\\[(.*?)\\]([^\\[]*)"); Pattern attrPattern=Pattern.compile("(.*?)=(.*)\\v"); Map<String, Map<String, String>> m; try(Scanner s=new Scanner(Paths.get(context.io.iniFilename))) { m = findAll(s, groupPattern).collect(Collectors.toMap( gm -> gm.group(1), gm -> results(attrPattern.matcher(gm.group(2))) .collect(Collectors.toMap(am->am.group(1), am->am.group(2))))); } 

the resulting map m contains all the information, matching from group names to another map containing key / value pairs, i.e. you can print the equivalent .ini file using:

 m.forEach((group,attr)-> { System.out.println("["+group+"]"); attr.forEach((key,value)->System.out.println(key+"="+value)); }); 
+6
source

Attempt:

 try { Path file = Paths.get("G:\\tmp", "img.ini"); Stream<String> lines = Files.lines(file); lines.filter(line->{ if("pos".equalsIgnoreCase(line.split("=")[0])){ //process pos line here System.out.println("pos"+line); return false; } return true; }).filter(line->{ System.out.println("2"+line); if("Collapsed".equalsIgnoreCase(line.split("=")[0])){ //process Collapsed line here System.out.println("Collapsed"+line); return false; } return true; }).filter(line->{ System.out.println("3"+line); if("Size".equalsIgnoreCase(line.split("=")[0])){ //process Size line here System.out.println("Size"+line); return false; } return true; }).forEach(line->{ //settings = new Settings(); });; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } 
+4
source

By focusing on the question “is there a way to simulate the behavior of a switch statement”, I think the answer is that you could with minimal effort. I asked myself that a couple of years ago, and did the following as an exercise (and then never used it again):

 private static <T> Predicate<T> testAndConsume(Predicate<T> pred, Consumer<T> cons) { return t -> { boolean result = pred.test(t); if (result) cons.accept(t); return result; }; } public static class SwitchConsumer<T> { Predicate<T> conditionalConsumer; private SwitchConsumer(Predicate<T> pred) { conditionalConsumer = pred; } public static <C> SwitchConsumer<C> inCase(Predicate<C> pred, Consumer<C> cons) { return new SwitchConsumer<>(testAndConsume(pred, cons)); } public SwitchConsumer<T> elseIf(Predicate<T> pred, Consumer<T> cons) { return new SwitchConsumer<>(conditionalConsumer.or(testAndConsume(pred,cons))); } public Consumer<T> elseDefault(Consumer<T> cons) { return testAndConsume(conditionalConsumer.negate(),cons)::test; // ::test converts Predicate to Consumer } } 

testAndConsume composes Predicate and a Consumer , creating a Predicate that returns the same value but calls Consumer as a side effect if the value is true. This becomes the basis for each “case” in the “switch”. Each “case” is bound together by Predicate.or() , which provides a short-circuit of the “else-if” nature of the switch. Finally, the compiled Predicate turns into a Consumer , adding ::test to the Predicate .

Applying it to a piece of code, it looks like this:

  Stream.of("Pos=320,5", "Size=550,680", "Collapsed=0") .map(s -> s.split("=")) .forEach(SwitchConsumer.<String[]> inCase(arr -> "Pos".equals(arr[0]), arr -> settings.pos = arr[1]) .elseIf(arr -> "Size".equals(arr[0]), arr -> settings.size = arr[1]) .elseIf(arr -> "Collapsed".equals(arr[0]), arr -> settings.collapsed = arr[1]) .elseDefault(arr -> {})); 

This is something like switch-ish, as it can get without actually switching in the Consumer body.

+3
source

Another way to read your configuration file:

 public class Main { public static void main(String[] args) throws IOException { Path path = Paths.get("D:\\Development\\workspace\\Application\\src\\main\\resources\\init.txt"); String content = new String(Files.readAllBytes(path)); Map<String, Config> configMap = Stream.of(content.split("\\n\\r")) .map(config -> Arrays.asList(config.split("\\r"))) .collect(HashMap<String, Config>::new, (map, list) -> { String header = list.get(0); String pos = list.get(1); String size = list.get(2); String collapsed = list.get(3); map.put(header, new Config(pos.substring(pos.indexOf("=") + 1), size.substring(size.indexOf("=") + 1), collapsed.substring(collapsed.indexOf("=") + 1))); }, (m, u) -> {}); System.out.println(configMap); } } class Config { public String pos; public String size; public String collapsed; public Config(String pos, String size, String collapsed) { this.pos = pos; this.size = size; this.collapsed = collapsed; } @Override public String toString() { return "Config{" + "pos='" + pos + '\'' + ", size='" + size + '\'' + ", collapsed='" + collapsed + '\'' + '}'; } } 

The result will be displayed:

 { [Debug]=Config{pos='7,79', size='507,392', collapsed='0'}, [ImGui Demo]=Config{pos='320,5', size='550,680', collapsed='0'} } 
+1
source

Source: https://habr.com/ru/post/1269201/


All Articles