Java tutorial
/*--------------- Kalypso-Header -------------------------------------------------------------------- This file is part of kalypso. Copyright (C) 2004, 2005 by: Technical University Hamburg-Harburg (TUHH) Institute of River and coastal engineering Denickestr. 22 21073 Hamburg, Germany http://www.tuhh.de/wb and Bjoernsen Consulting Engineers (BCE) Maria Trost 3 56070 Koblenz, Germany http://www.bjoernsen.de 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 Contact: E-Mail: belger@bjoernsen.de schlienger@bjoernsen.de v.doemming@tuhh.de ---------------------------------------------------------------------------------------------------*/ package org.kalypso.ogc.sensor.diagview.grafik; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.io.Writer; import java.net.URL; import java.text.DateFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.TimeZone; import java.util.TreeSet; import java.util.logging.Logger; import org.apache.commons.io.FileUtils; import org.apache.commons.io.input.NullInputStream; import org.apache.commons.io.output.NullOutputStream; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.kalypso.commons.java.io.FileUtilities; import org.kalypso.commons.java.lang.ProcessHelper; import org.kalypso.commons.resources.SetContentHelper; import org.kalypso.contribs.eclipse.core.resources.ResourceUtilities; import org.kalypso.contribs.eclipse.core.runtime.IStatusCollector; import org.kalypso.contribs.eclipse.core.runtime.StatusCollector; import org.kalypso.contribs.java.io.filter.PrefixSuffixFilter; import org.kalypso.contribs.java.lang.ICancelable; import org.kalypso.contribs.java.lang.NumberUtils; import org.kalypso.contribs.java.lang.ProgressCancelable; import org.kalypso.contribs.java.net.UrlResolver; import org.kalypso.contribs.java.util.DoubleComparator; import org.kalypso.ogc.sensor.DateRange; import org.kalypso.ogc.sensor.IAxis; import org.kalypso.ogc.sensor.IAxisRange; import org.kalypso.ogc.sensor.IObservation; import org.kalypso.ogc.sensor.ITupleModel; import org.kalypso.ogc.sensor.ObservationTokenHelper; import org.kalypso.ogc.sensor.ObservationUtilities; import org.kalypso.ogc.sensor.SensorException; import org.kalypso.ogc.sensor.diagview.DiagView; import org.kalypso.ogc.sensor.diagview.DiagViewUtils; import org.kalypso.ogc.sensor.metadata.ITimeseriesConstants; import org.kalypso.ogc.sensor.metadata.MetadataList; import org.kalypso.ogc.sensor.status.KalypsoStatusUtils; import org.kalypso.ogc.sensor.template.ObsView; import org.kalypso.ogc.sensor.timeseries.TimeseriesUtils; import org.kalypso.ogc.sensor.zml.ZmlFactory; import org.kalypso.template.obsdiagview.Obsdiagview; import org.kalypso.template.obsdiagview.TypeCurve; import org.kalypso.template.obsdiagview.TypeObservation; import org.kalypso.ui.KalypsoGisPlugin; import org.kalypso.ui.internal.i18n.Messages; /** * GrafikLauncher * * @author schlienger */ public class GrafikLauncher { public final static String GRAFIK_ENCODING = "Cp1252"; //$NON-NLS-1$ /** file extension of the grafik template files */ public final static String TPL_FILE_EXTENSION = "tpl"; //$NON-NLS-1$ /** date format understood by the grafik tool */ protected final static DateFormat GRAFIK_DF = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); //$NON-NLS-1$ private final static NumberFormat GRAFIK_NF_W = NumberFormat.getIntegerInstance(); static { GRAFIK_NF_W.setGroupingUsed(false); // set to false, else we got '.' in 1000ers, causing the grafik exe to read it // as 1.0 GRAFIK_NF_W.setMaximumFractionDigits(0); } private GrafikLauncher() { // no instanciation } /** * Opens the grafik tool using an observation template file. Note: this method should be called using a * WorkspaceModifyOperation. * * @return the created tpl file * @throws SensorException */ public static IStatus startGrafikODT(final IFile odtFile, final IFolder dest, final IProgressMonitor monitor) throws SensorException, CoreException { final Obsdiagview odt; try { odt = DiagViewUtils.loadDiagramTemplateXML(odtFile.getContents()); } catch (final Exception e) { e.printStackTrace(); throw new SensorException(e); } return startGrafikODT(odtFile.getName(), odt, dest, monitor); } /** * Open the grafik tool using a zml file. */ public static IStatus startGrafikZML(final IFile zmlFile, final IFolder dest, final IProgressMonitor monitor) throws SensorException { final DiagView diag = new DiagView(zmlFile.getName(), Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.3"), true); //$NON-NLS-1$ try { final URL context = ResourceUtilities.createURL(zmlFile); final IStatus status = diag.loadObservation(context, context.toExternalForm(), false, ObservationTokenHelper.DEFAULT_ITEM_NAME, ObsView.DEFAULT_ITEM_DATA, true); if (!status.isOK()) return status; final Obsdiagview odt = DiagViewUtils.buildDiagramTemplateXML(diag, null); return startGrafikODT(zmlFile.getName(), odt, dest, monitor); } catch (final Exception e) { e.printStackTrace(); throw new SensorException(e); } finally { diag.dispose(); } } /** * Opens the grafik tool using an observation template xml object. Note: this method should be called using a * WorkspaceModifyOperation. * * @param fileName * the filename to use for the grafik template file * @param odt * the xml binding object * @throws SensorException */ public static IStatus startGrafikODT(final String fileName, final Obsdiagview odt, final IFolder dest, final IProgressMonitor monitor) throws SensorException, CoreException { final String taskName = Messages.getString("GrafikLauncher.0"); //$NON-NLS-1$ monitor.beginTask(taskName, 100); monitor.setTaskName(taskName); monitor.subTask(Messages.getString("GrafikLauncher.1")); //$NON-NLS-1$ try { if (!dest.exists()) dest.create(true, true, new SubProgressMonitor(monitor, 5)); final IFile tplFile = dest.getFile(FileUtilities.nameWithoutExtension(fileName) + ".tpl"); //$NON-NLS-1$ final StringWriter strWriter = new StringWriter(); final RememberForSync[] syncs = odt2tpl(odt, dest, strWriter, new SubProgressMonitor(monitor, 5)); strWriter.close(); // use the windows encoding for the Vorlage because of the grafik tool // which uses it when reading... final SetContentHelper sch = new SetContentHelper( Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.5")) //$NON-NLS-1$ { @Override protected void write(final OutputStreamWriter writer) throws Throwable { writer.write(strWriter.toString()); } }; sch.setFileContents(tplFile, false, false, new SubProgressMonitor(monitor, 5), GRAFIK_ENCODING); startGrafikTPL(tplFile, syncs, new SubProgressMonitor(monitor, 85, SubMonitor.SUPPRESS_NONE)); return Status.OK_STATUS; } catch (final CoreException e) { throw e; } catch (final Throwable e) // generic exception caught { throw new SensorException(e); } } /** * Starts the grafik.exe with an eclipse IFile tpl-File. * * @param tplFile * the Grafik-Vorlage * @throws SensorException */ public static IStatus startGrafikTPL(final IFile tplFile, final RememberForSync[] syncs, final IProgressMonitor monitor) throws SensorException { final File file = ResourceUtilities.makeFileFromPath(tplFile.getFullPath()); return startGrafikTPL(file, syncs, monitor); } /** * Starts the grafik with a java.lang.File tpl-File. */ private static IStatus startGrafikTPL(final File tplFile, final RememberForSync[] syncs, final IProgressMonitor monitor) throws SensorException { try { final File grafikExe = getGrafikProgramPath(); final String[] commands = new String[] { // grafikExe.getAbsolutePath(), "/V", tplFile.getAbsolutePath() }; //$NON-NLS-1$ final ICancelable cancelable = new ProgressCancelable(monitor); final ProcessHelper helper = new ProcessHelper(commands, null, grafikExe.getParentFile(), cancelable, 0, NullOutputStream.NULL_OUTPUT_STREAM, NullOutputStream.NULL_OUTPUT_STREAM, new NullInputStream(0)); helper.setKillOnCancel(false); helper.start(); if (monitor.isCanceled()) return new Status(IStatus.CANCEL, KalypsoGisPlugin.PLUGIN_ID, Messages.getString("GrafikLauncher.3")); //$NON-NLS-1$ return syncBack(syncs); } catch (final Exception e) { throw new SensorException(e); } } private static IStatus syncBack(final RememberForSync[] syncs) { final IStatusCollector log = new StatusCollector(KalypsoGisPlugin.getId()); for (final RememberForSync rfs : syncs) { try { rfs.synchronizeZml(); } catch (final Exception e) { e.printStackTrace(); final String msg = Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.8") //$NON-NLS-1$ + rfs.getDatFile().getName() + Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.9") //$NON-NLS-1$ + rfs.getZmlFile().getName(); log.add(IStatus.ERROR, msg, e); } } return log.asMultiStatusOrOK(Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.7")); //$NON-NLS-1$ } /** * Return the file representing the Grafik Program. Since the Grafik.exe is located in the resources of this plugin, * it must first be extracted into a temp file in the local file system. */ public static File getGrafikProgramPath() throws IOException { // create the grafik exe final File grafikExe = makeFileFromURL("grafik", ".exe", //$NON-NLS-1$//$NON-NLS-2$ GrafikLauncher.class.getResource("/org/kalypso/ui/resources/exe/grafik.exe_"), true); //$NON-NLS-1$ grafikExe.deleteOnExit(); // also create the help file if not already existing final File grafikHelp = new File(grafikExe.getParentFile(), FileUtilities.nameWithoutExtension(grafikExe.getName()) + ".hlp"); //$NON-NLS-1$ grafikHelp.deleteOnExit(); if (!grafikHelp.exists()) { final File tmp = makeFileFromURL("grafik", ".hlp", //$NON-NLS-1$//$NON-NLS-2$ GrafikLauncher.class.getResource("/org/kalypso/ui/resources/exe/grafik.hlp"), true); //$NON-NLS-1$ // the help must have the same name as the exe (except file-extension) FileUtils.copyFile(tmp, grafikHelp); tmp.delete(); } return grafikExe; } /** * Creates a new temporary file given its pathName and an InputStream. The content from the InputStream is written * into the file. The file will be deleted after the VM shuts down * * @param charMode * @param prefix * prefix of file name * @param suffix * suffix of file name * @param ins * the input stream, that is the source * @param useCache * if true tries to use an existing file with these prefix/suffix * @return the newly created file or null if an exception was thrown. * @throws IOException * problems reading from stream or writing to temp. file */ private static File makeFileFromURL(final String prefix, final String suffix, final URL input, final boolean useCache) throws IOException { if (useCache) { try { return fileExistsInDir(prefix, suffix, System.getProperty(FileUtilities.JAVA_IO_TMPDIR)); } catch (final FileNotFoundException ignored) { // ignored // ignored.printStackTrace(); } } final File tmp = File.createTempFile(prefix, suffix); tmp.deleteOnExit(); FileUtils.copyURLToFile(input, tmp); return tmp; } /** * Looks in the given path if a file with the given prefix and suffix exists. Returns the file in the positive. If * more than one such file is found, returns the first of them. * * @param prefix * name of the file should begin with this prefix * @param suffix * name of the file should end with this suffix * @param path * @return the (first) File found * @throws FileNotFoundException * when file was not found or path does not denote a directory * @see PrefixSuffixFilter */ private static File fileExistsInDir(final String prefix, final String suffix, final String path) throws FileNotFoundException { final File dir = new File(path); if (dir.isDirectory()) { final PrefixSuffixFilter filter = new PrefixSuffixFilter(prefix, suffix); final File[] files = dir.listFiles(filter); if (files.length > 0) return files[0]; } throw new FileNotFoundException( Messages.getString("org.kalypso.commons.java.io.FileUtilities.0", prefix, suffix, path)); //$NON-NLS-1$ } /** * Converts a diagram template file to a grafik tpl. * <p> * Important note: the XML-Schema for the diag template file says that if no curve element or specified for a given observation, then all curves of that observation should be displayed. This is not * possible here using the grafik tool. As a conclusion: when a template file is meant to be used with the grafik tool, then curves need to be explicitely specified in the xml. */ private static RememberForSync[] odt2tpl(final Obsdiagview odt, final IFolder dest, final Writer writer, final IProgressMonitor monitor) throws CoreException, IOException { final List<RememberForSync> sync = new ArrayList<>(); final UrlResolver urlRes = new UrlResolver(); final URL context = ResourceUtilities.createURL(dest.getParent()); final GrafikAchsen gAchsen = new GrafikAchsen(odt.getAxis()); final GrafikKurven gKurven = new GrafikKurven(gAchsen); Date xLower = null; Date xUpper = null; Number yLower = new Double(Double.MAX_VALUE); Number yUpper = new Double(-Double.MAX_VALUE); final Set<XLine> xLines = new TreeSet<>(); final Map<Double, ValueAndColor> yLines = new HashMap<>(); // set the timezone of the dateformat if (odt.getTimezone() != null && odt.getTimezone().length() > 0) { // FIXME: changing static data here.... final TimeZone timeZone = TimeZone.getTimeZone(odt.getTimezone()); GRAFIK_DF.setTimeZone(timeZone); } final Logger logger = Logger.getLogger(GrafikLauncher.class.getName()); final IStatusCollector stati = new StatusCollector(KalypsoGisPlugin.getId()); int cc = 1; final TypeObservation[] tobs = odt.getObservation().toArray(new TypeObservation[0]); for (final TypeObservation element : tobs) { // now try to locate observation file final URL url = urlRes.resolveURL(context, element.getHref()); final IFile zmlFile = ResourceUtilities.findFileFromURL(url); // if file cannot be found, that probably means it is not local... // maybe make a better test later? if (zmlFile == null) { final String msg = Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.18") //$NON-NLS-1$ + url.toExternalForm(); logger.warning(msg); stati.add(IStatus.WARNING, msg); continue; } final IObservation obs; final ITupleModel values; try { obs = ZmlFactory.parseXML(zmlFile.getLocationURI().toURL()); values = obs.getValues(null); } catch (final Exception e) { final String msg = Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.19") //$NON-NLS-1$ + zmlFile.getName() + Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.20") //$NON-NLS-1$ + e.getLocalizedMessage(); logger.warning(msg); stati.add(IStatus.WARNING, msg, e); continue; } // find out which axes to use final IAxis[] axes = obs.getAxes(); final IAxis dateAxis = ObservationUtilities.findAxisByClass(axes, Date.class); // REMARK: we use the version with many classes, so no exception is thrown if now number-axis was found. final IAxis[] numberAxes = KalypsoStatusUtils.findAxesByClasses(axes, new Class[] { Number.class }, true); // Just ignore this obs, if it has no number axises if (numberAxes.length == 0) continue; final List<IAxis> displayedAxes = new ArrayList<>(numberAxes.length); final List<TypeCurve> curves = element.getCurve(); for (final TypeCurve tc : curves) { // create a corresponding dat-File for the current observation file final String datFileProtoName = FileUtilities.nameWithoutExtension(zmlFile.getName()) + "-" + cc //$NON-NLS-1$ + ".dat"; //$NON-NLS-1$ final String datFileName = datFileProtoName.replace(' ', '_'); final IFile datFile = dest.getFile(datFileName); final IAxis axis = gKurven.addCurve(datFile, tc, numberAxes); if (axis != null) { displayedAxes.add(axis); // convert to dat-file, ready to be read by the grafik tool zml2dat(values, datFile, dateAxis, axis, monitor); final RememberForSync rfs = new RememberForSync(zmlFile, datFile, axis); sync.add(rfs); cc++; try { // fetch Y axis range for placing possible scenario text item final IAxisRange range = values.getRange(axis); if (range != null) { final DoubleComparator dc = new DoubleComparator(0.001); final Number lower = (Number) range.getLower(); final Number upper = (Number) range.getUpper(); if (dc.compare(lower, yLower) < 0) yLower = lower; if (dc.compare(upper, yUpper) > 0) yUpper = upper; } } catch (final SensorException e) { e.printStackTrace(); } } else Logger.getLogger(GrafikLauncher.class.getName()) .warning(Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.23") //$NON-NLS-1$ + tc.getName() + Messages .getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.24")); //$NON-NLS-1$ } try { // fetch X axis range for placing possible scenario text item final IAxisRange range = values.getRange(dateAxis); if (range != null) { final Date d1 = (Date) range.getLower(); final Date d2 = (Date) range.getUpper(); if (xLower == null || d1.before(xLower)) xLower = d1; if (xUpper == null || d2.after(xUpper)) xUpper = d2; } } catch (final SensorException e) { e.printStackTrace(); } // is this obs a forecast? // TODO: check if odt wants forecast to be shown final DateRange fr = TimeseriesUtils.isTargetForecast(obs); if (fr != null) { final String strDate = GRAFIK_DF.format(fr.getFrom()); xLines.add(new XLine( Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.25") + strDate, //$NON-NLS-1$ strDate)); } // does is have Alarmstufen? only check if we are displaying at least a // W-axis try { ObservationUtilities.findAxisByType(displayedAxes.toArray(new IAxis[displayedAxes.size()]), ITimeseriesConstants.TYPE_WATERLEVEL); final MetadataList mdl = obs.getMetadataList(); final String[] mds = TimeseriesUtils.findOutMDAlarmLevel(obs); for (final String element2 : mds) { final String alarmLevel = mdl.getProperty(element2); final Double value = NumberUtils.parseQuietDouble(alarmLevel); yLines.put(value, new ValueAndColor(element2 + " (" + GRAFIK_NF_W.format(value) + ")", //$NON-NLS-1$//$NON-NLS-2$ value.doubleValue())); } } catch (final NoSuchElementException e) { // ignored } displayedAxes.clear(); } writer.write(gKurven.toVorlagentext()); writer.write("\n"); //$NON-NLS-1$ writer.write("HTitel:\t" + odt.getTitle() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ writer.write("xTitel:\t" + gAchsen.getBottomLabel() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ writer.write("yTitel1:\t" + gAchsen.getLeftLabel() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ writer.write("yTitel2:\t" + gAchsen.getRightLabel() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ // constant vertical lines... for (final Object element : xLines) { final String strDate = element.toString(); writer.write("Senkrechte:" + strDate + '\n'); //$NON-NLS-1$ } // constant horizontal lines... for (final Object element : yLines.keySet()) { final ValueAndColor vac = yLines.get(element); writer.write("yKonst:" + GRAFIK_NF_W.format(vac.value) + " " + vac.label + '\n'); //$NON-NLS-1$ //$NON-NLS-2$ } final IStatus status = stati .asMultiStatusOrOK(Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.17")); //$NON-NLS-1$ if (!status.isOK()) throw new CoreException(status); final RememberForSync[] syncs = sync.toArray(new RememberForSync[sync.size()]); return syncs; } /** * Converts a zml file to a dat file that the grafik tool can load. */ private static IStatus zml2dat(final ITupleModel values, final IFile datFile, final IAxis dateAxis, final IAxis axis, final IProgressMonitor monitor) throws CoreException { final SetContentHelper sch = new SetContentHelper( Messages.getString("org.kalypso.ogc.sensor.diagview.grafik.GrafikLauncher.45")) //$NON-NLS-1$ { @Override protected void write(final OutputStreamWriter writer) throws Throwable { for (int i = 0; i < values.size(); i++) { if (monitor.isCanceled()) return; final Object elt = values.get(i, dateAxis); final String text = GRAFIK_DF.format(elt); writer.write(text); writer.write('\t'); writer.write(values.get(i, axis).toString()); writer.write('\n'); } } }; sch.setFileContents(datFile, false, false, monitor); return Status.OK_STATUS; } /** * mini helper class for storing a value and a color * * @author schlienger */ private final static class ValueAndColor { final double value; final String label; public ValueAndColor(final String lbl, final double val) { label = lbl; value = val; } } private final static class XLine implements Comparable<XLine> { // public final String label; public final String strDate; public XLine(@SuppressWarnings("unused") final String lbl, final String strdate) { // label = lbl; strDate = strdate; } @Override public String toString() { return strDate; } @Override public int compareTo(final XLine o) { return strDate.compareTo(o.strDate); } } }