Java tutorial
/** * Copyright 5AM Solutions Inc, ESAC, ScenPro & SAIC * * Distributed under the OSI-approved BSD 3-Clause License. * See http://ncip.github.com/caintegrator/LICENSE.txt for details. */ package gov.nih.nci.caintegrator.common; import gov.nih.nci.cagrid.common.ZipUtilities; import gov.nih.nci.caintegrator.application.analysis.InvalidSurvivalValueDefinitionException; import gov.nih.nci.caintegrator.application.study.AbstractClinicalSourceConfiguration; import gov.nih.nci.caintegrator.application.study.AnnotationTypeEnum; import gov.nih.nci.caintegrator.application.study.Status; import gov.nih.nci.caintegrator.application.study.StudyConfiguration; import gov.nih.nci.caintegrator.domain.analysis.GisticAnalysis; import gov.nih.nci.caintegrator.domain.annotation.SurvivalValueDefinition; import gov.nih.nci.caintegrator.domain.annotation.SurvivalValueTypeEnum; import gov.nih.nci.caintegrator.domain.application.TimeStampable; import gov.nih.nci.caintegrator.domain.genomic.AbstractReporter; import gov.nih.nci.caintegrator.domain.genomic.AmplificationTypeEnum; import gov.nih.nci.caintegrator.domain.genomic.Gene; import gov.nih.nci.caintegrator.domain.genomic.GisticGenomicRegionReporter; import java.awt.Color; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import au.com.bytecode.opencsv.CSVReader; /** * This is a static utility class used by different caIntegrator2 objects. */ public final class Cai2Util { private static final Integer BUFFER_SIZE = 4096; private static final String ZIP_FILE_SUFFIX = ".zip"; private static final double COLOR_SATURATION = 0.89; private static final double COLOR_BRIGHTNESS = 0.89; private static final double GOLDEN_ANGLE = 0.381966; private static final double MAX_ANGLE_CONSTANT = 360; private static final Set<Color> COLOR_PALETTE = new HashSet<Color>(); private static final double NATURAL_LOG_OF_2 = Math.log(2); private static final int MAX_DESCRIPTION_LENGTH = 255; private static final int ONE_MILLION = 1000000; private static final Logger LOGGER = Logger.getLogger(Cai2Util.class); /** * Max description length for lists. */ public static final int MAX_LIST_DESCRIPTION_LENGTH = 200; private Cai2Util() { } /** * @param statusDescription the string to trim * @return trimmed string if too long, or full string if it isn't too long. */ public static String trimDescription(String statusDescription) { return StringUtils.abbreviate(statusDescription, MAX_DESCRIPTION_LENGTH); } /** * Used to see if a Collection of strings contains a string, ignoring case. * @param l - collection of strings. * @param s - string item to see if exists in the collection. * @return true/false value. */ public static boolean containsIgnoreCase(Collection<String> l, String s) { Iterator<String> it = l.iterator(); while (it.hasNext()) { if (it.next().equalsIgnoreCase(s)) { return true; } } return false; } /** * Takes in a directory and zips it up. * * @param dir - Directory to zip. * @return Zipped file, stored in same location, with same name as the directory with .zip attached. * @throws IOException - In case cannot be read. */ public static File zipAndDeleteDirectory(String dir) throws IOException { File directory = new File(dir); if (!directory.isDirectory()) { throw new IllegalArgumentException("Not a directory: " + dir); } int index = directory.getPath().indexOf(directory.getName()); String[] entries = directory.list(); if (entries.length == 0) { return null; } File zipfile = new File(dir + ZIP_FILE_SUFFIX); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipfile)); addDir(directory, out, index); out.close(); FileUtils.deleteDirectory(directory); return zipfile; } /** * Adds files to an already created zip file. * @param sourceZipfile to add files to. * @param files to add to zip file. * @return the new zip file. * @throws IOException if unable to add to zip file. */ public static File addFilesToZipFile(File sourceZipfile, File... files) throws IOException { if (!sourceZipfile.getAbsolutePath().endsWith(ZIP_FILE_SUFFIX)) { throw new IllegalArgumentException("The zipfile isn't a .zip type."); } for (File file : files) { LOGGER.info("File name is " + file.getName()); ZipUtilities.insertEntry(sourceZipfile, file.getName(), IOUtils.toByteArray(new FileInputStream(file))); } return sourceZipfile; } private static void addDir(File dirObj, ZipOutputStream out, int index) throws IOException { File[] files = dirObj.listFiles(); for (int i = 0; i < files.length; i++) { File curFile = files[i]; if (curFile.isDirectory()) { addDir(curFile, out, index); continue; } addFile(curFile, out, index); } } private static void addFile(File curFile, ZipOutputStream out, int index) throws IOException { byte[] tmpBuf = new byte[BUFFER_SIZE]; FileInputStream in = new FileInputStream(curFile); String relativePathName = curFile.getPath().substring(index); out.putNextEntry(new ZipEntry(relativePathName)); int len; while ((len = in.read(tmpBuf)) > 0) { out.write(tmpBuf, 0, len); } // Complete the entry out.closeEntry(); in.close(); } /** * Validate a ZIP file. * @param file the zip file to be validated. * @return boolean if valid or not. */ public static boolean isValidZipFile(final File file) { ZipFile zipfile = null; try { zipfile = new ZipFile(file); ZipFile zipIn2 = new ZipFile(file); zipIn2.close(); LOGGER.info("zipFile validated = " + file.getAbsolutePath()); return true; } catch (ZipException e) { return false; } catch (IOException e) { return false; } catch (Exception e) { return false; } finally { try { if (zipfile != null) { zipfile.close(); zipfile = null; } } catch (Exception e) { LOGGER.info("Exception in isValidZipFile = " + e); } } } /** * Extract the host name from the url. * @param url the url * @return the host name */ public static String getHostNameFromUrl(String url) { if (StringUtils.isEmpty(url)) { return null; } else { try { return url.split("/")[2]; } catch (Exception e) { return null; // Error parsing url. } } } /** * Used by classes to create a palette of unique colors. * @param totalNumberOfUniqueColors - total number of unique colors to be created in the palette. */ public static void setColorPalette(int totalNumberOfUniqueColors) { for (int i = 0; i < MAX_ANGLE_CONSTANT; i += MAX_ANGLE_CONSTANT / totalNumberOfUniqueColors) { double colorNumberAsAngle = i * GOLDEN_ANGLE; double hue = colorNumberAsAngle - Math.floor(colorNumberAsAngle); COLOR_PALETTE.add(Color.getHSBColor((float) hue, (float) COLOR_SATURATION, (float) COLOR_BRIGHTNESS)); } } /** * Used by classes to retrieve a color based on a number (0-10) uses a basic * ten color palette. Greater than 9 uses an unlimited color palette. * @param colorNumber - a number which selects the color in the palette. * @return - Color object for that number. */ public static Color getColor(int colorNumber) { Color colorToBeReturned; if (colorNumber > 10) { colorToBeReturned = getUnlimitedColor(colorNumber); } else { colorToBeReturned = getBasicColor(colorNumber); } return colorToBeReturned; } /** * Used by classes to retrieve a color based on a number from an unlimited number of colors. * @param colorNumber - number to use. * @return - Color object for that number. */ public static Color getUnlimitedColor(int colorNumber) { int maxColorsInPalette = COLOR_PALETTE.size(); Color[] colorsArray = COLOR_PALETTE.toArray(new Color[maxColorsInPalette]); Color colorToBeReturned; if (colorNumber < 1 || colorsArray.length == 0) { colorToBeReturned = Color.BLACK; } else if (colorNumber > maxColorsInPalette) { colorToBeReturned = Color.BLACK; } else { colorToBeReturned = colorsArray[colorNumber - 1]; } return colorToBeReturned; } /** * Used by classes to retrieve a color based on a number from a ten color palette. * (1-10) are colors and anything else returns black. * @param colorNumber - number to use. * @return - Color object for that number. */ public static Color getBasicColor(int colorNumber) { switch (colorNumber) { case 1: return Color.GREEN; case 2: return Color.BLUE; case 3: return Color.RED; case 4: return Color.CYAN; case 5: return Color.DARK_GRAY; case 6: return Color.YELLOW; case 7: return Color.LIGHT_GRAY; case 8: return Color.MAGENTA; case 9: return Color.ORANGE; case 10: return Color.PINK; default: return Color.BLACK; } } /** * Turns a comma separated list of strings into a List of strings. * @param commaDelimitedString comma separated list of strings. * @return List of strings. */ public static List<String> createListFromCommaDelimitedString(String commaDelimitedString) { return StringUtils.isBlank(commaDelimitedString) ? new ArrayList<String>() : Arrays.asList(commaDelimitedString.replaceAll(" ", "").split(",")); } /** * Validates the survival value definition, and throws an exception if it is invalid. * @param survivalValueDefinition to validate. * @throws InvalidSurvivalValueDefinitionException if the definition is invalid. */ public static void validateSurvivalValueDefinition(SurvivalValueDefinition survivalValueDefinition) throws InvalidSurvivalValueDefinitionException { if (SurvivalValueTypeEnum.DATE.equals(survivalValueDefinition.getSurvivalValueType())) { validateDateTypeSurvivalValueDefinition(survivalValueDefinition); } else if (SurvivalValueTypeEnum.LENGTH_OF_TIME.equals(survivalValueDefinition.getSurvivalValueType())) { validateLengthTypeSurvivalValueDefinition(survivalValueDefinition); } } private static void validateLengthTypeSurvivalValueDefinition(SurvivalValueDefinition survivalValueDefinition) throws InvalidSurvivalValueDefinitionException { if (survivalValueDefinition.getSurvivalLength() == null) { throw new InvalidSurvivalValueDefinitionException( "Must have a Survival Length defined for definition."); } if (survivalValueDefinition.getSurvivalStatus() != null && StringUtils.isBlank(survivalValueDefinition.getValueForCensored())) { throw new InvalidSurvivalValueDefinitionException("'Value for Censored' cannot be blank."); } } private static void validateDateTypeSurvivalValueDefinition(SurvivalValueDefinition survivalValueDefinition) throws InvalidSurvivalValueDefinitionException { if (survivalValueDefinition.getSurvivalStartDate() == null || survivalValueDefinition.getDeathDate() == null || survivalValueDefinition.getLastFollowupDate() == null) { throw new InvalidSurvivalValueDefinitionException( "Must have a Start Date, Death Date, and Last Followup " + " Date defined for definition '" + survivalValueDefinition.getName() + "'."); } if (survivalValueDefinition.getSurvivalStartDate() == survivalValueDefinition.getDeathDate() || survivalValueDefinition.getSurvivalStartDate() == survivalValueDefinition.getLastFollowupDate() || survivalValueDefinition.getLastFollowupDate() == survivalValueDefinition.getDeathDate()) { throw new InvalidSurvivalValueDefinitionException("Start Date, Death Date, and Last Followup " + " Date must be unique for definition '" + survivalValueDefinition.getName() + "'."); } if (!AnnotationTypeEnum.DATE.equals(survivalValueDefinition.getSurvivalStartDate().getDataType()) || !AnnotationTypeEnum.DATE.equals(survivalValueDefinition.getDeathDate().getDataType()) || !AnnotationTypeEnum.DATE.equals(survivalValueDefinition.getLastFollowupDate().getDataType())) { throw new InvalidSurvivalValueDefinitionException("Start Date, Death Date, and Last Followup " + " Date must all be a 'DATE' type for definition '" + survivalValueDefinition.getName() + "'."); } } /** * Validates the given SurvivalvalueDefinitions and only returns valid ones. * @param survivalValueDefinitions to validate. * @return valid survival value definitions. */ public static Set<SurvivalValueDefinition> retrieveValidSurvivalValueDefinitions( Collection<SurvivalValueDefinition> survivalValueDefinitions) { Set<SurvivalValueDefinition> validDefinitions = new HashSet<SurvivalValueDefinition>(); for (SurvivalValueDefinition survivalValueDefinition : survivalValueDefinitions) { try { Cai2Util.validateSurvivalValueDefinition(survivalValueDefinition); } catch (InvalidSurvivalValueDefinitionException e) { continue; } validDefinitions.add(survivalValueDefinition); } return validDefinitions; } /** * Compute log2 of a given value. * @param value to do log2 * @return log2 of the input value */ public static double log2(double value) { return Math.log(value) / NATURAL_LOG_OF_2; } /** * Compute the anti-log2 of a given value. * @param value to do anti-log2 * @return anti-log2 of the input value */ public static double antiLog2(double value) { return Math.pow(2, value); } /** * Retrieves the last modified date (in String format) of the list of timestamped objects. * @param timeStampedObjects list of timestamped objects to retrieve latest date from. * @return last modified date of latest timestamped object (in string format). */ public static String retrieveLatestLastModifiedDate(List<TimeStampable> timeStampedObjects) { TimeStampable latestTimestamp = null; for (TimeStampable currentTimetStampedObject : timeStampedObjects) { if (latestTimestamp == null) { latestTimestamp = currentTimetStampedObject; continue; } if (currentTimetStampedObject.getDisplayableLastModifiedDate() != null && (latestTimestamp.getLastModifiedDate() == null || currentTimetStampedObject .getLastModifiedDate().compareTo(latestTimestamp.getLastModifiedDate()) > 0)) { latestTimestamp = currentTimetStampedObject; } } return latestTimestamp == null ? DateUtil.TIMESTAMP_UNAVAILABLE_STRING : latestTimestamp.getDisplayableLastModifiedDate(); } /** * The editable-select for internet explorer submits an extra URL after comma. * ex: "http://url, http://url" instead of just "http://url". * @param originalUrl url to convert. * @return fixed URL. */ public static String fixUrlForEditableSelect(String originalUrl) { return Pattern.compile(",\\s.*").matcher(originalUrl).replaceAll(""); } /** * Fills the amplifiedGenes and deletedGenes with the amplified and deleted genes associated * with the gisticAnalysis. * @param gisticAnalysis to get genes for. * @param amplifiedGenes amplified genes associated with gisticAnalysis. * @param deletedGenes deleted genes associated with gisticAnalysis. */ public static void retrieveGisticAmplifiedDeletedGenes(GisticAnalysis gisticAnalysis, List<Gene> amplifiedGenes, List<Gene> deletedGenes) { if (gisticAnalysis.getReporterList() != null) { for (AbstractReporter reporter : gisticAnalysis.getReporterList().getReporters()) { if (reporter instanceof GisticGenomicRegionReporter) { GisticGenomicRegionReporter gisticReporter = (GisticGenomicRegionReporter) reporter; if (AmplificationTypeEnum.AMPLIFIED.equals(gisticReporter.getGeneAmplificationType())) { amplifiedGenes.addAll(gisticReporter.getGenes()); } else if (AmplificationTypeEnum.DELETED.equals(gisticReporter.getGeneAmplificationType())) { deletedGenes.addAll(gisticReporter.getGenes()); } } } Collections.sort(amplifiedGenes); Collections.sort(deletedGenes); } } /** * @param chromosomeNumber to convert to 1-24 range * @return internal value of chromosome number. */ public static String getInternalChromosomeNumber(String chromosomeNumber) { if ("X".equalsIgnoreCase(chromosomeNumber)) { return "23"; } else if ("Y".equalsIgnoreCase(chromosomeNumber)) { return "24"; } return chromosomeNumber; } /** * @param chromosomeNumber the internal chromosome number * @return display value of chromosome number. */ public static String getDisplayChromosomeNumber(String chromosomeNumber) { if ("23".equals(chromosomeNumber)) { return "X / 23"; } else if ("24".equals(chromosomeNumber)) { return "Y / 24"; } return chromosomeNumber; } /** * @param chromosomeNumber to get the alternate * @return the alternate chromosome number if any */ public static String getAlternateChromosome(String chromosomeNumber) { if ("X".equalsIgnoreCase(chromosomeNumber)) { return "23"; } else if ("Y".equalsIgnoreCase(chromosomeNumber)) { return "24"; } else if ("23".equals(chromosomeNumber)) { return "X"; } else if ("24".equals(chromosomeNumber)) { return "Y"; } return null; } /** * * @param studyConfiguration the study. * @return true if any subject source is in progress (currently being loaded). */ public static boolean isAnySubjectSourceInProgress(StudyConfiguration studyConfiguration) { for (AbstractClinicalSourceConfiguration source : studyConfiguration.getClinicalConfigurationCollection()) { if (Status.PROCESSING.equals(source.getStatus())) { return true; } } return false; } /** * Read the next data line. * @param reader the CSVReader * @return the data fields * @throws IOException reading error */ public static String[] readDataLine(CSVReader reader) throws IOException { String[] fields; while ((fields = reader.readNext()) != null) { if (!(fields.length == 1 && StringUtils.isBlank(fields[0])) && !fields[0].startsWith("#-#")) { return fields; } } return fields; } /** * @return the jvm heap size in MB. */ public static long getHeapSizeMB() { return Runtime.getRuntime().maxMemory() / ONE_MILLION; } /** * Lists the directory contents. * @param dir the File directory of which the contents will be logged. */ public static void printDirContents(File dir) { String[] files = dir.list(); if (files != null) { LOGGER.info("Listing: Contents of " + files.length + " files in dir " + dir.getAbsolutePath()); for (int i = 0; i < files.length; i++) { LOGGER.info("Listing: Files: " + files[i]); } } else { LOGGER.info("Listing: files object is null."); } } }