Utils.java

package com.github.dakusui.symfonion.utils;

import com.github.dakusui.symfonion.exceptions.SymfonionException;
import com.github.dakusui.valid8j_pcond.forms.Printables;

import java.io.*;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.stream.Collector;

import static com.github.dakusui.symfonion.exceptions.ExceptionThrower.*;
import static com.github.dakusui.valid8j.Requires.require;
import static com.github.dakusui.valid8j_pcond.forms.Predicates.isNotNull;
import static java.nio.charset.StandardCharsets.UTF_8;


public enum Utils {
  ;
  public static final java.util.regex.Pattern lengthPattern = java.util.regex.Pattern.compile("([1-9][0-9]*)(\\.*)");

  /**
   * Count occurrences of a given character {@code ch} in a string {@code s}.
   *
   * @param ch A character to count its occurrences.
   * @param s A string in which the number of {@code ch} should be counted.
   * @return The number of occurrences of {@code ch} in string {@code s}.
   */
  public static int count(char ch, String s) {
    int ret = 0;
    for (int i = s.indexOf(ch); i >= 0; i = s.indexOf(ch, i + 1)) {
      ret++;
    }
    return ret;
  }

  public static String loadResource(String resourceName) throws SymfonionException {
    StringBuffer b = new StringBuffer(4096);
    try {
      InputStream is = new BufferedInputStream(require(ClassLoader.getSystemResourceAsStream(resourceName), resourceIsNotNull(resourceName)));
      loadFromInputStream(b, is);
    } catch (IOException e) {
      throw loadResourceException(resourceName, e);
    }
    return b.toString();
  }

  public static String loadFile(String fileName) throws SymfonionException {
    StringBuffer b = new StringBuffer(4096);
    File f = new File(fileName);
    try (InputStream is = new BufferedInputStream(new FileInputStream(f))){
      loadFromInputStream(b, is);
    } catch (FileNotFoundException e) {
      throw fileNotFoundException(f, e);
    } catch (IOException e) {
      throw loadFileException(e);
    }
    return b.toString();
  }

  private static void loadFromInputStream(StringBuffer b, InputStream is) throws IOException {
    Reader r = new InputStreamReader(is, UTF_8);
    int c;
    while ((c = r.read()) != -1) {
      b.append((char) c);
    }
  }

  public static Fraction parseNoteLength(String length) {
    Matcher m = lengthPattern.matcher(length);
    Fraction ret = null;
    if (m.matches()) {
      int l = Integer.parseInt(m.group(1));
      ret = new Fraction(1, l);
      int dots = Utils.count('.', m.group(2));
      for (int i = 0; i < dots; i++) {
        l *= 2;
        ret = Fraction.add(ret, new Fraction(1, l));
      }
    } else if (!"0".equals(length)) {
    } else {
      ret = new Fraction(0, 1);
    }
    return ret;
  }

  public static byte[] getIntBytes(int input) {
    byte[] ret = new byte[3];

    ret[0] = (byte) (input >> 16 & 0xff);
    ret[1] = (byte) (input >> 8 & 0xff);
    ret[2] = (byte) (input & 0xff);

    return ret;
  }

  /**
   * This method was copied from <a href="https://stackoverflow.com/questions/22694884/filter-java-stream-to-1-and-only-1-element/22695424#22695424">stackoverflow.com</a> and renamed.
   *
   * @param <E> Type of the element to be collected.
   * @return A collector
   */
  public static <E> Collector<E, ?, Optional<E>> onlyElement() {
    return onlyElement((e1, e2) -> {
      throw new IllegalArgumentException("Multiple values are found in the stream: <" + e1 + "> and <" + e2 + ">");
    });
  }


  public static <E> Collector<E, AtomicReference<E>, Optional<E>> onlyElement(BiFunction<E, E, ? extends RuntimeException> multipleElements) {
    return Collector.of(
        AtomicReference::new,
        (ref, e) -> {
          if (!ref.compareAndSet(null, e)) {
            throw multipleElements.apply(ref.get(), e);
          }
        },
        (ref1, ref2) -> {
          if (ref1.get() == null) {
            return ref2;
          } else if (ref2.get() != null) {
            throw multipleElements.apply(ref1.get(), ref2.get());
          } else {
            return ref1;
          }
        },
        ref -> Optional.ofNullable(ref.get()),
        Collector.Characteristics.UNORDERED);
  }

  private static Predicate<InputStream> resourceIsNotNull(String resourceName) {
    return Printables.predicate(() -> "isNotNull[resourceLoadedFrom[" + resourceName + "]]", isNotNull());
  }

}