/* * file: MPDUtility.java * author: Jon Iles * copyright: (c) Packwood Software 2007 * date: 02-Feb-2006 */ /* * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; either version 2.1 of the License, or (at your * option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ package net.sf.mpxj.mpd; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.DecimalFormat; import java.util.Map; import java.util.Map.Entry; import net.sf.mpxj.CurrencySymbolPosition; import net.sf.mpxj.Duration; import net.sf.mpxj.ProjectFile; import net.sf.mpxj.TimeUnit; /** * This class implements common utility methods used when processing * MPD files. */ public final class MPDUtility { /** * This method maps the currency symbol position from the * representation used in the MPP file to the representation * used by MPX. * * @param value MPP symbol position * @return MPX symbol position */ public static CurrencySymbolPosition getSymbolPosition(int value) { CurrencySymbolPosition result; switch (value) { case 1: { result = CurrencySymbolPosition.AFTER; break; } case 2: { result = CurrencySymbolPosition.BEFORE_WITH_SPACE; break; } case 3: { result = CurrencySymbolPosition.AFTER_WITH_SPACE; break; } case 0: default: { result = CurrencySymbolPosition.BEFORE; break; } } return (result); } /** * This method converts between the duration units representation * used in the MPP file, and the standard MPX duration units. * If the supplied units are unrecognised, the units default to days. * * @param type MPP units * @return MPX units */ public static final TimeUnit getDurationTimeUnits(int type) { TimeUnit units; switch (type & DURATION_UNITS_MASK) { case 3: { units = TimeUnit.MINUTES; break; } case 4: { units = TimeUnit.ELAPSED_MINUTES; break; } case 5: { units = TimeUnit.HOURS; break; } case 6: { units = TimeUnit.ELAPSED_HOURS; break; } case 8: { units = TimeUnit.ELAPSED_DAYS; break; } case 9: { units = TimeUnit.WEEKS; break; } case 10: { units = TimeUnit.ELAPSED_WEEKS; break; } case 11: { units = TimeUnit.MONTHS; break; } case 12: { units = TimeUnit.ELAPSED_MONTHS; break; } default: case 7: { units = TimeUnit.DAYS; break; } } return (units); } /** * Given a duration and the time units for the duration extracted from an MPP * file, this method creates a new Duration to represent the given * duration. This instance has been adjusted to take into account the * number of "hours per day" specified for the current project. * * @param file parent file * @param duration duration length * @param timeUnit duration units * @return Duration instance */ public static Duration getAdjustedDuration(ProjectFile file, int duration, TimeUnit timeUnit) { Duration result; switch (timeUnit) { case MINUTES: case ELAPSED_MINUTES: { double totalMinutes = duration / 10d; result = Duration.getInstance(totalMinutes, timeUnit); break; } case HOURS: case ELAPSED_HOURS: { double totalHours = duration / 600d; result = Duration.getInstance(totalHours, timeUnit); break; } case DAYS: { double unitsPerDay = file.getProjectProperties().getMinutesPerDay().doubleValue() * 10d; double totalDays = 0; if (unitsPerDay != 0) { totalDays = duration / unitsPerDay; } result = Duration.getInstance(totalDays, timeUnit); break; } case ELAPSED_DAYS: { double unitsPerDay = 24d * 600d; double totalDays = duration / unitsPerDay; result = Duration.getInstance(totalDays, timeUnit); break; } case WEEKS: { double unitsPerWeek = file.getProjectProperties().getMinutesPerWeek().doubleValue() * 10d; double totalWeeks = 0; if (unitsPerWeek != 0) { totalWeeks = duration / unitsPerWeek; } result = Duration.getInstance(totalWeeks, timeUnit); break; } case ELAPSED_WEEKS: { double unitsPerWeek = (60 * 24 * 7 * 10); double totalWeeks = duration / unitsPerWeek; result = Duration.getInstance(totalWeeks, timeUnit); break; } case ELAPSED_MONTHS: { double unitsPerMonth = (60 * 24 * 30 * 10); double totalMonths = duration / unitsPerMonth; result = Duration.getInstance(totalMonths, timeUnit); break; } case MONTHS: { double totalMonths = duration / 96000d; result = Duration.getInstance(totalMonths, timeUnit); break; } default: { result = Duration.getInstance(duration, timeUnit); break; } } return (result); } /** * Reads a duration value. This method relies on the fact that * the units of the duration have been specified elsewhere. * * @param value Duration value * @param type type of units of the duration * @return Duration instance */ public static final Duration getDuration(double value, TimeUnit type) { double duration; // Value is given in 1/10 of minute switch (type) { case MINUTES: case ELAPSED_MINUTES: { duration = value / 10; break; } case HOURS: case ELAPSED_HOURS: { duration = value / 600; // 60 * 10 break; } case DAYS: { duration = value / 4800; // 8 * 60 * 10 break; } case ELAPSED_DAYS: { duration = value / 14400; // 24 * 60 * 10 break; } case WEEKS: { duration = value / 24000; // 5 * 8 * 60 * 10 break; } case ELAPSED_WEEKS: { duration = value / 100800; // 7 * 24 * 60 * 10 break; } case MONTHS: { duration = value / 96000; // 4 * 5 * 8 * 60 * 10 break; } case ELAPSED_MONTHS: { duration = value / 432000; // 30 * 24 * 60 * 10 break; } default: { duration = value; break; } } return (Duration.getInstance(duration, type)); } /** * Dump the contents of a row from an MPD file. * * @param row row data */ public static void dumpRow(Map<String, Object> row) { for (Entry<String, Object> entry : row.entrySet()) { Object value = entry.getValue(); System.out.println(entry.getKey() + " = " + value + " ( " + (value == null ? "" : value.getClass().getName()) + ")"); } } /** * This method generates a formatted version of the data contained * in a byte array. The data is written both in hex, and as ASCII * characters. * * @param buffer data to be displayed * @param offset offset of start of data to be displayed * @param length length of data to be displayed * @param ascii flag indicating whether ASCII equivalent chars should also be displayed * @return formatted string */ public static final String hexdump(byte[] buffer, int offset, int length, boolean ascii) { StringBuilder sb = new StringBuilder(); if (buffer != null) { char c; int loop; int count = offset + length; for (loop = offset; loop < count; loop++) { sb.append(" "); sb.append(HEX_DIGITS[(buffer[loop] & 0xF0) >> 4]); sb.append(HEX_DIGITS[buffer[loop] & 0x0F]); } if (ascii == true) { sb.append(" "); for (loop = offset; loop < count; loop++) { c = (char) buffer[loop]; if ((c > 200) || (c < 27)) { c = ' '; } sb.append(c); } } } return (sb.toString()); } /** * This method generates a formatted version of the data contained * in a byte array. The data is written both in hex, and as ASCII * characters. * * @param buffer data to be displayed * @param ascii flag indicating whether ASCII equivalent chars should also be displayed * @return formatted string */ public static final String hexdump(byte[] buffer, boolean ascii) { int length = 0; if (buffer != null) { length = buffer.length; } return (hexdump(buffer, 0, length, ascii)); } /** * This method generates a formatted version of the data contained * in a byte array. The data is written both in hex, and as ASCII * characters. The data is organised into fixed width columns. * * @param buffer data to be displayed * @param ascii flag indicating whether ASCII equivalent chars should also be displayed * @param columns number of columns * @param prefix prefix to be added before the start of the data * @return formatted string */ public static final String hexdump(byte[] buffer, boolean ascii, int columns, String prefix) { StringBuilder sb = new StringBuilder(); if (buffer != null) { int index = 0; DecimalFormat df = new DecimalFormat("00000"); while (index < buffer.length) { if (index + columns > buffer.length) { columns = buffer.length - index; } sb.append(prefix); sb.append(df.format(index)); sb.append(":"); sb.append(hexdump(buffer, index, columns, ascii)); sb.append('\n'); index += columns; } } return (sb.toString()); } /** * Writes a hex dump to a file for a large byte array. * * @param fileName output file name * @param data target data */ public static final void fileHexDump(String fileName, byte[] data) { try { FileOutputStream os = new FileOutputStream(fileName); os.write(hexdump(data, true, 16, "").getBytes()); os.close(); } catch (IOException ex) { ex.printStackTrace(); } } /** * Writes a hex dump to a file from a POI input stream. * Note that this assumes that the complete size of the data in * the stream is returned by the available() method. * * @param fileName output file name * @param is input stream */ public static final void fileHexDump(String fileName, InputStream is) { try { byte[] data = new byte[is.available()]; is.read(data); fileHexDump(fileName, data); } catch (IOException ex) { ex.printStackTrace(); } } /** * Writes a large byte array to a file. * * @param fileName output file name * @param data target data */ public static final void fileDump(String fileName, byte[] data) { try { FileOutputStream os = new FileOutputStream(fileName); os.write(data); os.close(); } catch (IOException ex) { ex.printStackTrace(); } } /** * Constants used to convert bytes to hex digits. */ private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** * Mask used to remove flags from the duration units field. */ private static final int DURATION_UNITS_MASK = 0x1F; }