QuimpToolsCollection.java

package com.github.celldynamics.quimp.utils;

import java.io.IOException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import org.apache.commons.lang3.text.WordUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.celldynamics.quimp.Outline;
import com.github.celldynamics.quimp.PropertyReader;
import com.github.celldynamics.quimp.QuimP;
import com.github.celldynamics.quimp.QuimpVersion;
import com.github.celldynamics.quimp.Vert;

/**
 * Collection of tools used across QuimP.
 * 
 * @author Richard
 * @author p.baniukiewicz
 */
public class QuimpToolsCollection {

  /**
   * The Constant LOGGER.
   */
  static final Logger LOGGER = LoggerFactory.getLogger(QuimpToolsCollection.class.getName());
  /**
   * Message returned by {@link #getQuimPBuildInfo()} if info is not found in jar.
   */
  public static final String defNote = "0.0.0";

  /**
   * Prepare info plate for QuimP.
   * 
   * @return QuimP version
   * 
   * @see #getFormattedQuimPversion(QuimpVersion)
   */
  public String getQuimPversion() {
    QuimpVersion quimpBuildInfo = getQuimPBuildInfo();
    return getFormattedQuimPversion(quimpBuildInfo);
  }

  /**
   * Prepare info plate for QuimP.
   * 
   * <p>It contains version, names, etc. By general QuimpToolsCollection class is static. These
   * methods can not be so they must be called:
   * 
   * <pre>
   * <code>LOGGER.debug(new Tool().getQuimPversion());</code>
   * </pre>
   * 
   * @param quimpBuildInfo info read from jar
   * @return Formatted string with QuimP version and authors
   * @see #getQuimPBuildInfo()
   */
  public static String getFormattedQuimPversion(QuimpVersion quimpBuildInfo) {
    //!>
    String infoPlate = "---------------------------------------------------------\n"
            + "| QuimP, by                                             |\n"
            + "| Richard Tyson (richard.tyson@warwick.ac.uk)           |\n"
            + "| Till Bretschneider (Till.Bretschneider@warwick.ac.uk) |\n"
            + "| Piotr Baniukiewicz (P.Baniukiewicz@warwick.ac.uk)     |\n"
            + "| Web page: "
            + new PropertyReader().readProperty("quimpconfig.properties", "webPage")
            + "                     |\n"
            + "---------------------------------------------------------\n";
    //!<
    infoPlate = infoPlate.concat("\n");
    infoPlate = infoPlate.concat("QuimP version: " + quimpBuildInfo.getVersion());
    infoPlate = infoPlate.concat("\n");
    infoPlate = infoPlate.concat("Build by: " + quimpBuildInfo.getBuildstamp());
    infoPlate = infoPlate.concat("\n");
    infoPlate = infoPlate.concat("Internal name: " + quimpBuildInfo.getName());
    infoPlate = infoPlate.concat("\n");
    return infoPlate;
  }

  /**
   * Get build info read from jar file.
   * 
   * @return Formatted strings with build info and version.
   */
  public QuimpVersion getQuimPBuildInfo() {
    String[] ret = new String[3];
    try {
      Enumeration<URL> resources = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF");
      // get internal name - jar name
      String iname = new PropertyReader().readProperty("quimpconfig.properties", "internalName");
      while (resources.hasMoreElements()) {
        URL reselement = resources.nextElement();
        if (!reselement.toString().contains("/" + iname)) {
          continue;
        }
        Manifest manifest = new Manifest(reselement.openStream());
        Attributes attributes = manifest.getMainAttributes();
        try {
          String date = attributes.getValue("Implementation-Date");

          ret[1] = attributes.getValue("Built-By") + " on: " + implementationDateConverter(date);
          ret[0] = attributes.getValue("Implementation-Version");
          ret[2] = attributes.getValue("Implementation-Title");
          LOGGER.trace(Arrays.toString(ret));
        } catch (Exception e) {
          ; // do not care about problems - just use defaults defined on beginning
        }
      }
    } catch (IOException e) {
      ; // do not care about problems - just use defaults defined on beginning
    }
    // replace possible nulls with default text
    ret[0] = ret[0] == null ? defNote : ret[0];
    ret[1] = ret[1] == null ? defNote : ret[1];
    ret[2] = ret[2] == null ? defNote : ret[2];
    // prepare output
    QuimpVersion retmap = new QuimpVersion(ret[0], ret[1], ret[2]);
    return retmap;
  }

  /**
   * Reformat date from jar (put there by Maven).
   * 
   * @param dateString string in format "2017-02-24T08:55:44+0000"
   * @return String in format "yyyy-MM-dd hh:mm:ss"
   */
  public static String implementationDateConverter(String dateString) {
    DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
    TemporalAccessor accessor = timeFormatter.parse(dateString);
    Date date = Date.from(Instant.from(accessor));
    SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return dt.format(date);
  }

  /**
   * S 2 d.
   *
   * @param s the s
   * @return the double
   */
  public static double s2d(String s) {
    Double d;
    try {
      d = new Double(s);
    } catch (NumberFormatException e) {
      d = null;
    }
    if (d != null) {
      return (d.doubleValue());
    } else {
      return (0.0);
    }
  }

  /**
   * Get file name without extension.
   * 
   * @param filename name of the file
   * @return file (with path) without extension
   */
  public static String removeExtension(String filename) {
    // extract fileName without extension

    int dotI = filename.lastIndexOf(".");
    if (dotI > 0 && filename.length() - dotI <= 6) { // assume extension of length max 5+dot
      filename = filename.substring(0, dotI);
    }
    return filename;
  }

  /**
   * Get file extension.
   * 
   * @param filename Name of file
   * @return extension without dot
   */
  public static String getFileExtension(String filename) {
    // extract fileName without extension

    int dotI = filename.lastIndexOf(".");
    if (dotI > 0) {
      filename = filename.substring(dotI + 1, filename.length());
    }
    return filename;
  }

  /**
   * Closest floor.
   *
   * @param o the o
   * @param target the target
   * @return the vert
   */
  public static Vert closestFloor(Outline o, double target) {
    // find the vert with coor closest (floored) to target coordinate

    Vert v = o.getHead();
    double coordA;
    double coordB;

    do {
      // coordA = v.fCoord;
      // coordB = v.getNext().fCoord;
      coordA = v.coord;
      coordB = v.getNext().coord;
      // System.out.println("A: " + coordA + ", B: "+ coordB);

      if ((coordA > coordB)) {
        if (coordA <= target && coordB + 1 > target) {
          break;
        }

        if (coordA - 1 <= target && coordB > target) {
          break;
        }

      } else {
        if (coordA <= target && coordB > target) {
          break;
        }
      }
      v = v.getNext();
    } while (!v.isHead());

    return v;
  }

  /**
   * Distance to scale.
   *
   * @param value the value
   * @param scale the scale
   * @return the double
   */
  public static double distanceToScale(double value, double scale) {
    // assums pixelwidth is in micro meters
    return value * scale;
  }

  /**
   * Distance to scale.
   *
   * @param value the value
   * @param scale the scale
   * @return the double
   */
  public static double distanceToScale(int value, double scale) {
    return value * scale;
  }

  /**
   * Area to scale.
   *
   * @param value the value
   * @param scale the scale
   * @return the double
   */
  public static double areaToScale(double value, double scale) {
    // assums pixelwidth is in micro meters
    return value * (scale * scale);
  }

  /**
   * Speed to scale.
   *
   * @param value the value
   * @param scale the scale
   * @param frameInterval the frame interval
   * @return the double
   */
  public static double speedToScale(double value, double scale, double frameInterval) {
    return (value * scale) / frameInterval;
  }

  /**
   * Speed to scale.
   *
   * @param value the value
   * @param scale the scale
   * @param frameInterval the frame interval
   * @return the double
   */
  public static double speedToScale(int value, double scale, double frameInterval) {
    return (value * scale) / frameInterval;
  }

  /**
   * Distance from scale.
   *
   * @param value the value
   * @param scale the scale
   * @return the double
   */
  public static double distanceFromScale(double value, double scale) {
    return value / scale;
  }

  /**
   * Date as string.
   *
   * @return the string
   */
  public static String dateAsString() {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    Date date = new Date();
    return formatter.format(date);
  }

  /**
   * Sets the limits equal.
   *
   * @param migLimits the mig limits
   * @return the double[]
   */
  public static double[] setLimitsEqual(double[] migLimits) { // min and max
    if (migLimits.length < 2) {
      LOGGER.warn("Array to short. Needs a min and max");
      return migLimits;
    }
    // Set limits to equal positive and negative
    if (migLimits[1] < 0) {
      migLimits[1] = -migLimits[0];
    }
    if (migLimits[0] > 0) {
      migLimits[0] = -migLimits[1];
    }

    // Make min and max equal for mig and conv
    if (migLimits[0] < -migLimits[1]) {
      migLimits[1] = -migLimits[0];
    } else {
      migLimits[0] = -migLimits[1];
    }

    return migLimits;
  }

  /**
   * Insert \n character after given number of chars trying to not break words.
   * 
   * @param in Input string
   * @param len line length
   * @return Wrapped string
   * @see <a href=
   *      "link">http://stackoverflow.com/questions/8314566/splitting-a-string-on-to-several-different-lines-in-java</a>
   */
  public static String stringWrap(String in, int len) {
    return stringWrap(in, len, "\n");
  }

  /**
   * Insert \n character after default number of chars trying to not break words.
   * 
   * @param in Input string
   * @return Wrapped string
   * @see <a href=
   *      "link">http://stackoverflow.com/questions/8314566/splitting-a-string-on-to-several-different-lines-in-java</a>
   */
  public static String stringWrap(String in) {
    return stringWrap(in, QuimP.LINE_WRAP, "\n");
  }

  /**
   * Insert any symbol after given number of chars trying to not break words.
   * 
   * <p>It preserve position of new line symbol if present in text.
   * 
   * @param in Input string
   * @param len line length
   * @param brek symbol to insert on line break
   * @return Wrapped string
   * @see <a href=
   *      "link">http://stackoverflow.com/questions/8314566/splitting-a-string-on-to-several-different-lines-in-java</a>
   */
  public static String stringWrap(String in, int len, String brek) {
    String[] sp = in.split("\n");
    String str = "";
    for (String s : sp) {
      s = s.concat("\n");
      str = str.concat(WordUtils.wrap(s, len, brek, false, "( |/|\\\\)"));
    }
    str = str.trim();
    // String str = WordUtils.wrap(in, len, brek, false, "( |/|\\\\)");
    return str;
  }

}