package org.petitparser.parser.primitive;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * Internal class to build a {@link CharacterPredicate} from single characters or ranges of
 * characters.
 */
class CharacterRange {

  static CharacterPredicate toCharacterPredicate(List<CharacterRange> ranges) {

    // 1. sort the ranges
    List<CharacterRange> sortedRanges = new ArrayList<>(ranges);
    sortedRanges.sort(new Comparator<CharacterRange>() {
      @Override
      public int compare(CharacterRange first, CharacterRange second) {
        return Character.compare(first.start, second.start);
      }
    });

    // 2. merge adjacent or overlapping ranges
    List<CharacterRange> mergedRanges = new ArrayList<>();
    for (CharacterRange currentRange : sortedRanges) {
      if (mergedRanges.isEmpty()) {
        mergedRanges.add(currentRange);
      } else {
        CharacterRange lastRange = mergedRanges.get(mergedRanges.size() - 1);
        if (lastRange.stop + 1 >= currentRange.start) {
          mergedRanges.set(mergedRanges.size() - 1, new CharacterRange(
              lastRange.start < currentRange.start ? lastRange.start : currentRange.start,
              lastRange.stop > currentRange.stop ? lastRange.stop : currentRange.stop));
        } else {
          mergedRanges.add(currentRange);
        }
      }
    }

    // 3. build the best resulting predicates
    if (mergedRanges.isEmpty()) {
      return CharacterPredicate.none();
    } else if (mergedRanges.size() == 1) {
      return mergedRanges.get(0).start == mergedRanges.get(0).stop
          ? CharacterPredicate.of(mergedRanges.get(0).start)
          : CharacterPredicate.range(mergedRanges.get(0).start, mergedRanges.get(0).stop);
    } else {
      char[] starts = new char[mergedRanges.size()];
      char[] stops = new char[mergedRanges.size()];
      for (int i = 0; i < mergedRanges.size(); i++) {
        starts[i] = mergedRanges.get(i).start;
        stops[i] = mergedRanges.get(i).stop;
      }
      return CharacterPredicate.ranges(starts, stops);
    }

  }

  private final char start;
  private final char stop;

  CharacterRange(char start, char stop) {
    this.start = start;
    this.stop = stop;
  }
}

@@ -149,16 +149,27 @@
  build a
+n optimized
  %7B@link 
@@ -210,16 +210,19 @@
 aracters
+%0A *
  or rang
@@ -226,19 +226,16 @@
 anges of
-%0A *
  charact
@@ -461,20 +461,16 @@
 es.sort(
-new 
 Comparat
@@ -475,69 +475,28 @@
 ator
-%3CCharacterRange%3E() %7B%0A      @Override%0A      public int 
+%0A        .
 compar
-e
+ing(
 (Cha
@@ -511,114 +511,91 @@
 nge 
-first, CharacterRange second) %7B%0A        return Character.compare(first.start, second.start);%0A      %7D%0A    %7D
+range) -%3E range.start)%0A        .thenComparing((CharacterRange range) -%3E range.stop)
 );%0A%0A
@@ -724,23 +724,20 @@
 erRange 
-current
+this
 Range : 
@@ -805,39 +805,36 @@
 ergedRanges.add(
-current
+this
 Range);%0A      %7D 
@@ -952,23 +952,20 @@
  + 1 %3E= 
-current
+this
 Range.st
@@ -985,123 +985,59 @@
     
-mergedRanges.set(mergedRanges.size() - 1, new CharacterRange(%0A              lastRange.start %3C currentRange.start ? 
+CharacterRange characterRange = new CharacterRange(
 last
@@ -1047,34 +1047,30 @@
 ge.start
- : current
+, this
 Range.st
 art,%0A   
@@ -1061,20 +1061,20 @@
 Range.st
-art,
+op);
 %0A       
@@ -1080,84 +1080,64 @@
     
-    lastRange.stop %3E current
+merged
 Range
+s
 .s
-top ? last
+et(merged
 Range
+s
 .s
-top : currentRange.stop)
+ize() - 1, characterRange
 );%0A 
@@ -1183,15 +1183,12 @@
 add(
-current
+this
 Rang
@@ -1389,63 +1389,112 @@
     
-return mergedRanges.get(0).start == mergedRanges.get(0)
+CharacterRange characterRange = mergedRanges.get(0);%0A      return characterRange.start == characterRange
 .sto
@@ -1529,35 +1529,30 @@
 cate.of(
-mergedRanges.get(0)
+characterRange
 .start)%0A
@@ -1592,54 +1592,44 @@
 nge(
-mergedRanges.get(0).start, mergedRanges.get(0)
+characterRange.start, characterRange
 .sto

Simplify some of the character predicate code.