diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..10df782f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/dasCore/nbproject/private +/dasCore/nbproject.hide/private +/dasCore/target +/dasCore/platver.class + diff --git a/README.md b/README.md new file mode 100644 index 000000000..aaa735be3 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# DasCore, original version + +This is an older edition of the dasCore java library. It's not used in +Autoplot but it is needed to build some special purpose programs such as +the MARSIS AIS Ionogram tool. It builds as a standard Apache Maven project. + +To learn more about this library visit [das2.org](https://das2.org). + diff --git a/dasCore/Linux.mak b/dasCore/Linux.mak new file mode 100644 index 000000000..21656b143 --- /dev/null +++ b/dasCore/Linux.mak @@ -0,0 +1,150 @@ +# A mostly generic makefile that assumes there is one or more app jars in +# +# ./target +# +# and 0 or more library jars +# +# ./target/lib +# +# Since mave does most of the work, you're pom.xml will have to configure +# the maven-dependency-plugin to put the needed jars into ./target/lib + + +# Generic definitions that have to be formed in a platform specific way: + +ifeq ($(JAVA_HOME),) +$(error set JAVA_HOME first, ex: JAVA_HOME=/usr/lib/jvm/java-11-openjdk) +endif + + +ifeq ($(H_ARCH),) +JAVAVER:=$(shell $(JAVA_HOME)/bin/javac platver.java && $(JAVA_HOME)/bin/java -cp . platver 2>/dev/null) +ifeq ($(JAVAVER),) +$(error Couldn't determine your java platform version) +endif +H_ARCH=java$(JAVAVER) +endif + +ifeq ($(INST_HOST_LIB),) +INST_HOST_LIB=$(PREFIX)/lib/$(H_ARCH) +endif +export INST_HOST_LIB + +ifeq ($(INST_EXT_LIB),) +INST_EXT_LIB=$(PREFIX)/lib/$(N_ARCH)/$(H_ARCH) +endif + +# Project Targets ############################################################ + +LIB_JAR=dasCore-2.2.jar + +DEPEND_JARS=batik-awt-util-1.5.jar batik-svggen-1.5.jar \ + batik-util-1.5.jar bcmail-jdk14-138.jar bcprov-jdk14-138.jar \ + itext-2.0.7.jar junit-3.8.1.jar swing-layout-1.0.3.jar + +#SCRIPTS=das2_runrdr + +# Targets with autopaths ##################################################### + +BUILD_JAR=$(patsubst %.jar, target/%.jar, $(LIB_JAR)) + +#BUILD_SCRIPTS=$(patsubst %, target/%, $(SCRIPTS)) + +BUILD_DEPEND_JARS=$(patsubst %.jar, target/lib/%.jar, $(DEPEND_JARS)) + +INST_LIB_JARS=$(patsubst %.jar, $(INST_HOST_LIB)/%.jar, $(LIB_JAR)) \ + $(patsubst %.jar, $(INST_HOST_LIB)/%.jar, $(DEPEND_JARS)) + +#INST_SCRIPTS=$(patsubst %, $(INST_NAT_BIN)/%, $(SCRIPTS)) + +# Implicit Rules ############################################################# + + +# Building a bash script +#target/%:src/main/sh/%.in +# ./envsubst.py $< $@ + +# Installing a script +#$(INST_NAT_BIN)/%:target/% +# install -D -m 775 $< $@ + +# Installing libary jar +$(INST_HOST_LIB)/%.jar:target/%.jar + install -D -m 0664 $< $@ + +# Installing dependency libaries +$(INST_HOST_LIB)/%.jar:target/lib/%.jar + install -D -m 0664 $< $@ + + + +# Explicit rules ############################################################# + +# Maven is a development tool, not a deployment tool, hence it is being run +# by make as if it were just a compliler tool. Then install steps are handled +# here + +.PHONY : build package test install clean distclean + +build: $(BUILD_JAR) $(BUILD_DEPEND_JARS) $(BUILD_SCRIPTS) #\ +# target/site/apidocs/index.html + +$(BUILD_JAR) $(BUILD_DEPEND_JARS): + mvn -Dmaven.javadoc.skip=true install + +target/site/apidocs/index.html: + mvn generate-sources javadoc:javadoc + +test: + mvn integration-test + +show: + @echo BUILD_JARS: $(BUILD_JAR) $(BUILD_DEPEND_JARS) + @echo INST_LIB_JARS: $(INST_LIB_JARS) + + +install: $(INST_LIB_JARS) $(INST_SCRIPTS) + +#-mvn -Dmaven.javadoc.skip=true install + +$(INST_DOC)/dasCore/index.html:target/site/apidocs/index.html + if [ ! -e $(INST_DOC)/dasCore ]; then mkdir -p $(INST_DOC)/dasCore; fi + cp --remove-destination -r target/site/apidocs/* $(INST_DOC)/dasCore + chmod -R g+w $(INST_DOC)/dasCore + +clean: + mvn clean + + +# Need to get *.java file dependencies in here... + +distclean: + rm -r target + + +# maven build phases are (from apach.org): +# +# validate - validate the project is correct and all necessary information +# is available +# +# compile - compile the source code of the project +# +# test - test the compiled source code using a suitable unit testing +# framework. These tests should not require the code be packaged or +# deployed +# +# package - take the compiled code and package it in its distributable format, +# such as a JAR. +# +# integration-test - process and deploy the package if necessary into an +# environment where integration tests can be run +# +# verify - run any checks to verify the package is valid and meets quality +# criteria +# +# install - install the package into the local repository, for use as a +# dependency in other projects locally +# +# deploy - done in an integration or release environment, copies the final +# package to the remote repository for sharing with other developers +# and projects. diff --git a/dasCore/Makefile b/dasCore/Makefile new file mode 100644 index 000000000..631a3a366 --- /dev/null +++ b/dasCore/Makefile @@ -0,0 +1,46 @@ +############################################################################## +# Generic definitions for: Native + Hosted Java + +ifeq ($(PREFIX),) +ifeq ($(HOME),) +PREFIX=$(USERPROFILE) +else +PREFIX=$(HOME) +endif +endif + +ifeq ($(INST_ETC),) +INST_ETC=$(PREFIX)/etc +endif + +ifeq ($(INST_SHARE),) +INST_SHARE=$(PREFIX)/share +endif + +ifeq ($(INST_DOC),) +INST_DOC=$(INST_SHARE)/doc +endif + +ifeq ($(INST_INC),) +INST_INC=$(PREFIX)/include +endif + +ifeq ($(N_ARCH),) +N_ARCH=$(shell uname -s).$(shell uname -p) +endif + +ifeq ($(INST_NAT_BIN),) +INST_NAT_BIN=$(PREFIX)/bin/$(N_ARCH) +endif + +ifeq ($(INST_NAT_LIB),) +INST_NAT_LIB=$(PREFIX)/lib/$(N_ARCH) +endif + +############################################################################## +# Explicit Rules + +UNAME = $(shell uname) + +include $(UNAME).mak + diff --git a/dasCore/README.SVN.TXT b/dasCore/README.SVN.TXT deleted file mode 100644 index d8c2040e6..000000000 --- a/dasCore/README.SVN.TXT +++ /dev/null @@ -1,2 +0,0 @@ -Branched just before 4348 -Branched again 2010-10-11 10:40am Central time diff --git a/dasCore/SunOS.mak b/dasCore/SunOS.mak new file mode 100644 index 000000000..8ba374ed6 --- /dev/null +++ b/dasCore/SunOS.mak @@ -0,0 +1,134 @@ +# A mostly generic makefile that assumes there is one or more app jars in +# +# ./target +# +# and 0 or more library jars +# +# ./target/lib +# +# Since mave does most of the work, you're pom.xml will have to configure +# the maven-dependency-plugin to put the needed jars into ./target/lib + + +# Generic definitions that have to be formed in a platform specific way: + +ifeq ($(H_ARCH),) +JAVAVER:=$(shell javac platver.java && java -cp . platver 2>/dev/null) +ifeq ($(JAVAVER),) +$(error Couldn't determine your java platform version) +endif +H_ARCH=java$(JAVAVER) +endif + +ifeq ($(INST_HOST_LIB),) +INST_HOST_LIB=$(PREFIX)/lib/$(H_ARCH) +endif +export INST_HOST_LIB + +ifeq ($(INST_EXT_LIB),) +INST_EXT_LIB=$(PREFIX)/lib/$(N_ARCH)/$(H_ARCH) +endif + +# Project Targets ############################################################ + +LIB_JAR=dasCore-1.0-SNAPSHOT.jar + +DEPEND_JARS=batik-awt-util-1.5.jar batik-svggen-1.5.jar \ + batik-util-1.5.jar bcmail-jdk14-138.jar bcprov-jdk14-138.jar \ + itext-2.0.7.jar junit-3.8.1.jar swing-layout-1.0.3.jar + +#SCRIPTS=das2_runrdr + +# Targets with autopaths ##################################################### + +BUILD_JAR=$(patsubst %.jar, target/%.jar, $(LIB_JAR)) + +#BUILD_SCRIPTS=$(patsubst %, target/%, $(SCRIPTS)) + +BUILD_DEPEND_JARS=$(patsubst %.jar, target/lib/%.jar, $(DEPEND_JARS)) + +INST_LIB_JARS=$(patsubst %.jar, $(INST_HOST_LIB)/%.jar, $(LIB_JAR)) \ + $(patsubst %.jar, $(INST_HOST_LIB)/%.jar, $(DEPEND_JARS)) + +#INST_SCRIPTS=$(patsubst %, $(INST_NAT_BIN)/%, $(SCRIPTS)) + +# Implicit Rules ############################################################# + + +# Building a bash script +#target/%:src/main/sh/%.in +# ./envsubst.py $< $@ + +# Installing a script +#$(INST_NAT_BIN)/%:target/% +# install -D -m 775 $< $@ + +# Installing libary jar +$(INST_HOST_LIB)/%.jar:target/%.jar + install -D -m 0664 $< $@ + +# Installing dependency libaries +$(INST_HOST_LIB)/%.jar:target/lib/%.jar + install -D -m 0664 $< $@ + + + +# Explicit rules ############################################################# + +# Maven is a development tool, not a deployment tool, hence it is being run +# by make as if it were just a compliler tool. Then install steps are handled +# here + +.PHONY : build package test install clean distclean + +build: $(BUILD_JAR) $(BUILD_DEPEND_JARS) $(BUILD_SCRIPTS) + +$(BUILD_JAR) $(BUILD_DEPEND_JARS): + mvn package + +test: + mvn integration-test + +show: + @echo BUILD_JARS: $(BUILD_JAR) $(BUILD_DEPEND_JARS) + @echo INST_LIB_JARS: $(INST_LIB_JARS) + + +install: $(INST_LIB_JARS) $(INST_SCRIPTS) + +clean: + mvn clean + + +# Need to get *.java file dependencies in here... + +distclean: + rm -r target + + +# maven build phases are (from apach.org): +# +# validate - validate the project is correct and all necessary information +# is available +# +# compile - compile the source code of the project +# +# test - test the compiled source code using a suitable unit testing +# framework. These tests should not require the code be packaged or +# deployed +# +# package - take the compiled code and package it in its distributable format, +# such as a JAR. +# +# integration-test - process and deploy the package if necessary into an +# environment where integration tests can be run +# +# verify - run any checks to verify the package is valid and meets quality +# criteria +# +# install - install the package into the local repository, for use as a +# dependency in other projects locally +# +# deploy - done in an integration or release environment, copies the final +# package to the remote repository for sharing with other developers +# and projects. diff --git a/dasCore/lib/CopyLibs-2/org-netbeans-modules-java-j2seproject-copylibstask.jar b/dasCore/lib/CopyLibs-2/org-netbeans-modules-java-j2seproject-copylibstask.jar new file mode 100644 index 000000000..8e1ec53a1 Binary files /dev/null and b/dasCore/lib/CopyLibs-2/org-netbeans-modules-java-j2seproject-copylibstask.jar differ diff --git a/dasCore/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar b/dasCore/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar deleted file mode 100644 index afa176cc6..000000000 Binary files a/dasCore/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar and /dev/null differ diff --git a/dasCore/lib/commons/commons-httpclient-3.0.jar b/dasCore/lib/commons/commons-httpclient-3.0.jar deleted file mode 100644 index 54a9300e6..000000000 Binary files a/dasCore/lib/commons/commons-httpclient-3.0.jar and /dev/null differ diff --git a/dasCore/lib/commons/commons-logging-1.0.4.jar b/dasCore/lib/commons/commons-logging-1.0.4.jar deleted file mode 100644 index b73a80fab..000000000 Binary files a/dasCore/lib/commons/commons-logging-1.0.4.jar and /dev/null differ diff --git a/dasCore/lib/commons/commons-net-2.0.jar b/dasCore/lib/commons/commons-net-2.0.jar deleted file mode 100644 index dd7a52a69..000000000 Binary files a/dasCore/lib/commons/commons-net-2.0.jar and /dev/null differ diff --git a/dasCore/lib/commons/commons-net-ftp-2.0.jar b/dasCore/lib/commons/commons-net-ftp-2.0.jar deleted file mode 100644 index f697534cf..000000000 Binary files a/dasCore/lib/commons/commons-net-ftp-2.0.jar and /dev/null differ diff --git a/dasCore/lib/commons/commons-vfs-1.0.jar b/dasCore/lib/commons/commons-vfs-1.0.jar deleted file mode 100644 index 7992d21b0..000000000 Binary files a/dasCore/lib/commons/commons-vfs-1.0.jar and /dev/null differ diff --git a/dasCore/lib/commons/jsch-20090723.jar b/dasCore/lib/commons/jsch-20090723.jar deleted file mode 100644 index 2c9cb6f6f..000000000 Binary files a/dasCore/lib/commons/jsch-20090723.jar and /dev/null differ diff --git a/dasCore/lib/nblibraries.properties b/dasCore/lib/nblibraries.properties index 11a39d3a3..d0770cfc9 100644 --- a/dasCore/lib/nblibraries.properties +++ b/dasCore/lib/nblibraries.properties @@ -3,7 +3,7 @@ libs.BatikSVGGen1.5.classpath=\ ${base}/BatikSVGGen1.5/batik-svggen.jar:\ ${base}/BatikSVGGen1.5/batik-util.jar libs.CopyLibs.classpath=\ - ${base}/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar + ${base}/CopyLibs-2/org-netbeans-modules-java-j2seproject-copylibstask.jar libs.CopyLibs.displayName=CopyLibs Task libs.CopyLibs.prop-version=2.0 libs.iText1.3.classpath=\ @@ -18,10 +18,3 @@ libs.junit.classpath=\ ${base}/junit/junit-3.8.2.jar libs.junit.javadoc=\ ${base}/junit/junit-3.8.2-api.zip -libs.ApacheCommons.classpath=\ - ${base}/commons/commons-httpclient-3.0.jar:\ - ${base}/commons/commons-logging-1.0.4.jar:\ - ${base}/commons/commons-net-2.0.jar:\ - ${base}/commons/commons-net-ftp-2.0.jar:\ - ${base}/commons/commons-vfs-1.0.jar:\ - ${base}/commons/jsch-20090723.jar diff --git a/dasCore/nbproject/.cvsignore b/dasCore/nbproject.hide/.cvsignore similarity index 100% rename from dasCore/nbproject/.cvsignore rename to dasCore/nbproject.hide/.cvsignore diff --git a/dasCore/nbproject.hide/build-impl.xml b/dasCore/nbproject.hide/build-impl.xml new file mode 100644 index 000000000..fc888cda8 --- /dev/null +++ b/dasCore/nbproject.hide/build-impl.xml @@ -0,0 +1,1421 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dasCore/nbproject.hide/genfiles.properties b/dasCore/nbproject.hide/genfiles.properties new file mode 100644 index 000000000..b02193fc5 --- /dev/null +++ b/dasCore/nbproject.hide/genfiles.properties @@ -0,0 +1,11 @@ +build.xml.data.CRC32=dc210ac0 +build.xml.script.CRC32=daa5182f +build.xml.stylesheet.CRC32=e2435cb5 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=93be8afa +nbproject/build-impl.xml.script.CRC32=7ee31339 +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.1.48 +nbproject/profiler-build-impl.xml.data.CRC32=df41f1af +nbproject/profiler-build-impl.xml.script.CRC32=abda56ed +nbproject/profiler-build-impl.xml.stylesheet.CRC32=42cb6bcf diff --git a/dasCore/nbproject/profiler-build-impl.xml b/dasCore/nbproject.hide/profiler-build-impl.xml similarity index 100% rename from dasCore/nbproject/profiler-build-impl.xml rename to dasCore/nbproject.hide/profiler-build-impl.xml diff --git a/dasCore/nbproject.hide/project.properties b/dasCore/nbproject.hide/project.properties new file mode 100644 index 000000000..325a3927b --- /dev/null +++ b/dasCore/nbproject.hide/project.properties @@ -0,0 +1,77 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +application.args= +application.title=dasCore +application.vendor=jbf +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/dasCore.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.dasCore-src=src +includes=** +jar.compress=false +javac.classpath=\ + ${libs.BatikSVGGen1.5.classpath}:\ + ${libs.iText1.3.classpath}:\ + ${libs.swing-layout.classpath} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=true +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding= +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle=das2 +# Property libs.absolutelayout.classpath is set here just to make sharing of project simpler. +# The library definition has always preference over this property. +libs.absolutelayout.classpath=../../../../../Program Files/netbeans-5.5beta2-1/ide7/modules/ext/AbsoluteLayout.jar +# Property libs.swing-layout.classpath is set here just to make sharing of project simpler. +# The library definition has always preference over this property. +libs.swing-layout.classpath=../../../../../Program Files/netbeans-5.5beta2-1/platform6/modules/ext/swing-layout-1.0.jar +main.class=test.stream.TestDas2StreamRead +manifest.file=manifest.mf +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs=-Xms256m -Xmx512m +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +src.dir=${file.reference.dasCore-src} +test.test.dir=${file.reference.dasCore-test} diff --git a/dasCore/nbproject.hide/project.xml b/dasCore/nbproject.hide/project.xml new file mode 100644 index 000000000..94a964d07 --- /dev/null +++ b/dasCore/nbproject.hide/project.xml @@ -0,0 +1,18 @@ + + + org.netbeans.modules.java.j2seproject + + + dasCore + 1.6.5 + + + + + + + ./lib/nblibraries.properties + + + + diff --git a/dasCore/nbproject/build-impl.xml b/dasCore/nbproject/build-impl.xml deleted file mode 100644 index 2416ac1be..000000000 --- a/dasCore/nbproject/build-impl.xml +++ /dev/null @@ -1,1461 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set src.dir - Must set build.dir - Must set dist.dir - Must set build.classes.dir - Must set dist.javadoc.dir - Must set build.test.classes.dir - Must set build.test.results.dir - Must set build.classes.excludes - Must set dist.jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No tests executed. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set JVM to use for profiling in profiler.info.jvm - Must set profiler agent JVM arguments in profiler.info.jvmargs.agent - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - - - - - - java -cp "${run.classpath.with.dist.jar}" ${main.class} - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - java -jar "${dist.jar.resolved}" - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - Must select one file in the IDE or set run.class - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set debug.class - - - - - Must select one file in the IDE or set debug.class - - - - - Must set fix.includes - - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - Must select one file in the IDE or set profile.class - This target only works when run from inside the NetBeans IDE. - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - - - Must select some files in the IDE or set test.includes - - - - - Must select one file in the IDE or set run.class - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - Some tests failed; see details above. - - - - - - - - - Must select some files in the IDE or set test.includes - - - - Some tests failed; see details above. - - - - Must select some files in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - Some tests failed; see details above. - - - - - Must select one file in the IDE or set test.class - - - - Must select one file in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - - - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dasCore/nbproject/genfiles.properties b/dasCore/nbproject/genfiles.properties deleted file mode 100644 index 299a270c5..000000000 --- a/dasCore/nbproject/genfiles.properties +++ /dev/null @@ -1,11 +0,0 @@ -build.xml.data.CRC32=dc210ac0 -build.xml.script.CRC32=daa5182f -build.xml.stylesheet.CRC32=e2435cb5 -# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. -# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=e30d843e -nbproject/build-impl.xml.script.CRC32=b4f627be -nbproject/build-impl.xml.stylesheet.CRC32=c6d2a60f@1.56.1.46 -nbproject/profiler-build-impl.xml.data.CRC32=df41f1af -nbproject/profiler-build-impl.xml.script.CRC32=abda56ed -nbproject/profiler-build-impl.xml.stylesheet.CRC32=42cb6bcf diff --git a/dasCore/nbproject/project.properties b/dasCore/nbproject/project.properties deleted file mode 100644 index c8cdac574..000000000 --- a/dasCore/nbproject/project.properties +++ /dev/null @@ -1,87 +0,0 @@ -annotation.processing.enabled=true -annotation.processing.enabled.in.editor=false -annotation.processing.processors.list= -annotation.processing.run.all.processors=true -application.args= -application.title=dasCore -application.vendor=jbf -build.classes.dir=${build.dir}/classes -build.classes.excludes=**/*.java,**/*.form -# This directory is removed when the project is cleaned: -build.dir=build -build.generated.sources.dir=${build.dir}/generated-sources -# Only compile against the classpath explicitly listed here: -build.sysclasspath=ignore -build.test.classes.dir=${build.dir}/test/classes -build.test.results.dir=${build.dir}/test/results -debug.classpath=\ - ${run.classpath} -debug.test.classpath=\ - ${run.test.classpath} -# This directory is removed when the project is cleaned: -dist.dir=dist -dist.jar=${dist.dir}/dasCore.jar -dist.javadoc.dir=${dist.dir}/javadoc -endorsed.classpath= -excludes= -file.reference.dasCore-src=src -includes=** -jar.compress=true -javac.classpath=\ - ${libs.swing-layout.classpath}:\ - ${libs.iText1.3.classpath}:\ - ${libs.BatikSVGGen1.5.classpath}:\ - ${libs.ApacheCommons.classpath}:\ - ${reference.dasCoreDatum.jar}:\ - ${reference.dasCoreUtil.jar}:\ - ${reference.QDataSet.jar} -# Space-separated list of extra javac options -javac.compilerargs= -javac.deprecation=true -javac.processorpath=\ - ${javac.classpath} -javac.source=1.7 -javac.target=1.7 -javac.test.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir}:\ - ${libs.junit.classpath} -javadoc.additionalparam= -javadoc.author=false -javadoc.encoding= -javadoc.noindex=false -javadoc.nonavbar=false -javadoc.notree=false -javadoc.private=false -javadoc.splitindex=true -javadoc.use=true -javadoc.version=false -javadoc.windowtitle=das2 -# Property libs.absolutelayout.classpath is set here just to make sharing of project simpler. -# The library definition has always preference over this property. -libs.absolutelayout.classpath=../../../../../Program Files/netbeans-5.5beta2-1/ide7/modules/ext/AbsoluteLayout.jar -# Property libs.swing-layout.classpath is set here just to make sharing of project simpler. -# The library definition has always preference over this property. -libs.swing-layout.classpath=../../../../../Program Files/netbeans-5.5beta2-1/platform6/modules/ext/swing-layout-1.0.jar -main.class=test.components.DemoMultiValuePropertyEditor -manifest.file=manifest.mf -mkdist.disabled=false -platform.active=default_platform -project.dasCoreDatum=../dasCoreDatum -project.dasCoreUtil=../dasCoreUtil -project.QDataSet=../QDataSet -reference.dasCoreDatum.jar=${project.dasCoreDatum}/dist/dasCoreDatum.jar -reference.dasCoreUtil.jar=${project.dasCoreUtil}/dist/dasCoreUtil.jar -reference.QDataSet.jar=${project.QDataSet}/dist/QDataSet.jar -run.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -# Space-separated list of JVM arguments used when running the project -# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value -# or test-sys-prop.name=value to set system properties for unit tests): -run.jvmargs=-Xms256m -Xmx512m -run.test.classpath=\ - ${javac.test.classpath}:\ - ${build.test.classes.dir} -src.dir=${file.reference.dasCore-src} -test.test.dir=${file.reference.dasCore-test} diff --git a/dasCore/nbproject/project.xml b/dasCore/nbproject/project.xml deleted file mode 100644 index 50782c03d..000000000 --- a/dasCore/nbproject/project.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - org.netbeans.modules.java.j2seproject - - - dasCore - 1.6.5 - - - - - - - ./lib/nblibraries.properties - - - - QDataSet - jar - - jar - clean - jar - - - dasCoreDatum - jar - - jar - clean - jar - - - dasCoreUtil - jar - - jar - clean - jar - - - - diff --git a/dasCore/platver.java b/dasCore/platver.java new file mode 100644 index 000000000..1a8a613c8 --- /dev/null +++ b/dasCore/platver.java @@ -0,0 +1,9 @@ +public class platver { + + public static void main(String[] args){ + + String sVersion = System.getProperty("java.specification.version"); + System.out.print(sVersion + '\n'); + System.exit(0); + } +} diff --git a/dasCore/pom.xml b/dasCore/pom.xml new file mode 100644 index 000000000..bf129f33a --- /dev/null +++ b/dasCore/pom.xml @@ -0,0 +1,127 @@ + + 4.0.0 + + org.das2 + dasCore + 2.2 + jar + + dasCore + http://maven.apache.org + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.0.2 + + 1.8 + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.6 + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9 + + + attach-javadocs + + jar + + + -Xdoclint:none + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar + + + + + + + + + + http://repo1.maven.org/maven2/ + swing-layout + default + Repository for library Library[swing-layout] + + + + + true + false + org.das2.releases + Das2 Releases + http://das2.org/maven2/releases + + + + + UTF-8 + + + + + junit + junit + 3.8.1 + test + + + org.swinglabs + swing-layout + 1.0.3 + + + com.lowagie + itext + 2.0.7 + + + batik + batik-svggen + 1.5 + + + batik + batik-awt-util + 1.5 + + + batik + batik-util + 1.5 + + + diff --git a/dasCore/src/images/das2logo-130.png b/dasCore/src/images/das2logo-130.png new file mode 100644 index 000000000..010564be3 Binary files /dev/null and b/dasCore/src/images/das2logo-130.png differ diff --git a/dasCoreUtil/src/images/das2logo-64.png b/dasCore/src/images/das2logo-64.png similarity index 100% rename from dasCoreUtil/src/images/das2logo-64.png rename to dasCore/src/images/das2logo-64.png diff --git a/dasCore/src/images/icons/eventsBar.png b/dasCore/src/images/icons/eventsBar.png deleted file mode 100644 index a21191328..000000000 Binary files a/dasCore/src/images/icons/eventsBar.png and /dev/null differ diff --git a/dasCore/src/images/icons/pitchAngleDistribution.png b/dasCore/src/images/icons/pitchAngleDistribution.png deleted file mode 100644 index d00e4ea76..000000000 Binary files a/dasCore/src/images/icons/pitchAngleDistribution.png and /dev/null differ diff --git a/dasCore/src/images/icons/rebin.altNearestNeighbor.png b/dasCore/src/images/icons/rebin.altNearestNeighbor.png deleted file mode 100644 index 480ea145f..000000000 Binary files a/dasCore/src/images/icons/rebin.altNearestNeighbor.png and /dev/null differ diff --git a/dasCore/src/images/icons/rebin.interpXbinY.png b/dasCore/src/images/icons/rebin.interpXbinY.png deleted file mode 100644 index 6ca692812..000000000 Binary files a/dasCore/src/images/icons/rebin.interpXbinY.png and /dev/null differ diff --git a/dasCore/src/images/icons/rebin.lanlNearestNeighbor.png b/dasCore/src/images/icons/rebin.lanlNearestNeighbor.png deleted file mode 100644 index 6ca692812..000000000 Binary files a/dasCore/src/images/icons/rebin.lanlNearestNeighbor.png and /dev/null differ diff --git a/dasCore/src/images/icons/rebin.oldNearestNeighbor.png b/dasCore/src/images/icons/rebin.oldNearestNeighbor.png deleted file mode 100644 index 480ea145f..000000000 Binary files a/dasCore/src/images/icons/rebin.oldNearestNeighbor.png and /dev/null differ diff --git a/dasCore/src/main/java/org/das2/CancelledOperationException.java b/dasCore/src/main/java/org/das2/CancelledOperationException.java new file mode 100644 index 000000000..9c2b306c2 --- /dev/null +++ b/dasCore/src/main/java/org/das2/CancelledOperationException.java @@ -0,0 +1,47 @@ +/* File: CancelledOperationException.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on January 20, 2004, 11:33 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2; + +import java.io.InterruptedIOException; + +/** + * + * @author eew + */ +public class CancelledOperationException extends DasException { + + /** Creates a new instance of CancelledOperationException */ + public CancelledOperationException() { + super(); + } + + public CancelledOperationException(String message) { + } + + public CancelledOperationException(InterruptedIOException iioe) { + super(iioe.getMessage()); + initCause(iioe); + } + +} diff --git a/dasCore/src/main/java/org/das2/DasApplication.java b/dasCore/src/main/java/org/das2/DasApplication.java new file mode 100755 index 000000000..ac7c8a79e --- /dev/null +++ b/dasCore/src/main/java/org/das2/DasApplication.java @@ -0,0 +1,415 @@ +/* File: DasApplication.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2; + +import org.das2.dataset.LimitSizeBytesDataSetCache; +import org.das2.dataset.NullDataSetCache; +import org.das2.system.DefaultExceptionHandler; +import org.das2.system.DasLogger; +import org.das2.system.ExceptionHandler; +import org.das2.system.LoggerId; +import org.das2.system.NullMonitorFactory; +import org.das2.system.MonitorFactory; +import org.das2.system.DefaultMonitorFactory; +import org.das2.system.ThrowRuntimeExceptionHandler; +import org.das2.util.Splash; +import org.das2.util.DasExceptionHandler; +import org.das2.util.ClassMap; +import org.das2.client.InputStreamMeter; +import org.das2.dataset.DataSetCache; +import org.das2.graph.DasAnnotation; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasColorBar; +import org.das2.graph.DasColumn; +import org.das2.graph.DasPlot; +import org.das2.graph.DasRow; +import java.awt.event.*; +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.*; +import java.util.prefs.*; +import javax.swing.*; + +/** + * DasApplication object manages per-application resources, like object name space, + * dataset caching, progress monitoring, exception handling and a network speed limiter. + * + * @author Edward West + */ +public class DasApplication { + + private static DasApplication DEFAULT; + + private JFrame mainFrame; + + /** + * three-state register for keeping track of is applet: null, TRUE, FALSE + * null->try to detect. + * TRUE|FALSE->explicit setting by application + */ + private Boolean applet; + + /** + * three-state register for keeping track of headless: null, TRUE, FALSE + * null->try to detect. + * TRUE|FALSE->explicit setting by application + * in headless, we assume non-interactive. progress monitor factory always returns ProgressMonitor.NULL, dataSetCache is trivial. + * + */ + private Boolean headless; // tristate: null, TRUE, FALSE + + private NameContext nameContext; + + private DataSetCache dataSetCache; + + public Logger getLogger() { + return DasLogger.getLogger(); + } + + /** + * @deprecated use DasLogger.getLogger( LoggerId ) + */ + public Logger getLogger( LoggerId id ) { + return DasLogger.getLogger(id); + } + + /** Creates a new instance of DasApplication */ + private DasApplication() { + nameContext = new NameContext(); + applet= null; + } + + public NameContext getNameContext() { + return nameContext; + } + + static ClassMap classNameMap= new ClassMap(); + static { + classNameMap.put( DasPlot.class, "plot" ); + classNameMap.put( DasAxis.class, "axis" ); + classNameMap.put( DasColorBar.class, "colorbar" ); + classNameMap.put( DasRow.class, "row"); + classNameMap.put( DasColumn.class, "column"); + classNameMap.put( DasAnnotation.class, "annotation"); + classNameMap.put( Object.class, "object" ); + classNameMap.put( DasCanvasComponent.class, "canvasComponent" ); + classNameMap.put( DasCanvas.class, "canvas" ); + } + + private Map hitsMap= new HashMap(); + + // note that only CanvasComponents have a name. + public String suggestNameFor( Object c ) { + String type= (String)classNameMap.get( c.getClass() ); + Integer hits= (Integer)hitsMap.get(type); + int ihits; + if ( hits==null ) { + ihits=0; + } else { + ihits= (hits.intValue())+1; + } + hitsMap.put( type, new Integer(ihits)); + return type+"_"+ihits; + } + + public static DasApplication getDefaultApplication() { + if ( DEFAULT==null ) { + DEFAULT= new DasApplication(); + } + return DEFAULT; + } + + /** + * nasty, evil method for releasing resources on a server. DO NOT USE THIS!!!! + */ + public static void resetDefaultApplication() { + DEFAULT= null; + } + + + public final boolean isApplet() { + if ( applet==null ) { + return Thread.currentThread().getContextClassLoader().getClass().getName().indexOf("plugin") > -1; + } else { + return applet.booleanValue(); + } + } + + /** + * check the security manager to see if all permissions are allowed, + * True indicates is not an applet running in a sandbox. + * @return true if all permissions are allowed + */ + public static boolean hasAllPermission() { + try { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new java.security.AllPermission()); + } + return true; + } catch ( SecurityException ex ) { + return false; + } + } + + /** + * support restricted security environment by checking permissions before + * checking property. + * @param name + * @param deft + * @return + */ + public static String getProperty( String name, String deft ) { + try { + return System.getProperty(name, deft); + } catch ( SecurityException ex ) { + return deft; + } + } + + + // force the application state to be applet or application + public void setApplet( boolean applet ) { + this.applet= Boolean.valueOf(applet); + } + + public void setReloadLoggingProperties( boolean v ) { + if ( v ) { + try { + DasLogger.reload(); + DasLogger.printStatus(); + } catch ( IOException e ) { + DasExceptionHandler.handle(e); + } + } + } + + public boolean isReloadLoggingProperties() { + return false; + } + + private static boolean isX11() { + String osName= System.getProperty( "os.name" ); // applet okay + return "SunOS".equals( osName ) + || "Linux".equals( osName ); + } + + /** + * returns the location of the local directory sandbox. For example, + * The web filesystem object downloads temporary files to here, logging + * properties file, etc. + * + * Assume that this File is local, so I/O is quick, and that the process + * has write access to this area. + * For definition, assume that at least 1Gb of storage is available as + * well. + */ + public static File getDas2UserDirectory() { + File local; + // for applets, if we are running from a disk, then it is okay to write local files, but we can't check permissions + if ( DasApplication.getProperty("user.name", "Web").equals("Web") ) { + local= new File("/tmp"); + } else { + local= new File( System.getProperty("user.home") ); + } + local= new File( local, ".das2" ); + return local; + } + + public static boolean isHeadAvailable() { + return true; + /* + return ( System.getProperty( "awt.toolkit" ) != null ); + /* + //boolean headAvailable= !java.awt.GraphicsEnvironment.isHeadless(); + boolean result= false; + if ( isApplet() ) result= true; + getDefaultApplication().getLogger().fine( System.getProperty( "os.name" ) ); + String osName= System.getProperty( "os.name" ); + if ( "Windows".equals( osName ) ) { + result= true; + } else if ( "Windows XP".equals( osName ) ) { + result= true; + } else if ( isX11() ) { + String DISPLAY= System.getProperty( "DISPLAY" ); + getDefaultApplication().getLogger().fine( System.getProperty( "DISPLAY" ) ); + if ( "".equals(DISPLAY) ) { + result= false; + } else { + result= true; + } + } + return result; + */ + } + + public boolean isHeadless() { +/* if ( !headAvailable() && !"true".equals(System.getProperty("headless")) ) { + getLogger().info("setting headless to true"); + setHeadless( true ); + } */ + if ( headless!=null ) { + return headless.booleanValue(); + } else { + return "true".equals(DasApplication.getProperty("java.awt.headless","false")); + } + } + + public void setHeadless( boolean headless ) { + this.headless= Boolean.valueOf(headless); + if ( headless ) { + System.setProperty("java.awt.headless","true"); + } else { + if ( ! isHeadAvailable() ) { + throw new IllegalArgumentException( "attempt to unset headless when environment is headless." ); + } + System.setProperty("java.awt.headless","false"); + } + } + + + InputStreamMeter meter; + public InputStreamMeter getInputStreamMeter() { + if ( meter==null ) { + meter= new InputStreamMeter(); + } + return meter; + } + + /** + * @deprecated use createMainFrame( String title, Container container ); + */ + public JFrame createMainFrame( java.awt.Container container ) { + return createMainFrame( "Das2", container ); + } + + public JFrame createMainFrame( String title, java.awt.Container container ) { + JFrame frame= createMainFrame(title); + frame.getContentPane().add(container); + frame.pack(); + frame.setVisible(true); + return frame; + } + + /** + * @deprecated use createMainFrame(String title) + */ + public JFrame createMainFrame() { + return createMainFrame("Das2"); + } + + public JFrame createMainFrame( String title ) { + mainFrame= new JFrame(title); + final Preferences prefs= Preferences.userNodeForPackage(DasApplication.class); + int xlocation= prefs.getInt( "xlocation", 20 ); + int ylocation= prefs.getInt( "ylocation", 20 ); + mainFrame.setLocation(xlocation, ylocation); + mainFrame.addWindowListener( new WindowAdapter() { + public void windowClosing( WindowEvent e ) { + quit(); + } + } ); + mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + System.setProperty( "sun.awt.exception.handler", DasExceptionHandler.class.getName() ); + return mainFrame; + } + + public JFrame getMainFrame() { + return this.mainFrame; + } + + public void quit() { + final Preferences prefs= Preferences.userNodeForPackage(DasApplication.class); + prefs.putInt( "xlocation", mainFrame.getLocation().x ); + prefs.putInt( "ylocation", mainFrame.getLocation().y ); + System.out.println("bye!"+mainFrame.getLocation()); + System.exit(0); + } + + /** + * Holds value of property interactive. + */ + private boolean interactive=true; + + /** + * Getter for property interactive. + * @return Value of property interactive. + */ + public boolean isInteractive() { + return this.interactive; + } + + /** + * Setter for property interactive. + * @param interactive New value of property interactive. + */ + public void setInteractive(boolean interactive) { + this.interactive = interactive; + } + + public String getDas2Version() { + return Splash.getVersion(); + } + + private MonitorFactory monitorFactory; + + public MonitorFactory getMonitorFactory() { + if ( monitorFactory==null ) { + if ( !isHeadless() ) { + monitorFactory= new DefaultMonitorFactory(); + } else { + monitorFactory= new NullMonitorFactory(); + } + } + return monitorFactory; + } + + public DataSetCache getDataSetCache() { + if ( dataSetCache==null ) { + if ( isHeadless() ) { + dataSetCache= new NullDataSetCache(); + } else { + dataSetCache= new LimitSizeBytesDataSetCache(30000000); + } + } + return dataSetCache; + } + + ExceptionHandler exceptionHandler; + + public ExceptionHandler getExceptionHandler() { + if ( exceptionHandler==null ) { + if ( isHeadless() ) { + exceptionHandler= new ThrowRuntimeExceptionHandler(); + } else { + exceptionHandler= new DefaultExceptionHandler(); + } + } + return exceptionHandler; + } + +} diff --git a/dasCore/src/main/java/org/das2/DasException.java b/dasCore/src/main/java/org/das2/DasException.java new file mode 100644 index 000000000..7acd248cd --- /dev/null +++ b/dasCore/src/main/java/org/das2/DasException.java @@ -0,0 +1,48 @@ +/* File: DasException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2; + +/** + * + * @author jbf + */ +public class DasException extends java.lang.Exception { + + /** Creates a new instance of DasException */ + public DasException() { + super(); + } + + public DasException(String message) { + super(message); + } + + public DasException( Throwable ex) { + super(ex); + } + + public DasException( String msg, Throwable ex) { + super( msg, ex ); + } +} diff --git a/dasCore/src/main/java/org/das2/DasIOException.java b/dasCore/src/main/java/org/das2/DasIOException.java new file mode 100644 index 000000000..7b3746092 --- /dev/null +++ b/dasCore/src/main/java/org/das2/DasIOException.java @@ -0,0 +1,55 @@ +/* File: DasIOException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2; + +/** + * + * @author jbf + */ +public class DasIOException extends org.das2.DasException { + + /** + * Creates a new instance of DasIOException without detail message. + */ + public DasIOException() { + } + + + /** + * Constructs an instance of DasIOException with the specified detail message. + * @param msg the detail message. + */ + public DasIOException(String msg) { + super(msg); + } + + public DasIOException(String msg, Throwable t) { + super(msg,t); + } + + public DasIOException(java.io.IOException ex) { + super( ex.getMessage() ); + initCause(ex); + } +} diff --git a/dasCore/src/main/java/org/das2/DasNameException.java b/dasCore/src/main/java/org/das2/DasNameException.java new file mode 100644 index 000000000..a39b758cd --- /dev/null +++ b/dasCore/src/main/java/org/das2/DasNameException.java @@ -0,0 +1,40 @@ +/* File: DasNameException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2; + +/** + * + * @author eew + */ +public class DasNameException extends org.das2.DasException { + + /** Creates a new instance of DasNameException */ + public DasNameException() { + } + + public DasNameException(String message) { + super(message); + } + +} diff --git a/dasCore/src/main/java/org/das2/DasProperties.java b/dasCore/src/main/java/org/das2/DasProperties.java new file mode 100755 index 000000000..baf86e0fe --- /dev/null +++ b/dasCore/src/main/java/org/das2/DasProperties.java @@ -0,0 +1,308 @@ +/* File: DasProperties.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2; + +import javax.swing.*; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableModel; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Properties; +import java.util.logging.*; + +public class DasProperties extends Properties { + + // Contains the global user-configurable parameters that are + // persistent between sessions. + + private RenderingHints hints; + private boolean antiAlias; + private boolean visualCues; + private Logger logger; + private static ArrayList propertyOrder; + private static Editor editor; + private static JFrame jframe; + + private DasProperties() { + super(); + hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + setDefaults(); + propertyOrder= new ArrayList(); + if ( DasApplication.hasAllPermission() ) readPersistentProperties(); + logger= Logger.getLogger("das2"); + setPropertyOrder(); + } + + private void setPropertyOrder() { + propertyOrder.add(0,"username"); + propertyOrder.add(1,"password"); + propertyOrder.add(2,"debugLevel"); + propertyOrder.add(3,"antiAlias"); + propertyOrder.add(4,"visualCues"); + for (Iterator i= this.keySet().iterator(); i.hasNext(); ) { + String s= (String)i.next(); + if (!propertyOrder.contains(s)) { + propertyOrder.add(s); + } + } + } + + private void setDefaults() { + setProperty("username",""); + setProperty("password",""); + setProperty("debugLevel","endUser"); + setProperty("antiAlias","off"); + setProperty("visualCues","off"); + setProperty("defaultServer","http://www-pw.physics.uiowa.edu/das/dasServer"); + } + + public static RenderingHints getRenderingHints() { + return instance.hints; + } + + public static Logger getLogger() { + return instance.logger; + } + + public static DasProperties getInstance() { + return instance; + } + + private static class DasPropertiesTableModel extends AbstractTableModel { + public int getColumnCount() { return 2; } + public int getRowCount() { + return instance.size(); + } + public Object getValueAt(int row, int col) { + String propertyName= (String)propertyOrder.get(row); + String value; + if (col==0) { + value= propertyName; + } else { + value= instance.getProperty(propertyName); + if (propertyName.equals("password")) { + value=""; + } + } + return value; + } + public void setValueAt(Object value, int row, int col) { + String propertyName= (String)propertyOrder.get(row); + if (propertyName.equals("password")) { + if (!value.toString().equals("")) { + value= org.das2.util.Crypt.crypt(value.toString()); + } + } else if ( propertyName.equals("debugLevel") ) { + String debugLevel= value.toString(); + if (debugLevel.equals("endUser")) { + Logger.getLogger("").setLevel(Level.WARNING); + Logger.getLogger("das2").setLevel(Level.WARNING); + } else if (debugLevel.equals("dasDeveloper")) { + Logger.getLogger("").setLevel(Level.FINE); + Logger.getLogger("das2").setLevel(Level.FINE); + } + else instance.logger.setLevel(Level.parse(debugLevel)); + org.das2.util.DasDie.setDebugVerbosityLevel(value.toString()); + } + instance.setProperty(propertyName,value.toString()); + editor.setDirty(true); + } + + public boolean isCellEditable(int row, int col) { return (col==1); } + } + + private static TableModel getTableModel() { + return new DasPropertiesTableModel(); + } + + private static JTable getJTable() { + return new JTable(getTableModel()) { + {setDefaultRenderer(Object.class,new DefaultTableCellRenderer(){ + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + String propertyName= (String)propertyOrder.get(row); + if (propertyName.equals("password") && column==1 ) { + return super.getTableCellRendererComponent( table, "* * * *", isSelected, hasFocus, row, column ); + } else { + return super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column ); + } + } + });} + public TableCellEditor getCellEditor(int row, int col) { + String propertyName= (String)propertyOrder.get(row); + if (propertyName.equals("password")) { + return new DefaultCellEditor(new JPasswordField()); + } else if (propertyName.equals("debugLevel")) { + String[] data= {"endUser","dasDeveloper"}; + return new DefaultCellEditor(new JComboBox(data)); + } else if (propertyName.equals("antiAlias")) { + String[] data= {"on","off"}; + return new DefaultCellEditor(new JComboBox(data)); + } else if (propertyName.equals("visualCues")) { + String[] data= {"on","off"}; + return new DefaultCellEditor(new JComboBox(data)); + } else { + return super.getCellEditor(row,col); + } + } + }; + } + + + private static TableCellEditor getTableCellEditor() { + return new DefaultCellEditor(new JTextField()) { + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + String propertyName= (String)propertyOrder.get(row); + if (propertyName.equals("password")) { + return new JPasswordField(); + } else if (propertyName.equals("debugLevel")) { + String[] data= {"endUser","dasDeveloper"}; + return new JList(data); + } else { + return super.getTableCellEditorComponent(table,value,isSelected,row,column); + } + } + }; + } + + public static class Editor extends JPanel implements ActionListener { + JButton saveButton; + + Editor() { + super(); + setLayout(new BoxLayout(this,BoxLayout.Y_AXIS)); + + JTable jtable= getJTable(); + add(jtable); + + JPanel controlPanel= new JPanel(); + controlPanel.setLayout(new BoxLayout(controlPanel,BoxLayout.X_AXIS)); + + controlPanel.add(Box.createHorizontalGlue()); + + JButton b; + saveButton= new JButton("Save"); + saveButton.setActionCommand("Save"); + saveButton.addActionListener(this); + saveButton.setToolTipText("save to $HOME/.das2rc"); + + controlPanel.add(saveButton); + + b= new JButton("Dismiss"); + b.addActionListener(this); + controlPanel.add(b); + + add( Box.createVerticalGlue() ); + + add(controlPanel); + + } + + public void actionPerformed(ActionEvent e) { + String command= e.getActionCommand(); + if (command.equals("Save")) { + instance.writePersistentProperties(); + setDirty(false); + } else if (command.equals("Dismiss")) { + jframe.dispose(); + } + + } + + public void setDirty(boolean dirty){ + if (dirty) { + saveButton.setText("Save*"); + } else { + saveButton.setText("Save"); + } + } + } + + public static void showEditor() { + jframe= new JFrame("Das Properities"); + editor= new Editor(); + + jframe.setSize(400,300); + jframe.setContentPane(editor); + jframe.setVisible(true); + } + + private static final DasProperties instance = new DasProperties(); + + public void readPersistentProperties() { + + try { + String file= System.getProperty("user.home")+System.getProperty("file.separator")+".das2rc"; + File f= new File(file); + + if (f.canRead()) { + try { + InputStream in= new FileInputStream(f); + load(in); + in.close(); + } catch (IOException e) { + org.das2.util.DasExceptionHandler.handle(e); + } + } else { + try { + OutputStream out= new FileOutputStream(f); + store(out,""); + out.close(); + } catch (IOException e) { + org.das2.util.DasExceptionHandler.handle(e); + } + } + } catch ( SecurityException ex ) { + ex.printStackTrace(); + } + } + + public void writePersistentProperties() { + + String file= System.getProperty("user.home")+System.getProperty("file.separator")+".das2rc"; + File f= new File(file); + + if (f.canWrite()) { + org.das2.util.DasDie.println("Attempt to write .das2rc..."); + try { + OutputStream out= new FileOutputStream(f); + store(out,""); + out.close(); + } catch (IOException e) { + org.das2.util.DasExceptionHandler.handle(e); + } + } else { + DasException e= new org.das2.DasIOException("Can't write to file "+f); + org.das2.util.DasExceptionHandler.handle(e); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/DasPropertyException.java b/dasCore/src/main/java/org/das2/DasPropertyException.java new file mode 100644 index 000000000..dec265786 --- /dev/null +++ b/dasCore/src/main/java/org/das2/DasPropertyException.java @@ -0,0 +1,99 @@ +/* File: DasPropertyException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2; + +public class DasPropertyException extends org.das2.DasException { + + public static final class MessageType { + private final String message; + private MessageType(String message) { + this.message = message; + } + public String toString() { + return message; + } + } + + public static final MessageType NOT_DEFINED + = new MessageType("The property is undefined for ."); + public static final MessageType READ_ONLY + = new MessageType("The property of is read-only."); + public static final MessageType TYPE_MISMATCH + = new MessageType("Type mismatch: of cannot be set to "); + public static final MessageType NOT_INDEXED + = new MessageType("The property of is not an indexed property"); + + private String propertyName; + private String objectName; + private String value; + private MessageType type; + + public DasPropertyException(MessageType type, String propertyName, String objectName, String value) { + this.type = type; + this.propertyName = propertyName; + this.objectName = objectName; + this.value = value; + } + + public DasPropertyException(MessageType type, String propertyName, String objectName) { + this(type, propertyName, objectName, null); + } + + public DasPropertyException(MessageType type) { + this(type, null, null); + } + + public String getPropertyName() { + return propertyName; + } + + public void setPropertyName(String name) { + propertyName = name; + } + + public String getObjectName() { + return objectName; + } + + public void setObjectName(String name) { + objectName = name; + } + + public MessageType getType() { + return type; + } + + public void setMessageType(MessageType type) { + this.type = type; + } + + public String getMessage() { + String message = type.toString(); + String objStr = objectName == null ? "" : objectName; + String ptyStr = propertyName == null ? "" : propertyName; + String valueStr = value == null ? "" : value; + return message.replaceAll("", objStr) + .replaceAll("", ptyStr) + .replaceAll("", valueStr); + } +} diff --git a/dasCore/src/main/java/org/das2/NameContext.java b/dasCore/src/main/java/org/das2/NameContext.java new file mode 100644 index 000000000..11851c3a1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/NameContext.java @@ -0,0 +1,365 @@ +/* File: NameContext.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2; + +import org.das2.datum.Datum; +import org.das2.datum.TimeUtil; +import org.das2.beans.BeansUtil; +import org.das2.dasml.ParsedExpression; +import org.das2.dasml.ParsedExpressionException; + +import java.beans.*; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** An instance of NameContext defines the name space for a + * dasml/das2 application. Methods for querying values of properties are + * also provided. + * + * @author eew + */ +public class NameContext { + + private static final String SIMPLE_NAME_STRING = "[A-Za-z][A-Za-z0-9_]*"; + private static final String INDEX_STRING = "0|[1-9][0-9]*"; + private static final String INDEXED_NAME_STRING + = "(" + SIMPLE_NAME_STRING + ")" + "\\[(" + INDEX_STRING + ")\\]"; + private static final String QUALIFIED_NAME_STRING + = SIMPLE_NAME_STRING + "(\\." + SIMPLE_NAME_STRING + "|\\." + INDEXED_NAME_STRING + ")*"; + + public static final Pattern SIMPLE_NAME = Pattern.compile(SIMPLE_NAME_STRING); + public static final Pattern INDEXED_NAME = Pattern.compile(INDEXED_NAME_STRING); + public static final Pattern QUALIFIED_NAME = Pattern.compile(QUALIFIED_NAME_STRING); + public static final Pattern refPattern = Pattern.compile("\\$\\{([^\\}]+)\\}"); + public static final Pattern intPattern = Pattern.compile("-?(0|[1-9][0-9]*)"); + public static final Pattern floatPattern = Pattern.compile("-?[0-9]*(\\.[0-9]*)?([eE]-?[0-9]+)?"); + + + private Map nameMap; + private Map propertyMap; + + /** Creates a new instance of NameContext */ + NameContext() { + nameMap = new HashMap(); + propertyMap = new HashMap(); + } + + /** Associates a value with a name in this context. The name + * parameter must being with a letter and can only consist of alphanumeric + * characters and '_'. + * @param name the name for the value to be associated with + * @param value the value being named + */ + public void put(String name, Object value) throws DasNameException { + Matcher m = SIMPLE_NAME.matcher(name); + if (m.matches()) { + nameMap.put(name, new WeakReference( value ) ); + } + else { + throw new DasNameException(name + " must match " + SIMPLE_NAME_STRING); + } + } + + public Object get(String name) throws DasPropertyException, InvocationTargetException { + if (name == null) { + throw new NullPointerException("name"); + } + Matcher m = SIMPLE_NAME.matcher(name); + if (m.matches()) { + return ((WeakReference)nameMap.get(name)).get(); + } + int index = name.lastIndexOf('.'); + Object obj = get(name.substring(0, index)); + String property = name.substring(index + 1); + m = INDEXED_NAME.matcher(property); + if (m.matches()) { + property = m.group(1); + index = Integer.parseInt(m.group(2)); + return getIndexedPropertyValue(obj, property, index); + } + else { + return getPropertyValue(obj, property); + } + } + + public void set(String name, Object value) throws InvocationTargetException, ParsedExpressionException, DasPropertyException, DasNameException { + if (name == null) { + throw new NullPointerException("name"); + } + Matcher m = SIMPLE_NAME.matcher(name); + if (m.matches()) { + put(name, value); + } + int index = name.lastIndexOf('.'); + Object obj = get(name.substring(0, index)); + String property = name.substring(index + 1); + m = INDEXED_NAME.matcher(property); + if (m.matches()) { + property = m.group(1); + index = Integer.parseInt(m.group(2)); + setIndexedPropertyValue(obj, property, index, value); + } + else { + setPropertyValue(obj, property, value); + } + } + + public Object getPropertyValue(Object obj, String property) throws DasPropertyException, InvocationTargetException { + try { + Class type = obj.getClass(); + maybeLoadPropertiesForClass(type); + Map map = (Map)propertyMap.get(type); + PropertyDescriptor pd = (PropertyDescriptor)map.get(property); + if (pd == null) { + throw new DasPropertyException(DasPropertyException.NOT_DEFINED, null, property); + } + Method readMethod = pd.getReadMethod(); + return readMethod.invoke(obj); + } + catch (IllegalAccessException iae) { + throw new RuntimeException(iae); + } + } + + public Object getIndexedPropertyValue(Object obj, String property, int index) throws DasPropertyException, InvocationTargetException { + try { + Class type = obj.getClass(); + maybeLoadPropertiesForClass(type); + Map map = (Map)propertyMap.get(type); + PropertyDescriptor pd = (PropertyDescriptor)map.get(property); + if (pd == null) { + throw new DasPropertyException(DasPropertyException.NOT_DEFINED, null, property); + } + if (!(pd instanceof IndexedPropertyDescriptor)) { + throw new DasPropertyException(DasPropertyException.NOT_INDEXED, null, property); + } + IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor)pd; + Method readMethod = ipd.getIndexedReadMethod(); + return readMethod.invoke(obj, new Object[] { new Integer(index) }); + } + catch (IllegalAccessException iae) { + throw new RuntimeException(iae); + } + } + + public void setPropertyValue(Object obj, String property, Object value) throws InvocationTargetException, ParsedExpressionException, DasPropertyException { + try { + Class type = obj.getClass(); + maybeLoadPropertiesForClass(type); + Map map = (Map)propertyMap.get(type); + PropertyDescriptor pd = (PropertyDescriptor)map.get(property); + if (pd == null) { + throw new DasPropertyException(DasPropertyException.NOT_DEFINED, null, property); + } + Method writeMethod = pd.getWriteMethod(); + if (writeMethod == null) { + throw new DasPropertyException(DasPropertyException.READ_ONLY, null, property); + } + Class propertyType = pd.getPropertyType(); + if (value instanceof String) { + value = parseValue((String)value, propertyType); + } + if (!propertyType.isInstance(value) + && !(propertyType == boolean.class && value instanceof Boolean) + && !(propertyType == char.class && value instanceof Character) + && !(propertyType == double.class && value instanceof Double) + && !(propertyType == short.class && value instanceof Short) + && !(propertyType == int.class && value instanceof Integer) + && !(propertyType == float.class && value instanceof Float) + && !(propertyType == byte.class && value instanceof Byte) + && !(propertyType == long.class && value instanceof Long)) { + throw new DasPropertyException(DasPropertyException.TYPE_MISMATCH, null, property); + } + writeMethod.invoke(obj, new Object[] { value } ); + } + catch (IllegalAccessException iae) { + throw new RuntimeException(iae); + } + } + + public void setIndexedPropertyValue(Object obj, String property, int index, Object value) throws InvocationTargetException, ParsedExpressionException, DasPropertyException { + try { + Class type = obj.getClass(); + maybeLoadPropertiesForClass(type); + Map map = (Map)propertyMap.get(type); + PropertyDescriptor pd = (PropertyDescriptor)map.get(property); + if (pd == null) { + throw new DasPropertyException(DasPropertyException.NOT_DEFINED, null, property); + } + if (!(pd instanceof IndexedPropertyDescriptor)) { + throw new DasPropertyException(DasPropertyException.NOT_INDEXED, null, property); + } + IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor)pd; + Method writeMethod = ipd.getIndexedWriteMethod(); + if (writeMethod == null) { + throw new DasPropertyException(DasPropertyException.READ_ONLY, null, property); + } + Class propertyType = pd.getPropertyType(); + if (value instanceof String) { + value = parseValue((String)value, propertyType); + } + if (!propertyType.isInstance(value) + && !(propertyType == boolean.class && value instanceof Boolean) + && !(propertyType == char.class && value instanceof Character) + && !(propertyType == double.class && value instanceof Double) + && !(propertyType == short.class && value instanceof Short) + && !(propertyType == int.class && value instanceof Integer) + && !(propertyType == float.class && value instanceof Float) + && !(propertyType == byte.class && value instanceof Byte) + && !(propertyType == long.class && value instanceof Long)) { + throw new DasPropertyException(DasPropertyException.TYPE_MISMATCH, null, property); + } + writeMethod.invoke(obj, new Object[] { new Integer(index), value }); + } + catch (IllegalAccessException iae) { + throw new RuntimeException(iae); + } + } + + private void maybeLoadPropertiesForClass(Class cl) { + try { + if (propertyMap.get(cl) == null) { + BeanInfo info = BeansUtil.getBeanInfo(cl); + HashMap map = new HashMap(); + PropertyDescriptor[] properties = info.getPropertyDescriptors(); + for (int i = 0; i < properties.length; i++) { + if (properties[i].getReadMethod() == null) { + continue; + } + if (properties[i] instanceof IndexedPropertyDescriptor) { + IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor)properties[i]; + if (ipd.getIndexedReadMethod() == null) { + continue; + } + } + map.put(properties[i].getName(), properties[i]); + } + propertyMap.put(cl, map); + } + } + catch (IntrospectionException ie) { + } + } + + public void remove(String name) { + nameMap.remove(name); + } + + /** + * Parses the given String object in an attempt to + * produce the an object of the given type. + * + * @param valueString the given String + * @param type the given type + */ + public Object parseValue(String valueString, Class type) throws org.das2.dasml.ParsedExpressionException, InvocationTargetException, DasPropertyException { + Object parsedValue; + valueString = replaceReferences(valueString); + if (type == String.class) { + return valueString; + } + else if (type == boolean.class || type == Boolean.class) { + if (valueString.equals("true")) return Boolean.TRUE; + if (valueString.equals("false")) return Boolean.FALSE; + ParsedExpression exp = new ParsedExpression(valueString); + Object o = exp.evaluate(this); + if (!(o instanceof Boolean)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a boolean value"); + return o; + } + else if (type == int.class || type == Integer.class) { + if (intPattern.matcher(valueString).matches()) { + return new Integer(valueString); + } + ParsedExpression exp = new ParsedExpression(valueString); + Object o = exp.evaluate(this); + if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); + return (o instanceof Integer ? (Integer)o : new Integer(((Number)o).intValue())); + } + else if (type == long.class || type == Long.class) { + if (intPattern.matcher(valueString).matches()) { + parsedValue = new Long(valueString); + } + ParsedExpression exp = new ParsedExpression(valueString); + Object o = exp.evaluate(this); + if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); + return new Long(((Number)o).longValue()); + } + else if (type == float.class || type == Float.class) { + if (floatPattern.matcher(valueString).matches()) { + parsedValue = new Float(valueString); + } + ParsedExpression exp = new ParsedExpression(valueString); + Object o = exp.evaluate(this); + if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); + return new Float(((Number)o).floatValue()); + } + else if (type == double.class || type == Double.class) { + if (floatPattern.matcher(valueString).matches()) { + parsedValue = new Double(valueString); + } + ParsedExpression exp = new ParsedExpression(valueString); + Object o = exp.evaluate(this); + if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); + return (o instanceof Double ? (Double)o : new Double(((Number)o).doubleValue())); + } + else if (type == Datum.class) { + try { + return TimeUtil.create(valueString); + } + catch ( java.text.ParseException ex ) { + try { + return Datum.create(Double.parseDouble(valueString)); + } + catch (NumberFormatException iae) { + throw new ParsedExpressionException(valueString + " cannot be parsed as a Datum"); + } + } + + } + else { + throw new IllegalStateException(type.getName() + " is not a recognized type"); + } + } + + protected String replaceReferences(String str) throws DasPropertyException, InvocationTargetException { + Matcher matcher = refPattern.matcher(str); + while (matcher.find()) { + String name = matcher.group(1).trim(); + Object value = get(name); + str = matcher.replaceFirst(value.toString()); + matcher.reset(str); + } + return str; + } + + public String toString() { + return getClass().getName() + nameMap.keySet().toString(); + } +} diff --git a/dasCore/src/main/java/org/das2/apiProblems.txt b/dasCore/src/main/java/org/das2/apiProblems.txt new file mode 100644 index 000000000..b958202f4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/apiProblems.txt @@ -0,0 +1,18 @@ +1. event package + 1. mouse events / drag renderers / mouse modules + 1. difficult to reuse existing objects because a lack of composition. + 2. too much typeness in drag renderers, should let composing code enforce typeness. + 3. poor definition of the role of each class. + 4. need definition of how key strokes can be used, which are used by the DMIA which + can the mousemodule use. + +2. system/das package + 1. DasApplication + 1. DasApplication.getDefaultApplication() does not allow for multiple applications to + run within the same java context, as in a web applications server. + 2. Perhaps the application object should be explicitly created, then canvases and JPanels attached to + it. An implicit application object, perhaps the legacy DasApplication.getDefaultApplication() + could be used as well. + + + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/beans/AccessLevelBeanInfo.java b/dasCore/src/main/java/org/das2/beans/AccessLevelBeanInfo.java new file mode 100644 index 000000000..d048e1f38 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/AccessLevelBeanInfo.java @@ -0,0 +1,347 @@ +/* File: AccessLevelBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import org.das2.DasApplication; +import java.beans.*; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * This class is designed to implement access levels for bean properties. + * The system property "edu.uiowa.physics.das.beans.AccessLevelBeanInfo.AccessLevel" will + * determine the access level of the bean. The access levels that are currently supported + * are "ALL" and "END_USER". The access level must be set prior to this class being loaded. + * + * @author Edward West + */ +public abstract class AccessLevelBeanInfo extends SimpleBeanInfo { + + /** + * Type-safe enumeration class used to specify access levels + * for bean properties. + * + * **NOTE TO DEVELOPERS** + * The order parameter for the constructor should not be specifed as a negative number + */ + public static class AccessLevel implements Comparable { + public static final AccessLevel ALL = new AccessLevel("ALL", 0); + public static final AccessLevel DASML = new AccessLevel("DASML", 1000); + public static final AccessLevel END_USER = new AccessLevel("END_USER", 0x7FFF0000); + private String str; + private int order; + private AccessLevel(String str, int order) { + this.str = str; this.order = order; + } + public int compareTo(Object o) { + return order - ((AccessLevel)o).order; + } + public String toString() { + return str; + } + } + + /** + * this level indicates what persistence is allowed. + */ + public static class PersistenceLevel implements Comparable { + public static final PersistenceLevel NONE = new PersistenceLevel( "NONE", 0); + public static final PersistenceLevel TRANSIENT = new PersistenceLevel( "TRANSIENT", 1000 ); + public static final PersistenceLevel PERSISTENT = new PersistenceLevel( "PERSISTENT", 2000); + + private String str; + private int order; + private PersistenceLevel(String str, int order) { + this.str = str; this.order = order; + } + public int compareTo(Object o) { + return order - ((PersistenceLevel)o).order; + } + public String toString() { + return str; + } + } + + private Property[] properties; + private Class beanClass; + private static AccessLevel accessLevel; + private static Object lockObject = new Object(); + + static { + String level = DasApplication.getProperty("edu.uiowa.physics.das.beans.AccessLevelBeanInfo.AccessLevel",null); + if (level==null) { + accessLevel = AccessLevel.ALL; + } else if (level.equals("ALL")) { + accessLevel = AccessLevel.ALL; + } else if (level.equals("DASML")) { + accessLevel = AccessLevel.DASML; + } else if (level.equals("END_USER")) { + accessLevel = AccessLevel.END_USER; + } else { + accessLevel = AccessLevel.ALL; + } + } + + /** + * Returns the access level for AccessLevelBeanInfo objects. + */ + public static AccessLevel getAccessLevel() { + return accessLevel; + } + + /** + * Sets the access level for AccessLevelBeanInfo objects. + */ + public static void setAccessLevel(AccessLevel level) { + synchronized (lockObject) { + accessLevel = level; + } + } + + public static Object getLock() { + return lockObject; + } + + /** + * Creates and instance of AccessLevelBeanInfo. + * Each element of the properties array must be of the type + * Object[] with the following format: + * { propertyName, accessorMethod, mutatorMethod, accessLevel} + * where the elements have the following meaning. + *
    + *
  • propertyName - A String naming the property being specified.
  • + *
  • accessorMethod - A String specifying the name of the read method + * for this property.
  • + *
  • mutatorMethod - A String specifying the name of the write method + * for this property
  • + *
  • accessLevel - A org.das2.beans.AccessLevelBeanInfo.AccessLevel instance specifying + * the access level for this property.
  • + *
+ */ + protected AccessLevelBeanInfo(Property[] properties, Class beanClass) { + this.properties = properties; + this.beanClass = beanClass; + } + + /** + * convenient method that only returns the descriptors for the specified persistence level. + * Also implements the property inheritance. + */ + public PropertyDescriptor[] getPropertyDescriptors( PersistenceLevel persistenceLevel ) { + synchronized (lockObject) { + try { + ArrayList result= new ArrayList(); + int propertyIndex = 0; + for (int index = 0; index < properties.length; index++) { + if (persistenceLevel.compareTo(properties[index].getPersistenceLevel()) <= 0) { + result.add( properties[index].getPropertyDescriptor(beanClass) ); + } + } + BeanInfo[] moreBeanInfos= getAdditionalBeanInfo() ; + if ( moreBeanInfos!=null ) { + for ( int i=0; i0) { + for ( int i=0; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import org.das2.beans.AccessLevelBeanInfo.AccessLevel; +import org.das2.beans.AccessLevelBeanInfo.Property; +import org.das2.graph.DasAxis; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; + +/** + * BeanInfo class for DasColorBar + * + * @author Edward West + */ +public class AttachedLabelBeanInfo extends AccessLevelBeanInfo { + + protected static final Property[] properties = { + new Property("label", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getLabel", "setLabel", null ), + new Property("orientation", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getOrientation", "setOrientation", null ), + new Property("emOffset", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getEmOffet", "setEmOffset", null ), + }; + + public AttachedLabelBeanInfo() { + super(properties, org.das2.graph.AttachedLabel.class); + } + + public BeanInfo[] getAdditionalBeanInfo() { + BeanInfo[] additional = { + new DasCanvasComponentBeanInfo(), + }; + return additional; + + /*try { + BeanInfo[] additional = { + Introspector.getBeanInfo( DasAxis.class ), + }; + return additional; + } catch ( IntrospectionException e ) { + throw new RuntimeException(e); + }*/ + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/BeansUtil.java b/dasCore/src/main/java/org/das2/beans/BeansUtil.java new file mode 100644 index 000000000..c5b557339 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/BeansUtil.java @@ -0,0 +1,284 @@ +/* + * BeanInfoUtil.java + * + * Created on May 31, 2005, 11:49 AM + */ +package org.das2.beans; + +import org.das2.graph.FillStyle; +import org.das2.graph.PsymConnector; +import org.das2.graph.Psym; +import org.das2.graph.PlotSymbol; +import org.das2.components.DatumEditor; +import org.das2.components.propertyeditor.BooleanEditor; +import org.das2.components.propertyeditor.EnumerationEditor; +import org.das2.components.propertyeditor.ColorEditor; +import org.das2.components.DatumRangeEditor; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.datum.NumberUnits; +import org.das2.DasApplication; +import org.das2.system.DasLogger; +import org.das2.util.ClassMap; +import java.awt.Color; +import java.beans.*; +import java.util.*; +import java.util.logging.Logger; + +/** + * + * @author Jeremy + */ +public class BeansUtil { + + static Logger log = DasLogger.getLogger(DasLogger.SYSTEM_LOG); + + + static { + String[] beanInfoSearchPath = {"org.das2.beans", "sun.beans.infos"}; + if (DasApplication.hasAllPermission()) { + Introspector.setBeanInfoSearchPath(beanInfoSearchPath); + } + } + static ClassMap editorRegistry = new ClassMap(); + + /** + * see BeanBindingDemo2.java + */ + public static void registerEditor(Class beanClass, Class editorClass) { + if ( DasApplication.hasAllPermission() ) { + PropertyEditorManager.registerEditor(beanClass, editorClass); + editorRegistry.put(beanClass, editorClass); + } else { + + } + } + /** + * See that the known editors are registered with the PropertyEditorManager + */ + + + static { + if (DasApplication.hasAllPermission()) { + registerEditor(Datum.class, DatumEditor.class); + registerEditor(DatumRange.class, DatumRangeEditor.class); + registerEditor(Color.class, ColorEditor.class); + registerEditor(Units.class, UnitsEditor.class); + registerEditor(NumberUnits.class, UnitsEditor.class); + registerEditor(Boolean.TYPE, BooleanEditor.class); + registerEditor(Boolean.class, BooleanEditor.class); + registerEditor(PsymConnector.class, EnumerationEditor.class); + registerEditor(Psym.class, EnumerationEditor.class); + registerEditor(PlotSymbol.class, EnumerationEditor.class); + registerEditor(FillStyle.class, EnumerationEditor.class); + // registerEditor(Rectangle.class, RectangleEditor.class); + //registerEditor(DasServer.class, DasServerEditor.class); + } + } + /** + * There's an annoyance with PropertyEditorManager.findEditor, in that it + * always goes looking for the editor with the classLoader. This is annoying + * for applets, because this causes an applet codebase hit each time its called, + * even if it's already been called for the given class. This is problematic + * with the propertyEditor, which calls this for each property, making it + * sub-interactive at best. Here we keep track of the results, either in a + * list of nullPropertyEditors or by registering the editor we just found. + */ + static HashSet nullPropertyEditors = new HashSet(); + + public static java.beans.PropertyEditor findEditor(Class propertyClass) { + java.beans.PropertyEditor result = null; + if (nullPropertyEditors.contains(propertyClass)) { + result = null; + } else { + result = PropertyEditorManager.findEditor(propertyClass); + + if (result == null) { + Class resultClass = (Class) editorRegistry.get(propertyClass); + if (resultClass != null) { + try { + result = (java.beans.PropertyEditor) resultClass.newInstance(); // TODO: this branch will cause excessive lookups for applets. + } catch (InstantiationException ex) { + ex.printStackTrace(); + } catch (IllegalAccessException ex) { + ex.printStackTrace(); + } + } + } + + // if still null, then keep track of the null so we don't have to search again. + if (result == null) { + nullPropertyEditors.add(propertyClass); + //result= new PropertyEditorSupport(); + } else { + if ( DasApplication.hasAllPermission() ) PropertyEditorManager.registerEditor(propertyClass, result.getClass()); // TODO: this will cause problems when the super class comes before subclass + } + + } + return result; + } + + /** + * One-stop place to get the editor for the given propertyDescriptor. + */ + public static java.beans.PropertyEditor getEditor(PropertyDescriptor pd) { + java.beans.PropertyEditor editor = null; + + try { + Class editorClass = pd.getPropertyEditorClass(); + if (editorClass != null) { + editor = (java.beans.PropertyEditor) editorClass.newInstance(); + } + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } + + if (editor == null) { + editor = BeansUtil.findEditor(pd.getPropertyType()); + } + return editor; + } + + /** + * Use reflection to get a list of all the property names for the class. + * The properties are returned in the order specified, and put inherited properties + * at the end of the list. Implement the include/exclude logic. + */ + public static PropertyDescriptor[] getPropertyDescriptors(Class c) { + Set excludePropertyNames; + excludePropertyNames = new HashSet(); + excludePropertyNames.add("class"); + excludePropertyNames.add("listLabel"); + excludePropertyNames.add("listIcon"); + + try { + BeanInfo beanInfo = getBeanInfo(c); + + List propertyList = new ArrayList(); + + PropertyDescriptor[] pdthis; + try { + pdthis = beanInfo.getPropertyDescriptors(); + } catch (IllegalStateException e) { + throw new RuntimeException(e); + } + + for (int i = 0; i < pdthis.length; i++) { + boolean isWriteable = pdthis[i].getWriteMethod() != null; + boolean isUseable = pdthis[i].getReadMethod() != null && !excludePropertyNames.contains(pdthis[i].getName()); + if (isUseable || (pdthis[i] instanceof IndexedPropertyDescriptor)) { + propertyList.add(pdthis[i]); + } + } + + if (beanInfo.getAdditionalBeanInfo() != null) { + List additionalBeanInfo = new ArrayList(Arrays.asList(beanInfo.getAdditionalBeanInfo())); + while (additionalBeanInfo.size() > 0) { + BeanInfo aBeanInfo = (BeanInfo) additionalBeanInfo.remove(0); + pdthis = aBeanInfo.getPropertyDescriptors(); + for (int i = 0; i < pdthis.length; i++) { + String name = pdthis[i].getName(); + boolean isWriteable = pdthis[i].getWriteMethod() != null; + boolean isUseable = pdthis[i].getReadMethod() != null && !excludePropertyNames.contains(name); + if (isUseable || (pdthis[i] instanceof IndexedPropertyDescriptor)) { + propertyList.add(pdthis[i]); + } + } + /* This is commented to mimic the behavior of the Introspector, which doesn't climb up the bean inheritance + tree unless the additional are specified via the Introspector. */ + // if ( aBeanInfo.getAdditionalBeanInfo()!=null ) { + // additionalBeanInfo.addAll( Arrays.asList( aBeanInfo.getAdditionalBeanInfo() ) ); + // } + } + } + + return (PropertyDescriptor[]) propertyList.toArray(new PropertyDescriptor[propertyList.size()]); + + } catch (IntrospectionException e) { + return null; + } + } + + public static BeanInfo getBeanInfo(final Class c) throws IntrospectionException { + + // goal: get BeanInfo for the class, or the AccessLevelBeanInfo if it exists. + BeanInfo beanInfo = null; + + if (c.getPackage() == null) { // e.g. String array + beanInfo = Introspector.getBeanInfo(c); + + log.finer("using BeanInfo " + beanInfo.getClass().getName() + " for " + c.getName()); + } else { + + String s; + try { + s = c.getName().substring(c.getPackage().getName().length() + 1); + } catch (Exception e) { + throw new RuntimeException(e); + } + + Class maybeClass = null; + String beanInfoClassName = null; + + try { + beanInfoClassName = c.getPackage() + "." + s + "BeanInfo"; + maybeClass = Class.forName(beanInfoClassName); + } catch (ClassNotFoundException e) { + try { + beanInfoClassName = "org.das2.beans." + s + "BeanInfo"; + maybeClass = Class.forName(beanInfoClassName); + } catch (ClassNotFoundException e2) { + beanInfo = Introspector.getBeanInfo(c); + beanInfoClassName = beanInfo.getClass().getName(); + } + } + + log.finer("using BeanInfo " + beanInfoClassName + " for " + c.getName()); + + if (beanInfo == null) { + try { + beanInfo = (BeanInfo) maybeClass.newInstance(); + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } catch (InstantiationException ex) { + throw new RuntimeException(ex); + } + } + } + return beanInfo; + } + + public static String[] getPropertyNames(PropertyDescriptor[] propertyList) { + String[] result = new String[propertyList.length]; + for (int i = 0; i < result.length; i++) { + result[i] = ((PropertyDescriptor) propertyList[i]).getName(); + } + return result; + } + + /** + * Use reflection to get a list of all the property names for the class. + * The properties are returned in the order specified, and put inherited properties + * at the end of the list. This is motivated by the arbitary order that the + * Introspector presents the properties, which is in conflict with our desire to control + * the property order. + */ + public static String[] getPropertyNames(Class c) { + PropertyDescriptor[] propertyList = getPropertyDescriptors(c); + return getPropertyNames(propertyList); + } + + /** + * Returns an AccessLevelBeanInfo for the BeanInfo class, implementing the logic + * of how to handle implicit properties. + */ + public static AccessLevelBeanInfo asAccessLevelBeanInfo(BeanInfo beanInfo, Class beanClass) { + if (beanInfo instanceof AccessLevelBeanInfo) { + return (AccessLevelBeanInfo) beanInfo; + } else { + return ImplicitAccessLevelBeanInfo.create(beanInfo, beanClass); + } + + } +} diff --git a/dasCore/src/main/java/org/das2/beans/ColumnColumnConnectorBeanInfo.java b/dasCore/src/main/java/org/das2/beans/ColumnColumnConnectorBeanInfo.java new file mode 100755 index 000000000..b046bf109 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/ColumnColumnConnectorBeanInfo.java @@ -0,0 +1,52 @@ +/* File: DasColorBarBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import org.das2.beans.AccessLevelBeanInfo.AccessLevel; +import org.das2.beans.AccessLevelBeanInfo.Property; +import org.das2.graph.ColumnColumnConnector; + +/** + * BeanInfo class for DasColorBar + * + * @author Edward West + */ +public class ColumnColumnConnectorBeanInfo extends AccessLevelBeanInfo { + + ColumnColumnConnector c; + + protected static final Property[] properties = { + new Property("bottomCurtain", AccessLevel.DASML, "isBottomCurtain", "setBottomCurtain", null), + new Property("curtainOpacityPercent", AccessLevel.DASML, "getCurtainOpacityPercent", "setCurtainOpacityPercent", null), + new Property("fill", AccessLevel.DASML, "isFill", "setFill", null), + new Property("fillColor", AccessLevel.DASML, "getFillColor", "setFillColor", null), + new Property("color", AccessLevel.DASML, "getForeground", "setForeground", null), + new Property("visible", AccessLevel.DASML, "isVisible", "setVisible", null), + }; + + public ColumnColumnConnectorBeanInfo() { + super(properties, org.das2.graph.ColumnColumnConnector.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/CrossHairRendererBeanInfo.java b/dasCore/src/main/java/org/das2/beans/CrossHairRendererBeanInfo.java new file mode 100644 index 000000000..1270c9730 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/CrossHairRendererBeanInfo.java @@ -0,0 +1,48 @@ +/* File: DasPlotBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.BeanInfo; + +public class CrossHairRendererBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("debugging", AccessLevel.DASML, "isDebugging", "setDebugging", null), + new Property("allPlanesReport", AccessLevel.DASML, "isAllPlanesReport", "setAllPlanesReport", null), + new Property("snapping", AccessLevel.DASML, "isSnapping", "setSnapping", null), + new Property("multiLine", AccessLevel.DASML, "isMultiLine", "setMultiLine", null), + }; + + public CrossHairRendererBeanInfo() { + super(properties, org.das2.event.CrossHairRenderer.class); + } + + public java.beans.BeanInfo[] getAdditionalBeanInfo() { + BeanInfo[] additional = { + new LabelDragRendererBeanInfo(), + }; + return additional; + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DasApplicationBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasApplicationBeanInfo.java new file mode 100644 index 000000000..285141d04 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasApplicationBeanInfo.java @@ -0,0 +1,48 @@ +/* File: RendererBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import org.das2.DasApplication; +import org.das2.beans.AccessLevelBeanInfo.AccessLevel; +import org.das2.beans.AccessLevelBeanInfo.Property; + + + +public class DasApplicationBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("reloadLoggingProperties", AccessLevel.DASML, "isReloadLoggingProperties", "setReloadLoggingProperties", null), + new Property("headless", AccessLevel.DASML, "isHeadless", null, null), + new Property("applet", AccessLevel.DASML, "isApplet", null, null), + new Property("dataSetCache", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getDataSetCache", null, null), + new Property("inputStreamMeter", AccessLevel.DASML, "getInputStreamMeter", null, null), + new Property("monitorManager", AccessLevel.DASML, "getMonitorFactory", null, null), + new Property("das2Version", AccessLevel.DASML, "getDas2Version", null, null), + }; + + public DasApplicationBeanInfo() { + super(properties, org.das2.DasApplication.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DasAxisBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasAxisBeanInfo.java new file mode 100644 index 000000000..fab7111db --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasAxisBeanInfo.java @@ -0,0 +1,63 @@ +/* File: DasAxisBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.BeanInfo; + +/** + * BeanInfo class for org.das2.graph.DasAxis. + * + * @author Edward West + * @see org.das2.graph.DasAxis + */ +public class DasAxisBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("datumRange", AccessLevel.END_USER, "getDatumRange", "setDatumRange", null ), + new Property("dataMaximum", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getDataMaximum", "setDataMaximum", null), + new Property("dataMinimum", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getDataMinimum", "setDataMinimum", null), + new Property("flipped", AccessLevel.DASML, "isFlipped", "setFlipped", null), + new Property("label", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getLabel", "setLabel", null), + new Property("log", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "isLog", "setLog", null), + new Property("units", AccessLevel.DASML, "getUnits", null, null), + new Property("format", AccessLevel.DASML, "getFormat", "setFormat", null), + new Property("tickLabelsVisible", AccessLevel.DASML, "isTickLabelsVisible", "setTickLabelsVisible", null), + new Property("oppositeAxisVisible", AccessLevel.DASML, "isOppositeAxisVisible", "setOppositeAxisVisible", null), + new Property("flipLabel", AccessLevel.DASML, "isFlipLabel", "setFlipLabel", null), + new Property("animated", AccessLevel.DASML, "isAnimated", "setAnimated", null), + new Property("dataPath", AccessLevel.DASML, "getDataPath", "setDataPath", null), + new Property("showTca", AccessLevel.DASML, "getDrawTca", "setDrawTca", null), + }; + + public DasAxisBeanInfo() { + super(properties, org.das2.graph.DasAxis.class); + } + + public BeanInfo[] getAdditionalBeanInfo() { + java.beans.BeanInfo[] additional = { + new DasCanvasComponentBeanInfo() + }; + return additional ; + } +} diff --git a/dasCore/src/main/java/org/das2/beans/DasCanvasBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasCanvasBeanInfo.java new file mode 100644 index 000000000..c68965ec7 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasCanvasBeanInfo.java @@ -0,0 +1,71 @@ +/* File: DasCanvasBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.MethodDescriptor; + +/** + * + * @author eew + */ +public class DasCanvasBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getDasName", "setDasName", null), + new Property("fitted", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "isFitted", "setFitted", null), + new Property("width", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getPreferredWidth", "setPreferredWidth", null), + new Property("height", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getPreferredHeight", "setPreferredHeight", null), + new Property("backgroundColor", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getBackground", "setBackground", null), + new Property("foregroundColor", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getForeground", "setForeground", null), + new Property("baseFont", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getBaseFont", "setBaseFont", null), + new Property("printTag", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getPrintingTag", "setPrintingTag", null ), + new Property("textAntiAlias", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "isTextAntiAlias", "setTextAntiAlias", null ), + new Property("antiAlias", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "isAntiAlias", "setAntiAlias", null ), + new Property("components", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getCanvasComponents", null, "getCanvasComponents", null, null ), + new Property("application", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getApplication", null, null), + }; + + private static MethodDescriptor[] methods; + static { + try { + methods = new MethodDescriptor[1]; + Class[] writeToPngParams = { String.class }; + methods[0] = new MethodDescriptor(org.das2.graph.DasCanvas.class.getMethod("writeToPng", writeToPngParams)); + } + catch (NoSuchMethodException nsme) { + IllegalStateException ise = new IllegalStateException(nsme.getMessage()); + ise.initCause(nsme); + throw ise; + } + } + + public DasCanvasBeanInfo() { + super(properties, org.das2.graph.DasCanvas.class); + } + + public MethodDescriptor[] getMethodDescriptors() { + return methods; + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DasCanvasComponentBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasCanvasComponentBeanInfo.java new file mode 100644 index 000000000..44d44b004 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasCanvasComponentBeanInfo.java @@ -0,0 +1,60 @@ +/* File: DasCanvasComponentBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import org.das2.graph.DasCanvasComponent; + +import java.beans.MethodDescriptor; + +public class DasCanvasComponentBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getDasName", "setDasName", null), + new Property("row", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getRow", "setRow", null), + new Property("column", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getColumn", "setColumn", null), + new Property("mouseAdapter", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getDasMouseInputAdapter", "setDasMouseInputAdapter", null) + }; + + private static MethodDescriptor[] methods; + static { + try { + methods = new MethodDescriptor[1]; + methods[0] = new MethodDescriptor(DasCanvasComponent.class.getMethod("update")); + } + catch (NoSuchMethodException nsme) { + IllegalStateException ise = new IllegalStateException(nsme.getMessage()); + ise.initCause(nsme); + throw ise; + } + } + + public DasCanvasComponentBeanInfo() { + super(properties, org.das2.graph.DasCanvasComponent.class); + } + + public MethodDescriptor[] getMethodDescriptors() { + return methods; + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DasColorBarBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasColorBarBeanInfo.java new file mode 100644 index 000000000..082360274 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasColorBarBeanInfo.java @@ -0,0 +1,67 @@ +/* File: DasColorBarBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import org.das2.beans.AccessLevelBeanInfo.AccessLevel; +import org.das2.beans.AccessLevelBeanInfo.Property; +import org.das2.graph.DasAxis; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import org.das2.components.propertyeditor.EnumerationEditor; + +/** + * BeanInfo class for DasColorBar + * + * @author Edward West + */ +public class DasColorBarBeanInfo extends AccessLevelBeanInfo { + + protected static final Property[] properties = { + new Property("type", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getType", "setType", EnumerationEditor.class), + new Property("fillColor", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getFillColor", "setFillColor", null ) + }; + + public DasColorBarBeanInfo() { + super(properties, org.das2.graph.DasColorBar.class); + } + + public BeanInfo[] getAdditionalBeanInfo() { + BeanInfo[] additional = { + new DasAxisBeanInfo(), + new DasCanvasComponentBeanInfo(), + }; + return additional; + + /*try { + BeanInfo[] additional = { + Introspector.getBeanInfo( DasAxis.class ), + }; + return additional; + } catch ( IntrospectionException e ) { + throw new RuntimeException(e); + }*/ + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DasColumnBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasColumnBeanInfo.java new file mode 100644 index 000000000..0f2645be4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasColumnBeanInfo.java @@ -0,0 +1,49 @@ +/* File: DasColumnBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +/** + * Bean Info implementation for DasDevicePosition + * + * @author Edward West + */ +public class DasColumnBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getDasName", "setDasName", null), + new Property("minimum", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getMinimum", "setMinimum", null), + new Property("maximum", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getMaximum", "setMaximum", null), + new Property("dminimum", AccessLevel.DASML, "getDMinimum", "setDMinimum", null), + new Property("dmaximum", AccessLevel.DASML, "getDMaximum", "setDMaximum", null), + new Property("emMinimum", AccessLevel.DASML, "getEmMinimum", "setEmMinimum", null), + new Property("emMaximum", AccessLevel.DASML, "getEmMaximum", "setEmMaximum", null), + new Property("ptMinimum", AccessLevel.DASML, "getPtMinimum", "setPtMinimum", null), + new Property("ptMaximum", AccessLevel.DASML, "getPtMaximum", "setPtMaximum", null), + }; + + public DasColumnBeanInfo() { + super(properties, org.das2.graph.DasColumn.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DasLabelAxisBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasLabelAxisBeanInfo.java new file mode 100644 index 000000000..4dfb88a20 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasLabelAxisBeanInfo.java @@ -0,0 +1,54 @@ +/* File: DasAxisBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.BeanInfo; + +/** + * BeanInfo class for org.das2.graph.DasAxis. + * + * @author Edward West + * @see org.das2.graph.DasAxis + */ +public class DasLabelAxisBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("label", AccessLevel.DASML, "getLabel", "setLabel", null), + new Property("outsidePadding", AccessLevel.DASML, "getOutsidePadding", "setOutsidePadding", null), + new Property("floppyItemSpacing", AccessLevel.DASML, "isFloppyItemSpacing", "setFloppyItemSpacing", null), + new Property("tickLabelsVisible", AccessLevel.DASML, "areTickLabelsVisible", "setTickLabelsVisible", null), + new Property("oppositeAxisVisible", AccessLevel.DASML, "isOppositeAxisVisible", "setOppositeAxisVisible", null), + }; + + public DasLabelAxisBeanInfo() { + super(properties, org.das2.graph.DasLabelAxis.class); + } + + public BeanInfo[] getAdditionalBeanInfo() { + java.beans.BeanInfo[] additional = { + new DasCanvasComponentBeanInfo() + }; + return additional ; + } +} diff --git a/dasCore/src/main/java/org/das2/beans/DasMouseInputAdapterBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasMouseInputAdapterBeanInfo.java new file mode 100644 index 000000000..4ab9c6f5d --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasMouseInputAdapterBeanInfo.java @@ -0,0 +1,44 @@ +/* File: DasPlotBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.BeanInfo; + +public class DasMouseInputAdapterBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("mouseModules", AccessLevel.DASML, "getMouseModules", null, "getMouseModule", null, null), + //new Property("hoverHighlite", AccessLevel.DASML, "isHoverHighlite", "setHoverHighlite", null ), + + new Property("primaryModule", AccessLevel.DASML, PersistenceLevel.PERSISTENT, + "getPrimaryModule", "setPrimaryModule", null ), + new Property("secondaryModule", AccessLevel.DASML, PersistenceLevel.PERSISTENT, + "getSecondaryModule", "setSecondaryModule", null ) }; + + + public DasMouseInputAdapterBeanInfo() { + super(properties, org.das2.event.DasMouseInputAdapter.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DasPlotBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasPlotBeanInfo.java new file mode 100644 index 000000000..c66c4befb --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasPlotBeanInfo.java @@ -0,0 +1,52 @@ +/* File: DasPlotBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.BeanInfo; + +public class DasPlotBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("title", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getTitle", "setTitle", null), + new Property("drawGrid", AccessLevel.DASML, "isDrawGrid", "setDrawGrid", null), + new Property("drawMinorGrid", AccessLevel.DASML, "isDrawMinorGrid", "setDrawMinorGrid", null), + new Property("preview", AccessLevel.DASML, "isPreviewEnabled", "setPreviewEnabled", null ), + new Property("oversize", AccessLevel.DASML, "isOverSize", "setOverSize", null ), + new Property("renderers", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getRenderers", null, "getRenderer", null, null), + new Property("xAxis", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getXAxis", "setXAxis", null), + new Property("yAxis", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getYAxis", "setYAxis", null), + }; + + public DasPlotBeanInfo() { + super(properties, org.das2.graph.DasPlot.class); + } + + public java.beans.BeanInfo[] getAdditionalBeanInfo() { + BeanInfo[] additional = { + new DasCanvasComponentBeanInfo() + }; + return additional; + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DasRowBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasRowBeanInfo.java new file mode 100644 index 000000000..57effa40d --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasRowBeanInfo.java @@ -0,0 +1,52 @@ +/* File: DasRowBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +/** + * Bean Info implementation for DasDevicePosition + * + * @author Edward West + */ +public class DasRowBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getDasName", "setDasName", null), + new Property("minimum", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getMinimum", "setMinimum", null), + new Property("maximum", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getMaximum", "setMaximum", null), + new Property("dminimum", AccessLevel.DASML, "getDMinimum", "setDMinimum", null), + new Property("dmaximum", AccessLevel.DASML, "getDMaximum", "setDMaximum", null), + new Property("emMinimum", AccessLevel.DASML, "getEmMinimum", "setEmMinimum", null), + new Property("emMaximum", AccessLevel.DASML, "getEmMaximum", "setEmMaximum", null), + new Property("ptMinimum", AccessLevel.DASML, "getPtMinimum", "setPtMinimum", null), + new Property("ptMaximum", AccessLevel.DASML, "getPtMaximum", "setPtMaximum", null), + /* new Property("top", AccessLevel.DASML, "getTop", "setTop", null), + new Property("bottom", AccessLevel.DASML, "getBottom", "setBottom", null), */ + + }; + + public DasRowBeanInfo() { + super(properties, org.das2.graph.DasRow.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DasServerBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DasServerBeanInfo.java new file mode 100644 index 000000000..b5e289a89 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DasServerBeanInfo.java @@ -0,0 +1,45 @@ +/* File: DasRowBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import org.das2.beans.AccessLevelBeanInfo.AccessLevel; +import org.das2.beans.AccessLevelBeanInfo.Property; +import org.das2.client.DasServer; + +/** + * Bean Info implementation for DasDevicePosition + * + * @author Edward West + */ +public class DasServerBeanInfo extends AccessLevelBeanInfo { + DasServer me; + private static Property[] properties = { + new Property("name", AccessLevel.DASML, "getName", null, null), + }; + + public DasServerBeanInfo() { + super(properties, org.das2.client.DasServer.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DataPointRecorderBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DataPointRecorderBeanInfo.java new file mode 100644 index 000000000..dead015d0 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DataPointRecorderBeanInfo.java @@ -0,0 +1,39 @@ +/* File: RendererBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + + + +public class DataPointRecorderBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("xTagWidth", AccessLevel.DASML, "getXTagWidth", "setXTagWidth", null), + new Property("snapToGrid", AccessLevel.DASML, "isSnapToGrid", "setSnapToGrid", null), + }; + + public DataPointRecorderBeanInfo() { + super(properties, org.das2.components.DataPointRecorder.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/DataSetDescriptorBeanInfo.java b/dasCore/src/main/java/org/das2/beans/DataSetDescriptorBeanInfo.java new file mode 100644 index 000000000..612ac8387 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/DataSetDescriptorBeanInfo.java @@ -0,0 +1,42 @@ +/* File: DasRowBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +/** + * Bean Info implementation for DasDevicePosition + * + * @author Edward West + */ +public class DataSetDescriptorBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("dataSetID", AccessLevel.DASML, "getDataSetID", null, null), + new Property("dataSetCache", AccessLevel.DASML, "getDataSetCache", null, null), + }; + + public DataSetDescriptorBeanInfo() { + super(properties, org.das2.dataset.DataSetDescriptor.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/ImplicitAccessLevelBeanInfo.java b/dasCore/src/main/java/org/das2/beans/ImplicitAccessLevelBeanInfo.java new file mode 100644 index 000000000..2c9d3b1c5 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/ImplicitAccessLevelBeanInfo.java @@ -0,0 +1,59 @@ +/* + * ImplicitAccessLevelBeanInfo.java + * + * Created on April 21, 2006, 3:58 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.beans; + +import org.das2.beans.AccessLevelBeanInfo.AccessLevel; +import org.das2.beans.AccessLevelBeanInfo.PersistenceLevel; +import org.das2.beans.AccessLevelBeanInfo.Property; +import java.beans.BeanInfo; +import java.beans.IndexedPropertyDescriptor; +import java.beans.PropertyDescriptor; + +/** + * ImplicitAccessLevelBeanInfo makes any BeanInfo class look like an AccessLevelBeanInfo by implementing + * the default access level and persistence level settings. + * + * @author Jeremy + */ +public class ImplicitAccessLevelBeanInfo extends AccessLevelBeanInfo { + + BeanInfo beanInfo; + + /** Creates a new instance of ImplicitAccessLevelBeanInfo */ + private ImplicitAccessLevelBeanInfo( BeanInfo beanInfo, Class beanClass, Property[] properties ) { + super( properties, beanClass ); + this.beanInfo= beanInfo; + } + + public static ImplicitAccessLevelBeanInfo create( BeanInfo beanInfo, Class beanClass ) { + Property[] properties; + PropertyDescriptor[] pds = BeansUtil.getPropertyDescriptors( beanClass ); + String[] propertyNameList= BeansUtil.getPropertyNames( pds ); + + properties= new Property[propertyNameList.length]; + for ( int i=0; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + + + +public class LabelDragRendererBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("tooltip", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "isTooltip", "setTooltip", null), + }; + + public LabelDragRendererBeanInfo() { + super(properties, org.das2.event.LabelDragRenderer.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/MouseModuleBeanInfo.java b/dasCore/src/main/java/org/das2/beans/MouseModuleBeanInfo.java new file mode 100644 index 000000000..c714d1cb2 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/MouseModuleBeanInfo.java @@ -0,0 +1,39 @@ +/* File: DasPlotBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.BeanInfo; + +public class MouseModuleBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("dragRenderer", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getDragRenderer", null, null), + new Property("label", AccessLevel.DASML, "getLabel", "setLabel", null), + }; + + public MouseModuleBeanInfo() { + super(properties, org.das2.event.MouseModule.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/RectangleEditor.java b/dasCore/src/main/java/org/das2/beans/RectangleEditor.java new file mode 100644 index 000000000..8846e50d9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/RectangleEditor.java @@ -0,0 +1,31 @@ +/* + * UnitsEditor.java + * + * Created on June 30, 2005, 10:23 AM + * + * To change this template, choose Tools | Options and locate the template under + * the Source Creation and Management node. Right-click the template and choose + * Open. You can then make changes to the template in the Source Editor. + */ + +package org.das2.beans; + +import org.das2.datum.Units; +import java.awt.Rectangle; +import java.beans.PropertyEditorSupport; +import java.beans.XMLEncoder; + +/** + * + * @author Jeremy + */ +public class RectangleEditor extends PropertyEditorSupport { + + public void setAsText(String str) throws IllegalArgumentException { + setValue( Units.getByName(str) ); + } + + public String getAsText() { + return String.valueOf( getValue() ); + } +} diff --git a/dasCore/src/main/java/org/das2/beans/RendererBeanInfo.java b/dasCore/src/main/java/org/das2/beans/RendererBeanInfo.java new file mode 100755 index 000000000..0f166fa7a --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/RendererBeanInfo.java @@ -0,0 +1,44 @@ +/* File: RendererBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + + + +public class RendererBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("active", AccessLevel.DASML, "isActive", "setActive", null), + new Property("dataSetID", AccessLevel.DASML, "getDataSetID", "setDataSetID", null), + new Property("dumpDataSet", AccessLevel.DASML, "isDumpDataSet", "setDumpDataSet", null), + new Property("dataSet", AccessLevel.DASML, "getDataSet", null, null), + new Property("lastException", AccessLevel.DASML, "getLastException", null, null), + new Property("dataLoader", AccessLevel.DASML, "getDataLoader", null, null), + //new Property("overLoading", AccessLevel.DASML, "isOverloading", "setOverloading", null), + }; + + public RendererBeanInfo() { + super(properties, org.das2.graph.Renderer.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/RowRowConnectorBeanInfo.java b/dasCore/src/main/java/org/das2/beans/RowRowConnectorBeanInfo.java new file mode 100644 index 000000000..41366ee4f --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/RowRowConnectorBeanInfo.java @@ -0,0 +1,43 @@ +/* File: DasColorBarBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import org.das2.components.propertyeditor.EnumerationEditor; +import java.beans.BeanInfo; + +/** + * BeanInfo class for DasColorBar + * + * @author Edward West + */ +public class RowRowConnectorBeanInfo extends AccessLevelBeanInfo { + + protected static final Property[] properties = { + }; + + public RowRowConnectorBeanInfo() { + super(properties, org.das2.graph.RowRowConnector.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/SpectrogramRendererBeanInfo.java b/dasCore/src/main/java/org/das2/beans/SpectrogramRendererBeanInfo.java new file mode 100755 index 000000000..5a597f306 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/SpectrogramRendererBeanInfo.java @@ -0,0 +1,49 @@ +/* File: SpectrogramRendererBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.BeanInfo; +import org.das2.components.propertyeditor.EnumerationEditor; + +public class SpectrogramRendererBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("rebinner", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getRebinner", "setRebinner", EnumerationEditor.class), + new Property("colorBar", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getColorBar", "setColorBar", null), + new Property("sliceRebinnedData", AccessLevel.DASML, "isSliceRebinnedData", "setSliceRebinnedData", null), + new Property("print300dpi", AccessLevel.DASML, "isPrint300dpi", "setPrint300dpi", null), + }; + + public SpectrogramRendererBeanInfo() { + super(properties, org.das2.graph.SpectrogramRenderer.class); + } + + public java.beans.BeanInfo[] getAdditionalBeanInfo() { + BeanInfo[] additional = { + new RendererBeanInfo() + }; + return additional; + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/StackedHistogramRendererBeanInfo.java b/dasCore/src/main/java/org/das2/beans/StackedHistogramRendererBeanInfo.java new file mode 100755 index 000000000..2b08eb15b --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/StackedHistogramRendererBeanInfo.java @@ -0,0 +1,54 @@ +/* File: DasStackedHistogramPlotBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.BeanInfo; +import org.das2.components.propertyeditor.EnumerationEditor; + +/** + * BeanInfo class for DasStackedHistogramPlot + * + * @author Edward West + */ +public class StackedHistogramRendererBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("ZAxis", AccessLevel.DASML, "getZAxis", "setZAxis", null), + new Property("PeaksIndicator", AccessLevel.DASML, "getPeaksIndicator", "setPeaksIndicator", EnumerationEditor.class ), + new Property("sliceRebinnedData", AccessLevel.DASML, "isSliceRebinnedData", "setSliceRebinnedData", null), + new Property("transparentBackground", AccessLevel.DASML, "isTransparentBackground", "setTransparentBackground", null) + }; + + public StackedHistogramRendererBeanInfo() { + super(properties, org.das2.graph.StackedHistogramRenderer.class); + } + + public BeanInfo[] getAdditionalBeanInfo() { + BeanInfo[] additional = { + new RendererBeanInfo() + }; + return additional; + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/StreamDataSetDescriptorBeanInfo.java b/dasCore/src/main/java/org/das2/beans/StreamDataSetDescriptorBeanInfo.java new file mode 100644 index 000000000..1e878088f --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/StreamDataSetDescriptorBeanInfo.java @@ -0,0 +1,52 @@ +/* File: DasRowBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.*; + +/** + * Bean Info implementation for DasDevicePosition + * + * @author Edward West + */ +public class StreamDataSetDescriptorBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("standardDataStreamSource", AccessLevel.DASML, "getStandardDataStreamSource", null, null), + new Property("restrictedAccess", AccessLevel.DASML, "isRestrictedAccess", null, null), + new Property("serverSideReduction", AccessLevel.DASML, "isServerSideReduction", null, null), + }; + + public BeanInfo[] getAdditionalBeanInfo() { + BeanInfo[] additional = { + new DataSetDescriptorBeanInfo() + }; + return additional; + } + + public StreamDataSetDescriptorBeanInfo() { + super(properties, org.das2.client.StreamDataSetDescriptor.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/SymbolLineRendererBeanInfo.java b/dasCore/src/main/java/org/das2/beans/SymbolLineRendererBeanInfo.java new file mode 100755 index 000000000..446e3934b --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/SymbolLineRendererBeanInfo.java @@ -0,0 +1,52 @@ +/* File: SymbolLineRendererBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +import java.beans.BeanInfo; +import org.das2.components.propertyeditor.EnumerationEditor; + +public class SymbolLineRendererBeanInfo extends AccessLevelBeanInfo { + + private static final Property[] properties = { + new Property("psym", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getPsym", "setPsym", EnumerationEditor.class), + new Property("psymConnector", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getPsymConnector", "setPsymConnector", EnumerationEditor.class), + new Property("histogram", AccessLevel.DASML, "isHistogram", "setHistogram", null), + new Property("color", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getColor", "setColor", null), + new Property("lineWidth", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getLineWidth", "setLineWidth", null), + new Property("symSize", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getSymSize", "setSymSize", null), + new Property("antiAliased", AccessLevel.DASML, "isAntiAliased", "setAntiAliased", null), + }; + + public SymbolLineRendererBeanInfo() { + super(properties, org.das2.graph.SymbolLineRenderer.class); + } + + public BeanInfo[] getAdditionalBeanInfo() { + BeanInfo[] additional = { + new RendererBeanInfo() + }; + return additional; + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/TickCurveRendererBeanInfo.java b/dasCore/src/main/java/org/das2/beans/TickCurveRendererBeanInfo.java new file mode 100644 index 000000000..ee24a529f --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/TickCurveRendererBeanInfo.java @@ -0,0 +1,38 @@ +/* File: DasRowBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.beans; + +public class TickCurveRendererBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("TickStyle", AccessLevel.DASML, "getTickStyle", "setTickStyle", null), + new Property("LineWidth", AccessLevel.DASML, "getLineWidth", "setLineWidth", null), + new Property("TickLength", AccessLevel.DASML, "getTickLength", "setTickLength", null), + }; + + public TickCurveRendererBeanInfo() { + super(properties, org.das2.graph.TickCurveRenderer.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/UnitsEditor.java b/dasCore/src/main/java/org/das2/beans/UnitsEditor.java new file mode 100644 index 000000000..23eebf689 --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/UnitsEditor.java @@ -0,0 +1,26 @@ +/* + * UnitsEditor.java + * + * Created on June 30, 2005, 10:23 AM + * + * To change this template, choose Tools | Options and locate the template under + * the Source Creation and Management node. Right-click the template and choose + * Open. You can then make changes to the template in the Source Editor. + */ + +package org.das2.beans; + +import org.das2.datum.Units; +import java.beans.PropertyEditorSupport; + +/** + * + * @author Jeremy + */ +public class UnitsEditor extends PropertyEditorSupport { + + public void setAsText(String str) throws IllegalArgumentException { + setValue( Units.getByName(str) ); + } + +} diff --git a/dasCore/src/main/java/org/das2/beans/package.html b/dasCore/src/main/java/org/das2/beans/package.html new file mode 100644 index 000000000..53a4182dc --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/package.html @@ -0,0 +1,22 @@ + +

Provides BeanInfos that wrap das2 objects to control the properties +that are exposed. When the PropertyEditor is used to edit an object, it +uses java bean conventions for identifying its properties. Properties of +objects that do not have a corresponding BeanInfo are discovered using the +java beans convention of looking for methods +of the form getX() and setX() where X is the property name.

+

The BeanInfos also provide a mechanism where the writable property set can be +reduced depending on the role of the person using the application. For example, +application developers would have the ability to freely adjust all parameters, +such as layout, datasets and rendering methods, while end users would only be +able to control the timeaxis. This mechanism has never been used, but it's worth +mentioning since the implementing code is in there. +

+Also a utility class, BeansUtil, provides methods for discovering object properties. + +

Note that this package might be removed in a future version of das2. We +plan to make everything more beany, and the BeanInfos may be moved into the same directory +as the Bean objects. Also, we expect that Java 5 annotations might be used to implement the +access levels and other property metadata. Also, XMLEncoder might be used to encode +the beans, instead of SerializeUtil.

+ \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/beans/scratchpad.txt b/dasCore/src/main/java/org/das2/beans/scratchpad.txt new file mode 100644 index 000000000..5a54ad42b --- /dev/null +++ b/dasCore/src/main/java/org/das2/beans/scratchpad.txt @@ -0,0 +1,43 @@ +Definition of a property. A property is an adjustable parameter of an object. A property may be read-only and used to peek +at the status of an object. A property may be another object. A property may be an array of objects. This is all beany. + +1. Attributes of Properties. + 1. Security model + 1. that allows us to restrict which properties may be adjusted by the end-user. + 2. that allows us to restrict the properties which may be stored persistently. + 2. Properties should be editable and serializable "for free," meaning that no effort is required to get these features for new objects. + 3. Properties of new objects should be discovered in a reasonable and safe way. + +2. Example uses. + 1. application development + 1. layout, labels, colorbar, etc are set interactively by the application developer. + 2. satisfied, the developer releases the application, but limiting adjustable properties to the time axis range. + 2. non-persistent properties + 1. the developer wishes to add a new parameter for testing which is used interactively + 2. this parameter should not be serialized to persistent storage, however, since its meaning may change. + +3. discovery of properties. + 1. a "BeanInfo" object for the object class identifies the properties explicitly + 2. or the java beans convention for getX/setX is used to identify implicit properties. + +4. Access Levels--not implemented + 1. ALL -- the property may be adjusted freely, no restrictions + 2. DEVELOPER -- adjustable by the application developer ( This is the default for implicit properties. ) + 3. END_USER -- adjustable by the end-user. + +5. Persistence. + 1. none -- the property should never be used as a part of the state. Including within session. + 2. transient -- the property is only persistent on a per-session basis. For example, undo/redo should include this. + 3. persistent -- the property may be serialized and stored. + +6. Arbitary xml to application + 1. bean requirements + 1. no-argument constructor + 2. properties adjusted in any order--this is a bean requirement because of property editor as well. + 3. must be named + 2. update messages + 1. propertyChanged messages used to implement bean inter-dependence. + 2. "listens to" notation needed. + 3. message-coalescing mechanism needs to be preserved. + 3. missing objects + 1. some sort of canvas layout manager that manages rows diff --git a/dasCore/src/main/java/org/das2/client/AccessDeniedException.java b/dasCore/src/main/java/org/das2/client/AccessDeniedException.java new file mode 100644 index 000000000..f1b8087fb --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/AccessDeniedException.java @@ -0,0 +1,37 @@ +/* File: AccessDeniedException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.client; + +/** + * + * @author jbf + */ +public class AccessDeniedException extends org.das2.DasException { + //The the provided key does not allow access to the dataset. + + /** Creates a new instance of NoKeyProvidedException */ + public AccessDeniedException(String msg) { + super(msg); + } + +} diff --git a/dasCore/src/main/java/org/das2/client/AccountManager.java b/dasCore/src/main/java/org/das2/client/AccountManager.java new file mode 100644 index 000000000..734847723 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/AccountManager.java @@ -0,0 +1,114 @@ +/* File: AccountManager.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.client; + +import org.das2.client.DasServer; + +import javax.swing.*; +import java.awt.*; + +public class AccountManager extends JPanel { + + JLabel feedbackLabel; + JTextField tfUser; + + JPasswordField tfPass; + JPasswordField tfNewPass; + JPasswordField tfConfirmPass; + + DasServer dasServer; + Key key; + + public AccountManager(DasServer dasServer) { + + this.dasServer= dasServer; + + setLayout( new BoxLayout(this,BoxLayout.Y_AXIS)); + + add(new JLabel(dasServer.getName(),JLabel.LEFT)); + add(new JLabel(dasServer.getLogo(),JLabel.LEFT)); + + add(new JLabel("Changing Password")); + + add(new JLabel("Username: ",JLabel.LEFT)); + tfUser= new JTextField(); + add(tfUser); + + add(new JLabel("Password: ",JLabel.LEFT)); + tfPass= new JPasswordField(); + add(tfPass); + + add(new JLabel("New Password: ",JLabel.LEFT)); + tfNewPass= new JPasswordField(); + add(tfNewPass); + + add(new JLabel("Confirm Password: ",JLabel.LEFT)); + tfConfirmPass= new JPasswordField(); + add(tfConfirmPass); + + feedbackLabel= new JLabel("",JLabel.LEFT); + add(feedbackLabel); + + } + + public void changePassword() { + + int okayCancel=JOptionPane.OK_OPTION; + boolean success= false; + + while ( okayCancel==JOptionPane.OK_OPTION && success==false ) { + okayCancel= + JOptionPane.showConfirmDialog(null,this,"Account Manager", + JOptionPane.OK_CANCEL_OPTION,JOptionPane.PLAIN_MESSAGE); + + if (okayCancel==JOptionPane.OK_OPTION) { + String pass= String.valueOf(tfPass.getPassword()); + String newPass= String.valueOf(tfNewPass.getPassword()); + String confirmPass= String.valueOf(tfConfirmPass.getPassword()); + + if (newPass.equals(confirmPass)) { + try { + dasServer.changePassword(tfUser.getText(),pass,newPass); + success= true; + } + catch ( org.das2.DasException e ) { + feedbackLabel.setText(e.toString()); + } + catch ( Exception e ) { + feedbackLabel.setText("Failed connect to server"); + } + } else { + feedbackLabel.setText("Passwords do not match"); + feedbackLabel.setForeground(Color.red); + } + } + } + } + + public static void main( String[] args ) throws Exception { + AccountManager a= new AccountManager(DasServer.create(new java.net.URL("http://www-pw.physics.uiowa.edu/das/dasServer"))); + a.changePassword(); + + } +} + diff --git a/dasCore/src/main/java/org/das2/client/Authenticator.java b/dasCore/src/main/java/org/das2/client/Authenticator.java new file mode 100755 index 000000000..b09a965ac --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/Authenticator.java @@ -0,0 +1,206 @@ +/* File: Authenticator.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.client; + +import org.das2.DasApplication; +import org.das2.DasProperties; +import java.awt.Color; +import java.awt.Component; +import java.awt.Toolkit; +import java.util.prefs.Preferences; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.util.List; + +public class Authenticator extends JPanel { + + JLabel feedbackLabel; + JTextField tfUser; + + JPasswordField tfPass; + + DasServer dasServer; + String resourceId; // identifies the DasServer and the resource + String resource; + + final String KEY_AUTOLOGIN= "autoLogin"; + final String KEY_SAVECREDENTIALS= "saveCredentials"; + + Preferences prefs= Preferences.userNodeForPackage( Authenticator.class ); + + public Authenticator(DasServer dasServer) { + this( dasServer, "" ); + } + + public Authenticator(DasServer dasServer, String restrictedResourceLabel ) { + + this.dasServer= dasServer; + this.resourceId= String.valueOf( dasServer.getURL() ) + "::" + restrictedResourceLabel;; + this.resource= restrictedResourceLabel; + + setLayout( new BoxLayout(this,BoxLayout.Y_AXIS)); + + add(new JLabel(dasServer.getName(),JLabel.LEFT)); + add(new JLabel(dasServer.getLogo(),JLabel.LEFT)); + + if ( ! "".equals( restrictedResourceLabel ) ) { + add( new JLabel( ""+restrictedResourceLabel ) ); + } + + add(new JLabel("Username: ",JLabel.LEFT)); + tfUser= new JTextField(); + add(tfUser); + + add(new JLabel("Password: ",JLabel.LEFT)); + tfPass= new JPasswordField(); + add(tfPass); + + JPanel prefsPanel= new JPanel(); + prefsPanel.setLayout( new BoxLayout( prefsPanel, BoxLayout.Y_AXIS ) ); + + { + final JCheckBox cb= new JCheckBox( ); + cb.setSelected( prefs.getBoolean( KEY_SAVECREDENTIALS, true) ); + cb.setAction( new AbstractAction("save credentials" ) { + public void actionPerformed( ActionEvent e ) { + prefs.putBoolean( KEY_SAVECREDENTIALS,cb.isSelected() ); + } + } ); + prefsPanel.add( cb ); + } + + { + final JCheckBox cb= new JCheckBox( ); + cb.setSelected( prefs.getBoolean(KEY_AUTOLOGIN,false) ); + cb.setAction( new AbstractAction( "allow automatic logins" ) { + public void actionPerformed( ActionEvent e ) { + prefs.putBoolean( KEY_AUTOLOGIN,cb.isSelected() ); + } + } ); + prefsPanel.add( cb ); + } + + add( prefsPanel ); + + if ( prefs.getBoolean(KEY_SAVECREDENTIALS,true) ) { + String username= prefs.get( resourceId+".username", DasProperties.getInstance().getProperty("username") ); + if (!"".equals(username)) tfUser.setText(username); + String passwordCrypt= prefs.get( resourceId+".passwordCrypt", DasProperties.getInstance().getProperty("password") ); + if (!"".equals(passwordCrypt)) tfPass.setText("usePrefs"); + } + + feedbackLabel= new JLabel("",JLabel.LEFT); + feedbackLabel.setForeground(Color.red); + add(feedbackLabel); + + try { + String lockingKeyWarning= ""; + if ( Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ) ) { + lockingKeyWarning+= ", CAPS LOCK is on"; + } + + if ( !"".equals( lockingKeyWarning ) ) { + feedbackLabel.setText(lockingKeyWarning.substring(2)); + } + } catch ( UnsupportedOperationException e ) { + // I sure hope they don't have caps lock on! + } + + } + + public Key authenticate() { + + Key result=null; + int okayCancel=JOptionPane.OK_OPTION; + + if ( prefs.getBoolean(KEY_AUTOLOGIN,false) ) { + String username= prefs.get( resourceId+".username", DasProperties.getInstance().getProperty("username") ); + String passCrypt= prefs.get( resourceId+".passwordCrypt", DasProperties.getInstance().getProperty("password") ); + result= dasServer.authenticate(username,passCrypt); + if ( result!=null ) { + if ( checkGroup(result) ) { + return result; + } else { + feedbackLabel.setText(username+" doesn't have access to "+resource); + } + } else { + feedbackLabel.setText("stored credentials rejected by server"); + } + } + + Component parent=DasApplication.getDefaultApplication().getMainFrame(); + + while ( okayCancel==JOptionPane.OK_OPTION && result==null ) { + okayCancel= + JOptionPane.showConfirmDialog(parent,this,"Authenticator", + JOptionPane.OK_CANCEL_OPTION,JOptionPane.PLAIN_MESSAGE); + + if (okayCancel==JOptionPane.OK_OPTION) { + + String username= tfUser.getText().trim(); + String password= String.valueOf( tfPass.getPassword() ); + + String passCrypt= null; + if ( password.equals("usePrefs") ) { + passCrypt= prefs.get( resourceId+".passwordCrypt", DasProperties.getInstance().getProperty("password") ); + } else { + passCrypt= org.das2.util.Crypt.crypt(password); + } + + try { + result= dasServer.authenticate(username,passCrypt); + if (result==null) { + feedbackLabel.setText("Login incorrect"); + } else { + if ( !checkGroup( result ) ) { + feedbackLabel.setText(username+" doesn't have access to "+resource ); + result= null; + } + if (prefs.getBoolean(KEY_SAVECREDENTIALS,true) ) { + prefs.put( resourceId+".username", username ); + prefs.put( resourceId+".passwordCrypt", passCrypt ); + prefs.flush(); + } + } + } catch ( Exception e ) { + feedbackLabel.setText("Failed connect to server"); + } + } + } + + return result; + } + + private boolean checkGroup(Key result) { + if ( resource.equals("") ) { + return true; + } else { + List groups= dasServer.groups(result); + return ( groups.contains( resource ) ); + } + } + +} + diff --git a/dasCore/src/main/java/org/das2/client/DasServer.java b/dasCore/src/main/java/org/das2/client/DasServer.java new file mode 100755 index 000000000..63b036690 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/DasServer.java @@ -0,0 +1,616 @@ +/* File: DasServer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.client; + +import org.das2.stream.StreamDescriptor; +import org.das2.stream.StreamException; +import org.das2.stream.DasStreamFormatException; +import org.das2.util.URLBuddy; +import org.das2.DasIOException; +import org.das2.system.DasLogger; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeModel; +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.logging.Logger; + +import org.das2.DasException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** Represents a remote Das 2.1 compliant server. + * + * Use the create() method to instantiate new Das 2 server objects. Each call to + * create() will only allocate a new server instance if no server matching the given URL + * has already been created. + * + * @author jbf + */ + +public class DasServer { + + private String sProto; + private String host; + private String path; + private int port; + + @Deprecated + private HashMap keys; // Holds a list of all non-http auth das2 server keys + + //Probably dead code, let's see + //private Key key; + + private static final Logger logger= DasLogger.getLogger( DasLogger.DATA_TRANSFER_LOG ); + + /* Holds the global list of Das2 Server objects */ + private static HashMap instanceHashMap= new HashMap(); + + @Deprecated + public static DasServer plasmaWaveGroup; + + @Deprecated + public static DasServer sarahandjeremy; + + static { + try { + plasmaWaveGroup= DasServer.create(new URL("http://www-pw.physics.uiowa.edu/das/das2Server")); + sarahandjeremy= DasServer.create(new URL("http://www.sarahandjeremy.net/das/dasServer.cgi")); + } catch ( java.net.MalformedURLException e ) { + org.das2.util.DasExceptionHandler.handle(e); + } + } + + private DasServer(String sProto, String host, String path) { + String[] s= host.split(":"); + if ( s.length>1 ) { + this.port= Integer.parseInt(s[1]); + host= s[0]; + } else { + port= -1; + } + this.sProto = sProto; + this.host= host; + this.path= path; + this.keys= new HashMap(); + } + + /** Provide the Das2 Server location. + * Note that more than one Das2 server may be present on a single host web-site. + * @return A URL string containing protocol, host and path information. + */ + public String getURL() { + if ( port==-1 ) { + return sProto+"://"+host+path; + } else { + return sProto+"://"+host+":"+port+path; + } + } + + /** Get a Das2 server instance. + * + * @param url A Das2 resource URL. Only the protocol, host, port and path information + * are used. All other items, such as a GET query are ignored. + * + * @return If a server matching the given url's protocol, host and path has already + * been created, that instance is returned, otherwise a new instance is created. + */ + public static DasServer create( URL url ) { + String proto = url.getProtocol(); + String host= url.getHost(); + int port = url.getPort(); + if ( port!=-1 ) { + host+= ":"+port; + } + String key= proto+"://" + host + url.getPath(); + if ( instanceHashMap.containsKey( key ) ) { + logger.fine( "Using existing DasServer for "+url); + return (DasServer) instanceHashMap.get( key ); + } else { + String path= url.getPath(); + logger.fine( "Creating DasServer for "+url); + DasServer result= new DasServer(proto, host, path); + instanceHashMap.put(key,result); + return result; + } + } + + /** Query the remote DasServer for it's id string. + * For Das 2.1 servers this is handled by sending the GET query ?server=id. + * + * @return A string containing the id, or the empty string if the query failed + */ + public String getName() { + String formData= "server=id"; + + try { + URL server= new URL("http",host,port,path+"?"+formData); + + logger.fine( "connecting to "+server); + URLConnection urlConnection = server.openConnection(); + urlConnection.connect(); + + String contentType = urlConnection.getContentType(); + InputStream in= urlConnection.getInputStream(); + + String result= new String( read(in) ); + logger.fine( "response="+result); + + return result; + } catch (IOException e) { + return ""; + } + } + + /** Query the remote DasServer for it's image icon. + * For Das 2.1 servers this is handled by sending the GET query ?server=logo. + * + * @return An ImageIcon with the server logo, or an empty ImageIcon if the request + * failed + */ + public ImageIcon getLogo() { + String formData= "server=logo"; + + try { + URL server= new URL("http",host,port,path+"?"+formData); + + logger.fine( "connecting to "+server); + URLConnection urlConnection = server.openConnection(); + urlConnection.connect(); + + String contentType = urlConnection.getContentType(); + InputStream in= urlConnection.getInputStream(); + + byte[] data= read(in); + logger.fine( "response="+data.length+" bytes"); + return new ImageIcon(data); + + } catch (IOException e) { + return new ImageIcon(); + } + } + + /** Query the remote DasServer for a hierarchical tree of all data sources on + * the server. + * For Das 2.1 servers this is handled by sending the GET query ?server=list. + * + * @return + */ + public TreeModel getDataSetList() throws org.das2.DasException { + String formData= "server=list"; + + try { + URL server= new URL("http",host,port,path+"?"+formData); + + logger.fine( "connecting to "+server); + + URLConnection urlConnection = server.openConnection(); + urlConnection.connect(); + + String contentType = urlConnection.getContentType(); + InputStream in= urlConnection.getInputStream(); + + TreeModel result= createModel(in); + logger.fine( "response->"+result); + return result; + + } catch (IOException e) { + throw new DasIOException( e.getMessage() ); + } + } + + private TreeModel createModel(InputStream uin) throws IOException { + + BufferedReader in = new BufferedReader( new InputStreamReader(uin) ); + + DefaultMutableTreeNode root = + new DefaultMutableTreeNode( getURL(), true ); + DefaultTreeModel model = new DefaultTreeModel(root, true); + String line = in.readLine(); + + while (line != null) { + DefaultMutableTreeNode current = root; + StringTokenizer tokenizer = new StringTokenizer(line, "/"); + token: while (tokenizer.hasMoreTokens()) { + String tok = tokenizer.nextToken(); + for (int index = 0; index < current.getChildCount(); index++) { + String str = current.getChildAt(index).toString(); + if (str.equals(tok)) { + current = + (DefaultMutableTreeNode)current.getChildAt(index); + continue token; + } + } + DefaultMutableTreeNode node = + new DefaultMutableTreeNode(tok, + (tokenizer.hasMoreElements() + ? true + : line.endsWith("/"))); + current.add(node); + current = node; + } + line = in.readLine(); + } + return model; + } + + public StandardDataStreamSource getStandardDataStreamSource(URL url) { + return new WebStandardDataStreamSource(this, url); + } + + public StreamDescriptor getStreamDescriptor( URL dataSetID ) throws DasException { + try { + String dsdf = dataSetID.getQuery().split("&")[0]; + URL url = new URL(sProto, host, port, path+"?server=dsdf&dataset=" + dsdf); + + logger.fine( "connecting to "+url); + URLConnection connection = url.openConnection(); + connection.connect(); + String contentType = connection.getContentType(); + String[] s1= contentType.split(";"); // dump charset info + contentType= s1[0]; + + InputStream inStream = null; + if(connection instanceof HttpURLConnection){ + HttpURLConnection httpConn = (HttpURLConnection) connection; + int nStatus = httpConn.getResponseCode(); + if(nStatus >= 400) + inStream = httpConn.getErrorStream(); + } + if(inStream == null) inStream = connection.getInputStream(); + + if (contentType.equalsIgnoreCase("text/plain") || + contentType.equalsIgnoreCase("text/vnd.das2.das2stream") ) { + PushbackReader reader = new PushbackReader(new InputStreamReader(inStream), 4); + char[] four = new char[4]; + reader.read(four); + if (new String(four).equals("[00]")) { + logger.fine("response is a das2Stream"); + reader.skip(6); + Document header = StreamDescriptor.parseHeader(reader); + Element root = header.getDocumentElement(); + switch(root.getTagName()){ + case "stream": + return new StreamDescriptor(root); + case "exception": + logger.fine("response is an exception"); + String type= root.getAttribute("type"); + String sMsg = root.getAttribute("message"); + String sExceptMsg = "stream exception: "+type; + if(!sMsg.isEmpty()) + sExceptMsg = sExceptMsg + ", " + sMsg; + StreamException se= new StreamException( sExceptMsg ); + throw new DasException(sExceptMsg, se); + case "": + throw new DasStreamFormatException(); + default: + throw new DasStreamFormatException(); + } + } + else { + logger.fine("response is a legacy descriptor"); + reader.unread(four); + BufferedReader in = new BufferedReader(reader); + StreamDescriptor result = StreamDescriptor.createLegacyDescriptor(in); + return result; + } + } + else { + BufferedReader in = new BufferedReader(new InputStreamReader(inStream)); + StringBuilder message = new StringBuilder(); + for (String line = in.readLine(); line != null; line = in.readLine()) { + message.append(line).append('\n'); + } + throw new IOException(message.toString()); + } + } catch ( MalformedURLException e ) { + throw new DataSetDescriptorNotAvailableException("malformed URL"); + } catch ( FileNotFoundException e ) { + throw new DasServerNotFoundException( e.getMessage() ); + } catch ( IOException e ) { + throw new DasIOException(e.toString()); + } + } + + /** Handles key based authentication */ + @Deprecated + public Key authenticate( String user, String passCrypt) { + try { + Key result= null; + + String formData= "server=authenticator"; + formData+= "&user="+URLBuddy.encodeUTF8(user); + formData+= "&passwd="+URLBuddy.encodeUTF8(passCrypt); + + URL server= new URL("http",host,port,path+"?"+formData); + + logger.fine( "connecting to "+server); + + InputStream in= server.openStream(); + BufferedInputStream bin= new BufferedInputStream(in); + + String serverResponse= readServerResponse(bin); + + String errTag= "error"; + String keyTag= "key"; + + if ( serverResponse.substring(0,keyTag.length()+2).equals("<"+keyTag+">")) { + int index= serverResponse.indexOf(""); + String keyString= serverResponse.substring(keyTag.length()+2,index); + result= new Key(keyString); + } else { + result= null; + } + return result; + } catch (UnsupportedEncodingException uee) { + throw new AssertionError("UTF-8 not supported"); + } catch ( IOException e ) { + return null; + } + } + + /** returns a List of resource Id's available with this key */ + @Deprecated + public List groups( Key key ) { + try { + String formData= "server=groups"; + formData+= "&key="+URLBuddy.encodeUTF8(key.toString()); + + URL server= new URL("http",host,port,path+"?"+formData); + + logger.fine( "connecting to "+server); + + InputStream in= server.openStream(); + BufferedInputStream bin= new BufferedInputStream(in); + + String serverResponse= readServerResponse(bin); + + String[] groups= serverResponse.split(","); + ArrayList result= new ArrayList(); + for ( int i=0; i")) { + int index= serverResponse.indexOf(""); + String errString= serverResponse.substring(errTag.length()+2,index); + if (errString.equals("")) { + throw new DasServerException("Bad User/Pass"); + } + } + } catch (UnsupportedEncodingException uee) { + throw new AssertionError("UTF-8 not supported"); + } catch ( IOException e ) { + throw new DasServerException("Failed Connection"); + } + + + } + + @Deprecated + public String readServerResponse( BufferedInputStream in ) { + // Read ..., leaving the InputStream immediately after // + + in.mark(Integer.MAX_VALUE); + + String das2Response; + + byte[] data = new byte[4096]; + + int lastBytesRead = -1; + + String s; + + int offset=0; + + try { + int bytesRead= in.read(data,offset,4096-offset); + + String das2ResponseTag= "das2Response"; + // beware das2ResponseTagLength=14 assumed below!!! + + if (bytesRead<(das2ResponseTag.length()+2)) { + offset+= bytesRead; + bytesRead= in.read(data,offset,4096-offset); + } + + if ( new String(data,0,14,"UTF-8").equals("<"+das2ResponseTag+">")) { + while ( !new String( data,0,offset,"UTF-8" ).contains("") && + offset<4096 ) { + offset+= bytesRead; + bytesRead= in.read(data,offset,4096-offset); + } + + int index= new String( data,0,offset,"UTF-8" ).indexOf(""); + + das2Response= new String(data,14,index-14); + + org.das2.util.DasDie.println("das2Response="+das2Response); + + in.reset(); + in.skip( das2Response.length() + 2 * das2ResponseTag.length() + 5 ); + + } else { + in.reset(); + + das2Response=""; + } + } catch ( IOException e ) { + das2Response= ""; + } + + logger.fine( "response="+das2Response); + + return das2Response; + } + + // Utility function to handle reading data off the HTTP stream. Used by functions + // such as getName and getLogo that don't expect to receive a Das2 Stream + private byte[] read(InputStream uin) throws IOException { + LinkedList list = new LinkedList(); + byte[] data; + int bytesRead=0; + int totalBytesRead=0; + + //BufferedInputStream in= new BufferedInputStream(uin,4096*2); + InputStream in= uin; + + long time = System.currentTimeMillis(); + // fireReaderStarted(); + + //FileOutputStream out= new FileOutputStream("x."+time+".dat"); + + data = new byte[4096]; + + int lastBytesRead = -1; + + String s; + + int offset=0; + + // if (requestor != null) { + // requestor.totalByteCount(-1); + // } + + bytesRead= in.read(data,offset,4096-offset); + + while (bytesRead != -1) { + + int bytesSoFar = totalBytesRead; + // fireReaderUpdate(bytesSoFar); + // if (requestor != null) { + // requestor.currentByteCount(bytesSoFar); + // } + + offset+=bytesRead; + lastBytesRead= offset; + + if (offset==4096) { + list.addLast(data); + data = new byte[4096]; + offset=0; + } + + totalBytesRead+= bytesRead; + + bytesRead= in.read(data,offset,4096-offset); + + } + + if (lastBytesRead<4096) { + list.addLast(data); + } + + if (list.size()== 0) { + return new byte[0]; + } + + int dataLength = (list.size()-1)*4096 + lastBytesRead; + + data = new byte[dataLength]; + + Iterator iterator = list.iterator(); + int i; + for (i = 0; i < list.size()-1; i++) { + System.arraycopy(iterator.next(), 0, data, i*4096, 4096); + } + System.arraycopy(iterator.next(), 0, data, i*4096, lastBytesRead); + + return data; + } + + public String getProto() { + return sProto; + } + + public String getHost() { + return host; + } + + public int getPort() { + // returns -1 if the port was not specified, which can be used with URL constructor + return port; + } + + public String getPath() { + return path; + } + + public URL getURL( String formData ) throws MalformedURLException { + return new URL( sProto, host, port, path+"?"+formData ); + } + + public Key getKey( String resource ) { + synchronized (this) { + if ( keys.get(resource)==null ) { + Authenticator authenticator; + authenticator= new Authenticator(this,resource); + Key key= authenticator.authenticate(); + if ( key!=null ) keys.put( resource, key); + } + } + return (Key)keys.get(resource); + } + + //Let's see if this is actually used anywhere + //public void setKey( Key key ) { + // this.key= key; + //} + + @Override + public String toString() { + return this.getURL(); + } +} diff --git a/dasCore/src/main/java/org/das2/client/DasServerException.java b/dasCore/src/main/java/org/das2/client/DasServerException.java new file mode 100644 index 000000000..a52df99c5 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/DasServerException.java @@ -0,0 +1,41 @@ +/* File: DasServerException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.client; + +/** + * + * @author jbf + */ +public class DasServerException extends org.das2.DasException { + + /** Creates a new instance of DasServerException */ + public DasServerException() { + super(); + } + + public DasServerException(String msg) { + super(msg); + } + +} diff --git a/dasCore/src/main/java/org/das2/client/DasServerNotFoundException.java b/dasCore/src/main/java/org/das2/client/DasServerNotFoundException.java new file mode 100644 index 000000000..99bb48c83 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/DasServerNotFoundException.java @@ -0,0 +1,46 @@ +/* File: DasServerNotFoundException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.client; + +/** + * + * @author jbf + */ +public class DasServerNotFoundException extends org.das2.DasException { + + /** + * Creates a new instance of DasServerNotFoundException without detail message. + */ + public DasServerNotFoundException() { + } + + + /** + * Constructs an instance of DasServerNotFoundException with the specified detail message. + * @param msg the detail message. + */ + public DasServerNotFoundException(String msg) { + super(msg); + } +} diff --git a/dasCore/src/main/java/org/das2/client/DataSetDescriptorNotAvailableException.java b/dasCore/src/main/java/org/das2/client/DataSetDescriptorNotAvailableException.java new file mode 100644 index 000000000..f7cff06b9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/DataSetDescriptorNotAvailableException.java @@ -0,0 +1,42 @@ +/* File: DataSetDescriptorNotAvailableException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.client; + +/** + * + * @author jbf + */ +public class DataSetDescriptorNotAvailableException extends org.das2.DasException { + /** + * Creates a new instance of DataSetDescriptionNotAvailableException without detail message. + */ + + /** + * Constructs an instance of DataSetDescriptionNotAvailableException with the specified detail message. + * @param msg the detail message. + */ + public DataSetDescriptorNotAvailableException(String msg) { + super(msg); + } +} diff --git a/dasCore/src/main/java/org/das2/client/DataSetStreamHandler.java b/dasCore/src/main/java/org/das2/client/DataSetStreamHandler.java new file mode 100755 index 000000000..f7194cf53 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/DataSetStreamHandler.java @@ -0,0 +1,403 @@ +/* File: DataSetStreamHandler.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on February 11, 2004, 4:26 PM + * by Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.client; + +import org.das2.stream.StreamYScanDescriptor; +import org.das2.stream.StreamComment; +import org.das2.stream.StreamMultiYDescriptor; +import org.das2.stream.StreamDescriptor; +import org.das2.stream.StreamException; +import org.das2.stream.StreamHandler; +import org.das2.stream.SkeletonDescriptor; +import org.das2.stream.PacketDescriptor; +import org.das2.util.monitor.NullProgressMonitor; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.dataset.CacheTag; +import org.das2.dataset.DataSet; +import org.das2.dataset.TableDataSetBuilder; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumRangeUtil; +import org.das2.datum.DatumUtil; +import org.das2.datum.DatumVector; +import org.das2.datum.TimeUtil; +import org.das2.datum.Units; +import org.das2.system.DasLogger; +import java.text.ParseException; +import java.util.*; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author Edward E. West + */ +public class DataSetStreamHandler implements StreamHandler { + + StreamHandlerDelegate delegate; + StreamDescriptor sd; + Map extraProperties; + ProgressMonitor monitor; + int totalPacketCount= -1; + int taskSize= -1; + int packetCount= 0; + Datum xTagMax= null; + boolean bReadPkts = true; + + private static final Logger logger= DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG); + private boolean monotonic= true; // generally streams are monotonic, so check for monotonicity. + + public DataSetStreamHandler( Map extraProperties, ProgressMonitor monitor ) { + this.extraProperties = new HashMap(extraProperties); + this.monitor= monitor==null ? new NullProgressMonitor() : monitor; + } + + @Override + public void streamDescriptor(StreamDescriptor sd) throws StreamException { + logger.finest("got stream descriptor"); + this.sd = sd; + Object o; + if ( ( o= sd.getProperty("taskSize") )!=null ) { + this.taskSize= ((Integer)o).intValue(); + monitor.setTaskSize( taskSize ); + monitor.started(); + } else if ( ( o= sd.getProperty("packetCount" ) )!=null ) { + this.totalPacketCount= ((Integer)o).intValue(); + monitor.setTaskSize( totalPacketCount ); + monitor.started(); + } + if ( ( o= sd.getProperty("cacheTagString" ) ) !=null ) { + // kludge to xmit cacheTags. "start,resolution,end" + try { + String[] ss= ((String)o).split(","); + Datum min= TimeUtil.create(ss[0]); + Datum max= TimeUtil.create(ss[2]); + Datum res= DatumUtil.parse(ss[1]); + if ( res.doubleValue( res.getUnits() ) == 0 ) res= null; // intrinsic resolution + extraProperties.put( DataSet.PROPERTY_CACHE_TAG, new CacheTag( min, max, res ) ); + } catch ( ParseException e ) { + e.printStackTrace(); + } + } + if ( ( o=sd.getProperty( DataSet.PROPERTY_X_MONOTONIC ) )!=null ) { + extraProperties.put( DataSet.PROPERTY_X_MONOTONIC, Boolean.valueOf((String)o) ); + + } + if ( ( o=sd.getProperty("pid") )!=null ) { + logger.log(Level.FINE, "stream pid={0}", o); + } + } + + @Override + public void packetDescriptor(PacketDescriptor pd) throws StreamException { + logger.finest("got packet descriptor"); + if (delegate == null) { + SkeletonDescriptor descriptor = pd.getYDescriptor(0); + if (descriptor instanceof StreamMultiYDescriptor) { + logger.fine("using VectorDS delegate"); + delegate = new VectorDataSetStreamHandler(pd); + } else if (descriptor instanceof StreamYScanDescriptor) { + logger.fine("using TableDS delegate"); + delegate = new TableDataSetStreamHandler(pd); + } + } else { + delegate.packetDescriptor(pd); + } + } + + public void setReadPackets(boolean b){ + bReadPkts = b; + } + + public boolean getReadPackets(){ + return bReadPkts; + } + + @Override + public void packet(PacketDescriptor pd, Datum xTag, DatumVector[] vectors) throws StreamException { + if(!bReadPkts) return; + + logger.finest("got packet"); + ensureNotNullDelegate(); + if ( xTagMax==null || xTag.ge( this.xTagMax ) ) { + xTagMax= xTag; + } else { + monotonic= false; + } + delegate.packet(pd, xTag, vectors); + packetCount++; + if ( totalPacketCount!=-1 ) { + monitor.setTaskProgress(packetCount); + } + } + + @Override + public void streamClosed(StreamDescriptor sd) throws StreamException { + logger.finest("got streamClosed"); + if (delegate != null) { + delegate.streamClosed(sd); + } + } + + @Override + public void streamException(StreamException se) throws StreamException { + logger.finest("got stream exception"); + } + + @Override + public void streamComment(StreamComment sc) throws StreamException { + logger.log(Level.FINEST, "got stream comment: {0}", sc); + + if (sc.getType().equals(sc.TYPE_TASK_SIZE)){ + if(! monitor.isCancelled()){ + taskSize = Integer.parseInt(sc.getValue()); + monitor.setTaskSize(taskSize); + monitor.started(); + } + return; + } + + if ( sc.getType().equals(sc.TYPE_TASK_PROGRESS) ) { + if ( taskSize != -1 && !monitor.isCancelled() ) + monitor.setTaskProgress( Long.parseLong(sc.getValue() ) ); + return; + } + + if ( sc.getType().matches(sc.TYPE_LOG) ) { + String level= sc.getType().substring(4); + Level l= Level.parse(level.toUpperCase()); + if ( l.intValue()>Level.FINE.intValue() ) { + logger.log(Level.FINE,sc.getValue()); + } else { + logger.log(l,sc.getValue()); + } + monitor.setProgressMessage(sc.getValue()); + } + } + + public DataSet getDataSet() { + if (delegate == null) { + System.err.println("never established delegate, which might mean the stream contains no packets."); + return null; + } else { + return delegate.getDataSet(); + } + } + + private void ensureNotNullDelegate() { + if (delegate == null) { + throw new IllegalStateException("Null delegate"); + } + } + + private static double getXWithBase(Datum base, Datum x) { + if (base == null) { + return x.doubleValue(x.getUnits()); + } else { + return base.doubleValue(base.getUnits()) + x.doubleValue(base.getUnits().getOffsetUnits()); + } + } + + private static interface StreamHandlerDelegate extends StreamHandler { + DataSet getDataSet(); + } + + private class VectorDataSetStreamHandler implements StreamHandlerDelegate { + + private VectorDataSetBuilder builder; + + private DatumRange validRange=null; + + private VectorDataSetStreamHandler(PacketDescriptor pd) throws StreamException { + StreamMultiYDescriptor y = (StreamMultiYDescriptor)pd.getYDescriptor(0); + Datum base = pd.getXDescriptor().getBase(); + Units xUnits = base == null ? pd.getXDescriptor().getUnits() : base.getUnits(); + Units yUnits = y.getUnits(); + builder = new VectorDataSetBuilder(xUnits,yUnits); + builder.addProperties( Collections.singletonMap( DataSet.PROPERTY_Y_LABEL, + y.getProperty("name") )); + for ( int i=1; i props= my.getProperties(); + for ( Entry p: props.entrySet() ) { + if ( i==0 ) { + builder.setProperty( p.getKey(), p.getValue() ); + } else { + builder.setProperty( my.getName()+"."+p.getKey(), p.getValue() ); + } + } + } else { + throw new StreamException("Mixed data sets are not currently supported"); + } + } + //TODO: see TableDataSet below, where PacketDescriptor can have properties. + + } + + @Override + public void packetDescriptor(PacketDescriptor pd) throws StreamException { + logger.log(Level.FINE, "got packet descriptor: {0}", pd); + for (int i = 1; i < pd.getYCount(); i++) { + StreamMultiYDescriptor y = (StreamMultiYDescriptor)pd.getYDescriptor(i); + builder.addPlane(y.getName(),y.getUnits()); + } + } + + @Override + public void streamClosed(StreamDescriptor sd) throws StreamException {} + + @Override + public void streamDescriptor(StreamDescriptor sd) throws StreamException {} + + @Override + public void streamException(StreamException se) throws StreamException {} + + @Override + public void streamComment(StreamComment sc) throws StreamException {} + + @Override + public DataSet getDataSet() { + builder.addProperties(sd.getProperties()); + builder.addProperties(extraProperties); + if ( monotonic&& builder.getProperty(DataSet.PROPERTY_X_MONOTONIC)==null ) { + builder.setProperty( DataSet.PROPERTY_X_MONOTONIC, Boolean.TRUE ); + } + return builder.toVectorDataSet(); + } + + } + + private class TableDataSetStreamHandler implements StreamHandlerDelegate { + + private TableDataSetBuilder builder; + + private TableDataSetStreamHandler(PacketDescriptor pd) throws StreamException { + StreamYScanDescriptor y = (StreamYScanDescriptor)pd.getYDescriptor(0); + Datum base = pd.getXDescriptor().getBase(); + Units xUnits = base != null ? base.getUnits() : pd.getXDescriptor().getUnits(); + Units yUnits = y.getYUnits(); + Units zUnits = y.getZUnits(); + + builder = new TableDataSetBuilder(xUnits, yUnits, zUnits, y.getName()); + this.packetDescriptor(pd); + } + + @Override + public void packet(PacketDescriptor pd, Datum xTag, DatumVector[] vectors) throws StreamException { + StreamYScanDescriptor yscan = (StreamYScanDescriptor)pd.getYDescriptor(0); + Datum base = pd.getXDescriptor().getBase(); + Datum x = base == null ? xTag : base.add(xTag); + DatumVector y = DatumVector.newDatumVector(yscan.getYTags(), yscan.getYUnits()); + String[] planeIDs = new String[pd.getYCount()]; + for (int i = 0; i < pd.getYCount(); i++) { + planeIDs[i] = ((StreamYScanDescriptor)pd.getYDescriptor(i)).getName(); + } + builder.insertYScan(x, y, vectors, planeIDs); + } + + @Override + public void packetDescriptor(PacketDescriptor pd) throws StreamException { + StreamYScanDescriptor y = (StreamYScanDescriptor)pd.getYDescriptor(0); + for (int i = 1; i < pd.getYCount(); i++) { + y = (StreamYScanDescriptor)pd.getYDescriptor(i); + builder.addPlane(y.getName(), y.getZUnits()); + } + Map p= pd.getProperties(); + for ( Entry e: p.entrySet() ) { + String key= (String)e.getKey(); + Object p0= builder.getProperty(key); + if ( p0==null ) { + builder.setProperty( key, e.getValue() ); + } else { + if ( ! p0.equals( e.getValue() ) ) { + int i2; + for ( i2=1; builder.getProperty(""+key+"."+i2)!=null; i2++ ) { /* nothing */ } + builder.setProperty( ""+key+"."+i2, e.getValue() ); + } + } + } + //TODO: see VectorDataSet above, where each plane can have properties. + } + + @Override + public void streamClosed(StreamDescriptor sd) throws StreamException {} + + @Override + public void streamDescriptor(StreamDescriptor sd) throws StreamException {} + + @Override + public void streamException(StreamException se) throws StreamException {} + + @Override + public void streamComment(StreamComment sc) throws StreamException {} + + @Override + public DataSet getDataSet() { + builder.addProperties(sd.getProperties()); + builder.addProperties(extraProperties); + if ( monotonic&& builder.getProperty(DataSet.PROPERTY_X_MONOTONIC)==null ) { + builder.setProperty( DataSet.PROPERTY_X_MONOTONIC, Boolean.TRUE ); + } + return builder.toTableDataSet(); + } + + } + +} diff --git a/dasCore/src/main/java/org/das2/client/FakeStandardDataStreamSource.java b/dasCore/src/main/java/org/das2/client/FakeStandardDataStreamSource.java new file mode 100644 index 000000000..214097593 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/FakeStandardDataStreamSource.java @@ -0,0 +1,123 @@ +/* File: FakeStandardDataStreamSource.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.client; + +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.DasException; + +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +/** + * + * @author jbf + */ +public class FakeStandardDataStreamSource implements StandardDataStreamSource { + + class FakeInputStream extends InputStream { + int nitems; + long nRecs; + double recsPerSecond; + long floatCount; + long byteCount; + long recCount; + long recSize; + float currentFloat; + byte[] iCurrentFloat; + ByteBuffer buff; + FloatBuffer fbuff; + double transferRateBps; + long transferBirthMilli; + + FakeInputStream( long nRecs, double recsPerSecond, int nitems ) { + this.nitems= nitems; + this.nRecs= nRecs; + this.recsPerSecond= recsPerSecond; + buff= ByteBuffer.allocate(4*(nitems+1)); + fbuff = buff.asFloatBuffer(); + buff.position(buff.limit()); + floatCount=0; + byteCount=0; + recCount=0; + recSize= (nitems+1)*4; + iCurrentFloat= new byte[4]; + transferRateBps= 2.1e5; + transferBirthMilli= System.currentTimeMillis(); + } + + public int read() throws java.io.IOException { + try { + while( ( byteCount * 1000 / ( 1 + ( System.currentTimeMillis() - transferBirthMilli ) ) ) > transferRateBps ) { + Thread.sleep(10); + } + } catch ( InterruptedException e ) { + } + + int result; + if ( ! buff.hasRemaining() ) { + buff.position(0); + fbuff.put(0, (float) ( recCount / recsPerSecond )); + for (int i=1; i<=nitems; i++) { + float f= (float)Math.random(); + fbuff.put(i,f); + } + recCount++; + } + + if (recCount>nRecs) { + return -1; + } else { + result= buff.get() & 0xff; + byteCount++; + return result; + } + } + + } + + /** Creates a new instance of FakeStandardDataStreamSource */ + public FakeStandardDataStreamSource() { + } + + public InputStream getInputStream( StreamDataSetDescriptor dsd, Datum start, Datum end) throws DasException { + + double recsPerSecond= 1 / 120000.0; + int nRec= (int) ( end.subtract(start).doubleValue(Units.seconds) * recsPerSecond ); + + int nitems = 12; + + InputStream result= new FakeStandardDataStreamSource.FakeInputStream(nRec,recsPerSecond,nitems); + return result; + } + + public InputStream getReducedInputStream( StreamDataSetDescriptor dsd, Datum start, Datum end, Datum timeResolution) throws DasException { + return getInputStream( dsd, start, end ); + } + + public void reset() { + } + +} diff --git a/dasCore/src/main/java/org/das2/client/InputStreamMeter.java b/dasCore/src/main/java/org/das2/client/InputStreamMeter.java new file mode 100644 index 000000000..924c8f9cb --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/InputStreamMeter.java @@ -0,0 +1,165 @@ +/* + * InputStreamMeter.java + * + * Created on October 21, 2005, 5:35 PM + * + * To change this template, choose Tools | Options and locate the template under + * the Source Creation and Management node. Right-click the template and choose + * Open. You can then make changes to the template in the Source Editor. + */ + +package org.das2.client; + +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.system.DasLogger; +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author Jeremy + */ +public class InputStreamMeter { + + long totalBytesRead; + long millisElapsed; + double speedLimit; + long meterCount; // only clock while there are > 0 inputStreams out there + long startTime; + + /** Creates a new instance of InputStreamMeter */ + public InputStreamMeter() { + this.speedLimit= 0; + this.totalBytesRead= 0; + this.millisElapsed= 0; + this.startTime= -1; + } + + /** MeteredInputStream is used to monitor the read. It is required to notify + * the InputStreamMeter of the number of bytesRead, and allow the InputStreamMeter + * to govern download speed by inserting blocking sleeps. + * + * @author jbf + */ + private class MeteredInputStream extends InputStream { + + InputStream in; + InputStreamMeter meter; + + /** Creates a new instance of MeteredInputStream + */ + private MeteredInputStream( InputStream in, InputStreamMeter meter ) { + this.meter= meter; + this.in= in; + } + + public int read( byte[] b, int off, int len ) throws IOException { + try { + int bytesRead= in.read(b,off,len); + meter.addBytes(bytesRead,this); + meter.governSpeed(this); + return bytesRead; + } catch ( IOException e ) { + meter.exception(this); + throw e; + } + } + + public int read() throws IOException { + try { + int byteRead= in.read(); + meter.addBytes(1,this); + meter.governSpeed(this); + return byteRead; + } catch ( IOException e ) { + meter.exception(this); + throw e; + } + } + + public void close() throws IOException { + meter.closing(this); + in.close(); + } + + } + + + public InputStream meterInputStream( InputStream in ) { + meterCount++; + if ( meterCount==1 ) { + startTime= System.currentTimeMillis(); + totalBytesRead= 0; + millisElapsed= 0; + } + return new MeteredInputStream( in, this ); + } + + /* limit total speed, possibly balancing load btw streams */ + private void governSpeed( MeteredInputStream mis ) { + if ( speedLimit>0 ) { + if ( calcTransmitSpeed() > speedLimit ) { + long targetMillis= (long) ( ( totalBytesRead ) / ( speedLimit / 1000. ) ); + long waitMs= Math.min( 1000, targetMillis - calcMillisElapsed() ); + DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG).fine("limiting speed by waiting "+waitMs+" ms"); + try { Thread.sleep(waitMs); } catch ( InterruptedException ex ) { } + } + } + } + + /* add these bytes to the meter, on behalf of mis. */ + private void addBytes( long bytes, MeteredInputStream mis ) { + totalBytesRead+= bytes; + } + + + private void closing( MeteredInputStream mis ) { + meterCount--; + if ( meterCount==0 ) { + this.millisElapsed+= System.currentTimeMillis() - startTime; + this.startTime=-1; + } + } + + private void exception( MeteredInputStream mis ) { + meterCount--; + if ( meterCount==0 ) { + this.millisElapsed+= System.currentTimeMillis() - startTime; + this.startTime=-1; + } + } + + private long calcMillisElapsed() { + long millis= this.millisElapsed; + if ( startTime!=-1 ) { + millis+= System.currentTimeMillis() - startTime; + } + return millis; + } + + private double calcTransmitSpeed() { + long millis= calcMillisElapsed(); + if ( millis==0 ) { + return Units.bytesPerSecond.getFillDouble(); + } else { + return 1000 * totalBytesRead / millis; + } + } + + public Datum getTransmitSpeed() { + return Units.bytesPerSecond.createDatum( calcTransmitSpeed(), 10 ); + } + + public long getBytesTransmitted() { + return totalBytesRead; + } + + public Datum getSpeedLimit() { + return Units.bytesPerSecond.createDatum( this.speedLimit ); + } + + public void setSpeedLimit( Datum speedLimit) { + this.speedLimit = speedLimit.doubleValue( Units.bytesPerSecond ); + } +} diff --git a/dasCore/src/main/java/org/das2/client/Key.java b/dasCore/src/main/java/org/das2/client/Key.java new file mode 100644 index 000000000..ee420e2ff --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/Key.java @@ -0,0 +1,42 @@ +/* File: Key.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.client; + +/** + * + * @author jbf + */ +public class Key { + + String value; + + /** Creates a new instance of Key */ + public Key(String value) { + this.value= value; + } + + public String toString() { + return this.value; + } +} diff --git a/dasCore/src/main/java/org/das2/client/NoSuchDataSetException.java b/dasCore/src/main/java/org/das2/client/NoSuchDataSetException.java new file mode 100644 index 000000000..ff922a174 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/NoSuchDataSetException.java @@ -0,0 +1,46 @@ +/* File: NoSuchDataSetException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.client; + +/** + * + * @author jbf + */ +public class NoSuchDataSetException extends org.das2.client.DasServerException { + + /** + * Creates a new instance of NoSuchDataSet without detail message. + */ + public NoSuchDataSetException() { + } + + + /** + * Constructs an instance of NoSuchDataSet with the specified detail message. + * @param msg the detail message. + */ + public NoSuchDataSetException(String msg) { + super(msg); + } +} diff --git a/dasCore/src/main/java/org/das2/client/StandardDataStreamSource.java b/dasCore/src/main/java/org/das2/client/StandardDataStreamSource.java new file mode 100644 index 000000000..645c949bb --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/StandardDataStreamSource.java @@ -0,0 +1,40 @@ +/* File: StandardDataStreamSource.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.client; + +import org.das2.datum.Datum; +import org.das2.DasException; + +import java.io.InputStream; + +/** + * + * @author jbf + */ +public interface StandardDataStreamSource { + + public InputStream getInputStream( StreamDataSetDescriptor dsd, Datum start, Datum end) throws DasException; + public InputStream getReducedInputStream( StreamDataSetDescriptor dsd, Datum start, Datum end, Datum timeResolution) throws DasException; + public void reset(); +} diff --git a/dasCore/src/main/java/org/das2/client/StreamDataSetDescriptor.java b/dasCore/src/main/java/org/das2/client/StreamDataSetDescriptor.java new file mode 100755 index 000000000..1f4770133 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/StreamDataSetDescriptor.java @@ -0,0 +1,475 @@ +/* File: DataSetDescriptor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.client; + +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.TableDataSetBuilder; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.datum.UnitsConverter; +import org.das2.stream.StreamYScanDescriptor; +import org.das2.stream.StreamMultiYDescriptor; +import org.das2.stream.StreamDescriptor; +import org.das2.stream.StreamException; +import org.das2.stream.PacketDescriptor; +import org.das2.DasIOException; +import org.das2.CancelledOperationException; +import org.das2.DasException; +import org.das2.util.DasProgressMonitorInputStream; +import org.das2.util.monitor.NullProgressMonitor; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.datum.DatumVector; +import org.das2.system.DasLogger; +import org.das2.util.StreamTool; + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; +import java.util.logging.Logger; +import javax.xml.parsers.*; +import org.xml.sax.*; +import org.w3c.dom.*; +import org.w3c.dom.Document; + +public class StreamDataSetDescriptor extends DataSetDescriptor { + + protected StandardDataStreamSource standardDataStreamSource; + private boolean serverSideReduction = true; + private PacketDescriptor defaultPacketDescriptor; + + private static final Logger logger= DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG); + + public Units getXUnits() { + return Units.us2000; + } + + /** + * Creates a new instance of StreamDataSetDescriptor + * from the specified file + */ + protected StreamDataSetDescriptor(Map properties) { + setProperties(properties); + } + + protected StreamDataSetDescriptor(Map properties, boolean legacy) { + setProperties(properties, legacy); + } + + public StreamDataSetDescriptor(StreamDescriptor sd, StandardDataStreamSource sdss) { + this(sd.getProperties(), "true".equals(sd.getProperty("legacy"))); + this.standardDataStreamSource = sdss; + } + + public void setStandardDataStreamSource(StandardDataStreamSource sdss) { + this.standardDataStreamSource= sdss; + } + + public StandardDataStreamSource getStandardDataStreamSource() { + return this.standardDataStreamSource; + } + + protected void setProperties(Map properties, boolean legacy) { + super.setProperties(properties); + if (properties.containsKey("form") && properties.get("form").equals("x_multi_y") + && properties.containsKey("items")) { + setDefaultCaching(false); + } + if (legacy) { + defaultPacketDescriptor = PacketDescriptor.createLegacyPacketDescriptor(properties); + } + } + + @Override + protected void setProperties( Map properties ) { + setProperties(properties, false); + } + + private ByteBuffer getByteBuffer(InputStream in) throws DasException { + byte[] data = readBytes(in); + ByteBuffer buffer = ByteBuffer.wrap(data); + return buffer; + } + + /** + * Auxiliary method used by readDoubles(InputStream, Object, Datum, Datum); + * + * Read data for the given start and end dates and returns an array of bytes + * + * @author eew + * @throws java.io.IOException If there is an error getting data from the reader, and IOException is thrown + */ + protected byte[] readBytes(InputStream in) throws DasException { + LinkedList list = new LinkedList(); + byte[] data = new byte[4096]; + int bytesRead=0; + int totalBytesRead=0; + int lastBytesRead = -1; + int offset=0; + long time = System.currentTimeMillis(); + + try { + bytesRead= in.read(data,offset,4096-offset); + while (bytesRead != -1) { + int bytesSoFar = totalBytesRead; + offset+=bytesRead; + lastBytesRead= offset; + if (offset==4096) { + list.addLast(data); + data = new byte[4096]; + offset=0; + } + totalBytesRead+= bytesRead; + bytesRead= in.read(data,offset,4096-offset); + } + } catch ( IOException e ) { + throw new DasIOException(e); + } + + if (lastBytesRead>=0 && lastBytesRead<4096) { + list.addLast(data); + } + + if (list.size()== 0) { + throw new DasIOException("Error reading data: no data available"); + } + int dataLength = (list.size()-1)*4096 + lastBytesRead; + data = new byte[dataLength]; + Iterator iterator = list.iterator(); + for (int i = 0; i < list.size()-1; i++) { + System.arraycopy(list.get(i), 0, data, i*4096, 4096); + } + System.arraycopy(list.get(list.size()-1), 0, data, (list.size() - 1)*4096, lastBytesRead); + return data; + } + + @Override + public String toString() { + return "dsd "+getDataSetID(); + } + + @Override + protected DataSet getDataSetImpl( Datum start, Datum end, Datum resolution, ProgressMonitor monitor ) throws DasException { + if ( resolution != null && !resolution.isFinite() ) throw new IllegalArgumentException( "resolution is not finite" ); + InputStream in; + DataSet result; + if ( serverSideReduction ) { + logger.fine("getting stream from standard data stream source"); + in= standardDataStreamSource.getReducedInputStream( this, start, end, resolution); + } else { + in= standardDataStreamSource.getInputStream( this, start, end ); + } + + logger.fine("reading stream"); + result = getDataSetFromStream( in, start, end, monitor ); + return result; + } + + protected DataSet getDataSetFromStream(InputStream in, Datum start, Datum end, ProgressMonitor monitor ) throws DasException { + if ( monitor==null ) monitor= new NullProgressMonitor(); + + PushbackInputStream pin = new PushbackInputStream(in, 4096); + try { + byte[] four = new byte[4]; + int bytesRead= pin.read(four); + logger.finer("read first four bytes bytesRead="+bytesRead); + if ( bytesRead!=4 ) { + logger.fine("no data returned from server"); + throw new DasIOException( "No data returned from server" ); + } + + if (new String(four).equals("[00]")) { + logger.finer("got stream header [00]"); + pin.unread(four); + + if ( monitor.isCancelled() ) { + pin.close(); + throw new InterruptedIOException("Operation cancelled"); + } + + final DasProgressMonitorInputStream mpin = new DasProgressMonitorInputStream(pin, monitor); + //InputStream mpin = pin; + + logger.finer("creating Channel"); + ReadableByteChannel channel = Channels.newChannel(mpin); + + DataSetStreamHandler handler = new DataSetStreamHandler( properties, monitor ) { + public void streamDescriptor(StreamDescriptor sd) throws StreamException { + super.streamDescriptor( sd ); + if ( super.taskSize!=-1 ) { + mpin.setEnableProgressPosition(false); + } + } + }; + + logger.finer("using StreamTool to read the stream"); + StreamTool.readStream(channel, handler); + return handler.getDataSet(); + } + else { + pin.unread(four); + + if ( monitor.isCancelled() ) { + pin.close(); + throw new InterruptedIOException("Operation cancelled"); + } + + monitor.started(); + + InputStream mpin = new DasProgressMonitorInputStream(pin, monitor); + + if (getProperty("form").equals("x_tagged_y_scan")) { + return getLegacyTableDataSet(mpin, start); + } + else if (getProperty("form").equals("x_multi_y")) { + return getLegacyVectorDataSet(mpin, start); + } + else { + throw new IllegalStateException("Unrecognized data set type: " + getProperty("form")); + } + } + } + catch (UnsupportedEncodingException uee) { + //UTF-8 should be supported by all JVM's + throw new RuntimeException(uee); + } + catch (IOException ioe) { + throw new DasIOException(ioe); + } + catch ( StreamException se ) { + /* kludge for when an InterruptedIOException is caused by the user's cancel. + * TODO: This is danger code, because it masks the condition where the + * interruption happened for some other reason the user isn't aware of. + */ + if ( se.getCause() instanceof InterruptedIOException ) { + DasException e= new CancelledOperationException(); + e.initCause(se); + throw e; + } else { + throw se; + } + } + finally { + try { pin.close(); } catch (IOException ioe) {} + } + } + + private static String getPacketID(byte[] four) throws DasException { + if ((four[0] == (byte)'[' && four[3] == (byte)']') || (four[0] == (byte)':' && four[3] == (byte)':')) { + return new String(new char[]{(char)four[1], (char)four[2]}); + } + else { + throw new DasException("Invalid stream, expecting 4 byte header, encountered '" + new String(four) + "'"); + } + } + + private DataSet getLegacyVectorDataSet(InputStream in0, Datum start) throws DasException { + try { + PushbackInputStream in = new PushbackInputStream(in0, 50); + PacketDescriptor sd = getPacketDescriptor(in); + VectorDataSetBuilder builder = new VectorDataSetBuilder(start.getUnits(),Units.dimensionless); // Units will be set when "" is encountered + for (Iterator i = sd.getYDescriptors().iterator(); i.hasNext();) { + Object o = i.next(); + if (o instanceof StreamMultiYDescriptor) { + StreamMultiYDescriptor y = (StreamMultiYDescriptor)o; + String name = y.getName(); + if (name != null && !name.equals("")) { + builder.addPlane(name,y.getUnits()); + } else if ( "".equals(name) ) { + builder.setYUnits(y.getUnits()); + } + } + else { + throw new DasIOException("Invalid Stream Header: Non-Y-descriptor encountered"); + } + } + StreamMultiYDescriptor[] yDescriptors = (StreamMultiYDescriptor[])sd.getYDescriptors().toArray(new StreamMultiYDescriptor[0]); + int planeCount = yDescriptors.length - 1; + String[] planeIDs = new String[planeCount]; + int recordSize = sd.getXDescriptor().getSizeBytes() + yDescriptors[0].getSizeBytes(); + for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) { + planeIDs[planeIndex] = yDescriptors[planeIndex+1].getName(); + recordSize += yDescriptors[planeIndex+1].getSizeBytes(); + } + ByteBuffer data = getByteBuffer(in); + double timeBaseValue = start.doubleValue(start.getUnits()); + Units offsetUnits = start.getUnits().getOffsetUnits(); + UnitsConverter uc = sd.getXDescriptor().getUnits().getConverter(offsetUnits); + while (data.remaining() > recordSize) { + DatumVector vector = sd.getXDescriptor().read(data); + double xTag = timeBaseValue + vector.doubleValue(0, offsetUnits); + vector = yDescriptors[0].read(data); + double yValue = vector.doubleValue(0, yDescriptors[0].getUnits()); + builder.insertY(xTag, yValue); + for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) { + vector = yDescriptors[planeIndex + 1].read(data); + yValue = vector.doubleValue(0, yDescriptors[planeIndex + 1].getUnits()); + builder.insertY(xTag, yValue, yDescriptors[planeIndex + 1].getName()); + } + } + + if ( properties.containsKey("x_sample_width") ) { + properties.put( "xTagWidth", Datum.create( ((Double)properties.get("x_sample_width")).doubleValue(), + Units.seconds ) ); + } + + builder.addProperties(properties); + VectorDataSet result = builder.toVectorDataSet(); + return result; + } + catch (DasException de) { + de.printStackTrace(); + throw de; + } + } + + private DataSet getLegacyTableDataSet(InputStream in0, Datum start) throws DasException { + PushbackInputStream in= new PushbackInputStream(in0,50); + PacketDescriptor sd = getPacketDescriptor(in); + TableDataSetBuilder builder = new TableDataSetBuilder(start.getUnits(),Units.dimensionless,Units.dimensionless); + Units yUnits = Units.dimensionless; + Units zUnits= Units.dimensionless; + + for (Iterator i = sd.getYDescriptors().iterator(); i.hasNext();) { + Object o = i.next(); + if (o instanceof StreamYScanDescriptor) { + StreamYScanDescriptor scan = (StreamYScanDescriptor)o; + String name = scan.getName(); + if (name != null && !name.equals("")) { + builder.addPlane(name,scan.getZUnits()); + } + } + else { + throw new DasIOException("Invalid Stream Header: Non-yScan descriptor encountered"); + } + } + StreamYScanDescriptor[] yScans = (StreamYScanDescriptor[])sd.getYDescriptors().toArray(new StreamYScanDescriptor[0]); + final int planeCount = yScans.length; + String[] planeIDs = new String[planeCount]; + int recordSize = sd.getXDescriptor().getSizeBytes(); + for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) { + planeIDs[planeIndex] = yScans[planeIndex].getName(); + recordSize += yScans[planeIndex].getSizeBytes(); + } + ByteBuffer data = getByteBuffer(in); + double timeBaseValue= start.doubleValue(start.getUnits()); + Units offsetUnits = start.getUnits().getOffsetUnits(); + Units xdescUnits= sd.getXDescriptor().getUnits(); + if ( xdescUnits==null ) xdescUnits= Units.seconds; + //UnitsConverter uc = xdescUnits.getConverter(offsetUnits); + double[] yCoordinates = yScans[0].getYTags(); + DatumVector y = DatumVector.newDatumVector(yCoordinates, yUnits); + + while (data.remaining() > recordSize) { + DatumVector vector = sd.getXDescriptor().read(data); + Datum xTag = start.add(vector.get(0)); + DatumVector[] z = new DatumVector[planeCount]; + for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) { + z[planeIndex]= yScans[planeIndex].read(data); + } + builder.insertYScan(xTag, y, z, planeIDs); + } + + if ( properties.containsKey("x_sample_width") ) { + properties.put( "xTagWidth", Datum.create( ((Double)properties.get("x_sample_width")).doubleValue(), + Units.seconds ) ); + } + + builder.addProperties(properties); + TableDataSet result = builder.toTableDataSet(); + return result; + } + + private static final byte[] HEADER = { (byte)'d', (byte)'a', (byte)'s', (byte)'2', (byte)0177, (byte)0177 }; + + private PacketDescriptor getPacketDescriptor(PushbackInputStream in) + throws DasIOException, StreamException + { + try { + byte[] four = new byte[HEADER.length]; + + int bytesRead = 0; + int totalBytesRead = 0; + do { + bytesRead = in.read(four, totalBytesRead, HEADER.length - totalBytesRead); + if (bytesRead != -1) totalBytesRead += bytesRead; + } while (totalBytesRead < HEADER.length && bytesRead != -1); + if (Arrays.equals(four, HEADER)) { + byte[] header = StreamTool.advanceTo(in, "\177\177".getBytes()); + ByteArrayInputStream source = new ByteArrayInputStream(header); + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document document = builder.parse(source); + Element docNode= document.getDocumentElement(); + PacketDescriptor packetDescriptor= new PacketDescriptor(docNode); + return packetDescriptor; + } + else { + in.unread(four, 0, totalBytesRead); + return defaultPacketDescriptor; + } + } + catch ( ParserConfigurationException ex ) { + throw new IllegalStateException(ex.getMessage()); + } + catch ( StreamTool.DelimeterNotFoundException dnfe) { + DasIOException dioe = new DasIOException(dnfe.getMessage()); + dioe.initCause(dioe); + throw dioe; + } + catch ( SAXException ex ) { + DasIOException e= new DasIOException(ex.getMessage()); + e.initCause(ex); + throw e; + } + catch ( IOException ex) { + throw new DasIOException(ex); + } + } + + public boolean isRestrictedAccess() { + boolean result; + if (getProperty("groupAccess") != null) { + result= !("".equals(getProperty("groupAccess"))); + } else { + result= false; + } + return result; + } + + public void setServerSideReduction(boolean x) { + serverSideReduction= x; + } + + public boolean isServerSideReduction() { + return serverSideReduction; + } + + public PacketDescriptor getDefaultPacketDescriptor() { + return defaultPacketDescriptor; + } + +} diff --git a/dasCore/src/main/java/org/das2/client/WebStandardDataStreamSource.java b/dasCore/src/main/java/org/das2/client/WebStandardDataStreamSource.java new file mode 100755 index 000000000..e8eb94b07 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/WebStandardDataStreamSource.java @@ -0,0 +1,483 @@ +/* File: WebStandardDataStreamSource.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.client; + +import org.das2.dataset.NoDataInIntervalException; +import org.das2.dataset.NoKeyProvidedException; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.stream.StreamYScanDescriptor; +import org.das2.DasApplication; +import org.das2.util.URLBuddy; +/** + * + * @author jbf + */ + +import org.das2.DasException; +import org.das2.DasIOException; +import org.das2.datum.format.DatumFormatter; +import java.io.*; +import java.net.*; +import java.util.logging.Logger; +import javax.swing.ImageIcon; +import org.das2.CancelledOperationException; +import org.das2.system.LogCategory; +import org.das2.util.CredentialsManager; + +/** Web standard data stream source + * + * This class handles Das 1 and Das 2 streams served from a server implementing the + * Das 2.1 client - server protocol, including HTTP authentication. The Das 2.2 + * client - server protocol is not handled at this time. + */ +public class WebStandardDataStreamSource implements StandardDataStreamSource { + + private DasServer server; + protected String m_sHost; + protected String m_sDataSet; + private String extraParameters; + + /* Holds value of property compress. */ + private boolean compress; + + /* Holds value of property lastRequestURL. */ + private String lastRequestURL; + + public WebStandardDataStreamSource(DasServer server, URL url) { + this.server = server; + m_sHost = url.getHost(); + String[] query = url.getQuery() == null ? new String[0] : url.getQuery().split("&"); + if (query.length > 0) m_sDataSet = query[0]; + if (query.length > 1) extraParameters = query[1]; + } + + @Override + public InputStream getInputStream(StreamDataSetDescriptor dsd, Datum start, Datum end) + throws DasException { + String serverType="dataset"; + + StringBuffer formData = new StringBuffer(); + formData.append("server=").append(serverType); + + if (extraParameters != null) { + formData.append("¶ms=").append(extraParameters); //Should already be url encoded. + } + + InputStream in= openURLConnection( dsd, start, end, formData ); + in= DasApplication.getDefaultApplication().getInputStreamMeter().meterInputStream(in); + return in; + } + + @Override + public InputStream getReducedInputStream( StreamDataSetDescriptor dsd, Datum start, Datum end, Datum timeResolution) throws DasException { + + StringBuffer formData = new StringBuffer(); + String form = (String)dsd.getProperty("form"); + + if ("true".equals(dsd.getProperty("legacy")) && "x_tagged_y_scan".equals(form) ) { + formData.append("server=compactdataset"); + StreamYScanDescriptor y = (StreamYScanDescriptor)dsd.getDefaultPacketDescriptor().getYDescriptors().get(0); + formData.append("&nitems=").append(y.getNItems() + 1); + if (timeResolution != null) { + formData.append("&resolution=").append(timeResolution.doubleValue(Units.seconds)); + } + } + else if( "1".equals(dsd.getProperty("requiresInterval")) + || ("x_multi_y".equals(form) && dsd.getProperty("items") != null) ){ + formData.append("server=dataset"); + if (timeResolution != null) { + formData.append("&interval=").append(timeResolution.doubleValue(Units.seconds)); + } + } + else { + formData.append("server=compactdataset"); + if (timeResolution != null) { + formData.append("&resolution=").append(timeResolution.doubleValue(Units.seconds)); + } + } + + if (extraParameters != null) { + formData.append("¶ms=").append(extraParameters); //Should already be url encoded. + } + + //if ( min!=null && min.calcTransmitSpeed()>30000 ) { + // compress= false; + //} + + //compress= true; + if (compress) formData.append("&compress=true"); + + if ( !devel.equals("") ) formData.append("&devel=").append(devel); + + InputStream in= openURLConnection( dsd, start, end, formData ); + + in= DasApplication.getDefaultApplication().getInputStreamMeter().meterInputStream(in); + return in; + } + + private String createFormDataString(String dataSetID, Datum start, Datum end, StringBuffer additionalFormData) throws UnsupportedEncodingException { + DatumFormatter formatter = start.getUnits().getDatumFormatterFactory().defaultFormatter(); + String startStr = formatter.format(start); + String endStr= formatter.format(end); + StringBuilder formData= new StringBuilder("dataset="); + formData.append(URLBuddy.encodeUTF8(dataSetID)); + formData.append("&start_time=").append(URLBuddy.encodeUTF8(startStr)); + formData.append("&end_time=").append(URLBuddy.encodeUTF8(endStr)); + formData.append("&").append(additionalFormData); + return formData.toString(); + } + + // Get the location ID associated with the dataSetId + protected String getLocId(){ + if((m_sHost != null)&&(m_sDataSet != null)) + return String.format("%s|%s", m_sHost, m_sDataSet); + else + return null; + } + + // Check / Set location information + protected void checkSetLocDescription(CredentialsManager cm){ + String sLocId = getLocId(); + if(sLocId == null) return; + + if(cm.hasDescription(sLocId)) return; + + String sSvrName = server.getName(); + String sDesc = String.format("

%s


Server: %s
" + + "Data Set: %s", sSvrName, m_sHost, m_sDataSet); + + ImageIcon icon = server.getLogo(); + cm.setDescription(sLocId, sDesc, icon); + } + + @SuppressWarnings("null") + protected synchronized InputStream openURLConnection( + StreamDataSetDescriptor dsd, Datum start, Datum end, StringBuffer additionalFormData ) + throws DasException { + + String[] tokens = dsd.getDataSetID().split("\\?|\\&"); + String dataSetID = tokens[1]; + + URL serverURL; + InputStream inStream = null; + String sContentType = null; + try{ + //Construct the GET string + String formData = createFormDataString(dataSetID, start, end, additionalFormData); + + //Handle old-style das2 authentication + if(dsd.isRestrictedAccess()){ + key = server.getKey(""); + if(key != null){ + formData += "&key=" + URLEncoder.encode(key.toString(), "UTF-8"); + } + } + + if(redirect){ + formData += "&redirect=1"; + } + serverURL = server.getURL(formData); + + // Loop handling authentication if needed + Logger.getLogger(LogCategory.DATA_TRANSFER_LOG).fine("opening " + serverURL.toString()); + String sLocId = getLocId(); + + while(inStream == null){ + HttpURLConnection httpConn = null; + URLConnection conn = serverURL.openConnection(); + if(serverURL.getProtocol().startsWith("http")){ + httpConn = (HttpURLConnection) conn; + } + else{ + throw new DasException("Das2 Server Protocol " + serverURL.getProtocol() + + "is not supported."); + } + + CredentialsManager cm = CredentialsManager.getMannager(); + if(cm.hasCredentials(sLocId)){ + httpConn.setRequestProperty("Authorization", + "Basic " + cm.getHttpBasicHash(sLocId)); + } + + httpConn.connect(); + sContentType = httpConn.getContentType(); + + int nStatus = httpConn.getResponseCode(); + + if(nStatus == HttpURLConnection.HTTP_UNAUTHORIZED){ + checkSetLocDescription(cm); + // If the cm had credentials for this location, they obviously didn't + // work, so invalidate them. + cm.invalidate(sLocId); + if(cm.getHttpBasicHash(sLocId) == null){ + throw new CancelledOperationException("Failed to gather credentials for "+sLocId); + } + + // Try again... + httpConn.disconnect(); + continue; + } + + if(nStatus >= 400){ + inStream = httpConn.getErrorStream(); + } + else{ + inStream = httpConn.getInputStream(); + } + } + } + catch(IOException ex){ + throw new DasIOException(ex); + } + + this.lastRequestURL = String.valueOf(serverURL); + + try{ + + //if (!contentType.equalsIgnoreCase("application/octet-stream")) { + if (sContentType.equalsIgnoreCase("text/plain")) { + BufferedReader bin = new BufferedReader(new InputStreamReader(inStream)); + String line = bin.readLine(); + String message = ""; + while (line != null) { + message = message.concat(line); + line = bin.readLine(); + } + throw new DasIOException(message); + } + + return processStream(inStream); + } + catch(IOException ex){ + throw new DasIOException(ex); + } + } + + private InputStream processStream(InputStream in) throws IOException, DasException { + /* advances the inputStream past the old das2server tags if needed */ + BufferedInputStream bin= new BufferedInputStream(in); + + bin.mark(Integer.MAX_VALUE); + String serverResponse= readServerResponse(bin); + + if ( serverResponse.equals("") ) { + return bin; + + } else { + + if (serverResponse.equals("")) { + throw new NoDataInIntervalException("no data in interval"); + } + + String errorTag= "error"; + if (serverResponse.startsWith("<"+errorTag+">")) { + int index2= serverResponse.indexOf(""); + + String error= serverResponse.substring( errorTag.length()+2, + serverResponse.length()-(errorTag.length()+3)); + + org.das2.util.DasDie.println("error="+error); + + /* presume that the endUser has opted out */ + if (error.equals("")) { + throw new NoKeyProvidedException(""); + } + + if (error.equals("")) { + /* the server says the key used does not have access to the resouce requested. Allow the user to reauthenticate */ + //server.setKey(null); + //server.getKey(""); + throw new AccessDeniedException(""); + } + + if (error.equals("" )) { + throw new NoKeyProvidedException("invalid Key"); + } + + if (error.equals("")) { + throw new NoSuchDataSetException(""); + } + + else { + throw new DasServerException("Error response from server: "+error); + } + } + + return bin; + } + } + + private String readServerResponse(InputStream in) { + + // Read ..., leaving the InputStream immediately after // + + String das2Response; + + byte[] data = new byte[4096]; + + int lastBytesRead = -1; + + String s; + + int offset=0; + + try { + int bytesRead= in.read(data,offset,4096-offset); + + String das2ResponseTag= "das2Response"; + // beware das2ResponseTagLength=14 assumed below!!! + + if (bytesRead<(das2ResponseTag.length()+2)) { + offset+= bytesRead; + bytesRead= in.read(data,offset,4096-offset); + } + + if ( new String(data,0,14,"UTF-8").equals("<"+das2ResponseTag+">")) { + while (! new String( data,0,offset,"UTF-8" ).contains("") && + offset<4096 ) { + offset+= bytesRead; + bytesRead= in.read(data,offset,4096-offset); + } + + int index= new String( data,0,offset,"UTF-8" ).indexOf(""); + + das2Response= new String(data,14,index-14); + + org.das2.util.DasDie.println("das2Response="+das2Response); + + in.reset(); + in.skip( das2Response.length() + 2 * das2ResponseTag.length() + 5 ); + + } else { + in.reset(); + + das2Response=""; + } + } catch ( IOException e ) { + das2Response= ""; + } + + return das2Response; + + } + + @Override + public void reset() { + } + + // Looks like graveyard code, bring back if app building fails + //public void authenticate( String restrictedResourceLabel ) { + // Authenticator authenticator; + // authenticator= new Authenticator(server,restrictedResourceLabel); + // Key key= authenticator.authenticate(); + // if ( key!=null ) server.setKey(key); + //} + + /** + * Getter for property compress. + * @return Value of property compress. + */ + public boolean isCompress() { + return this.compress; + } + + /** + * Setter for property compress. + * @param compress New value of property compress. + */ + public void setCompress(boolean compress) { + this.compress = compress; + } + + /** + * Getter for property lastRequestURL. + * @return Value of property lastRequestURL. + */ + public String getLastRequestURL() { + return this.lastRequestURL; + } + + public void setLastRequestURL( String url ) { + } + + public DasServer getDasServer() { + return this.server; + } + + /** + * Holds value of property redirect. + */ + private boolean redirect= false; + + /** + * Getter for property redirect. + * @return Value of property redirect. + */ + public boolean isRedirect() { + return this.redirect; + } + + /** + * Setter for property redirect. + * @param redirect New value of property redirect. + */ + public void setRedirect(boolean redirect) { + this.redirect = redirect; + } + + /** + * Holds value of property key. + */ + private Key key=null; + + /** + * Getter for property key. + * @return Value of property key. + */ + public Key getKey() { + return this.key; + } + + private String devel=""; + + /** + * use the develop version of the reader instead of the + * production version. + */ + public String getDevel() { + return this.devel; + } + + /** + * use the develop version of the reader instead of the + * production version. If a reader was in dasHome/readers, + * then use the one in /home//readers/ instead. + * + */ + public void setDevel(String devel) { + this.devel = devel; + } + +} diff --git a/dasCore/src/main/java/org/das2/client/package.html b/dasCore/src/main/java/org/das2/client/package.html new file mode 100644 index 000000000..8fdafbb99 --- /dev/null +++ b/dasCore/src/main/java/org/das2/client/package.html @@ -0,0 +1,6 @@ + +

Provides classes for interacting with a das2Server, such as data set queries, +authorization, and server identity.

+

Also support for reading Das2Streams and legacy +streams is found here. These classes should probably be moved to the stream package.

+ \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/components/AngleSpectrogramSlicer.java b/dasCore/src/main/java/org/das2/components/AngleSpectrogramSlicer.java new file mode 100644 index 000000000..2c83e5b5a --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/AngleSpectrogramSlicer.java @@ -0,0 +1,248 @@ +/* File: HorizontalSpectrogramSlicer.java + * Copyright (C) 2002-2008 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.graph.SymbolLineRenderer; +import org.das2.graph.DasColumn; +import org.das2.graph.DasCanvas; +import org.das2.graph.GraphUtil; +import org.das2.graph.DasRow; +import org.das2.graph.DasPlot; +import org.das2.graph.DasAxis; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.event.BoxSelectionEvent; +import org.das2.event.BoxSelectionListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.*; + + +public class AngleSpectrogramSlicer extends DasPlot implements BoxSelectionListener { + + private JDialog popupWindow; + + private SymbolLineRenderer renderer; + private DasPlot parentPlot; + private TableDataSetConsumer consumer; + private Datum xstart; + private Datum ystart; + private DatumRange xrange; + private DatumRange yrange; + + private int sliceDir; + private final int SLICEDIR_HORIZ=0; + private final int SLICEDIR_VERTICAL=1; + + private AngleSpectrogramSlicer(DasPlot plot, DasAxis xAxis, DasAxis yAxis, TableDataSetConsumer consumer ) { + super(xAxis, yAxis); + parentPlot = plot; + renderer= new SymbolLineRenderer(); + this.consumer= consumer; + addRenderer(renderer); + } + + public static AngleSpectrogramSlicer createSlicer( DasPlot plot, TableDataSetConsumer dataSetConsumer ) { + DasAxis sourceXAxis = plot.getXAxis(); + DasAxis xAxis = sourceXAxis.createAttachedAxis(DasAxis.HORIZONTAL); + DasAxis yAxis = dataSetConsumer.getZAxis().createAttachedAxis(DasAxis.VERTICAL); + + return new AngleSpectrogramSlicer(plot, xAxis, yAxis, dataSetConsumer ); + } + + public void showPopup() { + if (SwingUtilities.isEventDispatchThread()) { + showPopupImpl(); + } + else { + Runnable r = new Runnable() { + public void run() { + showPopupImpl(); + } + }; + } + } + + /** This method should ONLY be called by the AWT event thread */ + private void showPopupImpl() { + if (popupWindow == null) { + createPopup(); + } + popupWindow.setVisible(true); + } + + /** This method should ONLY be called by the AWT event thread */ + private void createPopup() { + int width = parentPlot.getCanvas().getWidth() / 2; + int height = parentPlot.getCanvas().getHeight() / 2; + DasCanvas canvas = new DasCanvas(width, height); + DasRow row = new DasRow(canvas, 0.1, 0.9); + DasColumn column = new DasColumn(canvas, 0.1, 0.9); + canvas.add(this, row, column); + + JPanel content = new JPanel(new BorderLayout()); + + JPanel buttonPanel = new JPanel(); + BoxLayout buttonLayout = new BoxLayout(buttonPanel, BoxLayout.X_AXIS); + JButton close = new JButton("Hide Window"); + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + popupWindow.setVisible(false); + } + }); + buttonPanel.setLayout(buttonLayout); + buttonPanel.add(Box.createHorizontalGlue()); + buttonPanel.add(close); + + content.add(canvas, BorderLayout.CENTER); + content.add(buttonPanel, BorderLayout.SOUTH); + + Window parentWindow = SwingUtilities.getWindowAncestor(parentPlot); + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } + else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } + else { + popupWindow = new JDialog(); + } + popupWindow.setTitle("Angle Slicer"); + popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + popupWindow.setContentPane(content); + popupWindow.pack(); + + Point parentLocation = new Point(); + SwingUtilities.convertPointToScreen(parentLocation, parentPlot.getCanvas()); + popupWindow.setLocation(parentLocation.x + parentPlot.getCanvas().getWidth(),parentLocation.y + height); + } + + + private VectorDataSet angleSliceHoriz( TableDataSet tds, DatumRange xlimit, Datum xbase, Datum ybase, Datum slope ) { + VectorDataSetBuilder builder= new VectorDataSetBuilder( tds.getXUnits(), tds.getZUnits() ); + int i0= DataSetUtil.closestColumn( tds, xlimit.min() ); + int i1= DataSetUtil.closestColumn( tds, xlimit.max() ); + + int irow0=0; + int irow1=0; + + Units zunits= tds.getZUnits(); + Units yunits= tds.getYUnits(); + + for ( int i=i0; i0 && tds.getYTagDatum( itable, irow0 ).gt( y ) ) irow0--; + irow1= irow0+1; + while ( (irow1+1)0 ) { + double z0= tds.getDouble( i, irow0, zunits ); + double z1= tds.getDouble( i, irow1, zunits ); + double y0= tds.getYTagDouble( itable, irow0, yunits ); + double y1= tds.getYTagDouble( itable, irow1, yunits ); + double yy= y.doubleValue(yunits); + double alpha= ( yy-y0 ) / ( y1-y0 ); + double zinterp= z0 + (z1-z0) * alpha; + builder.insertY( x, Datum.create(zinterp,zunits) ); + } + } + + return builder.toVectorDataSet(); + } + + @Override + public void drawContent(Graphics2D g) { + + double[] ixs; + double ix; + if ( sliceDir==SLICEDIR_HORIZ ) { + ixs= GraphUtil.transformRange( this.getXAxis(), xrange ); + ix= this.getXAxis().transform( xstart ); + } else { + ixs= GraphUtil.transformRange( this.getYAxis(), yrange ); + ix= this.getYAxis().transform( ystart ); + } + + DasRow row= getRow(); + + g.setColor( new Color(230,230,230) ); + g.fillRect( (int)ixs[0], row.getDMinimum(), (int)(ixs[1]-ixs[0]), row.getHeight() ); + + g.setColor( Color.LIGHT_GRAY ); + g.drawLine( (int)ix, row.getDMinimum(), (int)ix, row.getDMaximum() ); + + super.drawContent(g); + + } + + protected void processDasUpdateEvent(org.das2.event.DasUpdateEvent e) { + if (isDisplayable()) { + updateImmediately(); + resize(); + } + } + + public void BoxSelected(BoxSelectionEvent e) { + Datum xbase= e.getStartX(); + Datum ybase= e.getStartY(); + + xstart= e.getStartX(); + ystart= e.getStartY(); + + xrange= e.getXRange(); + yrange= e.getYRange(); + + Datum slope= e.getFinishY().subtract(ybase) .divide( e.getFinishX().subtract(xbase) ); + + DataSet ds = consumer.getConsumedDataSet(); + + if (ds==null || !(ds instanceof TableDataSet)) { + return; + } + + TableDataSet tds = (TableDataSet)ds; + VectorDataSet sliceDataSet; + + sliceDataSet= angleSliceHoriz( tds, getXAxis().getDatumRange(), xbase, ybase, slope ); + sliceDir= SLICEDIR_HORIZ; + + renderer.setDataSet(sliceDataSet); + + if (!(popupWindow == null || popupWindow.isVisible()) || getCanvas() == null) { + showPopup(); + } + + } +} diff --git a/dasCore/src/main/java/org/das2/components/AsciiFileParser.form b/dasCore/src/main/java/org/das2/components/AsciiFileParser.form new file mode 100644 index 000000000..9da4f7151 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/AsciiFileParser.form @@ -0,0 +1,182 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/dasCore/src/main/java/org/das2/components/AsciiFileParser.java b/dasCore/src/main/java/org/das2/components/AsciiFileParser.java new file mode 100644 index 000000000..15982d0c3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/AsciiFileParser.java @@ -0,0 +1,434 @@ +/* + * AsciiFileParser.java + * + * Created on July 13, 2006, 3:26 PM + */ + +package org.das2.components; + +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.util.DasExceptionHandler; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.MouseEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import javax.swing.JFileChooser; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; +import javax.swing.event.MouseInputAdapter; + +/** + * Interactive GUI for parsing ascii tables into VectorDataSets + * + * @author Jeremy + */ +public class AsciiFileParser extends javax.swing.JPanel { + + /** Creates new form AsciiFileParser */ + public AsciiFileParser() { + initComponents(); + jTextField1.addFocusListener( new FocusAdapter() { + public void focusLost(FocusEvent e) { + updateSkipLines(); + } + }); + model= new AsciiTableModel(); + } + + class AsciiTableModel { + int columnCount=0; + int[] columnOffsets= new int[50]; + int[] columnWidths= new int[50]; + Units[] units= new Units[50]; + String[] names= new String[50]; + int skipLines=0; + } + + AsciiTableModel model; + File file; + + public DataSet parse() throws FileNotFoundException, IOException, ParseException { + + ArrayList usableColumns= new ArrayList(); + for ( int i=0; i//GEN-BEGIN:initComponents + private void initComponents() { + jLabel1 = new javax.swing.JLabel(); + jLabel2 = new javax.swing.JLabel(); + delimSelector = new javax.swing.JComboBox(); + jLabel3 = new javax.swing.JLabel(); + jButton1 = new javax.swing.JButton(); + firstColumnTimeCheckBox = new javax.swing.JCheckBox(); + jScrollPane1 = new javax.swing.JScrollPane(); + jPanel1 = new javax.swing.JPanel(); + jTextArea1 = new FixedColumnTextArea(); + jTextArea2 = new FixedColumnTextArea(); + jLabel4 = new javax.swing.JLabel(); + jTextField1 = new javax.swing.JTextField(); + + jLabel1.setText("File:"); + + jLabel2.setText("f:\\myfile.txt"); + + delimSelector.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "WhiteSpace", "Comma", "RegExp (\\s\\s+)" })); + delimSelector.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + delimSelectorActionPerformed(evt); + } + }); + + jLabel3.setText("Delimiter:"); + + jButton1.setText("choose"); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); + + firstColumnTimeCheckBox.setText("FirstColumnTime"); + firstColumnTimeCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + firstColumnTimeCheckBox.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + jPanel1.setLayout(new java.awt.BorderLayout()); + + jTextArea1.setColumns(20); + jTextArea1.setRows(5); + jPanel1.add(jTextArea1, java.awt.BorderLayout.CENTER); + + jTextArea2.setBackground(new java.awt.Color(238, 238, 255)); + jTextArea2.setColumns(20); + jTextArea2.setRows(1); + MouseInputAdapter mia= new ColumnMouseInputAdapter((FixedColumnTextArea)jTextArea2); + jTextArea2.addMouseListener(mia); + jPanel1.add(jTextArea2, java.awt.BorderLayout.NORTH); + + jScrollPane1.setViewportView(jPanel1); + + jLabel4.setText("SkipLines:"); + + jTextField1.setText("0"); + jTextField1.addPropertyChangeListener(new java.beans.PropertyChangeListener() { + public void propertyChange(java.beans.PropertyChangeEvent evt) { + jTextField1PropertyChange(evt); + } + }); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(org.jdesktop.layout.GroupLayout.TRAILING, jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 453, Short.MAX_VALUE) + .add(layout.createSequentialGroup() + .add(jLabel1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 45, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jButton1) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jLabel2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 264, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(layout.createSequentialGroup() + .add(jLabel3) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(delimSelector, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(layout.createSequentialGroup() + .add(firstColumnTimeCheckBox) + .add(77, 77, 77) + .add(jLabel4) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jTextField1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 47, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(jLabel2) + .add(jLabel1) + .add(jButton1)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(delimSelector, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(jLabel3)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(firstColumnTimeCheckBox) + .add(jLabel4) + .add(jTextField1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 335, Short.MAX_VALUE) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + private void jTextField1PropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_jTextField1PropertyChange + updateSkipLines(); + }//GEN-LAST:event_jTextField1PropertyChange + + private void delimSelectorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_delimSelectorActionPerformed + try { + resetDelims(); + } catch ( IOException e ) { + handleException(e); + } + }//GEN-LAST:event_delimSelectorActionPerformed + + private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + JFileChooser chooser= new JFileChooser(); + int retVal= chooser.showOpenDialog(this); + if ( retVal==chooser.APPROVE_OPTION ) { + try { + setFile( chooser.getSelectedFile() ); + } catch (IOException ex) { + handleException(ex); + } + } + }//GEN-LAST:event_jButton1ActionPerformed + + private void handleException( Throwable t ) { + DasExceptionHandler.handle(t); + } + + private void setFile(File file) throws IOException { + this.file= file; + resetDelims(); + } + + public String getDelimRegex() { + String result; + switch ( delimSelector.getSelectedIndex() ) { + case 0: result="\\s+"; break; + case 1: result=","; break; + case 2: result="\\s\\s+"; break; + default: throw new IllegalStateException("not implemented"); + } + return result; + } + + public void resetDelims() throws IOException { + String regex= getDelimRegex(); + BufferedReader reader= new BufferedReader( new FileReader( file ) ); + + for ( int i=0; i=taskList.size() ) { + if ( exit ) System.exit(0); + } else { + DasLogger.getLogger(DasLogger.SYSTEM_LOG).fine( "itask="+taskList.get(itask) ); + DataRangeSelectionEvent ev= (DataRangeSelectionEvent) taskList.get(itask++); + fireDataRangeSelectionListenerDataRangeSelected( ev ); + try { + canvas.waitUntilIdle(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + tod.completeTask( ev.getDatumRange() ); + submitNextTask(); + } + } + }); + thread.start(); + } + + /** Utility field used by event firing mechanism. */ + private javax.swing.event.EventListenerList listenerList = null; + + /** Registers DataRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } + listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Removes DataRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); + } + + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { + ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); + } + } + } + + +} diff --git a/dasCore/src/main/java/org/das2/components/ColorBarComponent.java b/dasCore/src/main/java/org/das2/components/ColorBarComponent.java new file mode 100644 index 000000000..c96d59e01 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/ColorBarComponent.java @@ -0,0 +1,38 @@ +/* + * ColorBarComponent.java + * + * Created on November 15, 2003, 11:16 AM + */ + +package org.das2.components; + +import org.das2.graph.DasColorBar; +import org.das2.graph.DasColumn; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasRow; +import org.das2.datum.Datum; +import javax.swing.*; + +/** + * + * @author Owner + */ +public class ColorBarComponent extends JPanel { + DasColorBar colorBar; + DasCanvas canvas; + + /** Creates a new instance of ColorBarComponent */ + public ColorBarComponent(Datum min, Datum max, boolean isLog) { + canvas= new DasCanvas(100, 500); + DasRow row= DasRow.create(canvas); + DasColumn column= DasColumn.create(canvas); + colorBar= new DasColorBar( min, max, isLog ); + canvas.add(colorBar,row, column); + this.add(canvas); + } + + public DasColorBar getColorBar() { + return colorBar; + } + +} diff --git a/dasCore/src/main/java/org/das2/components/ComponentsUtil.java b/dasCore/src/main/java/org/das2/components/ComponentsUtil.java new file mode 100644 index 000000000..29764af39 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/ComponentsUtil.java @@ -0,0 +1,76 @@ +/* + * ComponentsUtil.java + * + * Created on February 28, 2006, 12:10 PM + * + * + */ + +package org.das2.components; + +import org.das2.graph.DasCanvas; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +/** + * + * @author Jeremy + */ +public class ComponentsUtil { + public static DasCanvas createPopupCanvas( Component parent, String title, int width, int height ) { + DasCanvas canvas = new DasCanvas(width, height); + + JPanel content = new JPanel(new BorderLayout()); + + JPanel buttonPanel = new JPanel(); + + BoxLayout buttonLayout = new BoxLayout(buttonPanel, BoxLayout.X_AXIS); + JButton close = new JButton("Hide Window"); + + buttonPanel.setLayout(buttonLayout); + buttonPanel.add(Box.createHorizontalGlue()); + buttonPanel.add(close); + + content.add(canvas, BorderLayout.CENTER); + content.add(buttonPanel, BorderLayout.SOUTH); + + final JDialog popupWindow; + Window parentWindow = SwingUtilities.getWindowAncestor(parent); + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } else { + popupWindow = new JDialog(); + } + + popupWindow.setTitle(title); + popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + popupWindow.setContentPane(content); + popupWindow.pack(); + + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + popupWindow.setVisible(false); + } + }); + + Point parentLocation = new Point(); + SwingUtilities.convertPointToScreen( parentLocation, parent ); + popupWindow.setLocation( parentLocation.x + parent.getWidth(),parentLocation.y); + + return canvas; + } +} diff --git a/dasCore/src/main/java/org/das2/components/DasAxisSelector.java b/dasCore/src/main/java/org/das2/components/DasAxisSelector.java new file mode 100644 index 000000000..a65c008c3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/DasAxisSelector.java @@ -0,0 +1,113 @@ +/* File: DasAxisSelector.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +/** + * + * @author jbf + */ +import org.das2.datum.Datum; +import org.das2.graph.DasAxis; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; +import java.text.DecimalFormat; + +public class DasAxisSelector extends javax.swing.JPanel implements ActionListener { + + private DasAxis axis= null; + + JTextField idStart= null; + JTextField idStop= null; + + /** Creates a new instance of DasTimeRangeSelector */ + private DasAxisSelector() { + super(); + buildComponents(); + } + + private void buildComponents() { + this.setLayout(new FlowLayout(FlowLayout.RIGHT)); + + idStart= new JTextField(""); + idStart.setSize(9,1); + idStart.addActionListener(this); + idStart.setActionCommand("setMinimum"); + this.add(idStart); + + idStop= new JTextField(""); + idStop.setSize(9,1); + idStop.addActionListener(this); + idStop.setActionCommand("setMaximum"); + this.add(idStop); + + } + + public DasAxisSelector(DasAxis axis) { + this(); + this.axis= axis; + update(); + } + + public double getStartTime() { + double s1= Double.valueOf(idStart.getText()).doubleValue(); + return s1; + } + + public double getEndTime() { + double s1= Double.valueOf(idStop.getText()).doubleValue(); + return s1; + } + + private void update() { + DecimalFormat df= new DecimalFormat(); + df.setMaximumFractionDigits(2); + idStart.setText(df.format(axis.getDataMinimum())); + idStop.setText(df.format(axis.getDataMaximum())); + idStart.setText(""+axis.getDataMinimum()); + idStop.setText(""+axis.getDataMaximum()); + } + + public void actionPerformed(java.awt.event.ActionEvent actionEvent) { + String command= actionEvent.getActionCommand(); + update(); + if (command.equals("setMinimum")) { + try { + axis.setDataRange(Datum.create(Double.valueOf(idStart.getText()).doubleValue(),axis.getUnits()), + axis.getDataMaximum()); + } catch (NumberFormatException e) { + org.das2.util.DasDie.println(e); + } + } else if (command.equals("setMaximum")) { + try { + axis.setDataRange(axis.getDataMinimum(), + Datum.create(Double.valueOf(idStop.getText()).doubleValue(), axis.getUnits() )); + } catch (NumberFormatException e) { + org.das2.util.DasDie.println(e); + } + } + } + +} diff --git a/dasCore/src/main/java/org/das2/components/DasProgressPanel.java b/dasCore/src/main/java/org/das2/components/DasProgressPanel.java new file mode 100755 index 000000000..1f7a04887 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/DasProgressPanel.java @@ -0,0 +1,565 @@ +/* File: DasProgressPanel.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.components; + +import org.das2.graph.DasCanvasComponent; +import org.das2.system.DasLogger; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.InvocationTargetException; +import java.text.DecimalFormat; +import java.util.logging.Level; +import javax.swing.*; +import javax.swing.border.*; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.NumberFormatUtil; +import java.util.*; +import java.util.logging.Logger; +import org.das2.graph.DasCanvas; + +/** + * + * @author eew + */ +public class DasProgressPanel implements ProgressMonitor { + + private long taskStartedTime; + private long currentTaskPosition; + private long maximumTaskPosition; + private DecimalFormat transferRateFormat; + private String transferRateString; + private JLabel taskLabel; + private boolean labelDirty = false; + private JLabel progressMessageLabel; + private String progressMessageString; + private boolean progressMessageDirty = false; + private JLabel kbLabel; + private JProgressBar progressBar; + private JFrame jframe = null; // created when createFramed() is used. + private boolean isCancelled = false; + private JButton cancelButton; + private int cancelCheckFailures = 0; // number of times client codes failed to check cancelled before setTaskProgress. + private boolean cancelChecked = false; + private String label; + private static final int hideInitiallyMilliSeconds = 300; + private static final int refreshPeriodMilliSeconds = 500; + private boolean running = false; + private boolean finished = false; + private long lastRefreshTime; + private ArrayList refreshTimeQueue; + private Thread updateThread; + private Logger logger = DasLogger.getLogger(DasLogger.SYSTEM_LOG); + private boolean showProgressRate; + private JPanel thePanel; + private boolean componentsInitialized; + private DasCanvasComponent parentComponent; + private DasCanvas parentCanvas; + private static int createComponentCount = 0; + + class MyPanel extends JPanel { + + protected void paintComponent(Graphics g1) { + Graphics2D g2 = (Graphics2D) g1; + + g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, + RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); + g2.setColor(new Color(0xdcFFFFFF, true)); + Rectangle rect = g2.getClipBounds(); + if (rect == null) { + g2.fillRect(0, 0, getWidth(), getHeight()); + } else { + g2.fillRect(rect.x, rect.y, rect.width, rect.height); + } + super.paintComponent(g1); + } + } // provides details button, which shows who creates and who consumes the ProgressPanel + final static boolean useDetails = false; + Exception source; + Exception consumer; + + public DasProgressPanel(String label) { + if (useDetails) + source = new Exception(); + + componentsInitialized = false; + this.label = label; + + transferRateFormat = NumberFormatUtil.getDecimalFormat(); + transferRateFormat.setMaximumFractionDigits(2); + maximumTaskPosition = -1; + lastRefreshTime = Integer.MIN_VALUE; + showProgressRate = true; + isCancelled = false; + running = false; + } + + private void details() { + System.err.println("Source: "); + source.printStackTrace(); + System.err.println("Consumer: "); + consumer.printStackTrace(); + String stateString; + if (finished) { + stateString = "finished"; + } else if (running) { + stateString = "running"; + } else if (isCancelled) { + stateString = "cancelled"; + } + System.err.println("State: "); + System.err.println(" running: " + running); + System.err.println(" cancelled: " + isCancelled); + System.err.println(" finished: " + finished); + } + + /** + * returns the JPanel component. + */ + public Component getComponent() { + if (!componentsInitialized) + initComponents(); + return this.thePanel; + } + + public static DasProgressPanel createComponentPanel(DasCanvasComponent component, String initialMessage) { + DasProgressPanel progressPanel = new DasProgressPanel(initialMessage); + progressPanel.parentComponent = component; + return progressPanel; + } + + public static DasProgressPanel createComponentPanel(DasCanvas canvas, String initialMessage) { + DasProgressPanel progressPanel = new DasProgressPanel(initialMessage); + progressPanel.parentCanvas = canvas; + return progressPanel; + } + + + /** Returning true here keeps the progress bar from forcing the whole canvas + * to repaint when the label of the progress bar changes. + */ + public boolean isValidateRoot() { + return true; + } + + public static DasProgressPanel createFramed(String label) { + DasProgressPanel result; + result = new DasProgressPanel(label); + result.jframe = new JFrame("Das Progress Monitor"); + result.initComponents(); + result.jframe.getContentPane().add(result.thePanel); + result.jframe.pack(); + result.jframe.setVisible(false); + result.jframe.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + return result; + } + + public void setLabel(String label) { + this.label = label; + this.labelDirty = true; + if (thePanel != null) + thePanel.repaint(); + } + + public String getLabel() { + return label; + } + + private void initComponents() { + // get a stack trace so we can see what caused this. + if (useDetails) + consumer = new Exception(); + + createComponentCount++; + //System.err.println("createComponentCount="+createComponentCount ); + JPanel mainPanel, buttonPanel; + + taskLabel = new JLabel(); + taskLabel.setOpaque(false); + taskLabel.setFont(new Font("Dialog", 1, 18)); + taskLabel.setHorizontalAlignment(JLabel.CENTER); + taskLabel.setText(label); + taskLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT); + + progressMessageLabel = new JLabel() { + + public void paint(Graphics g) { + ((java.awt.Graphics2D) g).setRenderingHint( + java.awt.RenderingHints.KEY_TEXT_ANTIALIASING, + java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + super.paint(g); + } + }; + progressMessageLabel.setOpaque(false); + progressMessageLabel.setFont(new Font("Dialog", 1, 8)); + progressMessageLabel.setHorizontalAlignment(JLabel.CENTER); + progressMessageLabel.setText(" "); + progressMessageLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT); + + progressBar = new JProgressBar(); + progressBar.setOpaque(false); + progressBar.setMaximumSize(progressBar.getPreferredSize()); + progressBar.setMinimumSize(progressBar.getPreferredSize()); + progressBar.setAlignmentX(JComponent.CENTER_ALIGNMENT); + + kbLabel = new JLabel(); + kbLabel.setOpaque(false); + kbLabel.setHorizontalAlignment(SwingConstants.CENTER); + kbLabel.setText("0 kb"); + kbLabel.setAlignmentX(0.5F); + kbLabel.setMaximumSize(progressBar.getPreferredSize()); + kbLabel.setMinimumSize(progressBar.getPreferredSize()); + kbLabel.setPreferredSize(progressBar.getPreferredSize()); + kbLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT); + + mainPanel = new JPanel(); + mainPanel.setOpaque(false); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + mainPanel.add(taskLabel); + mainPanel.add(progressMessageLabel); + mainPanel.add(progressBar); + mainPanel.add(kbLabel); + + Border lineBorder = new LineBorder(Color.BLACK, 2); + Border emptyBorder = new EmptyBorder(2, 2, 2, 2); + CompoundBorder border = new CompoundBorder(lineBorder, emptyBorder); + + JButton detailsButton; + if (useDetails) { + detailsButton = new JButton("details"); + detailsButton.setOpaque(false); + detailsButton.setBorder(border); + detailsButton.setFocusPainted(false); + detailsButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + details(); + } + }); + } + + cancelButton = new JButton("cancel"); + cancelButton.setEnabled(false); + cancelButton.setOpaque(false); + cancelButton.setBorder(border); + cancelButton.setFocusPainted(false); + cancelButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + cancel(); + } + }); + + buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel.setOpaque(false); + + if (useDetails) + buttonPanel.add(detailsButton); + buttonPanel.add(cancelButton); + + thePanel = new MyPanel(); + thePanel.setOpaque(false); + thePanel.setLayout(new BorderLayout()); + thePanel.add(mainPanel, BorderLayout.CENTER); + thePanel.add(buttonPanel, BorderLayout.SOUTH); + + if (parentComponent != null) { + thePanel.setSize(thePanel.getPreferredSize()); + int x = parentComponent.getColumn().getDMiddle(); + int y = parentComponent.getRow().getDMiddle(); + thePanel.setLocation(x - thePanel.getWidth() / 2, y - thePanel.getHeight() / 2); + ((Container) (parentComponent.getCanvas().getGlassPane())).add(thePanel); + thePanel.setVisible(false); + } else if ( parentCanvas!=null ) { + thePanel.setSize(thePanel.getPreferredSize()); + int x = parentCanvas.getWidth()/2; + int y = parentCanvas.getHeight()/2; + thePanel.setLocation(x - thePanel.getWidth() / 2, y - thePanel.getHeight() / 2); + ((Container) (parentCanvas.getGlassPane())).add(thePanel); + thePanel.setVisible(false); + } + + componentsInitialized = true; + } + + public synchronized void finished() { + running = false; + finished = true; + if (jframe == null) { + setVisible(false); + } else { + jframe.dispose(); + } + } + + /* ProgressMonitor interface */ + public void setTaskProgress(long position) throws IllegalStateException { + if (logger.isLoggable(Level.FINEST)) { + logger.finest("progressPosition=" + position); + } + + if (isCancelled) { + // note that the monitored process should check isCancelled before setTaskProgress, but this is no longer required. + // If this is not done, we throw a IllegalStateException to kill the thread, and the monitored process is killed that way. + logger.fine("setTaskProgress called when isCancelled true. consider checking isCancelled before calling setTaskProgress."); + throw new IllegalStateException("Operation cancelled: developers: consider checking isCancelled before calling setTaskProgress."); + } + + if (!running) { + throw new IllegalStateException("setTaskProgress called before started"); + } + + if (position != 0 && position < currentTaskPosition) { + logger.finest("progress position goes backwards"); + } + + if (!cancelChecked) { + // cancelCheckFailures is used to detect when if the monitored process is not checking cancelled. If it is not, then we + // disable the cancel button. Note the cancel() method can still be called from elsewhere, killing the process. + cancelCheckFailures++; + } + cancelChecked = false; // reset for next time, isCancelled will set true. + + if (maximumTaskPosition == 0) { + throw new IllegalArgumentException("when taskSize is 0, setTaskProgress must not be called."); + } + + currentTaskPosition = position; + + long elapsedTimeMs = System.currentTimeMillis() - taskStartedTime; + if (elapsedTimeMs > hideInitiallyMilliSeconds && !isVisible()) { + setVisible(true); + } + /* long tnow; + if ( (tnow=System.currentTimeMillis()) - lastRefreshTime > 30 ) { + updateUIComponents(); + if (Toolkit.getDefaultToolkit().getSystemEventQueue().isDispatchThread()) { + paintImmediately(0, 0, getWidth(), getHeight()); + } + else { + repaint(); + } + lastRefreshTime= tnow; + } */ + } + + private void startUpdateThread() { + Runnable run = new Runnable() { + + public void run() { + while (!DasProgressPanel.this.finished) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + updateUIComponents(); + thePanel.repaint(); + } + }); + Thread.sleep(refreshPeriodMilliSeconds); + } catch (InterruptedException ex) { + Logger.getLogger(DasProgressPanel.class.getName()).log(Level.SEVERE, null, ex); + } catch (InvocationTargetException ex) { + Logger.getLogger(DasProgressPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + }; + updateThread = new Thread(run, "progressMonitorUpdateThread"); + updateThread.start(); + } + + private void updateUIComponents() { + long elapsedTimeMs = System.currentTimeMillis() - taskStartedTime; + + long kb = currentTaskPosition; + + if (maximumTaskPosition == -1) { + progressBar.setIndeterminate(true); + } else { + progressBar.setIndeterminate(false); + } + if (maximumTaskPosition > 0) { + progressBar.setValue((int) (kb * 100 / (maximumTaskPosition))); + } else { + progressBar.setValue((int) kb % 100); + } + + String bytesReadLabel; + if (maximumTaskPosition > 0) { + bytesReadLabel = "" + kb + "/" + maximumTaskPosition + ""; + } else { + bytesReadLabel = "" + kb + ""; + } + + if (progressMessageDirty) { + if (progressMessageString.length() > 33) { + int n = progressMessageString.length(); + progressMessageString = progressMessageString.substring(0, 10) + "..." + progressMessageString.substring(n - 22, n); + } + progressMessageLabel.setText(progressMessageString); + progressMessageDirty = false; + } + + if (labelDirty) { + taskLabel.setText(label); + labelDirty = false; + } + + if (showProgressRate && elapsedTimeMs > 1000 && transferRateString != null) { + double transferRate = ((double) currentTaskPosition * 1000) / (elapsedTimeMs); + kbLabel.setText(bytesReadLabel + " " + transferRateString); + } else { + kbLabel.setText(bytesReadLabel); + } + + boolean cancelEnabled = cancelCheckFailures < 2; + if (cancelEnabled != cancelButton.isEnabled()) { + cancelButton.setEnabled(cancelEnabled); + } + } + + @Deprecated + public void setAdditionalInfo(String s) { + transferRateString = s; + } + + public long getTaskProgress() { + return currentTaskPosition; + } + + public long getTaskSize() { + return maximumTaskPosition; + } + + public void setTaskSize(long taskSize) { + if (taskSize < -1) { + throw new IllegalArgumentException("taskSize must be positive, -1, or 0, not " + taskSize); + } else { + if (componentsInitialized) + progressBar.setIndeterminate(false); + } + maximumTaskPosition = taskSize; + } + + public synchronized void setVisible(boolean visible) { + if (!componentsInitialized && !visible) + return; + if (!componentsInitialized && !finished) + initComponents(); + if (thePanel != null) + thePanel.setVisible(visible); + if (this.jframe != null) + this.jframe.setVisible(visible); + + if (visible) { + startUpdateThread(); + } + } + + public boolean isVisible() { + return (!componentsInitialized || thePanel.isVisible()); + } + + public void started() { + taskStartedTime = System.currentTimeMillis(); + running = true; + + if (hideInitiallyMilliSeconds > 0) { + setVisible(false); + new Thread(new Runnable() { + + public void run() { + try { + Thread.sleep(hideInitiallyMilliSeconds); + } catch (InterruptedException e) { + } + ; + if (running) { + logger.fine("hide time=" + (System.currentTimeMillis() - taskStartedTime)); + setVisible(true); + } + } + }, "progressPanelUpdateThread").start(); + } else { + setVisible(true); + } + + // cancel() might have been called before we got here, so check it. + if (isCancelled) + return; + if (maximumTaskPosition > 0) + setTaskProgress(0); + } + + public void cancel() { + isCancelled = true; + finished(); + } + + public boolean isCancelled() { + cancelCheckFailures = 0; + cancelChecked = true; + return isCancelled; + } + + public Exception getSource() { + return source; + } + + public Exception getConsumer() { + return consumer; + } + + /** + * Setter for property showProgressRate. + * @param showProgressRate New value of property showProgressRate. + */ + public void setShowProgressRate(boolean showProgressRate) { + this.showProgressRate = showProgressRate; + } + + public String toString() { + if (isCancelled) { + return "cancelled"; + } else if (finished) { + return "finished"; + } else if (running) { + return "" + currentTaskPosition + " of " + this.maximumTaskPosition; + } else { + return "waiting for start"; + } + } + + public void setProgressMessage(String message) { + this.progressMessageString = message; + this.progressMessageDirty = true; + } + + public boolean isStarted() { + return running; + } + + public boolean isFinished() { + return finished; + } +} diff --git a/dasCore/src/main/java/org/das2/components/DasTimeRangeSelector.java b/dasCore/src/main/java/org/das2/components/DasTimeRangeSelector.java new file mode 100755 index 000000000..5a5f173b4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/DasTimeRangeSelector.java @@ -0,0 +1,456 @@ +/* File: DasTimeRangeSelector.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.datum.DatumRangeUtil; +import org.das2.util.DasExceptionHandler; +/** + * + * @author jbf + */ +import org.das2.datum.TimeUtil; +import org.das2.event.TimeRangeSelectionEvent; +import org.das2.event.TimeRangeSelectionListener; +import org.das2.system.DasLogger; +import java.awt.CardLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; + +import javax.swing.event.EventListenerList; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.prefs.*; +import javax.swing.*; + +public class DasTimeRangeSelector extends JPanel implements TimeRangeSelectionListener { + public static final String PROP_RANGE = "range"; + + private DatumRange range= null; + + JTextField idStart= null; + JTextField idStop= null; + JButton viewButton= null; + JPanel startStopModePane=null; + CardLayout cardLayout= null; + + boolean updateRangeString= false; // true indicates use formatted range string in start time cell. + + /** Utility field used by event firing mechanism. */ + private EventListenerList listenerList = null; + + /** Action that is associated with the previous button. + * Access is given to subclasses so that other widgets can be associated + * with this action (Popup menu, etc). + */ + protected final Action previousAction = new AbstractAction("<<") { + public void actionPerformed(ActionEvent e) { + fireTimeRangeSelectedPrevious(); + } + }; + + /** Action that is associated with the next button. + * Access is given to subclasses so that other widgets can be associated + * with this action (Popup menu, etc). + */ + protected final Action nextAction = new AbstractAction(">>") { + public void actionPerformed(ActionEvent e) { + fireTimeRangeSelectedNext(); + } + }; + + protected final Action rangeAction = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + fireTimeRangeSelected(); + } + }; + + private boolean favoritesEnabled= false; + + private List favoritesList= null; + private JPopupMenu favoritesMenu= null; + + private final int FAVORITES_LIST_SIZE= 5; + + private String favoritesGroup; + + private JButton favoritesButton; + + private JPanel timesPane; + + private JComboBox rangeComboBox; + + private boolean pref; + + /** Creates a new instance of DasTimeRangeSelector */ + public DasTimeRangeSelector() { + super(); + pref= !org.das2.DasApplication.getDefaultApplication().isApplet(); + if ( pref ) { + updateRangeString= Preferences.userNodeForPackage(this.getClass()).getBoolean("updateRangeString", false); + } else { + updateRangeString= false; + } + buildComponents(); + } + + private Action getModeAction() { + return new AbstractAction("mode") { + public void actionPerformed( ActionEvent e ) { + updateRangeString= !updateRangeString; + if ( pref ) Preferences.userNodeForPackage(this.getClass()).putBoolean("updateRangeString", updateRangeString ); + revalidateUpdateMode(); + update(); + } + }; + } + + private void revalidateUpdateMode() { + if ( updateRangeString ) { + //idStop.setColumns(8); + idStart.setColumns(28); + idStop.setVisible(false); + viewButton.setVisible(true); + //cardLayout.show( timesPane, "range" ); + } else { + idStart.setColumns(18); + // idStop.setColumns(18); + idStop.setVisible(true); + //cardLayout.show( timesPane, "startStop" ); + } + startStopModePane.revalidate(); + } + + private void buildComponents() { + this.setLayout(new FlowLayout()); + + JButton b= new JButton(); + b.setAction(previousAction); + b.setActionCommand("previous"); + b.setToolTipText("Scan back in time"); + this.add(b); + + startStopModePane= new JPanel(new FlowLayout()); + + cardLayout= new CardLayout(); + timesPane= new JPanel( cardLayout ); + + JPanel startStopPane2= new JPanel(new FlowLayout()); + + idStart= new JTextField(18); + idStart.setAction(rangeAction); + idStart.setActionCommand("startTime"); + startStopPane2.add(idStart); + + idStop= new JTextField(18); + idStop.addActionListener(rangeAction); + idStop.setActionCommand("endTime"); + startStopPane2.add(idStop); + + timesPane.add( startStopPane2, "startStop" ); + + startStopModePane.add( timesPane ); + favoritesButton= new JButton("v"); + favoritesButton.setToolTipText("recently entries times"); + favoritesButton.setPreferredSize(new Dimension( 20,20 ) ); + favoritesButton.setVisible(false); + startStopModePane.add(favoritesButton); + + viewButton= new JButton(getModeAction()); + viewButton.setToolTipText("input mode: start/end vs time range string"); + viewButton.setPreferredSize(new Dimension( 20,20 ) ); + startStopModePane.add(viewButton); + + this.add(startStopModePane); + + b= new JButton(); + b.setAction(nextAction); + b.setActionCommand("next"); + b.setToolTipText("Scan forward in time"); + this.add(b); + + revalidateUpdateMode(); + } + + public DasTimeRangeSelector(Datum startTime, Datum endTime) { + this(new DatumRange( startTime, endTime )); + } + + public DasTimeRangeSelector( DatumRange range ) { + this(); + this.range= range; + update(); + } + + private void parseRange() { + boolean updateRangeString0= updateRangeString; + if ( idStop.getText().equals("") ) { + DatumRange dr; + try { + String rangeString= idStart.getText(); + if ( rangeString.equals("") ) { + rangeString= (String)rangeComboBox.getEditor().getItem(); + } + dr= DatumRangeUtil.parseTimeRange(rangeString); + DatumRange oldRange= range; + range= dr; + updateRangeString= true; + firePropertyChange( PROP_RANGE, oldRange, range ); + + } catch ( ParseException e ) { + DasExceptionHandler.handle(e); + } + } else { + updateRangeString= false; + try { + Datum s1= TimeUtil.create(idStart.getText()); + Datum s2= TimeUtil.create(idStop.getText()); + DatumRange oldRange= range; + range= new DatumRange(s1,s2); + firePropertyChange( PROP_RANGE, oldRange ,range); + } catch ( ParseException e ) { + DasExceptionHandler.handle(e); + } + } + if ( updateRangeString!=updateRangeString0 ) { + if (pref) Preferences.userNodeForPackage(getClass()).putBoolean("updateRangeString", updateRangeString ); + } + return; + } + + private void refreshFavorites() { + favoritesMenu.removeAll(); + + for ( Iterator i= favoritesList.iterator(); i.hasNext(); ) { + final String fav= (String) i.next(); + Action favAction= new AbstractAction( fav ) { + public void actionPerformed( ActionEvent e ) { + DasTimeRangeSelector.this.setRange( DatumRangeUtil.parseTimeRangeValid(fav) ); + fireTimeRangeSelected(new TimeRangeSelectionEvent(this,range)); + } + }; + favoritesMenu.add( favAction ); + } + } + + private void buildFavorites( ) { + String favorites=""; + if ( pref ) favorites= Preferences.userNodeForPackage(getClass()).get( "timeRangeSelector.favorites."+favoritesGroup, "" ); + String[] ss= favorites.split("\\|\\|"); + favoritesList= new ArrayList(); + for ( int i=0; i=0; i-=2) { + if (listeners[i]==TimeRangeSelectionListener.class) { + String logmsg= "fire event: "+this.getClass().getName()+"-->"+listeners[i+1].getClass().getName()+" "+event; + DasLogger.getLogger( DasLogger.GUI_LOG ).fine(logmsg); + ((org.das2.event.TimeRangeSelectionListener)listeners[i+1]).timeRangeSelected(event); + ((TimeRangeSelectionListener)listeners[i+1]).timeRangeSelected(event); + } + } + } + + public Dimension getMaximumSize() { + return super.getPreferredSize(); + } + + public Dimension getMinimumSize() { + return super.getPreferredSize(); + } + + private void saveFavorites() { + if ( favoritesList.size()==0 ) return; + StringBuffer favorites= new StringBuffer( (String)favoritesList.get(0) ); + for ( int i=1; i 0 ? nrow : 1; + return dataPoints.size(); + } + + public Object getValueAt(int i, int j) { + DataPoint x = (DataPoint) dataPoints.get(i); + if (j < x.data.length) { + Datum d = x.get(j); + DatumFormatter format = d.getFormatter(); + return format.format(d, unitsArray[j]); + } else { + Object o = x.getPlane(planesArray[j]); + if (o instanceof Datum) { + Datum d = (Datum) o; + return d.getFormatter().format(d, unitsArray[j]); + } else { + return (String) o; + } + } + } + } + + public void deleteRow(int row) { + dataPoints.remove(row); + modified = true; + updateClients(); + updateStatus(); + fireDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); + } + + class MyDataSetDescriptor extends DataSetDescriptor { + + MyDataSetDescriptor() { + super(null); + } + + public void fireUpdate() { + fireDataSetUpdateEvent(new DataSetUpdateEvent((Object) this)); + } + + protected DataSet getDataSetImpl(Datum s1, Datum s2, Datum s3, ProgressMonitor monitor) throws DasException { + if (dataPoints.size() == 0) { + return null; + } else { + VectorDataSetBuilder builder = new VectorDataSetBuilder(unitsArray[0], unitsArray[1]); + for (int irow = 0; irow < dataPoints.size(); irow++) { + DataPoint dp = (DataPoint) dataPoints.get(irow); + builder.insertY(dp.get(0), dp.get(1)); + } + return builder.toVectorDataSet(); + } + } + + public Units getXUnits() { + return unitsArray[0]; + } + } + private MyDataSetDescriptor dataSetDescriptor; + + /** + * @deprecated use getDataSet() and getSelectedDataSet() instead + */ + public DataSetDescriptor getDataSetDescriptor() { + if (dataSetDescriptor == null) { + dataSetDescriptor = new MyDataSetDescriptor(); + } + return dataSetDescriptor; + } + + /** + * returns a data set of the table data. + */ + public VectorDataSet getDataSet() { + if (dataPoints.size() == 0) { + return null; + } else { + VectorDataSetBuilder builder = new VectorDataSetBuilder(unitsArray[0], unitsArray[1]); + for (int i = 2; i < planesArray.length; i++) { + if (unitsArray[i] != null) { + builder.addPlane(planesArray[i], unitsArray[i]); + } + } + for (int irow = 0; irow < dataPoints.size(); irow++) { + DataPoint dp = (DataPoint) dataPoints.get(irow); + builder.insertY(dp.get(0), dp.get(1)); + for (int i = 2; i < planesArray.length; i++) { + if (unitsArray[i] != null) { + Object s= dp.getPlane(planesArray[i]); + if ( s instanceof Datum ) { + builder.insertY(dp.get(0), (Datum)s, planesArray[i]); + } else { + builder.insertY(dp.get(0), ((EnumerationUnits)unitsArray[i]).createDatum(s),planesArray[i]); + } + } + } + } + if (this.xTagWidth != null) { + builder.setProperty("xTagWidth", xTagWidth); + } + return builder.toVectorDataSet(); + } + } + + /** + * returns a data set of the selected table data + */ + public VectorDataSet getSelectedDataSet() { + int[] selectedRows = table.getSelectedRows(); + if (selectedRows.length == 0) { + return null; + } else { + VectorDataSetBuilder builder = new VectorDataSetBuilder(unitsArray[0], unitsArray[1]); + for (int j = 2; j < planesArray.length; j++) { + builder.addPlane(planesArray[j], unitsArray[j]); + } + for (int i = 0; i < selectedRows.length; i++) { + int irow = selectedRows[i]; + DataPoint dp = (DataPoint) dataPoints.get(irow); + builder.insertY(dp.get(0), dp.get(1)); + Map map = dp.planes; + for (int j = 2; j < planesArray.length; j++) { + Object s= dp.getPlane(planesArray[j]); + if ( s instanceof Datum ) { + builder.insertY(dp.get(0).doubleValue(unitsArray[0]), + ((Datum) dp.getPlane(planesArray[j])).doubleValue(unitsArray[j]), + planesArray[j]); + } else { + builder.insertY(dp.get(0).doubleValue(unitsArray[0]), + ((EnumerationUnits)unitsArray[j]).createDatum(dp.getPlane(planesArray[j])).doubleValue(unitsArray[j]), + planesArray[j]); + } + } + } + if (this.xTagWidth != null) { + builder.setProperty("xTagWidth", xTagWidth); + } + return builder.toVectorDataSet(); + } + } + + /** + * Selects all the points within the DatumRange + * @param x the x value + * @param y the y value (may not be used, currently isn't). + */ + public void selectClosest( Datum x, Datum y ) { + int close= -1; + Datum closex= null; + Datum closedx= null; + for (int i = 0; i < dataPoints.size(); i++) { + DataPoint p = (DataPoint) dataPoints.get(i); + Datum thisx= p.get(0); + Datum dist= thisx.subtract(x).abs(); + if ( closex==null || dist.lt(closedx) ) { + close= i; + closex= thisx; + closedx= dist; + } + } + table.getSelectionModel().clearSelection(); + table.getSelectionModel().addSelectionInterval(close,close); + } + + /** + * Selects all the points within the DatumRange + */ + public void select(DatumRange xrange, DatumRange yrange) { + List selectMe = new ArrayList(); + for (int i = 0; i < dataPoints.size(); i++) { + DataPoint p = (DataPoint) dataPoints.get(i); + if (xrange.contains(p.data[0]) && yrange.contains(p.data[1])) { + selectMe.add(new Integer(i)); + } + } + table.getSelectionModel().clearSelection(); + for (int i = 0; i < selectMe.size(); i++) { + int iselect = ((Integer) selectMe.get(i)).intValue(); + table.getSelectionModel().addSelectionInterval(iselect, iselect); + } + } + + public void saveToFile(File file) throws IOException { + FileOutputStream out = new FileOutputStream(file); + BufferedWriter r = new BufferedWriter(new OutputStreamWriter(out)); + + StringBuffer header = new StringBuffer(); + header.append("## "); + for (int j = 0; j < planesArray.length; j++) { + header.append(myTableModel.getColumnName(j) + "\t"); + } + r.write(header.toString()); + r.newLine(); + for (int i = 0; i < dataPoints.size(); i++) { + DataPoint x = (DataPoint) dataPoints.get(i); + StringBuffer s = new StringBuffer(); + for (int j = 0; j < 2; j++) { + DatumFormatter formatter = x.get(j).getFormatter(); + s.append(formatter.format(x.get(j), unitsArray[j]) + "\t"); + } + for (int j = 2; j < planesArray.length; j++) { + Object o = x.getPlane(planesArray[j]); + if (unitsArray[j] == null) { + if (o == null) { + o = ""; + } + s.append("\"" + o + "\"\t"); + } else { + if ( o instanceof Datum ) { + Datum d = (Datum) o; + DatumFormatter f = d.getFormatter(); + s.append(f.format(d, unitsArray[j]) + "\t"); + } else { + s.append( o.toString() + "\t" ); + } + } + } + r.write(s.toString()); + r.newLine(); + prefs.put("components.DataPointRecorder.lastFileSave", file.toString()); + prefs.put("components.DataPointRecorder.lastFileLoad", file.toString()); + } + r.close(); + modified = false; + updateStatus(); + + } + + public void loadFromFile(File file) throws IOException { + + ProgressMonitor mon= new NullProgressMonitor(); + + try { + active = false; + + FileInputStream in = new FileInputStream(file); + BufferedReader r = new BufferedReader(new InputStreamReader(in)); + + int lineCount = 0; + for (String line = r.readLine(); line != null; line = r.readLine()) { + lineCount++; + } + r.close(); + + in = new FileInputStream(file); + r = new BufferedReader(new InputStreamReader(in)); + + dataPoints.clear(); + String[] planesArray = null; + Units[] unitsArray = null; + + Datum x; + Datum y; + Map planes;// = new LinkedHashMap(); + + if (lineCount > 500) { + mon = DasProgressPanel.createFramed("reading file"); + } else { + mon = new NullProgressMonitor(); + } + + // tabs detected in file. + boolean hasTabs= false; + String delim= "\t"; + + mon.setTaskSize(lineCount); + mon.started(); + int linenum = 0; + for (String line = r.readLine(); line != null; line = r.readLine()) { + linenum++; + if (mon.isCancelled()) { + break; + } + mon.setTaskProgress(linenum); + if (line.startsWith("## ")) { + if ( unitsArray!=null ) continue; + line = line.substring(3); + if ( line.indexOf("\t")==-1 ) delim= "\\s+"; + String[] s = line.trim().split(delim); + Pattern ordinalPattern= Pattern.compile("(.+)\\((.*)\\(ordinal\\)\\)"); + Pattern p = Pattern.compile("(.+)\\((.*)\\)"); + planesArray = new String[s.length]; + unitsArray = new Units[s.length]; + for (int i = 0; i < s.length; i++) { + Matcher m= ordinalPattern.matcher(s[i]); + if ( m.matches() ) { + planesArray[i] = m.group(1).trim(); + String sunits= m.group(2).trim(); + try { + unitsArray[i] = Units.getByName(sunits); // does it exist already + } catch ( IllegalArgumentException ex ) { + unitsArray[i] = new EnumerationUnits(sunits); + } + continue; + } + m = p.matcher(s[i]); + if (m.matches()) { + //System.err.printf("%d %s\n", i, m.group(1) ); + planesArray[i] = m.group(1).trim(); + String sunits= m.group(2).trim(); + unitsArray[i] = Units.lookupUnits(sunits); + } else { + planesArray[i] = s[i]; + unitsArray[i] = null; + } + } + continue; + } + String[] s = line.trim().split(delim); + if (unitsArray == null) { + // support for legacy files + unitsArray = new Units[s.length]; + for (int i = 0; i < s.length; i++) { + String field= s[i].trim(); + if (field.charAt(0) == '"') { + unitsArray[i] = null; + } else if (TimeUtil.isValidTime(field)) { + unitsArray[i] = Units.us2000; + } else { + unitsArray[i] = DatumUtil.parseValid(field).getUnits(); + } + } + planesArray = new String[]{"X", "Y", "comment"}; + } + + try { + + x = unitsArray[0].parse(s[0]); + y = unitsArray[1].parse(s[1]); + + planes = new LinkedHashMap(); + + for (int i = 2; i < s.length; i++) { + String field= s[i].trim(); + if (unitsArray[i] == null) { + Pattern p = Pattern.compile("\"(.*)\".*"); + Matcher m = p.matcher(field); + if (m.matches()) { + planes.put(planesArray[i], m.group(1)); + } else { + throw new ParseException("parse error, expected \"\"", 0); + } + } else { + try { + planes.put(planesArray[i], unitsArray[i].parse(field)); + } catch (ParseException e) { + if ( unitsArray[i] instanceof EnumerationUnits ) { + EnumerationUnits u= (EnumerationUnits)unitsArray[i]; + u.createDatum(field); + planes.put(planesArray[i], unitsArray[i].parse(field)); + } else { + throw new RuntimeException("Parse exception at line "+linenum+" of "+file, e ); + } + } + } + } + + DataPointSelectionEvent e; + e = new DataPointSelectionEvent(this, x, y, planes); + dataPointSelected(e); + } catch (ParseException e) { + throw new RuntimeException("Parse exception at line "+linenum+" of "+file, e); + } catch ( NumberFormatException e ) { + throw new RuntimeException("Number format exception at line "+linenum+" of "+file, e ); + } + + + } + } finally { + + mon.finished(); + active = true; + modified = false; + + updateStatus(); + + updateClients(); + + prefs.put("components.DataPointRecorder.lastFileLoad", file.toString()); + fireDataSetUpdateListenerDataSetUpdated( + new DataSetUpdateEvent(this)); + + //table.getColumnModel().getColumn(0).setPreferredWidth(200); + + table.getColumnModel(); + myTableModel.fireTableStructureChanged(); + table.repaint(); + } + + } + + private class MyMouseAdapter extends MouseAdapter { + + JPopupMenu popup; + JMenuItem menuItem; + final JTable parent; + + MyMouseAdapter(final JTable parent) { + this.parent = parent; + popup = new JPopupMenu("Options"); + menuItem = new JMenuItem("Delete Row(s)"); + menuItem.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + int[] selectedRows = parent.getSelectedRows(); + for (int i = 0; i < selectedRows.length; i++) { + deleteRow(selectedRows[i]); + for (int j = i + 1; j < selectedRows.length; j++) { + selectedRows[j]--; // indeces change because of deletion + } + } + } + }); + popup.add(menuItem); + } + MouseAdapter mm; + + public void mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + int rowCount = parent.getSelectedRows().length; + menuItem.setText("Delete " + rowCount + " Row" + (rowCount != 1 ? "s" : "")); + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + + public void mouseReleased(MouseEvent e) { + // hide popup + } + } + + private Action getSaveAsAction() { + return new AbstractAction("Save As...") { + + public void actionPerformed(ActionEvent e) { + JFileChooser jj = new JFileChooser(); + String lastFileString = prefs.get("components.DataPointRecorder.lastFileSave", ""); + File lastFile = null; + if (lastFileString != "") { + lastFile = new File(lastFileString); + jj.setSelectedFile(lastFile); + } + + int status = jj.showSaveDialog(DataPointRecorder.this); + if (status == JFileChooser.APPROVE_OPTION) { + try { + DataPointRecorder.this.saveFile = jj.getSelectedFile(); + saveToFile(saveFile); + //messageLabel.setText("saved data to "+saveFile); + } catch (IOException e1) { + DasExceptionHandler.handle(e1); + } + + } + } + }; + } + + private Action getSaveAction() { + return new AbstractAction("Save") { + + public void actionPerformed(ActionEvent e) { + if (saveFile == null) { + getSaveAsAction().actionPerformed(e); + } else { + try { + saveToFile(saveFile); + } catch (IOException ex) { + DasExceptionHandler.handle(ex); + } + + } + } + }; + } + + private Action getLoadAction() { + return new AbstractAction("Open...") { + + public void actionPerformed(ActionEvent e) { + if (checkModified(e)) { + JFileChooser jj = new JFileChooser(); + String lastFileString = prefs.get("components.DataPointRecorder.lastFileLoad", ""); + File lastFile = null; + if (lastFileString != "") { + lastFile = new File(lastFileString); + jj.setSelectedFile(lastFile); + } + + int status = jj.showOpenDialog(DataPointRecorder.this); + if (status == JFileChooser.APPROVE_OPTION) { + final File loadFile = jj.getSelectedFile(); + prefs.put("components.DataPointRecorder.lastFileLoad", loadFile.toString()); + Runnable run = new Runnable() { + + public void run() { + try { + loadFromFile(loadFile); + saveFile = + loadFile; + updateStatus(); + } catch (IOException e) { + + DasExceptionHandler.handle(e); + } + + } + }; + new Thread(run).start(); + } + + } + } + }; + } + + /** + * returns true if the operation should continue, false + * if not, meaning the user pressed cancel. + */ + private boolean checkModified(ActionEvent e) { + if (modified) { + int n = JOptionPane.showConfirmDialog( + DataPointRecorder.this, + "Current work has not been saved.\n Save first?", + "Save work first", + JOptionPane.YES_NO_CANCEL_OPTION); + if (n == JOptionPane.YES_OPTION) { + getSaveAction().actionPerformed(e); + } + + return (n != JOptionPane.CANCEL_OPTION); + } else { + return true; + } + + } + + private Action getNewAction() { + return new AbstractAction("New") { + + public void actionPerformed(ActionEvent e) { + if (checkModified(e)) { + dataPoints.removeAll(dataPoints); + saveFile = + null; + updateStatus(); + updateClients(); + } + + } + }; + } + + private Action getPropertiesAction() { + return new AbstractAction("Properties") { + + public void actionPerformed(ActionEvent e) { + new PropertyEditor(DataPointRecorder.this).showDialog(DataPointRecorder.this); + } + }; + } + + private Action getUpdateAction() { + return new AbstractAction("Update") { + + public void actionPerformed(ActionEvent e) { + // what if no one is listening? + if (dataSetDescriptor != null) { + dataSetDescriptor.fireUpdate(); + } + + fireDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); + fireSelectedDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); + } + }; + } + + /** Creates a new instance of DataPointRecorder */ + public DataPointRecorder() { + super(); + dataPoints = + new ArrayList(); + myTableModel = + new MyTableModel(); + this.setLayout(new BorderLayout()); + + JMenuBar menuBar = new JMenuBar(); + JMenu fileMenu = new JMenu("File"); + fileMenu.add(new JMenuItem(getNewAction())); + fileMenu.add(new JMenuItem(getLoadAction())); + fileMenu.add(new JMenuItem(getSaveAction())); + fileMenu.add(new JMenuItem(getSaveAsAction())); + menuBar.add(fileMenu); + + JMenu editMenu = new JMenu("Edit"); + editMenu.add(new JMenuItem(getPropertiesAction())); + menuBar.add(editMenu); + + this.add(menuBar, BorderLayout.NORTH); + + planesArray = + new String[]{"X", "Y"}; + unitsArray = + new Units[]{null, null}; + + table = + new JTable(myTableModel); + + table.getTableHeader().setReorderingAllowed(true); + + table.setRowSelectionAllowed(true); + table.addMouseListener(new DataPointRecorder.MyMouseAdapter(table)); + table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + + public void valueChanged(ListSelectionEvent e) { + fireSelectedDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(DataPointRecorder.this)); + int selected = table.getSelectedRow(); // we could do a better job here + if (selected > -1) { + DataPoint dp = (DataPoint) dataPoints.get(selected); + DataPointSelectionEvent e2 = new DataPointSelectionEvent(DataPointRecorder.this, dp.get(0), dp.get(1)); + fireDataPointSelectionListenerDataPointSelected(e2); + } + + } + }); + + scrollPane = + new JScrollPane(table); + this.add(scrollPane, BorderLayout.CENTER); + + JPanel controlStatusPanel = new JPanel(); + controlStatusPanel.setLayout(new BoxLayout(controlStatusPanel, BoxLayout.Y_AXIS)); + + final JPanel controlPanel = new JPanel(); + controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.X_AXIS)); + + JButton updateButton = new JButton(getUpdateAction()); + + controlPanel.add(updateButton); + + messageLabel = + new JLabel("ready"); + messageLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT); + + controlStatusPanel.add(messageLabel); + + controlPanel.setAlignmentX(JLabel.LEFT_ALIGNMENT); + controlStatusPanel.add(controlPanel); + + this.add(controlStatusPanel, BorderLayout.SOUTH); + } + + public static DataPointRecorder createFramed() { + DataPointRecorder result; + JFrame frame = new JFrame("Data Point Recorder"); + result = + new DataPointRecorder(); + frame.getContentPane().add(result); + frame.pack(); + frame.setVisible(true); + frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + return result; + } + + /** + * update fires off the TableDataChanged, and sets the current selected + * row if necessary. + */ + private void updateClients() { + if (active) { + myTableModel.fireTableDataChanged(); + if (selectRow != -1 && table.getRowCount()>selectRow ) { + table.setRowSelectionInterval(selectRow, selectRow); + table.scrollRectToVisible(table.getCellRect(selectRow, 0, true)); + selectRow = + -1; + } + + } + } + + private void updateStatus() { + Runnable run= () -> { + String statusString = (saveFile == null ? "" : (String.valueOf(saveFile) + " ")) + + (modified ? "(modified)" : ""); + messageLabel.setText(statusString); + }; + SwingUtilities.invokeLater(run); + } + + private void insertInternal(DataPoint newPoint) { + int newSelect; + if (sorted) { + int index = Collections.binarySearch(dataPoints, newPoint); + if (index < 0) { + dataPoints.add(~index, newPoint); + newSelect = + ~index; + } else { + dataPoints.set(index, newPoint); + newSelect = + index; + } + + } else { + dataPoints.add(newPoint); + newSelect = + dataPoints.size() - 1; + } + + selectRow = newSelect; + modified = + true; + updateStatus(); + } + + public void addDataPoint(Datum x, Datum y, Map planes) { + if ( planes==null ) planes= new HashMap(); + if (dataPoints.size() == 0) { + Datum[] datums = new Datum[]{x, y}; + unitsArray = + new Units[2 + planes.size()]; + unitsArray[0] = x.getUnits(); + unitsArray[1] = y.getUnits(); + planesArray = + new String[2 + planes.size()]; + planesArray[0] = "x"; + planesArray[1] = "y"; + int index = 2; + for (Iterator i = planes.keySet().iterator(); i.hasNext();) { + Object key = i.next(); + planesArray[index] = String.valueOf(key); + Object value = planes.get(key); + if (value instanceof String) { + unitsArray[index] = EnumerationUnits.create("dpr_"+planesArray[index]); + } else { + unitsArray[index] = ((Datum) value).getUnits(); + } + + index++; + } + + //myTableModel.fireTableStructureChanged(); + } + + if (!x.getUnits().isConvertableTo(unitsArray[0])) { + throw new RuntimeException("inconvertable units: " + x + " expected " + unitsArray[0]); + } + + if (!y.getUnits().isConvertableTo(unitsArray[1])) { + throw new RuntimeException("inconvertable units: " + y + " expected " + unitsArray[1]); + } + + insertInternal(new DataPoint(x, y, new LinkedHashMap(planes))); + if (active) { + Runnable run= new Runnable() { + public void run() { + myTableModel.fireTableStructureChanged(); + fireDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); + } + }; + SwingUtilities.invokeLater(run); + + } + + } + + public void appendDataSet(VectorDataSet ds) { + + String comment; + + Map planesMap = new LinkedHashMap(); + + if (ds.getProperty("comment") != null) { + planesMap.put("comment", ds.getProperty("comment")); + } + + if (ds.getProperty("xTagWidth") != null) { + DataPointRecorder.this.xTagWidth = (Datum) ds.getProperty("xTagWidth"); + } + + String[] planes = ds.getPlaneIds(); + + for (int i = 0; i < + ds.getXLength(); i++) { + for (int j = 0; j < + planes.length; j++) { + if (!planes[j].equals("")) { + planesMap.put(planes[j], ((VectorDataSet) ds.getPlanarView(planes[j])).getDatum(i)); + } + + } + addDataPoint(ds.getXTagDatum(i), ds.getDatum(i), planesMap); + } + + updateClients(); + + } + + /** + * this adds all the points in the DataSet to the list. This will also check the dataset for the special + * property "comment" and add it as a comment. + */ + public DataSetUpdateListener getAppendDataSetUpListener() { + return new DataSetUpdateListener() { + + public void dataSetUpdated(DataSetUpdateEvent e) { + VectorDataSet ds = (VectorDataSet) e.getDataSet(); + if (ds == null) { + throw new RuntimeException("not supported, I need the DataSet in the update event"); + } else { + appendDataSet((VectorDataSet) e.getDataSet()); + } + + } + }; + } + + @SuppressWarnings("deprecation") + public void dataPointSelected(org.das2.event.DataPointSelectionEvent e) { + String comment = ""; + Map planesMap; + + if (e instanceof CommentDataPointSelectionEvent) { + comment = ((CommentDataPointSelectionEvent) e).getComment(); + planesMap = + new LinkedHashMap(); + planesMap.put("comment", comment); + } else { + String[] x = e.getPlaneIds(); + planesMap = + new LinkedHashMap(); + for (int i = 0; i < + x.length; i++) { + planesMap.put(x[i], e.getPlane(x[i])); + } + + } + + // if a point exists within xTagWidth of the point, then have this point replace + Datum x = e.getX(); + if (snapToGrid && xTagWidth != null && dataPoints.size() > 0) { + DataSet ds = getDataSet(); + int i = DataSetUtil.closestColumn(ds, e.getX()); + Datum diff = e.getX().subtract(ds.getXTagDatum(i)); + if (Math.abs(diff.divide(xTagWidth).doubleValue(Units.dimensionless)) < 0.5) { + x = ds.getXTagDatum(i); + } + + } + addDataPoint(x, e.getY(), planesMap); + updateClients(); + } + private javax.swing.event.EventListenerList listenerList = null; + + public synchronized void addDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + if (listenerList == null) { + listenerList = new javax.swing.event.EventListenerList(); + } + + + + + listenerList.add(org.das2.dataset.DataSetUpdateListener.class, listener); + } + + public synchronized void removeDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + listenerList.remove(org.das2.dataset.DataSetUpdateListener.class, listener); + } + + private void fireDataSetUpdateListenerDataSetUpdated(org.das2.dataset.DataSetUpdateEvent event) { + if (listenerList == null) { + return; + } + + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= + 0; i -= + 2) { + if (listeners[i] == org.das2.dataset.DataSetUpdateListener.class) { + ((org.das2.dataset.DataSetUpdateListener) listeners[i + 1]).dataSetUpdated(event); + } + } + + } + private javax.swing.event.EventListenerList selectedListenerList = null; + + public synchronized void addSelectedDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + if (selectedListenerList == null) { + selectedListenerList = new javax.swing.event.EventListenerList(); + } + + + + + selectedListenerList.add(org.das2.dataset.DataSetUpdateListener.class, listener); + } + + public synchronized void removeSelectedDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + selectedListenerList.remove(org.das2.dataset.DataSetUpdateListener.class, listener); + } + + private void fireSelectedDataSetUpdateListenerDataSetUpdated(org.das2.dataset.DataSetUpdateEvent event) { + if (selectedListenerList == null) { + return; + } + + Object[] listeners = selectedListenerList.getListenerList(); + for (int i = listeners.length - 2; i >= + 0; i -= + 2) { + if (listeners[i] == org.das2.dataset.DataSetUpdateListener.class) { + ((org.das2.dataset.DataSetUpdateListener) listeners[i + 1]).dataSetUpdated(event); + } + } + + } + /** + * Holds value of property sorted. + */ + private boolean sorted = true; + + /** + * Getter for property sorted. + * @return Value of property sorted. + */ + public boolean isSorted() { + + return this.sorted; + } + + /** + * Setter for property sorted. + * @param sorted New value of property sorted. + */ + public void setSorted(boolean sorted) { + + this.sorted = sorted; + } + + /** + * Registers DataPointSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + + if (listenerList == null) { + listenerList = new javax.swing.event.EventListenerList(); + } + + + + + listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** + * Removes DataPointSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + + listenerList.remove(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** + * Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataPointSelectionListenerDataPointSelected(org.das2.event.DataPointSelectionEvent event) { + if (listenerList == null) { + return; + } + + logger.fine("firing data point selection event"); + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= + 0; i -= + 2) { + if (listeners[i] == org.das2.event.DataPointSelectionListener.class) { + ((org.das2.event.DataPointSelectionListener) listeners[i + 1]).dataPointSelected(event); + } + } + + } + /** + * Holds value of property xTagWidth. + */ + private Datum xTagWidth = null; + + /** + * Getter for property xTagWidth. + * @return Value of property xTagWidth. + */ + public Datum getXTagWidth() { + return this.xTagWidth; + } + + /** + * Setter for property xTagWidth. + * @param xTagWidth New value of property xTagWidth. + */ + public void setXTagWidth(Datum xTagWidth) { + this.xTagWidth = xTagWidth; + } + /** + * Holds value of property snapToGrid. + */ + private boolean snapToGrid = false; + + /** + * Getter for property snapToGrid. + * @return Value of property snapToGrid. + */ + public boolean isSnapToGrid() { + + return this.snapToGrid; + } + + /** + * Setter for property snapToGrid. true indicates the xtag will be reset + * so that the tags are equally spaced, each xTagWidth apart. + * @param snapToGrid New value of property snapToGrid. + */ + public void setSnapToGrid(boolean snapToGrid) { + + this.snapToGrid = snapToGrid; + } +} diff --git a/dasCore/src/main/java/org/das2/components/DataPointReporter.java b/dasCore/src/main/java/org/das2/components/DataPointReporter.java new file mode 100644 index 000000000..0fe13292f --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/DataPointReporter.java @@ -0,0 +1,81 @@ +/* File: DataPointReporter.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.event.DataPointSelectionEvent; + + +/** + * + * @author Owner + */ +public class DataPointReporter extends javax.swing.JPanel implements org.das2.event.DataPointSelectionListener { + + private javax.swing.JTextField output; + + /** Creates a new instance of DataPointReporter */ + public DataPointReporter() { + super(); + this.setLayout(new java.awt.FlowLayout()); + output= new javax.swing.JTextField(20); + this.add(output); + + } + + public void dataPointSelected(org.das2.event.DataPointSelectionEvent e) { + output.setText("("+e.getX()+","+e.getY()+")"); + fireDataPointSelectionListenerDataPointSelected(e); + } + + /** Registers DataPointSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } + listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** Removes DataPointSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + listenerList.remove(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataPointSelectionListenerDataPointSelected(DataPointSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataPointSelectionListener.class) { + ((org.das2.event.DataPointSelectionListener)listeners[i+1]).dataPointSelected(event); + } + } + } +} diff --git a/dasCore/src/main/java/org/das2/components/DataSetBrowser.java b/dasCore/src/main/java/org/das2/components/DataSetBrowser.java new file mode 100644 index 000000000..c76615227 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/DataSetBrowser.java @@ -0,0 +1,108 @@ +/* File: DataSetBrowser.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.DasException; +import org.das2.DasIOException; +import org.das2.client.DasServer; + +import javax.swing.*; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.*; + +/** + * JTree veiw of a Das2Server's available data sets, with drag-n-drop of data set id's. + */ +public class DataSetBrowser extends JPanel implements DragSourceListener, DragGestureListener { + + DasServer dasServer; + JTree tree; + TreeModel dataSetListTreeModel; + + /** Creates a new instance of DataSetBrowser */ + public DataSetBrowser(DasServer dasServer) { + super(); + this.dasServer= dasServer; + try { + dataSetListTreeModel= dasServer.getDataSetList(); + } catch ( DasIOException e ) { + System.out.println(e); + } catch ( DasException e ) { + System.out.println(e); + } + + this.setLayout(new java.awt.BorderLayout()); + + tree= new JTree(dataSetListTreeModel); + + DragSource ds= DragSource.getDefaultDragSource(); + DragGestureRecognizer dgr= + ds.createDefaultDragGestureRecognizer(tree,DnDConstants.ACTION_COPY_OR_MOVE, + this ); + + this.add(new JScrollPane(tree),java.awt.BorderLayout.CENTER); + + } + + public String getSelectedDataSetId() { + TreePath tp= tree.getLeadSelectionPath(); + TreeNode tn = (TreeNode)tp.getLastPathComponent(); + if (tn.isLeaf()) { + String s = dasServer.getURL()+"?"+tp.getPathComponent(1); + for (int index = 2; index < tp.getPathCount(); index++) + s = s + "/" + tp.getPathComponent(index); + return s; + } else { + return null; + } + } + + public void dragDropEnd(java.awt.dnd.DragSourceDropEvent dragSourceDropEvent) { + } + + public void dragEnter(java.awt.dnd.DragSourceDragEvent dragSourceDragEvent) { + } + + public void dragExit(java.awt.dnd.DragSourceEvent dragSourceEvent) { + } + + public void dragGestureRecognized(java.awt.dnd.DragGestureEvent dragGestureEvent) { + String s= getSelectedDataSetId(); + if ( s!=null ) { + Transferable t= new StringSelection(s); + dragGestureEvent.startDrag(null,t,this); + } + } + + public void dragOver(java.awt.dnd.DragSourceDragEvent dragSourceDragEvent) { + } + + public void dropActionChanged(java.awt.dnd.DragSourceDragEvent dragSourceDragEvent) { + } + +} diff --git a/dasCore/src/main/java/org/das2/components/DatumEditor.java b/dasCore/src/main/java/org/das2/components/DatumEditor.java new file mode 100644 index 000000000..a1fa576e1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/DatumEditor.java @@ -0,0 +1,440 @@ +/* File: DatumEditor.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 1, 2003, 9:20 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.components; + +import org.das2.datum.Units; +import org.das2.datum.format.TimeDatumFormatter; +import org.das2.datum.Datum; +import org.das2.datum.TimeLocationUnits; + +import java.awt.*; +import java.awt.event.*; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.PropertyEditor; +import java.text.ParseException; +import java.util.EventObject; +import javax.swing.*; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.EventListenerList; +import javax.swing.table.TableCellEditor; + +/**maybeInitGui + * + * @author Edward West + */ +public class DatumEditor implements PropertyEditor, TableCellEditor { + + private JTextField editor; + private JPanel panel; + private JButton unitsButton; + private Units units = Units.dimensionless; + private ActionListener actionListener; + private Datum value; + private EventListenerList listeners; + private PropertyChangeSupport pcs = new PropertyChangeSupport(this); + + public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcs.removePropertyChangeListener(propertyName, listener); + } + + public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcs.addPropertyChangeListener(propertyName, listener); + } + + public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + /** Creates a new instance of DatumEditor */ + public DatumEditor() { + } + + private synchronized void initGui() { + initComponents(); + installListeners(); + initToolTips(); + panel.setFocusable(true); + } + + private synchronized void maybeInitGui() { + if (panel == null) { + initGui(); + } + } + + private void initComponents() { + panel = new JPanel(); + panel.setLayout(new BorderLayout()); + + editor = new JTextField(8); + if (value != null) setDatum(value); + editor.setFocusable(false); + panel.add(editor, BorderLayout.CENTER); + + unitsButton = new JButton(); + unitsButton.setFocusable(false); + unitsButton.setToolTipText("units selection"); + if ( units!=null ) { + setUnits(units); // set labels and such + } + panel.add(unitsButton, BorderLayout.EAST); + } + + private void installListeners() { + UniversalListener ul = new UniversalListener(); + editor.addMouseListener(ul); + unitsButton.addMouseListener(ul); + panel.addKeyListener(ul); + panel.addFocusListener(ul); + } + + private void initToolTips() { + ToolTipManager.sharedInstance().registerComponent(panel); + } + + public void setColumns(int columns) { + editor.setColumns(columns); + } + + /** + * @param value the Datum object to be editted. + * @throws IllegalArgumentException if value is not a Datum + */ + public void setValue(Object value) { + if (value instanceof Datum) { + setDatum((Datum) value); + } else { + throw new IllegalArgumentException(); + } + } + + /** + * @param datum the Datum object to be editted. + */ + private void setDatum(Datum datum) { + Datum oldValue = value; + value = datum; + Units u = datum.getUnits(); + if (editor != null) { + if (datum.getUnits() instanceof TimeLocationUnits) { + editor.setText(TimeDatumFormatter.DEFAULT.format(datum, u)); + } else { + editor.setText(datum.getFormatter().format(datum, u)); + } + } + setUnits(u); + if (oldValue != value && oldValue != null && !oldValue.equals(value)) { + pcs.firePropertyChange("value", oldValue, value); + } + } + + public void setAsText(String text) throws IllegalArgumentException { + try { + setDatum(units.parse(text)); + } catch (ParseException pe) { + IllegalArgumentException iae = new IllegalArgumentException(pe.getMessage()); + iae.initCause(pe); + throw iae; + } + } + + public Object getValue() { + return getDatum(); + } + + public Datum getDatum() { + try { + Datum d; + if (editor != null) { + String text = editor.getText(); + d = units.parse(text); + if (!d.equals(value)) { + Datum oldValue = value; + value = d; + pcs.firePropertyChange("value", oldValue, value); + } + } + return value; + } catch (ParseException e) { + if (value != null) { + setDatum(value); // cause reformat of old Datum + return value; + } else { + return null; + } + } + } + + public String getAsText() { + Datum v = getDatum(); + if (v == null) { + return null; + } else { + return v.toString(); + } + } + + public void setUnits(Units units) { + if (unitsButton != null) { + if (units instanceof TimeLocationUnits) { + unitsButton.setVisible(false); + } else { + unitsButton.setVisible(true); + unitsButton.setText(units.toString()); + unitsButton.setToolTipText("units selection"); + } + } + this.units = units; + } + + public Units getUnits() { + return units; + } + + private void fireActionPerformed() { + setDatum(getDatum()); + if (actionListener != null) { + actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "DatumEditor")); + } + } + + public void addActionListener(ActionListener al) { + actionListener = AWTEventMulticaster.add(actionListener, al); + } + + public void removeActionListener(ActionListener al) { + actionListener = AWTEventMulticaster.remove(actionListener, al); + } + + /* Why all one class? To reduce the number of object created. + * The purposes of the listeners are not necessarily related and it is + * generally not a good idea to implement multiple interfaces unless there + * is some sort of correlation between them. It is okay to do that here + * since this class is not a part of the public interface. + */ + private class UniversalListener implements MouseListener, KeyListener, FocusListener { + + /** Listens for focus events on the DatumEditor and sets the editor + * caret visible when focus is gained. + */ + public void focusGained(FocusEvent e) { + editor.getCaret().setVisible(true); + editor.getCaret().setSelectionVisible(true); + } + + /** Listens for focus events on the DatumEditor and sets the editor + * caret invisible when focus is lost. + */ + public void focusLost(FocusEvent e) { + editor.getCaret().setVisible(false); + editor.getCaret().setSelectionVisible(false); + } + + /** All key events are forwarded to the editor. Except for keyPresses + * for VK_ENTER + */ + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + fireActionPerformed(); + } else { + forwardKeyEvent(e); + } + } + + public void keyReleased(KeyEvent e) { + forwardKeyEvent(e); + } + + public void keyTyped(KeyEvent e) { + forwardKeyEvent(e); + } + + private void forwardKeyEvent(KeyEvent e) { + e.setSource(editor); + editor.dispatchEvent(e); + } + + /** Request focus when sub-components are clicked */ + public void mousePressed(MouseEvent e) { + panel.requestFocusInWindow(); + } + + /** Unused */ + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + public void mouseClicked(MouseEvent e) { + } + + public void mouseReleased(MouseEvent e) { + } + } + + public String getToolTipText(MouseEvent event) { + if (unitsButton.getBounds().contains(event.getX(), event.getY())) { + return unitsButton.getToolTipText(); + } else { + return null; + } + } + + /** @see java.beans.PropertyEditor#supportsCustomEditor() + * @return true + */ + public boolean supportsCustomEditor() { + return true; + } + + /** @see java.beans.PropertyEditor#getCustomEditor() + * @return this + */ + public Component getCustomEditor() { + maybeInitGui(); + return panel; + } + + /** + * This PropertyEditor implementation does not support generating java + * code. + * @return The string "???" + */ + public String getJavaInitializationString() { + return "???"; + } + + /** + * This PropertyEditor implementation does not support enumerated + * values. + * @return null + */ + public String[] getTags() { + return null; + } + + /** @see java.beans.PropertyEditor#isPaintable() + * @return false + */ + public boolean isPaintable() { + return false; + } + + /** Does nothing. + * @param g + * @param r + */ + public void paintValue(Graphics g, Rectangle r) { + } + + /* CellEditor stuff */ + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + setValue(value); + maybeInitGui(); + return panel; + } + + /** + */ + public void addCellEditorListener(CellEditorListener l) { + if (listeners == null) { + listeners = new EventListenerList(); + } + listeners.add(CellEditorListener.class, l); + } + + /** + */ + public void removeCellEditorListener(CellEditorListener l) { + if (listeners != null) { + listeners.remove(CellEditorListener.class, l); + } + } + + /** @see javax.swing.CellEditor#isCellEditable(java.util.EventObject) + * @return true + */ + public boolean isCellEditable(EventObject anEvent) { + return true; + } + + /** @see javax.swing.CellEditor#shouldSelectCell(java.util.EventObject) + * @return true + */ + public boolean shouldSelectCell(EventObject anEvent) { + return true; + } + + /** Returns the value stored in this editor. + * @return the current value being edited + */ + public Object getCellEditorValue() { + return getDatum(); + } + + public boolean stopCellEditing() { + if (getDatum() == null) { + return false; + } + fireEditingStopped(); + return true; + } + + public void cancelCellEditing() { + fireEditingCanceled(); + } + private ChangeEvent evt; + + private void fireEditingStopped() { + Object[] l = listeners.getListenerList(); + for (int i = 0; i < l.length; i += 2) { + if (l[i] == CellEditorListener.class) { + CellEditorListener cel = (CellEditorListener) l[i + 1]; + if (evt == null) { + evt = new ChangeEvent(this); + } + cel.editingStopped(evt); + } + } + } + + private void fireEditingCanceled() { + Object[] l = listeners.getListenerList(); + for (int i = 0; i < l.length; i += 2) { + if (l[i] == CellEditorListener.class) { + CellEditorListener cel = (CellEditorListener) l[i + 1]; + if (evt == null) { + evt = new ChangeEvent(this); + } + cel.editingCanceled(evt); + } + } + } +} diff --git a/dasCore/src/main/java/org/das2/components/DatumRangeEditor.java b/dasCore/src/main/java/org/das2/components/DatumRangeEditor.java new file mode 100644 index 000000000..0c2d8c0e0 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/DatumRangeEditor.java @@ -0,0 +1,388 @@ +/* File: DatumEditor.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 1, 2003, 9:20 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.DatumRangeUtil; +import org.das2.datum.TimeLocationUnits; + +import java.awt.*; +import java.awt.event.*; +import java.beans.PropertyEditor; +import java.text.ParseException; +import java.util.EventObject; +import javax.swing.*; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.EventListenerList; +import javax.swing.table.TableCellEditor; + + +/** + * + * @author Edward West + */ +public class DatumRangeEditor extends JComponent implements PropertyEditor, TableCellEditor { + + private JTextField editor; + private JButton unitsButton; + private Units units = Units.dimensionless; + private ActionListener actionListener; + private DatumRange value; + private EventListenerList listeners; + + /** Creates a new instance of DatumEditor */ + public DatumRangeEditor() { + initComponents(); + installListeners(); + initToolTips(); + setFocusable(true); + } + + private void initComponents() { + setLayout(new BorderLayout()); + + editor = new JTextField(8); + editor.setFocusable(false); + add(editor, BorderLayout.CENTER); + + unitsButton = new JButton(); + unitsButton.setFocusable(false); + unitsButton.setToolTipText("units selection"); + add(unitsButton, BorderLayout.EAST); + } + + private void installListeners() { + UniversalListener ul = new UniversalListener(); + editor.addMouseListener(ul); + unitsButton.addMouseListener(ul); + addKeyListener(ul); + addFocusListener(ul); + } + + private void initToolTips() { + ToolTipManager.sharedInstance().registerComponent(this); + } + + public void setColumns(int columns) { + editor.setColumns(columns); + } + + /** + * @param value the DatumRange to be editted. + * @throws IllegalArgumentException if value is not a Datum + */ + public void setValue(Object value) { + if (value instanceof DatumRange) { + setDatumRange((DatumRange)value); + } else { + throw new IllegalArgumentException(); + } + } + + /** + * @param datumRange the DatumRange to be edited. + */ + private void setDatumRange(DatumRange datumRange ) { + DatumRange oldValue = value; + value = datumRange; + Units u= datumRange.getUnits(); + editor.setText( datumRange.toString() ); + setUnits(u); + if (oldValue != value && oldValue != null && !oldValue.equals(value)) { + firePropertyChange("value", oldValue, value); + } + } + + public void setAsText(String text) throws IllegalArgumentException { + try { + setDatumRange( parseText( text ) ); + } catch (ParseException pe) { + IllegalArgumentException iae = new IllegalArgumentException(pe.getMessage()); + iae.initCause(pe); + throw iae; + } + } + + public Object getValue() { + return getDatumRange(); + } + + private DatumRange parseText( String text ) throws ParseException { + DatumRange result=null; + if ( units instanceof TimeLocationUnits ) { + result= DatumRangeUtil.parseTimeRange( text ); + } else { + result= DatumRangeUtil.parseDatumRange( text, value ); + } + return result; + } + + public DatumRange getDatumRange() { + try { + String text = editor.getText(); + DatumRange dr = parseText(text); + if (!dr.equals(value)) { + DatumRange oldValue = value; + value = dr; + firePropertyChange("value", oldValue, value); + } + return value; + } catch (ParseException e) { + if ( value!=null ) { + setDatumRange( value ); // cause reformat of old Datum + return value; + } else { + return null; + } + } + } + + public String getAsText() { + String text; + Object value = getDatumRange(); + if (value == null) { + return null; + } + return editor.getText(); + } + + public void setUnits(Units units) { + if (units instanceof TimeLocationUnits) { + unitsButton.setVisible(false); + } else { + unitsButton.setVisible(true); + unitsButton.setText(units.toString()); + unitsButton.setToolTipText("units selection"); + } + this.units = units; + } + + public Units getUnits() { + return units; + } + + private void fireActionPerformed() { + setDatumRange(getDatumRange()); + if (actionListener != null) { + actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "DatumEditor")); + } + } + + public void addActionListener(ActionListener al) { + actionListener = AWTEventMulticaster.add(actionListener, al); + } + + public void removeActionListener(ActionListener al) { + actionListener = AWTEventMulticaster.remove(actionListener, al); + } + + /* Why all one class? To reduce the number of object created. + * The purposes of the listeners are not necessarily related and it is + * generally not a good idea to implement multiple interfaces unless there + * is some sort of correlation between them. It is okay to do that here + * since this class is not a part of the public interface. + */ + private class UniversalListener implements MouseListener, KeyListener, FocusListener { + + /** Listens for focus events on the DatumEditor and sets the editor + * caret visible when focus is gained. + */ + public void focusGained(FocusEvent e) { + editor.getCaret().setVisible(true); + editor.getCaret().setSelectionVisible(true); + } + + /** Listens for focus events on the DatumEditor and sets the editor + * caret invisible when focus is lost. + */ + public void focusLost(FocusEvent e) { + editor.getCaret().setVisible(false); + editor.getCaret().setSelectionVisible(false); + } + + /** All key events are forwarded to the editor. Except for keyPresses + * for VK_ENTER + */ + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + fireActionPerformed(); + } else { + forwardKeyEvent(e); + } + } + public void keyReleased(KeyEvent e) { forwardKeyEvent(e); } + public void keyTyped(KeyEvent e) { forwardKeyEvent(e); } + private void forwardKeyEvent(KeyEvent e) { + e.setSource(editor); + editor.dispatchEvent(e); + } + + /** Request focus when sub-components are clicked */ + public void mousePressed(MouseEvent e) { + requestFocusInWindow(); + } + + /** Unused */ + public void mouseEntered(MouseEvent e) {} + public void mouseExited(MouseEvent e) {} + public void mouseClicked(MouseEvent e) {} + public void mouseReleased(MouseEvent e) {} + } + + public String getToolTipText(MouseEvent event) { + if (unitsButton.getBounds().contains(event.getX(), event.getY())) { + return unitsButton.getToolTipText(); + } else { + return null; + } + } + + /** @see java.beans.PropertyEditor#supportsCustomEditor() + * @return true + */ + public boolean supportsCustomEditor() { + return true; + } + + /** @see java.beans.PropertyEditor#getCustomEditor() + * @return this + */ + public Component getCustomEditor() { + return this; + } + + /** + * This PropertyEditor implementation does not support generating java + * code. + * @return The string "???" + */ + public String getJavaInitializationString() { + return "???"; + } + + /** + * This PropertyEditor implementation does not support enumerated + * values. + * @return null + */ + public String[] getTags() { return null; } + + /** @see java.beans.PropertyEditor#isPaintable() + * @return false + */ + public boolean isPaintable() { + return false; + } + + /** Does nothing. + * @param g + * @param r + */ + public void paintValue(Graphics g, Rectangle r) {} + + /* CellEditor stuff */ + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + setValue(value); + return this; + } + + /** + */ + public void addCellEditorListener(CellEditorListener l) { + if (listeners == null) { + listeners = new EventListenerList(); + } + listeners.add(CellEditorListener.class, l); + } + + /** + */ + public void removeCellEditorListener(CellEditorListener l) { + if (listeners != null) { + listeners.remove(CellEditorListener.class, l); + } + } + + /** @see javax.swing.CellEditor#isCellEditable(java.util.EventObject) + * @return true + */ + public boolean isCellEditable(EventObject anEvent) { + return true; + } + + /** @see javax.swing.CellEditor#shouldSelectCell(java.util.EventObject) + * @return true + */ + public boolean shouldSelectCell(EventObject anEvent) { + return true; + } + + /** Returns the value stored in this editor. + * @return the current value being edited + */ + public Object getCellEditorValue() { + return getDatumRange(); + } + + public boolean stopCellEditing() { + if (getDatumRange() == null) { + return false; + } + fireEditingStopped(); + return true; + } + + public void cancelCellEditing() { + fireEditingCanceled(); + } + + private ChangeEvent evt; + + private void fireEditingStopped() { + Object[] l = listeners.getListenerList(); + for (int i = 0; i < l.length; i+=2) { + if (l[i] == CellEditorListener.class) { + CellEditorListener cel = (CellEditorListener)l[i+1]; + if (evt == null) { evt = new ChangeEvent(this); } + cel.editingStopped(evt); + } + } + } + + private void fireEditingCanceled() { + Object[] l = listeners.getListenerList(); + for (int i = 0; i < l.length; i+=2) { + if (l[i] == CellEditorListener.class) { + CellEditorListener cel = (CellEditorListener)l[i+1]; + if (evt == null) { evt = new ChangeEvent(this); } + cel.editingCanceled(evt); + } + } + } +} diff --git a/dasCore/src/main/java/org/das2/components/FavoritesSelector.java b/dasCore/src/main/java/org/das2/components/FavoritesSelector.java new file mode 100644 index 000000000..2b07b39af --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/FavoritesSelector.java @@ -0,0 +1,188 @@ +/* + * FavoritesSelector.java + * + * Created on March 18, 2004, 5:33 PM + */ + +package org.das2.components; + +import java.util.*; +import java.util.prefs.*; +import javax.swing.*; +import java.awt.event.*; +import java.text.*; + +/** + * + * @author Jeremy + */ +public class FavoritesSelector { + + java.util.List favoritesList; + String favoritesType; + FavoritesListener listener=null; + ActionListener actionListener; + JPopupMenu popupMenu; + boolean nextSelectionDeletes= false; + + /** Creates a new instance of FavoritesSelector */ + private FavoritesSelector( String _favoritesType) { + favoritesList= new ArrayList(); + this.favoritesType= _favoritesType; + this.readFromPersistentPrefs(); + } + + public interface FavoritesListener extends EventListener { + void itemSelected( Object o ); + Object addFavoriteSelected(); + } + + public void addFavoritesListener( FavoritesListener _listener ) { + if ( this.listener!=null ) { + throw new IllegalArgumentException( "only one listener supported" ); + } + this.listener= _listener; + } + + public ActionListener getActionListener() { + if ( this.actionListener==null ) { + this.actionListener= new ActionListener() { + public void actionPerformed( ActionEvent ev ) { + try { + String cmd= ev.getActionCommand(); + System.out.println(cmd); + if ( cmd.equals("delete") ) { + FavoritesSelector.this.popupMenu.setVisible(true); + FavoritesSelector.this.nextSelectionDeletes= true; + } else if ( cmd.equals("add") ) { + if ( listener!=null ) { + Object o= FavoritesSelector.this.listener.addFavoriteSelected(); + if ( o!=null ) { + addFavorite(o); + } + } + } else { + if ( FavoritesSelector.this.nextSelectionDeletes ) { + removeFavorite( favoritesList.get( Integer.parseInt(cmd) ) ); + FavoritesSelector.this.nextSelectionDeletes= false; + } else { + FavoritesSelector.this.listener.itemSelected( favoritesList.get( Integer.parseInt(cmd) ) ); + } + } + } catch ( NumberFormatException e ) { + throw new RuntimeException(e); + } + } + }; + } + return this.actionListener; + } + + private void resetPopupMenu() { + JPopupMenu pm= getMenu(); + + JMenuItem item; + item= popupMenu.add("Add to favorites"); + item.setActionCommand("add"); + item.addActionListener(getActionListener()); + for ( int i= 0; i0 ) { + listString.append(favoritesList.get(0).toString()); + } + for ( int i=1; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.graph.SymbolLineRenderer; +import org.das2.graph.DasColorBar; +import org.das2.graph.SpectrogramRenderer; +import org.das2.graph.DasColumn; +import org.das2.graph.Psym; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasRow; +import org.das2.graph.DasPlot; +import org.das2.graph.DasAxis; +import org.das2.dataset.DefaultVectorDataSet; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.Units; +import org.das2.datum.format.TimeDatumFormatter; +import org.das2.datum.TimeLocationUnits; +import org.das2.util.DasMath; +import org.das2.datum.Datum; +import org.das2.event.DataPointSelectionEvent; +import org.das2.event.DataPointSelectionListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; +import java.util.List; +import javax.swing.*; + + +public class HistogramSlicer extends DasPlot implements DataPointSelectionListener { + + private JDialog popupWindow; + private Datum xValue; + private SpectrogramRenderer renderer; + + private HistogramSlicer(SpectrogramRenderer parentRenderer, DasAxis xAxis, DasAxis yAxis) { + super(xAxis, yAxis); + renderer = parentRenderer; + SymbolLineRenderer symLineRenderer= new SymbolLineRenderer(); + symLineRenderer.setHistogram(true); + symLineRenderer.setLineWidth(1.0f); + symLineRenderer.setPsym(Psym.CROSS); + addRenderer(symLineRenderer); + } + + public static HistogramSlicer createSlicer(SpectrogramRenderer renderer) { + DasAxis sourceZAxis = renderer.getColorBar(); + DasAxis xAxis = new DasAxis(sourceZAxis.getDataMinimum(), sourceZAxis.getDataMaximum(), DasAxis.HORIZONTAL, sourceZAxis.isLog()); + DasAxis yAxis = new DasAxis(Datum.create(0.0), Datum.create(1.0), DasAxis.VERTICAL); + return new HistogramSlicer(renderer, xAxis, yAxis); + } + + public void showPopup() { + if (SwingUtilities.isEventDispatchThread()) { + showPopupImpl(); + } + else { + Runnable r = new Runnable() { + public void run() { + showPopupImpl(); + } + }; + } + } + + /** This method should ONLY be called by the AWT event thread */ + private void showPopupImpl() { + if (popupWindow == null) { + createPopup(); + } + popupWindow.setVisible(true); + } + + /** This method should ONLY be called by the AWT event thread */ + private void createPopup() { + int width = renderer.getParent().getCanvas().getWidth() / 2; + int height = renderer.getParent().getCanvas().getHeight() / 2; + DasCanvas canvas = new DasCanvas(width, height); + DasRow row = new DasRow(canvas, 0.1, 0.9); + DasColumn column = new DasColumn(canvas, 0.1, 0.9); + canvas.add(this, row, column); + + JPanel content = new JPanel(new BorderLayout()); + + JPanel buttonPanel = new JPanel(); + BoxLayout buttonLayout = new BoxLayout(buttonPanel, BoxLayout.X_AXIS); + JButton close = new JButton("Hide Window"); + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + popupWindow.setVisible(false); + } + }); + buttonPanel.setLayout(buttonLayout); + buttonPanel.add(Box.createHorizontalGlue()); + buttonPanel.add(close); + + content.add(canvas, BorderLayout.CENTER); + content.add(buttonPanel, BorderLayout.SOUTH); + + Window parentWindow = SwingUtilities.getWindowAncestor(renderer.getParent()); + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } + else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } + else { + popupWindow = new JDialog(); + } + popupWindow.setTitle("Histogram Slicer"); + popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + popupWindow.setContentPane(content); + popupWindow.pack(); + + Point parentLocation = new Point(); + SwingUtilities.convertPointToScreen(parentLocation, renderer.getParent().getCanvas()); + popupWindow.setLocation(parentLocation.x + renderer.getParent().getCanvas().getWidth(),parentLocation.y + height); + } + + public void dataPointSelected(DataPointSelectionEvent e) { + + DataSet ds = e.getDataSet(); + if (ds==null || !(ds instanceof TableDataSet)) { + return; + } + + Datum yValue = e.getY(); + xValue = e.getX(); + + TableDataSet tds = (TableDataSet)ds; + //TableDataSet tds = (TableDataSet)renderer.getDataSet(); + + int itable= TableUtil.tableIndexAt( tds, DataSetUtil.closestColumn( tds, e.getX() ) ); + VectorDataSet sliceDataSet= tds.getYSlice( TableUtil.closestRow( tds, itable, e.getY() ), itable ); + + DasColorBar cb = renderer.getColorBar(); + DasAxis xAxis = getXAxis(); + if (!xAxis.getUnits().equals(cb.getUnits())) { + xAxis.setUnits(cb.getUnits()); + xAxis.setDataRange(cb.getDataMinimum(), cb.getDataMaximum()); + xAxis.setLog(cb.isLog()); + } + + VectorDataSet hist = getHistogram(sliceDataSet); + + getRenderer(0).setDataSet(hist); + + DatumFormatter formatter; + if ( xValue.getUnits() instanceof TimeLocationUnits ) { + formatter= TimeDatumFormatter.DEFAULT; + } else { + formatter= xValue.getFormatter(); + } + + if (!(popupWindow == null || popupWindow.isVisible()) || getCanvas() == null) { + showPopup(); + } + else { + repaint(); + } + } + + /** This should handle non-log data too probably. */ + public VectorDataSet getHistogram(VectorDataSet vds) { + final int BINS_PER_DECADE = 8; + DasAxis zAxis = renderer.getColorBar(); + Units yUnits = zAxis.getUnits(); + double min = getXAxis().getDataMinimum(yUnits); + double max = getXAxis().getDataMaximum(yUnits); + int sampleCount = vds.getXLength(); + if (zAxis.isLog()) { + double minLog = Math.floor(DasMath.log10(min)); + double maxLog = Math.ceil(DasMath.log10(max)); + int binCount = (int)(maxLog - minLog) * BINS_PER_DECADE; //4 bins per decade + double[] bins = new double[binCount]; + for (int i = 0; i < vds.getXLength(); i++) { + double y = vds.getDouble(i, yUnits); + if (yUnits.isFill(y) || Double.isNaN(y)) { + sampleCount--; + continue; + } + double yLog = DasMath.log10(y); + if (yLog < minLog) { + double newMinLog = Math.floor(yLog); + int binCountDelta = ((int)Math.floor(minLog - newMinLog)) * BINS_PER_DECADE; + binCount += binCountDelta; + double[] newBins = new double[binCount]; + System.arraycopy(bins, 0, newBins, binCountDelta, bins.length); + minLog = newMinLog; + bins = newBins; + } + else if (yLog >= maxLog) { + double newMaxLog = Math.ceil(yLog+0.001); + int binCountDelta = (int)(newMaxLog - maxLog) * BINS_PER_DECADE; + binCount += binCountDelta; + double[] newBins = new double[binCount]; + System.arraycopy(bins, 0, newBins, 0, bins.length); + maxLog = newMaxLog; + bins = newBins; + } + int index = (int)Math.floor((yLog - minLog) * (double)BINS_PER_DECADE); + if (index >= 0 && index < bins.length) { + bins[index] += 1.0; + } + } + double[] x = new double[binCount]; + for (int index = 0; index < binCount; index++) { + x[index] = DasMath.exp10(minLog + (index / (double)BINS_PER_DECADE)); + bins[index] = bins[index] / (double)sampleCount; + } + return new DefaultVectorDataSet(x, yUnits, bins, Units.dimensionless, Collections.EMPTY_MAP); + } + else { + //Should add logic that determine bin size instead of just using 1.0 + min = Math.floor(min); + max = Math.ceil(max); + int binCount = (int)(max - min); + double[] bins = new double[binCount]; + for (int i = 0; i < vds.getXLength(); i++) { + double y = vds.getDouble(i, yUnits); + if (yUnits.isFill(y) || Double.isNaN(y)) { + sampleCount--; + continue; + } + if (y < min) { + double newMin = Math.floor(y); + int binCountDelta = (int)(min - newMin); + binCount += binCountDelta; + double[] newBins = new double[binCount]; + System.arraycopy(bins, 0, newBins, binCountDelta, bins.length); + min = newMin; + bins = newBins; + } + else if (y >= max) { + double newMax = Math.ceil(y+0.001); + int binCountDelta = (int)(newMax - max); + binCount += binCountDelta; + double[] newBins = new double[binCount]; + System.arraycopy(bins, 0, newBins, 0, bins.length); + max = newMax; + bins = newBins; + } + int index = (int)Math.floor(y - min); + if (index >= 0 && index < bins.length) { + bins[index] += 1.0; + } + } + double[] x = new double[binCount]; + for (int index = 0; index < binCount; index++) { + x[index] = min + (double)index + 0.5; + bins[index] = bins[index] / (double)sampleCount; + } + return new DefaultVectorDataSet(x, yUnits, bins, Units.dimensionless, Collections.EMPTY_MAP); + } + } + + protected void uninstallComponent() { + super.uninstallComponent(); + } + + protected void installComponent() { + super.installComponent(); + } + +} diff --git a/dasCore/src/main/java/org/das2/components/HorizontalSpectrogramSlicer.java b/dasCore/src/main/java/org/das2/components/HorizontalSpectrogramSlicer.java new file mode 100755 index 000000000..68467f576 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/HorizontalSpectrogramSlicer.java @@ -0,0 +1,198 @@ +/* File: HorizontalSpectrogramSlicer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.graph.SymbolLineRenderer; +import org.das2.graph.DasColumn; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasRow; +import org.das2.graph.DasPlot; +import org.das2.graph.DasAxis; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.TimeDatumFormatter; +import org.das2.datum.TimeLocationUnits; +import org.das2.datum.Datum; +import org.das2.event.DataPointSelectionEvent; +import org.das2.event.DataPointSelectionListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.*; + + +public class HorizontalSpectrogramSlicer extends DasPlot implements DataPointSelectionListener { + + private JDialog popupWindow; + private Datum xValue; + private SymbolLineRenderer renderer; + private DasPlot parentPlot; + + private HorizontalSpectrogramSlicer(DasPlot plot, DasAxis xAxis, DasAxis yAxis) { + super(xAxis, yAxis); + parentPlot = plot; + renderer= new SymbolLineRenderer(); + addRenderer(renderer); + } + + public static HorizontalSpectrogramSlicer createSlicer(DasPlot plot, TableDataSetConsumer dataSetConsumer) { + DasAxis sourceXAxis = plot.getXAxis(); + DasAxis xAxis = sourceXAxis.createAttachedAxis(DasAxis.HORIZONTAL); + DasAxis yAxis = dataSetConsumer.getZAxis().createAttachedAxis(DasAxis.VERTICAL); + return new HorizontalSpectrogramSlicer(plot, xAxis, yAxis); + } + + public void showPopup() { + if (SwingUtilities.isEventDispatchThread()) { + showPopupImpl(); + } + else { + Runnable r = new Runnable() { + public void run() { + showPopupImpl(); + } + }; + } + } + + /** This method should ONLY be called by the AWT event thread */ + private void showPopupImpl() { + if (popupWindow == null) { + createPopup(); + } + popupWindow.setVisible(true); + } + + /** This method should ONLY be called by the AWT event thread */ + private void createPopup() { + int width = parentPlot.getCanvas().getWidth() / 2; + int height = parentPlot.getCanvas().getHeight() / 2; + DasCanvas canvas = new DasCanvas(width, height); + DasRow row = new DasRow(canvas, 0.1, 0.9); + DasColumn column = new DasColumn(canvas, 0.1, 0.9); + canvas.add(this, row, column); + + JPanel content = new JPanel(new BorderLayout()); + + JPanel buttonPanel = new JPanel(); + BoxLayout buttonLayout = new BoxLayout(buttonPanel, BoxLayout.X_AXIS); + JButton close = new JButton("Hide Window"); + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + popupWindow.setVisible(false); + } + }); + buttonPanel.setLayout(buttonLayout); + buttonPanel.add(Box.createHorizontalGlue()); + buttonPanel.add(close); + + content.add(canvas, BorderLayout.CENTER); + content.add(buttonPanel, BorderLayout.SOUTH); + + Window parentWindow = SwingUtilities.getWindowAncestor(parentPlot); + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } + else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } + else { + popupWindow = new JDialog(); + } + popupWindow.setTitle("Horizontal Slicer"); + popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + popupWindow.setContentPane(content); + popupWindow.pack(); + + Point parentLocation = new Point(); + SwingUtilities.convertPointToScreen(parentLocation, parentPlot.getCanvas()); + popupWindow.setLocation(parentLocation.x + parentPlot.getCanvas().getWidth(),parentLocation.y + height); + } + + public void dataPointSelected(DataPointSelectionEvent e) { + + DataSet ds = e.getDataSet(); + if (ds==null || !(ds instanceof TableDataSet)) { + return; + } + + Datum yValue = e.getY(); + xValue = e.getX(); + + TableDataSet tds = (TableDataSet)ds; + + int itable= TableUtil.tableIndexAt( tds, DataSetUtil.closestColumn( tds, e.getX() ) ); + VectorDataSet sliceDataSet= tds.getYSlice( TableUtil.closestRow( tds, itable, e.getY() ), itable ); + + renderer.setDataSet(sliceDataSet); + + DatumFormatter formatter; + if ( xValue.getUnits() instanceof TimeLocationUnits ) { + formatter= TimeDatumFormatter.DEFAULT; + } else { + formatter= xValue.getFormatter(); + } + + setTitle("x: "+ formatter.format(xValue) + " y: "+yValue); + + if (!(popupWindow == null || popupWindow.isVisible()) || getCanvas() == null) { + showPopup(); + } + } + + public void drawContent(Graphics2D g) { + super.drawContent(g); + int ix= (int)this.getXAxis().transform(xValue); + DasRow row= this.getRow(); + int iy0= row.getDMinimum(); + int iy1= row.getDMaximum(); + g.drawLine(ix+3,iy0,ix,iy0+3); + g.drawLine(ix-3,iy0,ix,iy0+3); + g.drawLine(ix+3,iy1,ix,iy1-3); + g.drawLine(ix-3,iy1,ix,iy1-3); + + g.setColor( new Color(230,230,230) ); + g.drawLine( ix, iy0+4, ix, iy1-4 ); + } + + protected void uninstallComponent() { + super.uninstallComponent(); + } + + protected void installComponent() { + super.installComponent(); + } + + protected void processDasUpdateEvent(org.das2.event.DasUpdateEvent e) { + if (isDisplayable()) { + updateImmediately(); + resize(); + } + } +} diff --git a/dasCore/src/main/java/org/das2/components/TearoffTabbedPane.java b/dasCore/src/main/java/org/das2/components/TearoffTabbedPane.java new file mode 100644 index 000000000..b0513bab9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/TearoffTabbedPane.java @@ -0,0 +1,687 @@ +/* + * TearoffTabbedPane.java + * + * Created on January 26, 2006, 7:31 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ +package org.das2.components; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowStateListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.logging.Logger; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; +import javax.swing.Icon; +import javax.swing.InputMap; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; + +/** + * + * @author Jeremy + */ +public class TearoffTabbedPane extends JTabbedPane { + + int selectedTab; + Point dragStart; + Point dragOffset; + JFrame draggingFrame; + JPopupMenu tearOffMenu = new JPopupMenu(); + JPopupMenu dockMenu = new JPopupMenu(); + + private TearoffTabbedPane parentPane; + + private TearoffTabbedPane rightPane = null; + private JFrame rightFrame = null; + private ComponentListener rightFrameListener; + private int rightOffset= 0; + + private final static Logger logger= Logger.getLogger( TearoffTabbedPane.class.getCanonicalName() ); + + HashMap tabs = new HashMap<>(); + int lastSelected; /* keep track of selected index before context menu */ + + private static void copyInputMap(JFrame parent, JFrame babySitter) { + Component c; + JComponent parentc, babySitterC; + + c = parent.getContentPane(); + if (!(c instanceof JComponent)) { + return; + } + parentc = (JComponent) c; + + c = babySitter.getContentPane(); + if (!(c instanceof JComponent)) { + return; + } + babySitterC = (JComponent) c; + + InputMap m = parentc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + if (m == null) { + return; + } + babySitterC.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, m); + ActionMap am = parentc.getActionMap(); + if (am == null) { + return; + } + babySitterC.setActionMap(am); + } + + public void slideRight(Component tab) { + throw new UnsupportedOperationException("Not yet implemented"); + } + + class TabDesc { + + Icon icon; + String title; + String tip; + int index; + Container babysitter; + Component component; + + TabDesc(String title, Icon icon, Component component, String tip, int index) { + this.title = title; + this.icon = icon; + this.component = component; + this.tip = tip; + this.index = index; + this.babysitter = null; + } + } + + public TearoffTabbedPane() { + this(null); + } + + private TearoffTabbedPane(TearoffTabbedPane parent) { + super(); + if (parent == null) { + MouseAdapter ma = getParentMouseAdapter(); + addMouseListener(ma); + addMouseMotionListener(getMouseMotionListener()); + } else { + parentPane = parent; + addMouseListener(getChildMouseAdapter()); + } + } + + private MouseMotionListener getMouseMotionListener() { + return new MouseMotionListener() { + + @Override + public void mouseDragged(MouseEvent e) { + if (selectedTab == -1) { + return; + } + if (dragStart == null) { + dragStart = e.getPoint(); + } else { + if (dragStart.distance(e.getPoint()) > 10) { + if (draggingFrame == null) { + dragOffset= getComponentAt(selectedTab).getLocationOnScreen(); + Point ds= new Point(dragStart); + SwingUtilities.convertPointToScreen(ds, e.getComponent() ); + int tabAndWindowHeight=40; // ubuntu, TODO: calculate + dragOffset.translate( -ds.x, -ds.y - tabAndWindowHeight ); + draggingFrame = TearoffTabbedPane.this.tearOffIntoFrame(selectedTab); + if (draggingFrame == null) { + return; + } + setCursor(new Cursor(Cursor.MOVE_CURSOR)); + if ( draggingFrame.getWidth()< -1*dragOffset.x ) { + int borderWidth=5; + dragOffset.x= -1*(draggingFrame.getWidth()-borderWidth); + } + } + Point p = e.getPoint(); + SwingUtilities.convertPointToScreen(p, (Component) e.getSource()); + p.translate(dragOffset.x, dragOffset.y); + draggingFrame.setLocation(p); + } + } + } + + @Override + public void mouseMoved(MouseEvent e) { + } + }; + } + + private void showPopupMenu(MouseEvent event) { + Component selectedComponent; + selectedTab = TearoffTabbedPane.this.indexAtLocation(event.getX(), event.getY()); + if (selectedTab != -1) { + selectedComponent = TearoffTabbedPane.this.getComponentAt(selectedTab); + if (parentPane == null && tabs.get(selectedComponent) != null) { + tearOffMenu.show(TearoffTabbedPane.this, event.getX(), event.getY()); + } else { + dockMenu.show(TearoffTabbedPane.this, event.getX(), event.getY()); + } + } + } + + private MouseAdapter getChildMouseAdapter() { + return new MouseAdapter() { + + Component selectedComponent; + + + { + dockMenu.add(new JMenuItem(new AbstractAction("dock") { + + @Override + public void actionPerformed(ActionEvent event) { + TabDesc desc = null; + + for (Iterator i = tabs.keySet().iterator(); i.hasNext();) { + Component key = (Component) i.next(); + TabDesc d = (TabDesc) tabs.get(key); + } + + if (parentPane != null) { + selectedComponent = getComponent(selectedTab); + remove(selectedComponent); + parentPane.dock(selectedComponent); + if ( getTabCount()==0 ) { + SwingUtilities.getWindowAncestor(TearoffTabbedPane.this).dispose(); + } + } else { + if (desc.babysitter instanceof Window) { + ((Window) desc.babysitter).dispose(); + } + parentPane.dock(selectedComponent); + } + + } + })); + } + + @Override + public void mousePressed(MouseEvent event) { + selectedTab = TearoffTabbedPane.this.indexAtLocation(event.getX(), event.getY()); + if (event.isPopupTrigger()) { + showPopupMenu(event); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (dragStart != null && selectedTab != -1) { + //JFrame f= TearoffTabbedPane.this.tearOffIntoFrame( selectedTab ); + //Point p= e.getPoint(); + //SwingUtilities.convertPointToScreen( p ,(Component) e.getSource() ); + //f.setLocation( p ); + setCursor(null); + draggingFrame = null; + } + dragStart = null; + if (e.isPopupTrigger()) { + showPopupMenu(e); + } + } + }; + + } + + private MouseAdapter getParentMouseAdapter() { + return new MouseAdapter() { + + { + tearOffMenu.add(new JMenuItem(new AbstractAction("undock") { + + @Override + public void actionPerformed(ActionEvent event) { + TearoffTabbedPane.this.tearOffIntoFrame(selectedTab); + } + })); + tearOffMenu.add(new JMenuItem(new AbstractAction("slide right") { + + @Override + public void actionPerformed(ActionEvent event) { + TearoffTabbedPane.this.slideRight(selectedTab); + } + })); + } + Component selectedComponent; + + + { + dockMenu.add(new JMenuItem(new AbstractAction("show") { + + @Override + public void actionPerformed(ActionEvent event) { + TabDesc desc = null; + Component babyComponent = null; + for (Iterator i = tabs.keySet().iterator(); i.hasNext();) { + Component key = (Component) i.next(); + TabDesc d = (TabDesc) tabs.get(key); + if (d.index == selectedTab) { + desc = d; + babyComponent = key; + break; + } + } + if (desc.babysitter instanceof Window) { + Window babySitter = (Window) desc.babysitter; + babySitter.setVisible(false); + babySitter.setVisible(true); + } else if ( desc.babysitter instanceof TearoffTabbedPane ) { + Window parent= SwingUtilities.getWindowAncestor(babyComponent); + parent.setVisible(false); + parent.setVisible(true); + } + + //babySitter.toFront(); // no effect on Linux/Gnome + } + })); + dockMenu.add(new JMenuItem(new AbstractAction("dock") { + + @Override + public void actionPerformed(ActionEvent event) { + TabDesc desc = null; + Component babyComponent = null; + for (Iterator i = tabs.keySet().iterator(); i.hasNext();) { + Component key = (Component) i.next(); + TabDesc d = (TabDesc) tabs.get(key); + if (d.index == selectedTab) { + desc = d; + babyComponent = key; + break; + + } + } + + if (desc.babysitter instanceof Window) { + ((Window) desc.babysitter).dispose(); + } else if ( desc.babysitter instanceof TearoffTabbedPane ) { + TearoffTabbedPane bb= (TearoffTabbedPane) desc.babysitter; + if ( bb.getTabCount()==1 ) { + SwingUtilities.getWindowAncestor(bb).dispose(); + } + // do nothing + } + + TearoffTabbedPane.this.dock(babyComponent); + } + })); + } + + @Override + public void mousePressed(MouseEvent event) { + selectedTab = TearoffTabbedPane.this.indexAtLocation(event.getX(), event.getY()); + if (event.isPopupTrigger()) { + showPopupMenu(event); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (dragStart != null && selectedTab != -1) { + //JFrame f= TearoffTabbedPane.this.tearOffIntoFrame( selectedTab ); + //Point p= e.getPoint(); + //SwingUtilities.convertPointToScreen( p ,(Component) e.getSource() ); + //f.setLocation( p ); + setCursor(null); + draggingFrame = null; + } + dragStart = null; + if (e.isPopupTrigger()) { + showPopupMenu(e); + } + } + }; + } + + /** + * get a component to occupy the space when a tab is undocked. + * @return + */ + static Component getTornOffComponent() { + JPanel tornOffComponent = new JPanel(); + tornOffComponent.setLayout(new BorderLayout()); + tornOffComponent.add(new JLabel("This tab is undocked. Right-click on the tab name and select dock."), BorderLayout.NORTH); + return tornOffComponent; + } + + public void tearOff(int tabIndex, Container newContainer) { + int lastSelected = this.lastSelected; + Component c = getComponentAt(tabIndex); + String title = super.getTitleAt(tabIndex); + super.removeTabAt(tabIndex); + super.insertTab("(" + title + ")", null, getTornOffComponent(), null, tabIndex); + super.setEnabledAt(tabIndex, false); + TabDesc td = ((TabDesc) tabs.get(c)); + td.babysitter = newContainer; + setSelectedIndex(lastSelected); + } + + private final static Object STICK_LEFT= "left"; + private final static Object STICK_RIGHT= "right"; + + /** + * get the listener that will keep the two JFrames close together + * @param panel1 component within the master frame. + * @param frame1 master frame that controls. + * @param panel2 component within the compliant frame + * @param frame2 compliant frame that follows. + * @param direction + * @return + */ + public ComponentListener getFrameComponentListener( + final Component panel1, final Component frame1, + final Component panel2, final Component frame2, final Object direction ) { + + return new ComponentListener() { + Component activeComponent; + long activeComponentTime=0; + + @Override + public void componentResized(ComponentEvent e) { + long t= System.currentTimeMillis(); + if ( ( t-activeComponentTime ) > 100 ) { + activeComponent= e.getComponent(); + } + if ( e.getComponent()==activeComponent ) { + activeComponentTime= t; + updateAttached( activeComponent, panel1, frame1, panel2, frame2, direction, true ); + } + } + + @Override + public void componentMoved(ComponentEvent e) { + long t= System.currentTimeMillis(); + if ( ( t-activeComponentTime ) > 100 ) { + activeComponent= e.getComponent(); + } + if ( e.getComponent()==activeComponent ) { + activeComponentTime= t; + updateAttached( activeComponent, panel1, frame1, panel2, frame2, direction, false ); + } + } + + @Override + public void componentShown(ComponentEvent e) { + } + + @Override + public void componentHidden(ComponentEvent e) { + } + }; + } + + private void updateAttached( + final Component active, + final Component panel1, final Component frame1, + final Component panel2, final Component frame2, Object direction, boolean updateSize ) { + Point p = SwingUtilities.convertPoint(panel1, 0, 0, frame1); + Point p2 = SwingUtilities.convertPoint(panel2, 0, 0, frame2); + Dimension s1= panel1.getSize(); + Dimension frameSize1= frame1.getSize(); + Dimension s2= panel2.getSize(); + Dimension frameSize2= frame2.getSize(); + + if ( direction==STICK_RIGHT ) { + if ( active==frame1 ) { + if ( updateSize ) frame2.setSize( new Dimension( s1.width, s1.height + p2.y ) ); + frame2.setLocation( frame1.getX() + frame1.getWidth() - p2.x + rightOffset, frame1.getY() + p.y - p2.y ); + } else { + if ( false && updateSize ) { + int frame1NotTabs= frameSize1.height-s1.height; + System.err.println(frame1NotTabs); + frame1.setSize( new Dimension( frameSize1.width, ( frameSize1.height-s1.height ) + s2.height ) ); + } + int x= Math.max( frame1.getX(), frame2.getX()-frameSize1.width + p2.x ); + rightOffset= frame2.getX()-s1.width - frame1.getX(); + if ( rightOffset>0 ) rightOffset=0; + if ( rightOffset< -1*s1.width ) { + x+= s1.width + rightOffset; + rightOffset= -1 * s1.width; + } + frame1.setLocation( x, frame2.getY() - p.y + p2.y ); + } + } + } + + private synchronized TearoffTabbedPane getRightTabbedPane() { + if (rightPane == null) { + + final JFrame parent = (JFrame) SwingUtilities.getWindowAncestor(this); + rightPane = new TearoffTabbedPane(this); + rightFrame = new JFrame(); + + rightFrame.add(rightPane); + rightFrame.setIconImage( parent.getIconImage() ); + + final WindowStateListener listener = new WindowStateListener() { + + public void windowStateChanged(WindowEvent e) { + rightFrame.setExtendedState(parent.getExtendedState()); + } + }; + parent.addWindowStateListener(listener); + + rightFrame.addWindowListener(new WindowAdapter() { + + public void windowClosing(WindowEvent e) { + parent.removeWindowStateListener(listener); + parent.removeComponentListener(rightFrameListener); + + for (Component c : new ArrayList(rightPane.tabs.keySet())) { + TearoffTabbedPane.this.dock(c); + } + + rightFrame = null; + rightPane = null; + } + }); + + copyInputMap(parent, rightFrame); + rightFrameListener = getFrameComponentListener(this, parent, rightPane, rightFrame, STICK_RIGHT ); + + parent.addComponentListener(rightFrameListener); + rightFrame.addComponentListener(rightFrameListener); + + rightPane.setPreferredSize(this.getSize()); + + rightFrame.pack(); + updateAttached( parent, this, parent, rightPane, rightFrame, STICK_RIGHT, true ); + + rightFrame.setVisible(true); + parent.toFront(); + + } + return rightPane; + } + + public void slideRight(int tabIndex) { + + final Component c = getComponentAt(tabIndex); + logger.finest("slideRight "+c); + + setSelectedIndex(tabIndex); + c.setVisible(true); // darwin bug297 + + TabDesc td = (TabDesc) tabs.get(c); + if (td == null) { + return; + } + final JFrame parent = (JFrame) SwingUtilities.getWindowAncestor(this); + + TearoffTabbedPane right = getRightTabbedPane(); + + tearOff(tabIndex, right); + + right.add(td.title, c); + right.setSelectedIndex(right.getTabCount()-1); + if ( !right.isShowing() ) { + Window w= SwingUtilities.getWindowAncestor(right); + w.setVisible(false); + w.setVisible(true); + } + } + + protected JFrame tearOffIntoFrame(int tabIndex) { + final Component c = getComponentAt(tabIndex); + logger.finest("tearOffInfoFrame "+c); + setSelectedIndex(tabIndex); + c.setVisible(true); // darwin bug297 + Point p = c.getLocationOnScreen(); + TabDesc td = (TabDesc) tabs.get(c); + if (td == null) { + return null; + } + final JFrame parent = (JFrame) SwingUtilities.getWindowAncestor(this); + final JFrame babySitter = new JFrame(td.title); + babySitter.setIconImage( parent.getIconImage() ); + final WindowStateListener listener = new WindowStateListener() { + + @Override + public void windowStateChanged(WindowEvent e) { + babySitter.setExtendedState(parent.getExtendedState()); + } + }; + parent.addWindowStateListener(listener); + + p.translate(20, 20); + babySitter.setLocation(p); + babySitter.addWindowListener(new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent e) { + parent.removeWindowStateListener(listener); + dock(c); + } + }); + + copyInputMap(parent, babySitter); + + JTabbedPane pane = new TearoffTabbedPane(this); + babySitter.getContentPane().add(pane); + + tearOff(tabIndex, babySitter); + pane.add(td.title, c); + + babySitter.pack(); + babySitter.setVisible(true); + + return babySitter; + } + + public void dock(Component c) { + logger.finest("dock "+c); + int selectedIndex = getSelectedIndex(); + TabDesc td = (TabDesc) tabs.get(c); + int index = td.index; + super.removeTabAt(index); + super.insertTab(td.title, td.icon, c, td.tip, index); + super.setEnabledAt(index, true); + setSelectedIndex(selectedIndex); + } + + @Override + public void addTab(String title, Icon icon, Component component) { + super.addTab(title, icon, component); + TabDesc td = new TabDesc(title, icon, component, null, indexOfComponent(component)); + tabs.put(component, td); + } + + @Override + public void addTab(String title, Component component) { + super.addTab(title, component); + TabDesc td = new TabDesc(title, null, component, null, indexOfComponent(component)); + tabs.put(component, td); + } + + @Override + public void insertTab(String title, Icon icon, Component component, String tip, int index) { + super.insertTab(title, icon, component, tip, index); + TabDesc td = new TabDesc(title, icon, component, tip, index); + tabs.put(component, td); + } + + @Override + public void addTab(String title, Icon icon, Component component, String tip) { + super.addTab(title, icon, component, tip); + TabDesc td = new TabDesc(title, icon, component, tip, indexOfComponent(component)); + tabs.put(component, td); + } + + // This implementation looks backwords. It looks like an implementation of + // getComponentByIndex, not getTabComponentByIndex + + private Component getTabComponentByIndex(int index) { + for (Component key : tabs.keySet()) { + TabDesc td = tabs.get(key); + if (td.index == index) { + return key; + } + } + return null; + } + + private Component getTabComponentByTitle(String title) { + for (Component key : tabs.keySet()) { + TabDesc td = tabs.get(key); + if (td.title == title) { + return key; + } + } + return null; + } + + @Override + public void removeTabAt(int index) { + Component c = getTabComponentByIndex(index); + super.removeTabAt(index); + TabDesc tab = tabs.get(c); + if ( tab!=null ) { + if ( tab.babysitter != null ) { //perhaps better to dock it first + if (tab.babysitter instanceof Window) { + ((Window) tab.babysitter).dispose(); + } + } + tabs.remove(c); + } else { + logger.fine("tabs didn't contain c, someone else removed it."); + //TODO: clean this up. + } + + } + + @Override + public void setSelectedIndex(int index) { + if (index != getSelectedIndex()) { + lastSelected = getSelectedIndex(); + } + super.setSelectedIndex(index); + logger.finest("setSelectedIndex "+getSelectedComponent()); + } +} diff --git a/dasCore/src/main/java/org/das2/components/Toolbox.java b/dasCore/src/main/java/org/das2/components/Toolbox.java new file mode 100644 index 000000000..7f9f65b59 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/Toolbox.java @@ -0,0 +1,513 @@ +/* File: Toolbox.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.graph.DasColorBar; +import org.das2.graph.SymbolLineRenderer; +import org.das2.graph.SpectrogramRenderer; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasPlot; +import org.das2.graph.DasAxis; +import org.das2.dasml.FormTab; +import org.das2.dasml.FormChoice; +import org.das2.dasml.FormList; +import org.das2.dasml.FormPanel; +import org.das2.dasml.FormText; +import org.das2.dasml.FormRadioButtonGroup; +import org.das2.dasml.FormRadioButton; +import org.das2.dasml.FormWindow; +import org.das2.dasml.FormTextField; +import org.das2.dasml.FormCheckBox; +import org.das2.dasml.TransferableFormComponent; +import org.das2.dasml.FormButton; + +import org.das2.graph.dnd.TransferableCanvas; +import org.das2.graph.dnd.TransferableCanvasComponent; +import org.das2.graph.dnd.TransferableRenderer; +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import java.awt.*; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.*; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.net.URL; + +/** + * A component that allows the user to create new objects and pass them to + * components using the drag and drop interface. The objects that can be + * created are represented by icons. The icons are grouped by tabs for each\ + * group. + * + * @author Edward West + */ +public class Toolbox extends JTabbedPane { + + + + + /** Image used to combine with Icons to create a pointer */ + private static Image pointerOverlay; + + + + + /** Dummy component used for loading images and creating pointer images */ + private static Component dummy = new Component(){}; + + + + + /** initialize comp and load pointer image */ + static { + /** get image from jar resource */ + Class cl = ToolComponent.class; + URL pointerURL = cl.getResource("/images/toolbox/dragpointer.gif"); + Image image = Toolkit.getDefaultToolkit().getImage(pointerURL); + /** Make sure the whole image is loaded */ + MediaTracker tracker = new MediaTracker(dummy); + tracker.addImage(image, 0); + try { + tracker.waitForAll(); + } + catch (InterruptedException ie) { + throw new RuntimeException(ie); + } + pointerOverlay = image; + } + + + + + /** Creates a new instance of Toolbox */ + public Toolbox() { + initializeFormToolComponent(); + initializeGraphToolComponent(); + } + + + + /** initializes the ToolComponent containing icons for form elements */ + private void initializeFormToolComponent() { + String[] ids = { + "form tab", + "window", + "panel", + "static text", + "text field", + "button", + "check box", + "button group", + "radio button", + "choice", + "list" + }; + Class c = Toolbox.class; + Icon[] icons = { + new ImageIcon(c.getResource("/images/toolbox/tab.gif")), + new ImageIcon(c.getResource("/images/toolbox/window.gif")), + new ImageIcon(c.getResource("/images/toolbox/panel.gif")), + new ImageIcon(c.getResource("/images/toolbox/text.gif")), + new ImageIcon(c.getResource("/images/toolbox/textfield.gif")), + new ImageIcon(c.getResource("/images/toolbox/button.gif")), + new ImageIcon(c.getResource("/images/toolbox/checkbox.gif")), + new ImageIcon(c.getResource("/images/toolbox/buttongroup.gif")), + new ImageIcon(c.getResource("/images/toolbox/radiobutton.gif")), + new ImageIcon(c.getResource("/images/toolbox/choice.gif")), + //new ImageIcon(l.getResource("/images/toolbox/list.png")), + }; + + ToolComponent tc = new ToolComponent(ids, icons, 4); + add("Form", tc); + } + + + + + /** initializes the ToolComponent containing icons for graph elements */ + private void initializeGraphToolComponent() { + String[] ids = { + "canvas", + "plot", + "axis", + "time axis", + "spectrogram renderer", + "line plot renderer", + "spectrogram plot" + }; + Class c = Toolbox.class; + Icon[] icons = { + new ImageIcon(c.getResource("/images/toolbox/canvas.gif")), + new ImageIcon(c.getResource("/images/toolbox/plot.gif")), + new ImageIcon(c.getResource("/images/toolbox/axis.gif")), + new ImageIcon(c.getResource("/images/toolbox/taxis.gif")), + new ImageIcon(c.getResource("/images/toolbox/spectrogram.gif")), + new ImageIcon(c.getResource("/images/toolbox/line.gif")), + new ImageIcon(c.getResource("/images/toolbox/spectrogram_plot.gif")) + }; + ToolComponent tc = new ToolComponent(ids, icons, 4); + add("Graph", tc); + } + + + + + + /** Creates a transferable based on the String parameter */ + private static Transferable createTransferable(String id) { + if (id.equals("form tab")) { + return new TransferableFormComponent( + new FormTab(null, "label")); + } + else if (id.equals("window")) { + return new TransferableFormComponent( + new FormWindow(null, "title", 640, 480)); + } + else if (id.equals("panel")) { + return new TransferableFormComponent(new FormPanel()); + } + else if (id.equals("static text")) { + return new TransferableFormComponent(new FormText()); + } + else if (id.equals("text field")) { + return new TransferableFormComponent(new FormTextField(null)); + } + else if (id.equals("button")) { + return new TransferableFormComponent( + new FormButton(null, "label")); + } + else if (id.equals("check box")) { + return new TransferableFormComponent( + new FormCheckBox(null, "label")); + } + else if (id.equals("button group")) { + return new TransferableFormComponent( + new FormRadioButtonGroup()); + } + else if (id.equals("radio button")) { + return new TransferableFormComponent( + new FormRadioButton(null, "label")); + } + else if (id.equals("choice")) { + return new TransferableFormComponent(new FormChoice(null)); + } + else if (id.equals("list")) { + return new TransferableFormComponent(new FormList(null)); + } + else if (id.equals("canvas")) { + return new TransferableCanvas( + DasCanvas.createFormCanvas(null, 640, 480)); + } + else if (id.equals("plot")) { + return new TransferableCanvasComponent( + DasPlot.createNamedPlot(null)); + } + else if (id.equals("axis")) { + return new TransferableCanvasComponent( + DasAxis.createNamedAxis(null)); + } + else if (id.equals("time axis")) { + return new TransferableCanvasComponent( + DasAxis.createNamedAxis(null)); + } + else if (id.equals("spectrogram renderer")) { + DasColorBar cb = DasColorBar.createNamedColorBar(null); + return new TransferableRenderer( + new SpectrogramRenderer(null, cb)); + } + else if (id.equals("line plot renderer")) { + return new TransferableRenderer( + new SymbolLineRenderer()); + } + else if (id.equals("spectrogram plot")) { + DasPlot plot = DasPlot.createNamedPlot(null); + DasColorBar colorBar = DasColorBar.createNamedColorBar( + plot.getDasName() + "_colorbar"); + SpectrogramRenderer renderer + = new SpectrogramRenderer(null, colorBar); + plot.addRenderer(renderer); + return new TransferableCanvasComponent(plot); + } + else { + throw new IllegalArgumentException(id); + } + } + + + + /** Creates an array of cursors to be used for drag and drop operations */ + private static Cursor[] getCursors(String[] ids, Icon[] icons) { + Cursor[] cursors = new Cursor[icons.length]; + Point origin = new Point(0, 0); + for (int i = 0; i < cursors.length; i++) { + int width = icons[i].getIconWidth(); + int height = icons[i].getIconHeight(); + BufferedImage cimage = new BufferedImage(32, 32, + BufferedImage.TYPE_INT_ARGB); + Graphics g = cimage.getGraphics(); + icons[i].paintIcon(dummy, g, 8, 8); + g.drawImage(pointerOverlay, 0, 0, dummy); + cursors[i] =Toolkit.getDefaultToolkit().createCustomCursor( + cimage, origin, ids[i]); + } + return cursors; + } + + + + /** DragGesture and DragSourceListener implementation used to initiate drag + * and drop operations for ToolboxComponents. + */ + private static class ToolboxDragGestureListener + implements DragGestureListener, DragSourceListener { + + /** A DragGestureRecognizer has detected + * a platform-dependent drag initiating gesture and + * is notifying this listener + * in order for it to initiate the action for the user. + *

+ * @param dge the DragGestureEvent describing + * the gesture that has just occurred + */ + public void dragGestureRecognized(DragGestureEvent dge) { + ToolComponent tc = (ToolComponent)dge.getComponent(); + int index = tc.selectedIndex; + if (index >= 0) { + Cursor dragCursor = tc.cursors[index]; + Transferable t = createTransferable(tc.ids[index]); + dge.startDrag(dragCursor, t, this); + } + } + + /** This method is invoked to signify that the Drag and Drop + * operation is complete. The getDropSuccess() method of + * the DragSourceDropEvent can be used to + * determine the termination state. The getDropAction() method + * returns the operation that the drop site selected + * to apply to the Drop operation. Once this method is complete, the + * current DragSourceContext and + * associated resources become invalid. + * + * @param dsde the DragSourceDropEvent + */ + public void dragDropEnd(DragSourceDropEvent dsde) { + } + + /** Called as the cursor's hotspot enters a platform-dependent drop site. + * This method is invoked when all the following conditions are true: + *

    + *
  • The cursor's hotspot enters the operable part of a platform- + * dependent drop site. + *
  • The drop site is active. + *
  • The drop site accepts the drag. + *
+ * + * @param dsde the DragSourceDragEvent + */ + public void dragEnter(DragSourceDragEvent dsde) { + } + + /** Called as the cursor's hotspot exits a platform-dependent drop site. + * This method is invoked when any of the following conditions are true: + *
    + *
  • The cursor's hotspot no longer intersects the operable part + * of the drop site associated with the previous dragEnter() invocation. + *
+ * OR + *
    + *
  • The drop site associated with the previous dragEnter() invocation + * is no longer active. + *
+ * OR + *
    + *
  • The current drop site has rejected the drag. + *
+ * + * @param dse the DragSourceEvent + */ + public void dragExit(DragSourceEvent dse) { + } + + /** Called as the cursor's hotspot moves over a platform-dependent drop site. + * This method is invoked when all the following conditions are true: + *
    + *
  • The cursor's hotspot has moved, but still intersects the + * operable part of the drop site associated with the previous + * dragEnter() invocation. + *
  • The drop site is still active. + *
  • The drop site accepts the drag. + *
+ * + * @param dsde the DragSourceDragEvent + */ + public void dragOver(DragSourceDragEvent dsde) { + } + + /** Called when the user has modified the drop gesture. + * This method is invoked when the state of the input + * device(s) that the user is interacting with changes. + * Such devices are typically the mouse buttons or keyboard + * modifiers that the user is interacting with. + * + * @param dsde the DragSourceDragEvent + */ + public void dropActionChanged(DragSourceDragEvent dsde) { + } + + } + + + + /** Displays a group of icons for the Toolbox component. */ + private static class ToolComponent extends JComponent { + + /** array of icons for this group */ + private Icon[] icons; + + + + /** array of ids for this group */ + private String[] ids; + + + + /** array of drag cursors for this group */ + private Cursor[] cursors; + + + + + /** maximum number of icons per row */ + private int width; + + + + + /** number of rows of icons */ + private int height; + + + + + /** The index of the last icon that that was the recipient of a + * mousePressed event + */ + private int selectedIndex = -1; + + + + /** Create a new ToolComponent */ + private ToolComponent(String[] ids, Icon [] icons, int w) { + this.ids = ids; + this.icons = icons; + this.cursors = getCursors(ids, icons); + this.width = w; + this.height = (int)Math.ceil((double)icons.length / (double)w); + setBackground(Color.WHITE); + setForeground(Color.BLACK); + setTransferHandler(null); + DragSource dragSource = DragSource.getDefaultDragSource(); + dragSource.createDefaultDragGestureRecognizer(this, + DnDConstants.ACTION_COPY, new ToolboxDragGestureListener()); + addMouseListener(new InputListener()); + ToolTipManager.sharedInstance().registerComponent(this); + }//public ToolComponent + + protected void paintComponent(Graphics g){ + Rectangle clip = g.getClipBounds(); + g.setColor(getBackground()); + if (clip == null) { + g.fillRect(0, 0, getWidth(), getHeight()); + } + else { + g.fillRect(clip.x, clip.y, clip.width, clip.height); + } + g.setColor(getForeground()); + for(int index = 0; index < icons.length; index++) { + int i = index % width; + int j = index / width; + int x = i * 32 + 7; + int y = j * 32 + 7; + icons[index].paintIcon(this,g, x, y); + g.drawRect(x - 1, y - 1, 24, 24); + } + }//public void paintComponent(Graphics g) + + public Dimension getPreferredSize() { + int w = width * 32 + 6; + int h = height * 32 + 6; + return new Dimension(w, h); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public Dimension getMaximumSize() { + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + private int positionToIndex(int x, int y) { + for (int index = 0; index < icons.length; index++) { + int i = index % width; + int j = index / width; + int xi = i * 32 + 7; + int yi = j * 32 + 7; + if (x >= xi && x < xi + 24 && y >= yi && y < yi + 24) { + return index; + } + } + return -1; + } + + public String getToolTipText(MouseEvent event) { + int index = positionToIndex(event.getX(), event.getY()); + if (index == -1) { + return null; + } + else { + return ids[index]; + } + } + + } + + /** A listener to listen for mousePressed events on a ToolComponent */ + private static class InputListener extends MouseInputAdapter { + public void mousePressed(MouseEvent e) { + ToolComponent tc = (ToolComponent)e.getComponent(); + tc.selectedIndex = tc.positionToIndex(e.getX(), e.getY()); + } + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + frame.getContentPane().add(new Toolbox(), "Center"); + frame.getContentPane().add(new JTextArea(10, 10), "West"); + frame.pack(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } + +} diff --git a/dasCore/src/main/java/org/das2/components/VerticalSpectrogramAverager.java b/dasCore/src/main/java/org/das2/components/VerticalSpectrogramAverager.java new file mode 100755 index 000000000..2d275dfb4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/VerticalSpectrogramAverager.java @@ -0,0 +1,195 @@ +/* File: VerticalSpectrogramAverager.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.graph.SymbolLineRenderer; +import org.das2.graph.DasColumn; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasRow; +import org.das2.graph.DasPlot; +import org.das2.graph.DasAxis; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.AverageTableRebinner; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.DatumRange; +import org.das2.DasException; +import org.das2.datum.Datum; +import org.das2.event.DataRangeSelectionEvent; +import org.das2.event.DataRangeSelectionListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.*; + + +public class VerticalSpectrogramAverager extends DasPlot implements DataRangeSelectionListener { + + private JDialog popupWindow; + + private Datum yValue; + + private DasPlot parentPlot; + private SymbolLineRenderer renderer; + + protected VerticalSpectrogramAverager(DasPlot plot, DasAxis xAxis, DasAxis yAxis) { + super(xAxis, yAxis); + parentPlot = plot; + renderer= new SymbolLineRenderer(); + addRenderer(renderer); + } + + public static VerticalSpectrogramAverager createAverager(DasPlot plot, TableDataSetConsumer dataSetConsumer) { + DasAxis sourceYAxis = plot.getYAxis(); + DasAxis xAxis = sourceYAxis.createAttachedAxis(DasAxis.HORIZONTAL); + DasAxis yAxis = dataSetConsumer.getZAxis().createAttachedAxis(DasAxis.VERTICAL); + return new VerticalSpectrogramAverager(plot, xAxis, yAxis); + } + + public void showPopup() { + if (SwingUtilities.isEventDispatchThread()) { + showPopupImpl(); + } else { + Runnable r = new Runnable() { + @Override + public void run() { + showPopupImpl(); + } + }; + } + } + + /** This method should ONLY be called by the AWT event thread */ + private void showPopupImpl() { + if (popupWindow == null) { + createPopup(); + } + popupWindow.setVisible(true); + } + + /** This method should ONLY be called by the AWT event thread */ + private void createPopup() { + int width = parentPlot.getCanvas().getWidth() / 2; + int height = parentPlot.getCanvas().getHeight() / 2; + DasCanvas canvas = new DasCanvas(width, height); + DasRow row = new DasRow(canvas, null, 0, 1.0, 3, -5, 0, 0 ); + DasColumn column = new DasColumn(canvas, null, 0, 1.0, 7, -3, 0, 0 ); + canvas.add(this, row, column); + + JPanel content = new JPanel(new BorderLayout()); + + JPanel buttonPanel = new JPanel(); + BoxLayout buttonLayout = new BoxLayout(buttonPanel, BoxLayout.X_AXIS); + JButton close = new JButton("Hide Window"); + close.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + popupWindow.setVisible(false); + } + }); + buttonPanel.setLayout(buttonLayout); + buttonPanel.add(Box.createHorizontalGlue()); + buttonPanel.add(close); + + content.add(canvas, BorderLayout.CENTER); + content.add(buttonPanel, BorderLayout.SOUTH); + + Window parentWindow = SwingUtilities.getWindowAncestor(parentPlot); + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } else { + popupWindow = new JDialog(); + } + popupWindow.setTitle("Vertical Slicer"); + popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + popupWindow.setContentPane(content); + popupWindow.pack(); + + Point parentLocation = new Point(); + SwingUtilities.convertPointToScreen(parentLocation, parentPlot.getCanvas()); + popupWindow.setLocation(parentLocation.x + parentPlot.getCanvas().getWidth(),parentLocation.y); + } + + @Override + protected void drawContent(Graphics2D g) { + super.drawContent(g); + /*int ix= (int)this.getXAxis().transform(yValue); + DasRow row= this.getRow(); + int iy0= (int)row.getDMinimum(); + int iy1= (int)row.getDMaximum(); + g.drawLine(ix+3,iy0,ix,iy0+3); + g.drawLine(ix-3,iy0,ix,iy0+3); + g.drawLine(ix+3,iy1,ix,iy1-3); + g.drawLine(ix-3,iy1,ix,iy1-3);*/ + } + + @Override + public void dataRangeSelected(DataRangeSelectionEvent e) { + DataSet ds = e.getDataSet(); + if (ds==null || !(ds instanceof TableDataSet)) + return; + TableDataSet xtys = (TableDataSet)ds; + Datum xValue1 = e.getMinimum(); + Datum xValue2 = e.getMaximum(); + + if ( xValue2.equals(xValue1) ) { + return; + } + + this.setTitle( new DatumRange( xValue1, xValue2 ).toString() ); + + RebinDescriptor ddX = new RebinDescriptor(xValue1, xValue2, 1, false); + ddX.setOutOfBoundsAction(RebinDescriptor.MINUSONE); + AverageTableRebinner rebinner = new AverageTableRebinner(); + try { + TableDataSet rebinned = (TableDataSet)rebinner.rebin(xtys, ddX, null, null); + VectorDataSet ds1 = rebinned.getXSlice(0); + renderer.setDataSet(ds1); + } catch (DasException de) { + //Do nothing. + } + + if (!(popupWindow == null || popupWindow.isVisible()) || getCanvas() == null) { + showPopup(); + } else { + repaint(); + } + } + + @Override + protected void uninstallComponent() { + super.uninstallComponent(); + } + + @Override + protected void installComponent() { + super.installComponent(); + getCanvas().getGlassPane().setVisible(false); + } + +} diff --git a/dasCore/src/main/java/org/das2/components/VerticalSpectrogramSlicer.java b/dasCore/src/main/java/org/das2/components/VerticalSpectrogramSlicer.java new file mode 100755 index 000000000..750b0cbd3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/VerticalSpectrogramSlicer.java @@ -0,0 +1,243 @@ +/* File: VerticalSpectrogramSlicer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.graph.SymbolLineRenderer; +import org.das2.graph.DasColumn; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasRow; +import org.das2.graph.DasPlot; +import org.das2.graph.DasAxis; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.TimeDatumFormatter; +import org.das2.datum.TimeLocationUnits; +import org.das2.system.DasLogger; +import org.das2.datum.Datum; +import org.das2.event.DataPointSelectionEvent; +import org.das2.event.DataPointSelectionListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.*; + + +public class VerticalSpectrogramSlicer +extends DasPlot implements DataPointSelectionListener { + + private JDialog popupWindow; + private DasPlot parentPlot; + protected Datum yValue; + private long eventBirthMilli; + private SymbolLineRenderer renderer; + private Color yMarkColor = new Color(230,230,230); + private String frameTitle = "Vertical Slicer"; + + protected VerticalSpectrogramSlicer(DasPlot parent, DasAxis xAxis, DasAxis yAxis) { + super( xAxis, yAxis); + this.parentPlot = parent; + renderer= new SymbolLineRenderer(); + addRenderer(renderer); + } + + protected void setDataSet( VectorDataSet ds ) { + renderer.setDataSet(ds); + } + + public static VerticalSpectrogramSlicer createSlicer( DasPlot plot, TableDataSetConsumer dataSetConsumer) { + DasAxis sourceYAxis = plot.getYAxis(); + DasAxis sourceZAxis = dataSetConsumer.getZAxis(); + DasAxis xAxis = sourceYAxis.createAttachedAxis(DasAxis.HORIZONTAL); + DasAxis yAxis = sourceZAxis.createAttachedAxis(DasAxis.VERTICAL); + return new VerticalSpectrogramSlicer(plot, xAxis, yAxis); + } + + public static VerticalSpectrogramSlicer createSlicer( DasPlot plot, DasAxis xAxis, TableDataSetConsumer dataSetConsumer) { + DasAxis sourceYAxis = plot.getYAxis(); + DasAxis sourceZAxis = dataSetConsumer.getZAxis(); + DasAxis yAxis = sourceZAxis.createAttachedAxis(DasAxis.VERTICAL); + return new VerticalSpectrogramSlicer(plot, xAxis, yAxis); + } + + public void showPopup() { + if (SwingUtilities.isEventDispatchThread()) { + showPopupImpl(); + } + else { + Runnable r = new Runnable() { + public void run() { + showPopupImpl(); + } + }; + } + } + + /** This method should ONLY be called by the AWT event thread */ + private void showPopupImpl() { + if (popupWindow == null) { + createPopup(); + } + popupWindow.setVisible(true); + } + + /* Would have done this in a base class but I didn't see one that handled the + * popup window */ + public void setFrameTitle(String s){ + frameTitle = s; + if(popupWindow != null) popupWindow.setTitle(frameTitle); + } + public String getFrameTitle(){ return frameTitle; } + + /** This method should ONLY be called by the AWT event thread */ + private void createPopup() { + int width = parentPlot.getCanvas().getWidth() / 2; + int height = parentPlot.getCanvas().getHeight() / 2; + DasCanvas canvas = new DasCanvas(width, height); + DasRow row = new DasRow(canvas, 0.1, 0.9); + DasColumn column = new DasColumn(canvas, 0.1, 0.9); + canvas.add(this, row, column); + + JPanel content = new JPanel(new BorderLayout()); + + JPanel buttonPanel = new JPanel(); + BoxLayout buttonLayout = new BoxLayout(buttonPanel, BoxLayout.X_AXIS); + JButton close = new JButton("Hide Window"); + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + popupWindow.setVisible(false); + } + }); + buttonPanel.setLayout(buttonLayout); + buttonPanel.add(Box.createHorizontalGlue()); + buttonPanel.add(close); + + content.add(canvas, BorderLayout.CENTER); + content.add(buttonPanel, BorderLayout.SOUTH); + + Window parentWindow = SwingUtilities.getWindowAncestor(parentPlot); + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } + else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } + else { + popupWindow = new JDialog(); + } + popupWindow.setTitle(frameTitle); + popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + popupWindow.setContentPane(content); + popupWindow.pack(); + + Point parentLocation = new Point(); + SwingUtilities.convertPointToScreen(parentLocation, parentPlot.getCanvas()); + popupWindow.setLocation(parentLocation.x + parentPlot.getCanvas().getWidth(),parentLocation.y); + } + + protected void drawContent(Graphics2D g) { + long x; + x= System.currentTimeMillis()-eventBirthMilli; + + int ix= (int)this.getXAxis().transform(yValue); + DasRow row= this.getRow(); + int iy0= (int)row.getDMinimum(); + int iy1= (int)row.getDMaximum(); + g.drawLine(ix+3,iy0,ix,iy0+3); + g.drawLine(ix-3,iy0,ix,iy0+3); + g.drawLine(ix+3,iy1,ix,iy1-3); + g.drawLine(ix-3,iy1,ix,iy1-3); + + g.setColor(yMarkColor); + g.drawLine( ix, iy0+4, ix, iy1-4 ); + + super.drawContent(g); + x= System.currentTimeMillis()-eventBirthMilli; + //org.das2.util.DasDie.println("event handled in "+x+" milliseconds"); + } + + protected boolean isPopupVisible() { + return ( popupWindow != null && popupWindow.isVisible()) && getCanvas() != null; + } + + public void dataPointSelected(DataPointSelectionEvent e) { + long xxx[]= { 0,0,0,0 }; + xxx[0] = System.currentTimeMillis()-e.birthMilli; + + DataSet ds = e.getDataSet(); + if (ds==null || !(ds instanceof TableDataSet)) + return; + + TableDataSet tds = (TableDataSet)ds; + + VectorDataSet sliceDataSet= tds.getXSlice( DataSetUtil.closestColumn( tds, e.getX() ) ); + + renderer.setDataSet(sliceDataSet); + DasLogger.getLogger(DasLogger.GUI_LOG).finest("setDataSet sliceDataSet"); + if (!isPopupVisible()) { + showPopup(); + } + + yValue= e.getY(); + Datum xValue = e.getX(); + + DatumFormatter formatter; + if ( xValue.getUnits() instanceof TimeLocationUnits ) { + formatter= TimeDatumFormatter.DEFAULT; + } else { + formatter= xValue.getFormatter(); + } + + setTitle("x: "+ formatter.format(xValue) + " y: "+yValue); + + eventBirthMilli= e.birthMilli; + } + + protected void uninstallComponent() { + super.uninstallComponent(); + } + + protected void installComponent() { + super.installComponent(); + getCanvas().getGlassPane().setVisible(false); + } + + protected void processDasUpdateEvent(org.das2.event.DasUpdateEvent e) { + if (isDisplayable()) { + updateImmediately(); + resize(); + } + } + + public Color getYMarkColor() { + return yMarkColor; + } + + public void setYMarkColor(Color yMarkColor) { + this.yMarkColor = yMarkColor; + } +} diff --git a/dasCore/src/main/java/org/das2/components/package.html b/dasCore/src/main/java/org/das2/components/package.html new file mode 100644 index 000000000..37e3d1e28 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/package.html @@ -0,0 +1,4 @@ + + Provides GUI components for building applications. Classes are generally +instances of Java Swing's JComponent. + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/BooleanEditor.java b/dasCore/src/main/java/org/das2/components/propertyeditor/BooleanEditor.java new file mode 100644 index 000000000..4eda4af37 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/BooleanEditor.java @@ -0,0 +1,188 @@ +/* + * BooleanEditor.java + * + * Created on April 14, 2005, 9:18 AM + */ + +package org.das2.components.propertyeditor; + +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.EventObject; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import javax.swing.DefaultButtonModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JCheckBox; +import javax.swing.JList; +import javax.swing.JTable; +import javax.swing.JToggleButton; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.EventListenerList; +import javax.swing.table.TableCellEditor; + +/** + * + * @author eew + */ +public class BooleanEditor implements java.beans.PropertyEditor, TableCellEditor { + + private JCheckBox editor; + private Model model; + private boolean selected; + private Class type; + private PropertyChangeSupport pcSupport; + private EventListenerList listeners = new EventListenerList(); + + /** Creates a new instance of BooleanEditor */ + public BooleanEditor() { + pcSupport = new PropertyChangeSupport(this); + } + + private void initEditor() { + if (editor == null) { + model = new Model(); + editor = new JCheckBox(); + editor.setModel(model); + } + } + + public String getAsText() { + return String.valueOf(selected); + } + + public Object getValue() { + return selected ? Boolean.TRUE : Boolean.FALSE; + } + + public void setAsText(String str) throws IllegalArgumentException { + Boolean value; + if ("true".equalsIgnoreCase(str)) { + value = Boolean.TRUE; + } + else if ("false".equalsIgnoreCase(str)) { + value = Boolean.FALSE; + } + else { + throw new IllegalArgumentException(str); + } + setValue(value); + } + + public void setValue(Object obj) { + Boolean oldValue; + Boolean value = (Boolean)obj; + + if (selected ^ value.booleanValue()) { + oldValue = selected ? Boolean.TRUE : Boolean.FALSE; + selected = value.booleanValue(); + pcSupport.firePropertyChange("value", oldValue, value); + } + } + + public boolean supportsCustomEditor() { + return true; + } + + public Component getCustomEditor() { + initEditor(); + return editor; + } + + public String getJavaInitializationString() { return "???"; } + + public String[] getTags() { return null; } + + public boolean isPaintable() { return false; } + + public void paintValue(Graphics g, Rectangle r) {} + + public void addPropertyChangeListener(PropertyChangeListener l) { + pcSupport.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + pcSupport.removePropertyChangeListener(l); + } + + /*TableCellEditor stuff*/ + + public Object getCellEditorValue() { + return selected ? Boolean.TRUE : Boolean.FALSE; + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + initEditor(); + editor.setForeground(table.getForeground()); + editor.setBackground(table.getBackground()); + setValue(value); + return editor; + } + + public boolean isCellEditable(EventObject evt) { return true; } + + public boolean shouldSelectCell(EventObject evt) { return true; } + + public boolean stopCellEditing() { + fireEditingStopped(); + return true; + } + + public void cancelCellEditing() { + fireEditingCanceled(); + } + + public void addCellEditorListener(CellEditorListener l) { + listeners.add(CellEditorListener.class, l); + } + + public void removeCellEditorListener(CellEditorListener l) { + listeners.add(CellEditorListener.class, l); + } + + private ChangeEvent evt; + + private void fireEditingStopped() { + Object[] l = listeners.getListenerList(); + for (int i = 0; i < l.length; i+=2) { + if (l[i] == CellEditorListener.class) { + CellEditorListener cel = (CellEditorListener)l[i+1]; + if (evt == null) { evt = new ChangeEvent(this); } + cel.editingStopped(evt); + } + } + } + + private void fireEditingCanceled() { + Object[] l = listeners.getListenerList(); + for (int i = 0; i < l.length; i+=2) { + if (l[i] == CellEditorListener.class) { + CellEditorListener cel = (CellEditorListener)l[i+1]; + if (evt == null) { evt = new ChangeEvent(this); } + cel.editingCanceled(evt); + } + } + } + + private class Model extends JToggleButton.ToggleButtonModel { + private Model() {} + public void setSelected(boolean b) { + setValue(b ? Boolean.TRUE : Boolean.FALSE); + fireEditingStopped(); + } + public boolean isSelected() { + return ((Boolean)getValue()).booleanValue(); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/ColorCellRenderer.java b/dasCore/src/main/java/org/das2/components/propertyeditor/ColorCellRenderer.java new file mode 100644 index 000000000..d9c7f049d --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/ColorCellRenderer.java @@ -0,0 +1,121 @@ +/* + * ColorCellRenderer.java + * + * Created on April 28, 2005, 4:39 PM + */ + +package org.das2.components.propertyeditor; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.util.HashMap; +import java.util.Map; +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JTable; +import javax.swing.ListCellRenderer; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.table.TableCellRenderer; + +/** + * + * @author eew + */ +class ColorCellRenderer implements ListCellRenderer, TableCellRenderer, Icon { + private static Map names = new HashMap(); + static { + names.put(Color.BLACK, "black"); + names.put(Color.WHITE, "white"); + names.put(Color.BLUE, "blue"); + names.put(Color.CYAN, "cyan"); + names.put(Color.DARK_GRAY, "dark gray"); + names.put(Color.GRAY, "gray"); + names.put(Color.GREEN, "green"); + names.put(Color.LIGHT_GRAY, "light gray"); + names.put(Color.MAGENTA, "magenta"); + names.put(Color.ORANGE, "orange"); + names.put(Color.PINK, "pink"); + names.put(Color.RED, "red"); + names.put(Color.YELLOW, "yellow"); + } + + private JLabel label; + private Border noFocusBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1); + private Color iconColor; + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowIndex, int columnIndex) { + Color f = isSelected ? table.getSelectionForeground() : table.getForeground(); + Color b = isSelected ? table.getSelectionBackground() : table.getBackground(); + return getLabel(table, f, b, value, isSelected, hasFocus); + } + + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) { + Color f = isSelected ? list.getSelectionForeground() : list.getForeground(); + Color b = isSelected ? list.getSelectionBackground() : list.getBackground(); + return getLabel(list, f, b, value, isSelected, hasFocus); + } + + public Component getLabel(JComponent c, Color f, Color b, Object value, boolean isSelected, boolean hasFocus) { + initLabel(); + label.setForeground(f); + label.setBackground(b); + label.setEnabled(c.isEnabled()); + label.setFont(c.getFont()); + label.setBorder(hasFocus ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder); + if (value instanceof Color) { + String name = (String)names.get(value); + if (name == null) { + if ( ((Color)value).getAlpha()==0 ) { + name="none"; + } else { + name = toString((Color)value); + } + } + label.setIcon(this); + label.setText(name); + iconColor = (Color)value; + } + else { + label.setIcon(null); + label.setText(String.valueOf(value)); + } + return label; + } + + private static String toString(Color c) { + return "[" + c.getRed() + "," + c.getGreen() + "," + c.getBlue() + "]"; + } + + private void initLabel() { + if (label == null) { + label = new JLabel(); + label.setOpaque(true); + label.setBorder(noFocusBorder); + } + } + + public int getIconHeight() { return 16; } + + public int getIconWidth() { return 16; } + + public void paintIcon(Component c, Graphics g, int x, int y) { + Color save = g.getColor(); + if ( iconColor.getAlpha()!=255 ) { + for ( int j=0; j<16/4; j++ ) { + for ( int i=0; i<16/4; i++ ) { + g.setColor( (i-j)%2 ==0 ? Color.GRAY : Color.WHITE ); + g.fillRect( x+i*4,y+j*4,4,4); + } + } + } + g.setColor(iconColor); + g.fillRect(x, y, getIconWidth(), getIconHeight()); + g.setColor(save); + } + +} diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/ColorEditor.java b/dasCore/src/main/java/org/das2/components/propertyeditor/ColorEditor.java new file mode 100644 index 000000000..dd94fc368 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/ColorEditor.java @@ -0,0 +1,200 @@ +/* + * ColorEditor.java + * + * Created on April 19, 2005, 2:52 PM + */ + +package org.das2.components.propertyeditor; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyEditorSupport; +import java.util.ArrayList; +import java.util.List; +import javax.swing.AbstractCellEditor; +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; +import javax.swing.JColorChooser; +import javax.swing.JComboBox; +import javax.swing.JTable; +import javax.swing.table.TableCellEditor; + +/** + * + * @author eew + */ +public class ColorEditor extends AbstractCellEditor implements java.beans.PropertyEditor, TableCellEditor { + + private static List colors = new ArrayList(); + static { + colors.add(Color.BLACK); + colors.add(Color.WHITE); + colors.add(Color.BLUE); + colors.add(Color.CYAN); + colors.add(Color.DARK_GRAY); + colors.add(Color.GRAY); + colors.add(Color.GREEN); + colors.add(Color.LIGHT_GRAY); + colors.add(Color.MAGENTA); + colors.add(Color.ORANGE); + colors.add(Color.PINK); + colors.add(Color.RED); + colors.add(Color.YELLOW); + colors.add( new Color(0,true) ); + } + + private JColorChooser custom; + private PropertyEditorSupport editorSupport; + private JComboBox choice; + + /** Creates a new instance of ColorEditor */ + public ColorEditor() { + editorSupport = new PropertyEditorSupport(this){}; + custom = new JColorChooser(); + choice = new JComboBox(new ColorChoiceModel()) { + public void setBounds(int x, int y, int width, int height) { + Dimension preferred = getPreferredSize(); + super.setBounds(x, y, width, preferred.height); + } + }; + choice.setRenderer(new ColorCellRenderer()); + choice.setBorder(null); + choice.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED + && choice.isDisplayable()) { + stopCellEditing(); + } + } + }); + custom.addPropertyChangeListener("color", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + setValue(e.getNewValue()); + } + }); + } + + public boolean supportsCustomEditor() { return true; } + + public String getAsText() { + int rgb= ((Color)editorSupport.getValue()).getRGB(); + String hex; + if ( rgb==0 ) { + hex= "#000000"; + } else { + hex= "#"+Integer.toHexString( rgb ).substring(2); + } + return hex; + } + + public Component getCustomEditor() { + Color c = (Color)getValue(); + custom.setColor(c); + return custom; + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + editorSupport.addPropertyChangeListener(l); + } + + public Object getCellEditorValue() { + return editorSupport.getValue(); + } + + public String getJavaInitializationString() { + return "???"; + } + + public String[] getTags() { + return null; + } + + public Object getValue() { + return editorSupport.getValue(); + } + + public boolean isPaintable() { + return false; + } + + public void paintValue(Graphics graphics, Rectangle rectangle) { + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + editorSupport.removePropertyChangeListener(l); + } + + public void setAsText(String str) throws IllegalArgumentException { + Color c= Color.decode(str); + setValue(c); + } + + public void setValue(Object obj) { + Object oldValue= this.editorSupport.getValue(); + editorSupport.setValue(obj); + if ( oldValue!=obj ) { + choice.setSelectedItem(obj); + choice.repaint(); + } + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean selected, int row, int column) { + setValue(value); + choice.setSelectedItem(value); + choice.setForeground(table.getForeground()); + choice.setBackground(table.getBackground()); + return choice; + } + + public Component getSmallEditor() { + choice.setSelectedItem(getValue()); + return choice; + } + + private class ColorChoiceModel extends AbstractListModel implements ComboBoxModel { + + private final String CUSTOM_LABEL = "custom..."; + + public Object getElementAt(int index) { + if (index < colors.size()) { + return colors.get(index); + } + else if (index == colors.size()) { + return CUSTOM_LABEL; + } + else { + throw new IndexOutOfBoundsException(Integer.toString(index)); + } + } + + public Object getSelectedItem() { + return getValue(); + } + + public int getSize() { + return colors.size() + 1; + } + + public void setSelectedItem(Object obj) { + if (obj instanceof Color) { + setValue(obj); + } + else if (CUSTOM_LABEL.equals(obj)) { + Color c = custom.showDialog(choice, "Color Editor", (Color)getValue()); + if (c != null) { + setValue(c); + } + } + else { + throw new IllegalArgumentException(String.valueOf(obj)); + } + } + } +} diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/Displayable.java b/dasCore/src/main/java/org/das2/components/propertyeditor/Displayable.java new file mode 100644 index 000000000..dc443d74f --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/Displayable.java @@ -0,0 +1,26 @@ +package org.das2.components.propertyeditor; + +import javax.swing.Icon; + +/** Type-safe enumerations that are used as property types + * that are editable with a PropertyEditor should + * implement this interface. + * + */ +public interface Displayable { + + /** return a String that will help the user + * identify this item when choosing from a list. + */ + String getListLabel(); + + /** An icon can be provided that will be shown in a list + * along with the textual description of the element. + * This method should return null if there + * is no icon available. + * + */ + Icon getListIcon(); + +} + diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/Editable.java b/dasCore/src/main/java/org/das2/components/propertyeditor/Editable.java new file mode 100644 index 000000000..d22dda6b1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/Editable.java @@ -0,0 +1,11 @@ +package org.das2.components.propertyeditor; + +/** Objects that are instances of classes that implement + * this interface can be expanded in a PropertyEditor + * property list. + * + */ +public interface Editable { + +} + diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/Enumeration.java b/dasCore/src/main/java/org/das2/components/propertyeditor/Enumeration.java new file mode 100644 index 000000000..163b99143 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/Enumeration.java @@ -0,0 +1,30 @@ +package org.das2.components.propertyeditor; + +import javax.swing.Icon; + +/** Type-safe enumerations that are used as property types + * that are editable with a PropertyEditor should + * implement this interface. + * + */ +public interface Enumeration { + + /** Type-safe Enumerations implementing this interface + * should override the toString() method to return a + * String that will be helpful to the user + * when choosing this as an option from a list. + * + */ + String toString(); + //TODO: getListLabel() better, because toString should be reserved for programmers. + + /** An icon can be provided that will be shown in a list + * along with the textual description of the element. + * This method should return null if there + * is no icon available. + * + */ + Icon getListIcon(); + +} + diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/EnumerationEditor.java b/dasCore/src/main/java/org/das2/components/propertyeditor/EnumerationEditor.java new file mode 100644 index 000000000..be9f7cd50 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/EnumerationEditor.java @@ -0,0 +1,338 @@ +/* + * EnumerationEditor.java + * + * Created on April 14, 2005, 9:18 AM + */ +package org.das2.components.propertyeditor; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EventObject; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.JTable; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.EventListenerList; +import javax.swing.table.TableCellEditor; + +/** + * + * @author eew + */ +public class EnumerationEditor implements java.beans.PropertyEditor, TableCellEditor { + + private JComboBox editor; + private Model model; + private Object selected; + private Class type; + private boolean guessType; + private PropertyChangeSupport pcSupport; + private EventListenerList listeners = new EventListenerList(); + private Map valueMap; + private Map nameMap; + private Map toStringMap; + + /** Creates a new instance of EnumerationEditor */ + public EnumerationEditor() { + guessType = true; + pcSupport = new PropertyChangeSupport(this); + } + + protected EnumerationEditor(Class c) { + setClass(c); + guessType = false; + } + + private void initEditor() { + if (editor == null) { + model = new Model(); + editor = new JComboBox(model) { + + public void setBounds(int x, int y, int width, int height) { + Dimension preferred = getPreferredSize(); + super.setBounds(x, y, width, preferred.height); + } + }; + editor.setRenderer(new Renderer()); + editor.setSelectedItem(selected); + } + } + private static final int PUBLIC_STATIC_FINAL = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; + + private void setClass(Class c) { + type = c; + Map valueM = new HashMap(); + Map nameM = new IdentityHashMap(); + Map toStringM = new HashMap(); + + if (c.isEnum()) { + Object[] vals = c.getEnumConstants(); + for (Object o : vals) { + Enum e = (Enum) o; + nameM.put(e.name(), e); + toStringM.put(e.toString(), e); + valueM.put(e, e.name()); + } + + } else { + + Field[] fields = type.getDeclaredFields(); + Set psf = new HashSet(); + for (int i = 0; i < fields.length; i++) { + int modifiers = fields[i].getModifiers(); + if ((modifiers & PUBLIC_STATIC_FINAL) == PUBLIC_STATIC_FINAL) { + psf.add(fields[i]); + } + } + for (Iterator i = psf.iterator(); i.hasNext();) { + try { + Field f = (Field) i.next(); + String name = f.getName(); + Object value = f.get(null); + nameM.put(name, value); + toStringM.put(value.toString(), value); + valueM.put(value, name); + } catch (IllegalAccessException iae) { + IllegalAccessError err = new IllegalAccessError(iae.getMessage()); + err.initCause(iae); + throw err; + } + } + } + nameMap = nameM; + valueMap = valueM; + toStringMap = toStringM; + } + + public String getAsText() { + return selected.toString(); + } + + public Object getValue() { + return selected; + } + + public void setAsText(String str) throws IllegalArgumentException { + Object oldValue; + Object value = nameMap.get(str); + if (value == null) { + value = toStringMap.get(str); + } + if (value == null) { + throw new IllegalArgumentException(str); + } + if (selected != value) { + oldValue = selected; + selected = value; + if ( editor!=null ) { + editor.setSelectedItem(selected); + editor.repaint(); + } + pcSupport.firePropertyChange("value", oldValue, selected); + } + } + + public void setValue(Object obj) { + Class c = getTypeClass(obj); + if (type != c) { + int size = 0; + if (model != null) { + size = model.getSize(); + } + setClass(c); + if (model != null) { + model.fireIntervalRemoved(model, 0, size - 1); + model.fireIntervalAdded(model, 0, model.getSize() - 1); + } + } + Object oldValue = selected; + selected = obj; + if (oldValue != obj) { + pcSupport.firePropertyChange("value", oldValue, selected); + if ( editor!=null ) { + editor.setSelectedItem(selected); + editor.repaint(); + } + } + } + + private Class getTypeClass(Object obj) { + Class c = obj.getClass(); + String name = c.getName(); + if (name.matches(".+?\\$\\d+")) { + c = c.getSuperclass(); + } + return c; + } + + public boolean supportsCustomEditor() { + return true; + } + + public Component getCustomEditor() { + initEditor(); + return editor; + } + + public String getJavaInitializationString() { + return "???"; + } + + public String[] getTags() { + return null; + } + + public boolean isPaintable() { + return false; + } + + public void paintValue(Graphics g, Rectangle r) { + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + pcSupport.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + pcSupport.removePropertyChangeListener(l); + } + + /*TableCellEditor stuff*/ + public Object getCellEditorValue() { + return selected; + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + initEditor(); + editor.setForeground(table.getForeground()); + editor.setBackground(table.getBackground()); + setValue(value); + return editor; + } + + public boolean isCellEditable(EventObject evt) { + return true; + } + + public boolean shouldSelectCell(EventObject evt) { + return true; + } + + public boolean stopCellEditing() { + fireEditingStopped(); + return true; + } + + public void cancelCellEditing() { + fireEditingCanceled(); + } + + public void addCellEditorListener(CellEditorListener l) { + listeners.add(CellEditorListener.class, l); + } + + public void removeCellEditorListener(CellEditorListener l) { + listeners.add(CellEditorListener.class, l); + } + private ChangeEvent evt; + + private void fireEditingStopped() { + Object[] l = listeners.getListenerList(); + for (int i = 0; i < l.length; i += 2) { + if (l[i] == CellEditorListener.class) { + CellEditorListener cel = (CellEditorListener) l[i + 1]; + if (evt == null) { + evt = new ChangeEvent(this); + } + cel.editingStopped(evt); + } + } + } + + private void fireEditingCanceled() { + Object[] l = listeners.getListenerList(); + for (int i = 0; i < l.length; i += 2) { + if (l[i] == CellEditorListener.class) { + CellEditorListener cel = (CellEditorListener) l[i + 1]; + if (evt == null) { + evt = new ChangeEvent(this); + } + cel.editingCanceled(evt); + } + } + } + + private class Renderer extends DefaultListCellRenderer { + + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) { + String s = (String) valueMap.get(value); + super.getListCellRendererComponent(list, s, index, isSelected, hasFocus); + if (value instanceof Enumeration) { + setText(value.toString()); + setIcon(((Enumeration) value).getListIcon()); + } + return this; + } + } + + private class Model extends AbstractListModel implements ComboBoxModel { + + private List list; + + private Model() { + list = new ArrayList(nameMap.keySet()); + Collections.sort(list, String.CASE_INSENSITIVE_ORDER); + } + + public Object getSelectedItem() { + return selected; + } + + public void setSelectedItem(Object o) { + setValue(o); + stopCellEditing(); + } + + public Object getElementAt(int index) { + return nameMap.get(list.get(index)); + } + + public int getSize() { + return list.size(); + } + + //TODO: remove? + protected void fireIntervalRemoved(Object source, int index0, int index1) { + super.fireIntervalRemoved(source, index0, index1); + } + + //TODO: remove? + protected void fireIntervalAdded(Object source, int index0, int index1) { + super.fireIntervalAdded(source, index0, index1); + } + + //TODO: remove? + protected void fireContentsChanged(Object source, int index0, int index1) { + super.fireContentsChanged(source, index0, index1); + } + } +} diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/FloatingPointDocumentFilter.java b/dasCore/src/main/java/org/das2/components/propertyeditor/FloatingPointDocumentFilter.java new file mode 100644 index 000000000..129452a01 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/FloatingPointDocumentFilter.java @@ -0,0 +1,70 @@ +package org.das2.components.propertyeditor; + +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.DocumentFilter; +import javax.swing.text.DocumentFilter.FilterBypass; + +class FloatingPointDocumentFilter extends DocumentFilter { + + public void insertString(FilterBypass fb, int offset, String text, AttributeSet atts) throws BadLocationException { + if (text.length() == 1) { + if (text.charAt(0) == '-') { + String content = fb.getDocument().getText(0, fb.getDocument().getLength()); + int eIndex = Math.max(content.indexOf('e'), content.indexOf('E')); + if (content.length() == 0) { + super.insertString(fb, 0, text, atts); + } + else if (eIndex < 0 || offset <= eIndex) { + if (content.charAt(0) == '-') { + super.remove(fb, 0, 1); + } + else { + super.insertString(fb, 0, text, atts); + } + } + else { + if (content.length() == eIndex+1) { + super.insertString(fb, eIndex+1, text, atts); + } + else if (content.charAt(eIndex+1) == '-') { + super.remove(fb, eIndex+1, 1); + } + else { + super.insertString(fb, eIndex+1, text, atts); + } + } + } + else if (text.charAt(0) == '.') { + String content = fb.getDocument().getText(0, fb.getDocument().getLength()); + int dotIndex = content.indexOf('.'); + if (offset <= dotIndex) { + super.replace(fb, offset, dotIndex-offset+1, text, atts); + } + else if (dotIndex < 0) { + super.insertString(fb, offset, text, atts); + } + } + else if (text.charAt(0) == 'e' || text.charAt(0) == 'E') { + String content = fb.getDocument().getText(0, fb.getDocument().getLength()); + int eIndex = Math.max(content.indexOf('e'), content.indexOf('E')); + if (offset <= eIndex) { + super.replace(fb, offset, eIndex-offset+1, text, atts); + } + else if (eIndex < 0) { + super.insertString(fb, offset, text, atts); + } + } + else if (Character.isDigit(text.charAt(0))) { + super.insertString(fb, offset, text, atts); + } + } + } + + public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet atts) throws BadLocationException { + remove(fb, offset, length); + insertString(fb, offset, text, atts); + } + +} + diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/FloatingPointFormatter.java b/dasCore/src/main/java/org/das2/components/propertyeditor/FloatingPointFormatter.java new file mode 100644 index 000000000..a2a8594c9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/FloatingPointFormatter.java @@ -0,0 +1,52 @@ +package org.das2.components.propertyeditor; + +import java.text.ParseException; +import javax.swing.JFormattedTextField; +import javax.swing.text.DocumentFilter; + +class FloatingPointFormatter extends JFormattedTextField.AbstractFormatter { + + /** Parses text returning an arbitrary Object. Some + * formatters may return null. + * + * @throws ParseException if there is an error in the conversion + * @param text String to convert + * @return Object representation of text + * + */ + public Object stringToValue(String text) throws ParseException { + try { + Double d = new Double(text); + if (d.doubleValue() == Double.NEGATIVE_INFINITY || + d.doubleValue() == Double.POSITIVE_INFINITY || + d.doubleValue() == Double.NaN) { + throw new ParseException("+/-infinity and NaN are not allowed", 0); + } + return d; + } + catch (NumberFormatException nfe) { + throw new ParseException(nfe.getMessage(), 0); + } + } + + /** Returns the string value to display for value. + * + * @throws ParseException if there is an error in the conversion + * @param value Value to convert + * @return String representation of value + * + */ + public String valueToString(Object value) throws ParseException { + if (value instanceof Number) { + double doubleValue = ((Number)value).doubleValue(); + return value.toString(); + } + else throw new ParseException("value must be of type Number", 0); + } + + protected DocumentFilter getDocumentFilter() { + return new FloatingPointDocumentFilter(); + } + +} + diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/IndexedPropertyItemTreeNode.java b/dasCore/src/main/java/org/das2/components/propertyeditor/IndexedPropertyItemTreeNode.java new file mode 100644 index 000000000..c7ec149ea --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/IndexedPropertyItemTreeNode.java @@ -0,0 +1,87 @@ +package org.das2.components.propertyeditor; +import org.das2.util.DasExceptionHandler; +import java.beans.IndexedPropertyDescriptor; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; + +class IndexedPropertyItemTreeNode extends PropertyTreeNode { + + private IndexedPropertyDescriptor indexedPropertyDescriptor; + + private int index; + + IndexedPropertyItemTreeNode(PropertyTreeNode parent, IndexedPropertyDescriptor indexedPropertyDescriptor, int index) { + super(Array.get(parent.value, index)); + setTreeModel(parent.treeModel); + this.index = index; + this.parent = parent; + this.propertyDescriptor = indexedPropertyDescriptor; + this.indexedPropertyDescriptor = indexedPropertyDescriptor; + } + + public boolean getAllowsChildren() { + return indexedPropertyDescriptor.getPropertyEditorClass() == null; + } + + public String getDisplayName() { + return propertyDescriptor.getName() + "[" + index + "]"; + } + + public void flush() { + try { + if (dirty) { + Method writeMethod = indexedPropertyDescriptor.getIndexedWriteMethod(); + writeMethod.invoke(parent.parent.value, new Object[]{new Integer(index), value} ); + dirty = false; + } + if (childDirty) { + for (Iterator i = children.iterator(); i.hasNext(); ) { + PropertyTreeNode child = (PropertyTreeNode)i.next(); + child.flush(); + } + childDirty = false; + } + } catch (IllegalAccessException iae) { + throw new RuntimeException(iae); + } catch ( InvocationTargetException e ) { + DasExceptionHandler.handle(e); + } + } + + protected Object read() { + Object[] parentValue= (Object[])parent.read(); + return parentValue[this.index]; + } + + public void refresh( ) { + Object newValue = read(); + boolean foldMe= false; + if ( newValue!=value ) { + boolean allowsChildren= getAllowsChildren(); + if ( allowsChildren ) { + foldMe= true; + } + } + if (newValue != value && newValue != null && !newValue.equals(value)) { + value = newValue; + } + if ( foldMe ) { + children= null; + treeModel.nodeStructureChanged( this ); + } else { + if (getAllowsChildren()) { + if (children != null) { + for (Iterator i = children.iterator(); i.hasNext();) { + PropertyTreeNode child = (PropertyTreeNode)i.next(); + child.refresh( ); + } + } + } + } + + } + +} + diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/IndexedPropertyTreeNode.java b/dasCore/src/main/java/org/das2/components/propertyeditor/IndexedPropertyTreeNode.java new file mode 100644 index 000000000..c32f9330a --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/IndexedPropertyTreeNode.java @@ -0,0 +1,109 @@ +package org.das2.components.propertyeditor; +import java.beans.IndexedPropertyDescriptor; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.MutableTreeNode; + +class IndexedPropertyTreeNode extends PropertyTreeNode { + + private IndexedPropertyDescriptor indexedPropertyDescriptor; + + IndexedPropertyTreeNode(PropertyTreeNode parent, IndexedPropertyDescriptor indexedPropertyDescriptor) throws InvocationTargetException { + super(parent, indexedPropertyDescriptor ); + this.indexedPropertyDescriptor = indexedPropertyDescriptor; + } + + public boolean getAllowsChildren() { + return true; + } + + protected void maybeLoadChildren() { + if (children == null) { + children = new ArrayList(); + int childCount = Array.getLength(value); + for (int i = 0; i < childCount; i++) { + children.add(new IndexedPropertyItemTreeNode(this, indexedPropertyDescriptor, i)); + } + } + } + + public String getDisplayName() { + return propertyDescriptor.getName() + "[]"; + } + + @Override + public Object getDisplayValue() { + return ""+ getChildCount() + " element" + ( getChildCount()!=1 ? "s" : "" ); + } + + + public void flush() { + if (childDirty) { + for (Iterator i = children.iterator(); i.hasNext(); ) { + PropertyTreeNode child = (PropertyTreeNode)i.next(); + child.flush(); + } + childDirty = false; + } + } + + protected Object read() { + if (propertyDescriptor == null) { + return value; + } else { + Method readMethod = propertyDescriptor.getReadMethod(); + if (readMethod == null) { + String pName = propertyDescriptor.getName(); + String pId = value.getClass().getName() + "#" + pName; + throw new IllegalStateException( + "Null read method for: " + pId); + } + try { + return readMethod.invoke(parent.value); + } catch (IllegalAccessException iae) { + Error err = new IllegalAccessError(iae.getMessage()); + err.initCause(iae); + throw err; + } catch (InvocationTargetException ite) { + Throwable cause = ite.getCause(); + if (cause instanceof Error) { + throw (Error)cause; + } else if (cause instanceof RuntimeException) { + throw (RuntimeException)cause; + } else { + throw new RuntimeException(cause); + } + } + } + } + public boolean isCellEditable(int column) { + return false; + } + + public void refresh( ) { + Object newValue = read(); + + value= newValue; + if ( children!=null ) { + if ( children.size()<((Object[])newValue).length ) { + children=null; + treeModel.nodeStructureChanged(this); + } else if ( children.size()>((Object[])newValue).length ) { + children= null; + treeModel.nodeStructureChanged(this); + } + } + if (children != null) { + for (Iterator i = children.iterator(); i.hasNext();) { + PropertyTreeNode child = (PropertyTreeNode)i.next(); + child.refresh( ); + } + } + + } +} + diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/MapEditor.java b/dasCore/src/main/java/org/das2/components/propertyeditor/MapEditor.java new file mode 100644 index 000000000..15bc183d5 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/MapEditor.java @@ -0,0 +1,73 @@ +/* + * MapEditor.java + * + * Created on September 27, 2005, 1:37 PM + * + * + */ + +package org.das2.components.propertyeditor; + +import java.util.Iterator; +import java.util.Map; +import javax.swing.JTextArea; + +/** + * + * @author Jeremy + */ +public class MapEditor implements java.beans.PropertyEditor { + Map value; + + public void addPropertyChangeListener(java.beans.PropertyChangeListener propertyChangeListener) { + } + + public String getAsText() { + return "lookup"; + } + + public java.awt.Component getCustomEditor() { + JTextArea result= new JTextArea(20,6); + for ( Iterator i= value.keySet().iterator(); i.hasNext(); ) { + Object key= i.next(); + Object value= i.next(); + result.append(""+key+"=\t"+value+"\n"); + } + return result; + } + + public String getJavaInitializationString() { + return "lookup"; + } + + public String[] getTags() { + return null; + } + + public Object getValue() { + return value; + } + + public boolean isPaintable() { + return false; + } + + public void paintValue(java.awt.Graphics graphics, java.awt.Rectangle rectangle) { + + } + + public void removePropertyChangeListener(java.beans.PropertyChangeListener propertyChangeListener) { + } + + public void setAsText(String str) throws IllegalArgumentException { + } + + public void setValue(Object obj) { + this.value= (Map)obj; + } + + public boolean supportsCustomEditor() { + return true; + } + +} diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/PeerPropertyTreeNode.java b/dasCore/src/main/java/org/das2/components/propertyeditor/PeerPropertyTreeNode.java new file mode 100644 index 000000000..998a1d3ed --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/PeerPropertyTreeNode.java @@ -0,0 +1,167 @@ +/* + * PeerPropertyTreeNode.java + * + * Created on December 20, 2005, 11:15 AM + * + * + */ + +package org.das2.components.propertyeditor; + +import java.beans.PropertyDescriptor; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; + +/** + * + * @author Jeremy + */ +public class PeerPropertyTreeNode implements PropertyTreeNodeInterface { + PeerPropertyTreeNode parent; + PropertyTreeNode leader; + PropertyTreeNode[] peers; + + public PeerPropertyTreeNode( PeerPropertyTreeNode parent, PropertyTreeNode leader, PropertyTreeNode[] peers ) { + this.parent= parent; + this.leader= leader; + this.peers= peers; + } + + public java.util.Enumeration children() { + return new java.util.Enumeration() { + int index=0; + public boolean hasMoreElements() { + return index= 10000.0) { + value = expFormat.format(doubleValue); + } + } + if ( value instanceof Displayable ) { + setText( ((Displayable)value).getListLabel() ); + } else { + setText(String.valueOf(value)); + } + setOpaque(true); + if (table != null) { + setBackground(isSelected ? table.getSelectionBackground() : table.getBackground()); + } + if (value instanceof Displayable) { + Displayable enm = (Displayable)value; + setIcon(enm.getListIcon()); + setDisabledIcon(enm.getListIcon()); + } else { + setIcon(null); + setDisabledIcon(null); + } + c = this; + + } + + c.setEnabled(writable); + return c; + } + + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean isExpanded, boolean isLeaf, int row, boolean hasFocus) { + setText(String.valueOf(value)); + setIcon(null); + setOpaque(false); + setEnabled(true); + return this; + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + /** Return a component that has been configured to display the specified + * value. That component's paint method is then called to + * "render" the cell. If it is necessary to compute the dimensions + * of a list because the list cells do not have a fixed size, this method + * is called to generate a component on which getPreferredSize + * can be invoked. + * + * @param list The JList we're painting. + * @param value The value returned by list.getModel().getElementAt(index). + * @param index The cells index. + * @param isSelected True if the specified cell was selected. + * @param cellHasFocus True if the specified cell has the focus. + * @return A component whose paint() method will render the specified value. + * + * @see JList + * @see ListSelectionModel + * @see ListModel + * + */ + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + if (value instanceof Displayable ) { + Displayable enm = (Displayable)value; + setIcon(enm.getListIcon()); + setText(enm.getListLabel()); + } else { + setIcon(null); + setText(String.valueOf(value)); + } + setOpaque(true); + setBackground(isSelected ? Color.gray : Color.lightGray); + return this; + } + +} + diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/PropertyEditor.java b/dasCore/src/main/java/org/das2/components/propertyeditor/PropertyEditor.java new file mode 100644 index 000000000..400a31291 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/PropertyEditor.java @@ -0,0 +1,493 @@ +/* File: PropertyEditor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.components.propertyeditor; + +import org.das2.components.treetable.TreeTableCellRenderer; +import org.das2.components.treetable.TreeTableModel; +import org.das2.dasml.SerializeUtil; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.graph.DasCanvas; +import org.das2.system.DasLogger; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.StringWriter; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Logger; +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.tree.DefaultTreeModel; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +//import org.apache.xml.serialize.OutputFormat; +//import org.apache.xml.serialize.XMLSerializer; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * This class implements a Hierarchical property editor + * + * @author Edward West + */ +public class PropertyEditor extends JComponent { + + static final Set editableTypes; + + + static { + HashSet set = new HashSet(); + + //Primitives + set.add(byte.class); + set.add(short.class); + set.add(int.class); + set.add(long.class); + set.add(float.class); + set.add(double.class); + set.add(boolean.class); + + //Object types + set.add(String.class); + set.add(Datum.class); + set.add(DatumRange.class); + set.add(Color.class); + //set.add(PsymConnector.class); + + editableTypes = Collections.unmodifiableSet(set); + } + private JTable table; + private JButton closeButton; + private JDialog dialog; + private Object bean; + /* row of the last mouse click. This object is the fellow who is edited when + * applying properties to a group + */ + private int focusRow = 0; + private JPopupMenu popupMenu; + private Logger logger = DasLogger.getLogger(DasLogger.GUI_LOG); + + private PropertyEditor(PropertyTreeNodeInterface root, Object bean) { + this.bean = bean; + setLayout(new BorderLayout()); + this.bean = bean; + + DefaultTreeModel treeModel = new DefaultTreeModel(root, true); + root.setTreeModel(treeModel); + TreeTableCellRenderer tree = new TreeTableCellRenderer(treeModel); + tree.setRootVisible(false); + tree.setShowsRootHandles(true); + TreeTableModel model = new TreeTableModel(root, tree); + table = new JTable(model); + table.setAutoCreateColumnsFromModel(false); + + add(new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER); + + initButtonPanel(bean instanceof DasCanvas); + initPopupMenu(); + + PropertyCellRenderer valueRenderer = new PropertyCellRenderer(); + //PropertyCellEditor editor = new PropertyCellEditor(tree); + PropertyEditorAdapter editor = new PropertyEditorAdapter(); + int cellHeight = 21; // c.getPreferredSize().height; + + table.setRowHeight(cellHeight); + tree.setRowHeight(cellHeight); + tree.setCellRenderer(valueRenderer); + table.getColumnModel().getColumn(0).setCellRenderer(tree); + table.getColumnModel().getColumn(1).setCellRenderer(valueRenderer); + table.getColumnModel().getColumn(0).setMaxWidth(250); + table.getColumnModel().getColumn(0).setPreferredWidth(150); + table.setDefaultEditor(Object.class, editor); + table.addMouseListener(new PropertyTableMouseListener()); + table.setSurrendersFocusOnKeystroke(true); + table.addKeyListener(getKeyListener()); + addActions(table); + table.getSelectionModel().addListSelectionListener(getListSelectionListener()); + } + + public PropertyEditor(Object bean) { + this(new PropertyTreeNode(bean), bean); + if (bean instanceof PropertyTreeNodeInterface) { + throw new IllegalArgumentException("whoops!"); + } + } + + public static PropertyEditor createPeersEditor(Object leader, Object[] peers) { + PropertyTreeNode[] peerNodes = new PropertyTreeNode[peers.length]; + for (int i = 0; i < peers.length; i++) { + peerNodes[i] = new PropertyTreeNode(peers[i]); + } + PeerPropertyTreeNode root = new PeerPropertyTreeNode(null, + new PropertyTreeNode(leader), + peerNodes); + return new PropertyEditor(root, null); + } + + private void addActions(final JTable table) { + table.getActionMap().put("MY_EDIT", new AbstractAction() { + + public void actionPerformed(ActionEvent e) { + table.editCellAt(focusRow, 1); + } + }); + table.getInputMap().put( + KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), + "MY_EDIT"); + } + + private ListSelectionListener getListSelectionListener() { + return new ListSelectionListener() { + + public void valueChanged(ListSelectionEvent e) { + focusRow = table.getSelectedRow(); // we could do a better job here + + logger.fine("focusRow=" + focusRow); + } + }; + } + + private KeyListener getKeyListener() { + KeyAdapter ka; + return new KeyAdapter() { + + public void keyReleased(KeyEvent event) { + logger.fine(String.valueOf(event)); + if (event.getKeyCode() == KeyEvent.VK_RIGHT) { + TreeTableModel model = (TreeTableModel) table.getModel(); + model.expand(focusRow); + } else if (event.getKeyCode() == KeyEvent.VK_LEFT) { + TreeTableModel model = (TreeTableModel) table.getModel(); + model.collapse(focusRow); + } + } + }; + } + + private Action createSaveAction(final Object bean) { + return new AbstractAction("Save") { + + public void actionPerformed(ActionEvent ev) { + try { + JFileChooser chooser = new JFileChooser(); + chooser.setFileFilter(new javax.swing.filechooser.FileFilter() { + + public boolean accept(File f) { + return f.toString().matches(".*\\.das2PropertySheet"); + } + + public String getDescription() { + return "*.das2PropertySheet"; + } + }); + chooser.setSelectedFile(new File("default.das2PropertySheet")); + int result = chooser.showSaveDialog(PropertyEditor.this); + if (result == JFileChooser.APPROVE_OPTION) { + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + Element element = SerializeUtil.getDOMElement(document, bean); + document.appendChild(element); + OutputStream out = new FileOutputStream(chooser.getSelectedFile()); + + StringWriter writer = new StringWriter(); + DOMImplementation impl = document.getImplementation(); + DOMImplementationLS ls = (DOMImplementationLS)impl.getFeature("LS", "3.0"); + LSSerializer serializer = ls.createLSSerializer(); + LSOutput output = ls.createLSOutput(); + output.setEncoding("UTF-8"); + output.setByteStream(out); + serializer.write(document, output); + + //OutputFormat format = new OutputFormat(org.apache.xml.serialize.Method.XML, "UTF-8", true); + //XMLSerializer serializer = new XMLSerializer( new OutputStreamWriter(out), format); + //serializer.serialize(document); + out.close(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + } + + private static Document readDocument(File file) throws IOException, ParserConfigurationException, SAXException { + InputStream in = new FileInputStream(file); + InputSource source = new InputSource(); + source.setCharacterStream(new InputStreamReader(in)); + DocumentBuilder builder; + ErrorHandler eh = null; + DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); + builder = domFactory.newDocumentBuilder(); + builder.setErrorHandler(eh); + Document document = builder.parse(source); + return document; + } + + private Action createLoadAction(final Object bean) { + return new AbstractAction("Load") { + + public void actionPerformed(ActionEvent ev) { + try { + JFileChooser chooser = new JFileChooser(); + chooser.setFileFilter(new javax.swing.filechooser.FileFilter() { + + public boolean accept(File f) { + return f.toString().matches(".*\\.das2PropertySheet"); + } + + public String getDescription() { + return "*.das2PropertySheet"; + } + }); + int result = chooser.showOpenDialog(PropertyEditor.this); + if (result == JFileChooser.APPROVE_OPTION) { + try { + Document document = readDocument(chooser.getSelectedFile()); + Element element = document.getDocumentElement(); + SerializeUtil.processElement(element, bean); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } catch (SAXException e) { + throw new RuntimeException(e); + } + + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + } + + private Action getEditSelectedAction() { + return new AbstractAction("Edit Selected") { + + public void actionPerformed(ActionEvent e) { + PropertyEditor p; + TreeTableModel model = (TreeTableModel) table.getModel(); + PropertyTreeNodeInterface node = (PropertyTreeNodeInterface) model.getNodeForRow(focusRow); + int[] selected = table.getSelectedRows(); + if (selected.length == 1) { + p = new PropertyEditor(node.getValue()); + } else { + Object[] peers = new Object[selected.length]; + for (int i = 0; i < selected.length; i++) { + peers[i] = ((PropertyTreeNode) model.getNodeForRow(selected[i])).getValue(); + } + p = createPeersEditor(node.getValue(), peers); + } + p.showDialog(PropertyEditor.this); + + } + }; + } + + private void initPopupMenu() { + popupMenu = new JPopupMenu(); + popupMenu.add(getEditSelectedAction()); + } + + private void initButtonPanel(boolean saveLoadButton) { + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + if (saveLoadButton) { + JButton saveButton = new JButton(createSaveAction(this.bean)); + buttonPanel.add(saveButton); + JButton loadButton = new JButton(createLoadAction(this.bean)); + buttonPanel.add(loadButton); + } + final JButton apply = new JButton("Apply Changes"); + closeButton = new JButton("Dismiss"); + + ActionListener al = new ActionListener() { + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == apply) { + globalApplyChanges(); + refresh(); + } else if (e.getSource() == closeButton) { + dismissDialog(); + } + } + }; + apply.addActionListener(al); + closeButton.addActionListener(al); + + JButton refresh = new JButton("Refresh"); + refresh.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + refresh(); + } + }); + + buttonPanel.add(refresh); + buttonPanel.add(Box.createHorizontalGlue()); + buttonPanel.add(apply); + buttonPanel.add(closeButton); + add(buttonPanel, BorderLayout.SOUTH); + } + + private void refresh() { + TreeTableModel model = (TreeTableModel) table.getModel(); + PropertyTreeNodeInterface root = (PropertyTreeNodeInterface) model.getRoot(); + root.refresh(); + model.fireTableDataChanged(); + } + + private void globalApplyChanges() { + TreeTableModel model = (TreeTableModel) table.getModel(); + PropertyTreeNodeInterface root = (PropertyTreeNodeInterface) model.getRoot(); + root.flush(); + } + + private void dismissDialog() { + PropertyTreeNodeInterface root = (PropertyTreeNodeInterface) ((TreeTableModel) table.getModel()).getRoot(); + if (root.isDirty()) { + String[] message = new String[]{ + "You have unsaved changes", + "Would you like to apply them?" + }; + int result = JOptionPane.showConfirmDialog(this, message, "", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); + if (result == JOptionPane.CANCEL_OPTION) { + return; + } + if (result == JOptionPane.YES_OPTION) { + globalApplyChanges(); + } + } + dialog.setVisible(false); + dialog.dispose(); + } + + public void showModalDialog(Component c) { + Container top = (c == null ? null : SwingUtilities.getAncestorOfClass(Window.class, c)); + if (top instanceof JFrame) { + dialog = new JDialog((JFrame) top); + } else if (top instanceof JDialog) { + dialog = new JDialog((JDialog) top); + } else { + dialog = new JDialog(); + } + dialog.setModal(true); + dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + dialog.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + dismissDialog(); + } + }); + dialog.setContentPane(this); + dialog.pack(); + if (c != null) { + dialog.setLocationRelativeTo(c); + } + dialog.setVisible(true); + } + + public void showDialog(Component c) { + if (dialog == null) { + Container top = (c == null ? null : SwingUtilities.getAncestorOfClass(Window.class, c)); + if (top instanceof JFrame) { + dialog = new JDialog((JFrame) top); + } else if (top instanceof JDialog) { + dialog = new JDialog((JDialog) top); + } else { + dialog = new JDialog(); + } + + dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + dialog.addWindowListener(new WindowAdapter() { + + public void windowClosing(WindowEvent e) { + dismissDialog(); + } + }); + dialog.setContentPane(this); + dialog.pack(); + } + if (c != null) { + dialog.setLocationRelativeTo(c); + } + dialog.setVisible(true); + } + + public void doLayout() { + if (SwingUtilities.isDescendingFrom(this, dialog)) { + closeButton.setVisible(true); + } else { + closeButton.setVisible(false); + } + super.doLayout(); + } + + class PropertyTableMouseListener extends MouseAdapter { + + public void mouseClicked(MouseEvent event) { + Point p = event.getPoint(); + int row = table.rowAtPoint(p); + int column = table.columnAtPoint(p); + TreeTableModel model = (TreeTableModel) table.getModel(); + PropertyTreeNodeInterface node = (PropertyTreeNodeInterface) model.getNodeForRow(row); + + focusRow = row; + int modifiers = event.getModifiers() & (MouseEvent.SHIFT_MASK | MouseEvent.CTRL_MASK); + + if (event.getButton() == MouseEvent.BUTTON1 && modifiers == 0 && !node.isLeaf()) { + model.toggleExpanded(row); + } + } + + public void mousePressed(MouseEvent event) { + Point p = event.getPoint(); + focusRow = table.rowAtPoint(p); + if (event.getButton() == MouseEvent.BUTTON3) { + popupMenu.show(PropertyEditor.this.table, event.getX(), event.getY()); + } + } + } +} diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/PropertyEditorAdapter.java b/dasCore/src/main/java/org/das2/components/propertyeditor/PropertyEditorAdapter.java new file mode 100644 index 000000000..f1c20215b --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/PropertyEditorAdapter.java @@ -0,0 +1,262 @@ +/* + * PropertyEditorAdapter.java + * + * Created on April 14, 2005, 2:53 PM + */ + +package org.das2.components.propertyeditor; + +import org.das2.beans.BeansUtil; +import org.das2.components.treetable.TreeTableModel; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.IndexedPropertyDescriptor; +import java.beans.PropertyDescriptor; +import java.beans.PropertyEditor; +import java.util.EventObject; +import javax.swing.JButton; +import javax.swing.JOptionPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.EventListenerList; +import javax.swing.table.TableCellEditor; + +/** + * + * @author eew + */ +public class PropertyEditorAdapter implements TableCellEditor { + + private EventListenerList listenerList = new EventListenerList(); + private PropertyEditor editor; + private EditorState state; + + /* Possible states */ + private EditorState simple = new SimpleEditor(); + private EditorState custom = new CustomEditor(); + private EditorState cellEditor = new CustomTableCellEditor(); + + /** Creates a new instance of PropertyEditorAdapter */ + public PropertyEditorAdapter() { + } + + public void addCellEditorListener(CellEditorListener l) { + listenerList.add(CellEditorListener.class, l); + } + + public void cancelCellEditing() { + if (state != null) state.cancel(); + state = null; + fireEditingCanceled(); + } + + public Object getCellEditorValue() { + return editor.getValue(); + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean selected, int rowIndex, int columnIndex) { + TreeTableModel model = (TreeTableModel)table.getModel(); + PropertyTreeNodeInterface node = (PropertyTreeNodeInterface)model.getNodeForRow(rowIndex); + PropertyDescriptor pd = node.getPropertyDescriptor(); + editor = getEditor(pd); + if (editor == null) { + cancelCellEditing(); + } + else { + editor.setValue(value); + } + + if (editor instanceof TableCellEditor) { + state = cellEditor; + } + else if (editor.supportsCustomEditor()) { + state = custom; + } + else { + state = simple; + } + return state.getEditorComponent(table, selected, rowIndex, columnIndex); + } + + private static PropertyEditor getEditor(PropertyDescriptor pd) { + PropertyEditor ed; + if (pd.getPropertyEditorClass() != null) { + try { + Object instance = pd.getPropertyEditorClass().newInstance(); + if (instance instanceof PropertyEditor) { + ed = (PropertyEditor)instance; + } + else { + ed = null; + } + } + catch (InstantiationException ie) { + ed = null; + } + catch (IllegalAccessException iae) { + ed = null; + } + } + else { + ed = BeansUtil.findEditor(pd instanceof IndexedPropertyDescriptor + ? ((IndexedPropertyDescriptor)pd).getIndexedPropertyType() + : pd.getPropertyType()); + } + return ed; + } + + public boolean isCellEditable(EventObject eventObject) { + return true; + } + + public void removeCellEditorListener(CellEditorListener l) { + listenerList.remove(CellEditorListener.class, l); + } + + public boolean shouldSelectCell(EventObject eventObject) { + return true; + } + + public boolean stopCellEditing() { + boolean stopped = state.stop(); + if (stopped) { + fireEditingStopped(); + state = null; + } + return stopped; + } + + private void fireEditingCanceled() { + Class clazz = CellEditorListener.class; + ChangeEvent e = null; + Object[] listeners = listenerList.getListenerList(); + for (int i = 0; i < listeners.length; i+=2) { + if (listeners[i] == clazz) { + CellEditorListener l = (CellEditorListener)listeners[i+1]; + if (e == null) { e = new ChangeEvent(this); } + l.editingCanceled(e); + } + } + } + + private void fireEditingStopped() { + Class clazz = CellEditorListener.class; + ChangeEvent e = null; + Object[] listeners = listenerList.getListenerList(); + for (int i = 0; i < listeners.length; i+=2) { + if (listeners[i] == clazz) { + CellEditorListener l = (CellEditorListener)listeners[i+1]; + if (e == null) { e = new ChangeEvent(this); } + l.editingStopped(e); + } + } + } + + private static interface EditorState { + void cancel(); + boolean stop(); + Component getEditorComponent(JTable table, boolean selected, int rowIndex, int columnIndex); + } + + private class SimpleEditor implements EditorState, ActionListener { + private JTextField textField; + public void cancel() {} + public boolean stop() { + String text = textField.getText(); + try { + editor.setAsText(text); + return true; + } + catch (IllegalArgumentException iae) { + return false; + } + } + public Component getEditorComponent(JTable table, boolean selected, int rowIndex, int columnIndex) { + initTextField(); + textField.setText(editor.getAsText()); + return textField; + } + public void actionPerformed(ActionEvent e) { + stopCellEditing(); + } + private void initTextField() { + if (textField == null) { + textField = new JTextField(); + textField.setBorder(null); + textField.addActionListener(this); + } + } + } + + private class CustomEditor implements EditorState, ActionListener { + private JButton button; + + public boolean stop() { + return true; + } + + public void cancel() { + } + + public Component getEditorComponent(JTable table, boolean selected, int rowIndex, int columnIndex) { + init(); + String s = editor.getAsText(); + if (s == null) { + s = String.valueOf(editor.getValue()); + } + button.setText(s); + return button; + } + + public void actionPerformed(ActionEvent e) { + Component customEditor = editor.getCustomEditor(); + int result = JOptionPane.showConfirmDialog(button, customEditor, "", JOptionPane.OK_CANCEL_OPTION); + if (result == JOptionPane.CLOSED_OPTION || result == JOptionPane.CANCEL_OPTION) { + cancelCellEditing(); + } + else { + stopCellEditing(); + } + } + + private void init() { + if (button == null) { + button = new JButton(); + button.setBorder(null); + button.addActionListener(this); + } + } + } + + private class CustomTableCellEditor implements EditorState, CellEditorListener { + public Component getEditorComponent(JTable table, boolean selected, int rowIndex, int columnIndex) { + TableCellEditor tce = (TableCellEditor)editor; + Component c = tce.getTableCellEditorComponent(table, editor.getValue(), selected, rowIndex, columnIndex); + tce.addCellEditorListener(this); + return c; + } + public boolean stop() { + TableCellEditor tce = (TableCellEditor)editor; + return tce.stopCellEditing(); + } + public void cancel() { + TableCellEditor tce = (TableCellEditor)editor; + tce.cancelCellEditing(); + } + + public void editingStopped(ChangeEvent e) { + TableCellEditor tce = (TableCellEditor)editor; + tce.removeCellEditorListener(this); + PropertyEditorAdapter.this.fireEditingStopped(); + } + + public void editingCanceled(ChangeEvent e) { + TableCellEditor tce = (TableCellEditor)editor; + tce.removeCellEditorListener(this); + PropertyEditorAdapter.this.fireEditingCanceled(); + } + } +} diff --git a/dasCore/src/main/java/org/das2/components/propertyeditor/PropertyTreeNode.java b/dasCore/src/main/java/org/das2/components/propertyeditor/PropertyTreeNode.java new file mode 100644 index 000000000..589ccd869 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/propertyeditor/PropertyTreeNode.java @@ -0,0 +1,361 @@ +package org.das2.components.propertyeditor; + +import org.das2.beans.BeansUtil; +import org.das2.system.DasLogger; +import org.das2.util.DasExceptionHandler; +import java.beans.*; +import java.beans.IndexedPropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.Enumeration; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; + +class PropertyTreeNode implements PropertyTreeNodeInterface { + + protected static final Object[] NULL_ARGS = new Object[0]; + + protected List children; + + protected PropertyTreeNode parent; + + protected PropertyDescriptor propertyDescriptor; + + protected DefaultTreeModel treeModel; + + protected Object value; + + protected boolean dirty; + + protected boolean childDirty; + + PropertyTreeNode( Object value ) { + this.value = value; + } + + /** + * Used to put the tree model into the root tree node so that it can be + * passed down into the tree. + */ + public void setTreeModel( DefaultTreeModel treeModel ) { + if ( this.treeModel!=null ) throw new IllegalArgumentException("Improper use, see documentation"); + this.treeModel= treeModel; + } + + PropertyTreeNode(PropertyTreeNode parent, PropertyDescriptor propertyDescriptor ) throws InvocationTargetException { + this.parent = parent; + this.propertyDescriptor = propertyDescriptor; + this.treeModel= parent.treeModel; + if ( treeModel==null ) { + throw new IllegalArgumentException("null treeModel in parent"); + } + try { + if ( propertyDescriptor.getReadMethod()==null ) { + throw new RuntimeException("read method not defined for "+propertyDescriptor.getName()); + } + value = propertyDescriptor.getReadMethod().invoke(parent.value, NULL_ARGS); + } catch (IllegalAccessException iae) { + throw new RuntimeException(iae); + } + } + + public Enumeration children() { + maybeLoadChildren(); + return Collections.enumeration(children); + } + + public boolean getAllowsChildren() { + + //No propertyDescriptor indicates the root node. + if (propertyDescriptor == null) { + return true; + } + + //Properties that define an editor should not be expanded + else if (propertyDescriptor.getPropertyEditorClass() != null) { + return false; + } + else { + Class type; + if (propertyDescriptor instanceof IndexedPropertyDescriptor) { + IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor)propertyDescriptor; + type = ipd.getIndexedPropertyType(); + } else { + type = propertyDescriptor.getPropertyType(); + } + + //Types with identified as editable by PropertyEditor and + //types with registered PropertyEditors should not be expanded. + return !PropertyEditor.editableTypes.contains(type) + && BeansUtil.findEditor(type) == null; + } + } + + public TreeNode getChildAt(int childIndex) { + maybeLoadChildren(); + return (TreeNode)children.get(childIndex); + } + + public int getChildCount() { + maybeLoadChildren(); + return children.size(); + } + + public int getIndex(TreeNode node) { + maybeLoadChildren(); + return children.indexOf(node); + } + + public TreeNode getParent() { + return parent; + } + + public boolean isLeaf() { + if ( value==null ) return true; + maybeLoadChildren(); + return children.isEmpty(); + } + + public PropertyDescriptor getPropertyDescriptor() { + return propertyDescriptor; + } + + + protected void maybeLoadChildren() { + if (children == null) { + ArrayList children = new ArrayList(); + if (getAllowsChildren()) { + try { + PropertyDescriptor[] properties= BeansUtil.getPropertyDescriptors(value.getClass()); + String[] propertyNameList= BeansUtil.getPropertyNames( properties ); + if ( propertyNameList==null ) { + propertyNameList= new String[ properties.length ]; + for ( int i=0; i + Classes implementing the property sheet. The property sheet is a table of bean properties organized hierarchically. If a bean has +another bean as a property, its properties may be accessed or hidden by folding and unfolding the property row (see treetable package). When an editor +for the property is found, the property may be edited directly. + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/components/treetable/TreeTableCellRenderer.java b/dasCore/src/main/java/org/das2/components/treetable/TreeTableCellRenderer.java new file mode 100644 index 000000000..5584dfe1d --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/treetable/TreeTableCellRenderer.java @@ -0,0 +1,40 @@ +package org.das2.components.treetable; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.table.TableCellRenderer; +import javax.swing.tree.TreeModel; + +public class TreeTableCellRenderer extends JTree implements TableCellRenderer { + + private JTable table; + + private int visibleRow; + + public TreeTableCellRenderer(TreeModel model) { + super(model); + } + + public void setBounds(int x, int y, int w, int h) { + super.setBounds(x, 0, w, table.getRowHeight()*table.getRowCount()); + } + + public void paint(Graphics g) { + g.translate(0, -visibleRow * getHeight()/getRowCount()); + super.paint(g); + } + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + this.table = table; + this.visibleRow = row; + if (table != null) { + setBackground(isSelected ? table.getSelectionBackground() : table.getBackground()); + } + return this; + } + +} + diff --git a/dasCore/src/main/java/org/das2/components/treetable/TreeTableModel.java b/dasCore/src/main/java/org/das2/components/treetable/TreeTableModel.java new file mode 100644 index 000000000..c7f941935 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/treetable/TreeTableModel.java @@ -0,0 +1,156 @@ +package org.das2.components.treetable; + +import javax.swing.JTree; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableModel; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +public class TreeTableModel extends AbstractTableModel implements TableModel { + + private TreeTableNode root; + + private JTree tree; + + public TreeTableModel(TreeTableNode root, JTree tree) { + this.root = root; + this.tree = tree; + tree.addTreeExpansionListener(new TreeTableTreeListener()); + TreeModelListener treeModelListener= new TreeTableTreeModelListener(); + tree.getModel().addTreeModelListener(new TreeTableTreeModelListener()); + } + + public Class getColumnClass(int columnIndex) { + return root.getColumnClass(columnIndex); + } + + public int getColumnCount() { + return root.getColumnCount(); + } + + public String getColumnName(int columnIndex) { + return root.getColumnName(columnIndex); + } + + public int getRowCount() { + return tree.getRowCount(); + } + + public Object getValueAt(int rowIndex, int columnIndex) { + return getNodeForRow(rowIndex).getValueAt(columnIndex); + } + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return getNodeForRow(rowIndex).isCellEditable(columnIndex); + } + + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + getNodeForRow(rowIndex).setValueAt(aValue, columnIndex); + } + + public void toggleExpanded(int rowIndex) { + if (tree.isExpanded(rowIndex)) { + tree.collapseRow(rowIndex); + } + else { + tree.expandRow(rowIndex); + } + } + + public void expand( int rowIndex ) { + if ( tree.isCollapsed(rowIndex) ) tree.expandRow(rowIndex); + } + + public void collapse( int rowIndex ) { + if ( tree.isExpanded(rowIndex) ) tree.collapseRow(rowIndex); + } + + public TreeTableNode getNodeForRow(int rowIndex) { + TreePath path = tree.getPathForRow(rowIndex); + return (TreeTableNode)path.getLastPathComponent(); + } + + public TreeTableNode getRoot() { + return root; + } + + public void setRoot(TreeTableNode node) { + if (node == null) { + throw new NullPointerException("null root node not allowed"); + } + tree.setModel(new DefaultTreeModel(node, true)); + } + + private class TreeTableTreeModelListener implements TreeModelListener { + + public void treeNodesChanged(TreeModelEvent e) { + TreePath path = new TreePath(e.getPath()); + int row = tree.getRowForPath(path); + TreeTableNode node = (TreeTableNode)path.getLastPathComponent(); + int count = node.getChildCount(); + if (row != -1 && tree.isExpanded(row)) { + TreeTableModel.this.fireTableRowsUpdated(row + 1, row + count); + } + } + + public void treeNodesInserted(TreeModelEvent e) { + TreePath path = e.getTreePath(); + int row = tree.getRowForPath(path); + if (row != -1 && tree.isExpanded(row)) { + int[] indices = e.getChildIndices(); + java.util.Arrays.sort(indices); + for (int i = 0; i < indices.length; i++) { + TreeTableModel.this.fireTableRowsInserted(indices[i], indices[i]); + } + } + } + + public void treeNodesRemoved(TreeModelEvent e) { + TreePath path = e.getTreePath(); + int row = tree.getRowForPath(path); + if (row != -1 && tree.isExpanded(row)) { + int[] indices = e.getChildIndices(); + java.util.Arrays.sort(indices); + for (int i = indices.length - 1; i >= 0; i++) { + TreeTableModel.this.fireTableRowsDeleted(indices[i], indices[i]); + } + } + } + + public void treeStructureChanged(TreeModelEvent e) { + TreeTableModel.this.fireTableStructureChanged(); + } + + } + + private class TreeTableTreeListener implements TreeExpansionListener { + + public void treeCollapsed(TreeExpansionEvent event) { + TreePath path = event.getPath(); + int row = tree.getRowForPath(path); + TreeTableNode node = (TreeTableNode)path.getLastPathComponent(); + int count = node.getChildCount(); + if (count != 0) { + TreeTableModel.this.fireTableRowsDeleted(row + 1, row + count); + } + } + + public void treeExpanded(TreeExpansionEvent event) { + TreePath path = event.getPath(); + int row = tree.getRowForPath(path); + TreeTableNode node = (TreeTableNode)path.getLastPathComponent(); + int count = node.getChildCount(); + if (count != 0) { + TreeTableModel.this.fireTableRowsInserted(row + 1, row + count); + } + } + + } + +} + diff --git a/dasCore/src/main/java/org/das2/components/treetable/TreeTableNode.java b/dasCore/src/main/java/org/das2/components/treetable/TreeTableNode.java new file mode 100644 index 000000000..c40231429 --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/treetable/TreeTableNode.java @@ -0,0 +1,40 @@ +/* File: TreeTableNode.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on January 28, 2004, 10:18 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components.treetable; + +import javax.swing.tree.TreeNode; + +/** + * + * @author eew + */ +public interface TreeTableNode extends TreeNode { + + boolean isCellEditable(int columnIndex); + Object getValueAt(int columnIndex); + void setValueAt(Object value, int columnIndex); + Class getColumnClass(int columnIndex); + int getColumnCount(); + String getColumnName(int columnIndex); +} diff --git a/dasCore/src/main/java/org/das2/components/treetable/package.html b/dasCore/src/main/java/org/das2/components/treetable/package.html new file mode 100644 index 000000000..ba06b7f5f --- /dev/null +++ b/dasCore/src/main/java/org/das2/components/treetable/package.html @@ -0,0 +1,4 @@ + + Contains classes implementing the TreeTable. A tree table is a Table with rows that may be folded or collapsed to expose row + children. + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/cvs_status.txt b/dasCore/src/main/java/org/das2/cvs_status.txt new file mode 100644 index 000000000..54ed33ec9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/cvs_status.txt @@ -0,0 +1,50 @@ +File: CancelledOperationException.java Status: Locally Modified +File: DasApplication.java Status: Locally Modified +File: DasException.java Status: Locally Modified +File: DasIOException.java Status: Locally Modified +File: DasNameException.java Status: Locally Modified +File: DasProperties.java Status: Locally Modified +File: DasPropertyException.java Status: Locally Modified +File: NameContext.java Status: Locally Modified +File: cvs_status.txt Status: Unknown +File: AbstractDataSet.java Status: Up-to-date +File: AbstractTableDataSet.java Status: Up-to-date +File: AbstractVectorDataSet.java Status: Up-to-date +File: AccessDeniedException.java Status: Up-to-date +File: AveragePeakTableRebinner.java Status: Up-to-date +File: AverageTableRebinner.java Status: Needs Merge +File: CacheTag.java Status: Up-to-date +File: ClippedTableDataSet.java Status: Locally Modified +File: ConstantDataSetDescriptor.java Status: Up-to-date +File: DataRequestThread.java Status: Up-to-date +File: DataRequestor.java Status: Up-to-date +File: DataSet.java Status: Up-to-date +File: DataSetCache.java Status: Up-to-date +File: DataSetConsumer.java Status: Up-to-date +File: DataSetDescriptor.java Status: Locally Modified +File: DataSetRebinner.java Status: Up-to-date +File: DataSetUpdateEvent.java Status: Up-to-date +File: DataSetUpdateListener.java Status: Up-to-date +File: DataSetUtil.java Status: Locally Modified +File: DefaultTableDataSet.java Status: Locally Modified +File: DefaultVectorDataSet.java Status: Needs Merge +File: GapListDouble.java Status: Up-to-date +File: NearestNeighborTableDataSet.java Status: Up-to-date +File: NearestNeighborTableRebinner.java Status: Up-to-date +File: NoDataInIntervalException.java Status: Up-to-date +File: NoKeyProvidedException.java Status: Up-to-date +File: PeakTableRebinner.java Status: Up-to-date +File: RebinDescriptor.java Status: Up-to-date +File: SyncUtil.java Status: Up-to-date +File: TableDataSet.java Status: Up-to-date +File: TableDataSetBuilder.java Status: Up-to-date +File: TableDataSetConsumer.java Status: Up-to-date +File: TableUtil.java Status: Locally Modified +File: VectorDataSet.java Status: Up-to-date +File: VectorDataSetBuilder.java Status: Needs Patch +File: VectorUtil.java Status: Up-to-date +File: ViewDataSet.java Status: Up-to-date +File: WritableTableDataSet.java Status: Up-to-date +File: XSliceDataSet.java Status: Up-to-date +File: YSliceDataSet.java Status: Up-to-date +File: scratchPad.txt Status: Locally Modified diff --git a/dasCore/src/main/java/org/das2/dasml/CommandAction.java b/dasCore/src/main/java/org/das2/dasml/CommandAction.java new file mode 100644 index 000000000..33bdb9a73 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/CommandAction.java @@ -0,0 +1,56 @@ +/* File: CommandAction.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.dasml; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +class CommandAction implements ActionListener { + + private CommandBlock commandBlock; + + public CommandAction(CommandBlock commandBlock) { + this.commandBlock = commandBlock; + } + + public void actionPerformed(ActionEvent e) { + try { + FormComponent x = (FormComponent)e.getSource(); + commandBlock.execute(x.getForm()); + } + catch (org.das2.DasException de) { + org.das2.util.DasExceptionHandler.handle(de); + } + catch (DataFormatException dfe) { + org.das2.util.DasExceptionHandler.handle(dfe); + } + catch (ParsedExpressionException pee) { + org.das2.util.DasExceptionHandler.handle(pee); + } + catch (java.lang.reflect.InvocationTargetException ite) { + org.das2.util.DasExceptionHandler.handle(ite.getCause()); + } + } + +} + diff --git a/dasCore/src/main/java/org/das2/dasml/CommandBlock.java b/dasCore/src/main/java/org/das2/dasml/CommandBlock.java new file mode 100644 index 000000000..bc14c9625 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/CommandBlock.java @@ -0,0 +1,392 @@ +/* File: CommandBlock.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.dasml; + +import org.das2.DasPropertyException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.regex.Matcher; + +public class CommandBlock { + + private static final int NONE = 0; + + private static final int SET = 1; + + private static final int UPDATE = 2; + + private static final int IF = 4; + + private static final int ELSEIF = 8; + + private static final int INVOKE = 16; + + private static final int ALERT = 32; + + ArrayList commandList; + + CommandBlock() { + commandList = new ArrayList(); + } + + CommandBlock(Element element, FormBase form) { + + commandList = new ArrayList(); + NodeList children = element.getChildNodes(); + int childCount = children.getLength(); + + for (int index = 0; index < childCount; index++) { + Node node = children.item(index); + if (node instanceof Element) { + String tagName = node.getNodeName(); + if (tagName.equals("set")) { + addCommand(processSetElement(form, (Element)node)); + } + else if (tagName.equals("if")) { + addCommand(processIfElement(form, (Element)node)); + } + else if (tagName.equals("elseif")) { + addCommand(processElseifElement(form, (Element)node)); + } + else if (tagName.equals("else")) { + addCommand(processElseElement(form, (Element)node)); + } + else if (tagName.equals("invoke")) { + addCommand(processInvokeElement(form, (Element)node)); + } + } + } + } + + private Command processSetElement(FormBase form, Element element) { + String property = element.getAttribute("property"); + String value = element.getAttribute("value"); + + if (!org.das2.NameContext.QUALIFIED_NAME.matcher(property).matches()) { + throw new IllegalArgumentException("property attribute must be a valid identifier: 0) { + String argsString = java.util.Arrays.asList(args).toString(); + argsString = argsString.substring(0, argsString.length() - 1); + element.setAttribute("args", argsString); + } + return element; + } + + public void setParent(CommandBlock parent) { + this.parent = parent; + } + + public CommandBlock getParent() { + return parent; + } + + public String toString() { + return "INVOKE " + target + (args == null ? "[]" : Arrays.asList(args).toString()); + } + + } + + static class IfCommand extends BlockCommand { + String test; + boolean shouldSkip; + + public IfCommand(String test) { + super(); + this.test = test; + } + + public IfCommand(String test, Element element, FormBase form) { + super(element, form); + this.test = test; + } + + public boolean getShouldSkip() { + return shouldSkip; + } + + public void execute(FormBase form)throws org.das2.DasException, DataFormatException, ParsedExpressionException, InvocationTargetException { + Matcher refMatcher = org.das2.NameContext.refPattern.matcher(test); + Object value; + if (refMatcher.matches()) { + value = form.getDasApplication().getNameContext().get(refMatcher.group(1)); + } + else { + value = form.getDasApplication().getNameContext().parseValue(test, boolean.class); + } + Boolean bool; + if (value instanceof Boolean) { + bool = (Boolean)value; + } + else { + throw new DataFormatException(value + " is not a boolean"); + } + if (bool.booleanValue()) { + super.execute(form); + } + shouldSkip = bool.booleanValue(); + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("if"); + element.setAttribute("test", test); + appendDOMElements(element); + return element; + } + + public String toString() { + return "IF " + test; + } + + } + + static class ElseIfCommand extends IfCommand { + ElseIfCommand(String test) { + super(test); + } + + ElseIfCommand(String test, Element element, FormBase form) { + super(test, element, form); + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("elseif"); + element.setAttribute("test", test); + appendDOMElements(element); + return element; + } + + public String toString() { + return "ELSEIF " + test; + } + } + + static class ElseCommand extends IfCommand { + ElseCommand() { + super("true"); + } + + ElseCommand(Element element, FormBase form) { + super("true", element, form); + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("else"); + appendDOMElements(element); + return element; + } + + public String toString() { + return "ELSE"; + } + } + + static interface Command { + CommandBlock getParent(); + void setParent(CommandBlock parent); + void execute(FormBase form)throws org.das2.DasException, DataFormatException, ParsedExpressionException, InvocationTargetException; + Element getDOMElement(Document document); + } + + static abstract class BlockCommand extends CommandBlock implements Command { + CommandBlock parent; + public BlockCommand() { + super(); + } + public BlockCommand(Element element, FormBase form) { + super(element, form); + } + public void setParent(CommandBlock parent) { + this.parent = parent; + } + public CommandBlock getParent() { + return parent; + } + } + + static class Identifier { + + public String text; + + public Identifier(String t) { text = t; } + + public String toString() { + return text; + } + + } + +} + diff --git a/dasCore/src/main/java/org/das2/dasml/CommandBlockEditor.java b/dasCore/src/main/java/org/das2/dasml/CommandBlockEditor.java new file mode 100644 index 000000000..ce1191665 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/CommandBlockEditor.java @@ -0,0 +1,955 @@ +/* File: CommandBlockEditor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.*; +import javax.swing.table.TableCellEditor; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.*; +import java.util.List; + +/** + * + * @author eew + */ +public class CommandBlockEditor extends JButton implements TableCellEditor, java.beans.PropertyEditor { + + EventListenerList listenerList = new EventListenerList(); + + JDialog dialog; + + JPanel contentPanel; + + JCheckBox enable; + + JTree commandTree; + + CommandBlockTreeModel commandBlockTreeModel; + + MultiPurposeListener listener; + + JButton commitChanges; + + JButton cancelEdit; + + JPanel editorPanel; + + CardLayout switcher; + + JTextField x1Field; + + JLabel x1FieldLabel; + + JButton x1Commit, x1Cancel; + + JTextField x2Field1, x2Field2; + + JLabel x2Field1Label, x2Field2Label; + + JButton x2Commit, x2Cancel; + + JButton newCommand; + + JButton editCommand; + + JButton removeCommand; + + JButton moveUpCommand; + + JButton moveDownCommand; + + /** Creates a new instance of CommandBlockEditor */ + public CommandBlockEditor() { + super("edit"); + + listener = new MultiPurposeListener(); + addActionListener(listener); + + commandBlockTreeModel = new CommandBlockTreeModel(new CommandBlock()); + commandTree = new JTree(commandBlockTreeModel); + commandTree.setRootVisible(true); + commandTree.setShowsRootHandles(false); + commandTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + commandTree.setVisibleRowCount(10); + commandTree.setCellRenderer(new CommandRenderer()); + + contentPanel = new JPanel(new BorderLayout()); + + JPanel centerPanel = new JPanel(new BorderLayout()); + + centerPanel.add(new JScrollPane(commandTree, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS), BorderLayout.CENTER); + centerPanel.add(editorPanel = initEditorPanels(), BorderLayout.SOUTH); + centerPanel.add(initButtonPanel(), BorderLayout.EAST); + + contentPanel.add(centerPanel, BorderLayout.CENTER); + + enable = new JCheckBox("Enabled", false); + setTopEnabled(false); + enable.addActionListener(listener); + contentPanel.add(enable, BorderLayout.NORTH); + + JPanel dialogButtons = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + commitChanges = new JButton("Commit Changes"); + commitChanges.addActionListener(listener); + cancelEdit = new JButton("Cancel Changes"); + cancelEdit.addActionListener(listener); + dialogButtons.add(commitChanges); + dialogButtons.add(cancelEdit); + + contentPanel.add(dialogButtons, BorderLayout.SOUTH); + } + + private JPanel initButtonPanel() { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + newCommand = new JButton("new"); + editCommand = new JButton("edit"); + removeCommand = new JButton("remove"); + moveUpCommand = new JButton("move up"); + moveDownCommand = new JButton("move down"); + Dimension d = moveDownCommand.getPreferredSize(); + newCommand.setPreferredSize(d); + newCommand.setMaximumSize(d); + editCommand.setPreferredSize(d); + editCommand.setMaximumSize(d); + removeCommand.setPreferredSize(d); + removeCommand.setMaximumSize(d); + moveUpCommand.setPreferredSize(d); + moveUpCommand.setMaximumSize(d); + newCommand.addActionListener(listener); + editCommand.addActionListener(listener); + removeCommand.addActionListener(listener); + moveUpCommand.addActionListener(listener); + moveDownCommand.addActionListener(listener); + panel.add(newCommand); + panel.add(editCommand); + panel.add(removeCommand); + panel.add(moveUpCommand); + panel.add(moveDownCommand); + return panel; + } + + void addCommand(CommandBlock.Command c) { + TreePath selection = commandTree.getSelectionPath(); + CommandBlock parent; + int index; + if (selection == null) { + parent = commandBlock; + index = parent.commandList.size(); + } + else if (selection.getLastPathComponent() instanceof CommandBlock) { + parent = (CommandBlock)selection.getLastPathComponent(); + index = parent.commandList.size(); + } + else { + CommandBlock.Command item = (CommandBlock.Command)selection.getLastPathComponent(); + parent = item.getParent(); + index = parent.indexOf(item) + 1; + } + parent.insertCommand(c, index); + Object[] path = commandBlockTreeModel.getPathToNode(parent); + commandBlockTreeModel.fireTreeNodeInserted(path, index, c); + } + + void removeCommands(TreePath[] selection) { + HashMap map = new HashMap(); + for (int i = 0; i < selection.length; i++) { + if (selection[i].getPathCount() == 1) { + continue; + } + TreePath parent = selection[i].getParentPath(); + Object child = selection[i].getLastPathComponent(); + List l = (List)map.get(parent); + if (l == null) { + l = new ArrayList(); + map.put(parent, l); + } + l.add(child); + } + for (Iterator i = map.entrySet().iterator(); i.hasNext();) { + Map.Entry entry = (Map.Entry)i.next(); + TreePath parent = (TreePath)entry.getKey(); + List list = (List)entry.getValue(); + CommandBlock.Command[] children = new CommandBlock.Command[list.size()]; + list.toArray(children); + int[] indices = new int[children.length]; + for (int index = 0; index < children.length; index++) { + indices[index] = children[index].getParent().indexOf(children[index]); + children[index].getParent().removeCommand(children[index]); + } + commandBlockTreeModel.fireTreeNodesRemoved(parent, indices, children); + } + } + + private JPanel initEditorPanels() { + switcher = new CardLayout(); + JPanel panel = new JPanel(switcher); + panel.setBorder(new CompoundBorder(new CompoundBorder(new EmptyBorder(2, 2, 2, 2), new EtchedBorder()), new EmptyBorder(2, 2, 2, 2))); + + JPanel emptyPanel = new JPanel(); + panel.add(emptyPanel, "EMPTY"); + + JPanel singlePanel = new JPanel(); + singlePanel.setLayout(new BoxLayout(singlePanel, BoxLayout.Y_AXIS)); + x1Field = new JTextField(30); + x1Field.setMaximumSize(x1Field.getPreferredSize()); + x1Field.setAlignmentX(JComponent.LEFT_ALIGNMENT); + x1FieldLabel = new JLabel("field", JLabel.LEFT); + x1FieldLabel.setAlignmentX(JComponent.LEFT_ALIGNMENT); + singlePanel.add(x1FieldLabel); + singlePanel.add(x1Field); + JPanel x1ButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + x1ButtonPanel.setAlignmentX(JComponent.LEFT_ALIGNMENT); + x1Commit = new JButton("Commit Changes"); + x1Commit.addActionListener(listener); + x1Cancel = new JButton("Cancel Changes"); + x1Cancel.addActionListener(listener); + x1ButtonPanel.add(x1Commit); + x1ButtonPanel.add(x1Cancel); + singlePanel.add(Box.createVerticalGlue()); + singlePanel.add(x1ButtonPanel); + panel.add(singlePanel, "SINGLE"); + + JPanel doublePanel = new JPanel(); + doublePanel.setLayout(new BoxLayout(doublePanel, BoxLayout.Y_AXIS)); + x2Field1 = new JTextField(30); + x2Field1.setMaximumSize(x2Field1.getPreferredSize()); + x2Field1.setAlignmentX(JComponent.LEFT_ALIGNMENT); + x2Field1Label = new JLabel("field1"); + x2Field1Label.setAlignmentX(JComponent.LEFT_ALIGNMENT); + x2Field2 = new JTextField(30); + x2Field2.setMaximumSize(x2Field2.getPreferredSize()); + x2Field2.setAlignmentX(JComponent.LEFT_ALIGNMENT); + x2Field2Label = new JLabel("field2"); + x2Field2Label.setAlignmentX(JComponent.LEFT_ALIGNMENT); + doublePanel.add(x2Field1Label); + doublePanel.add(x2Field1); + doublePanel.add(x2Field2Label); + doublePanel.add(x2Field2); + JPanel x2ButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + x2ButtonPanel.setAlignmentX(JComponent.LEFT_ALIGNMENT); + x2Commit = new JButton("Commit Changes"); + x2Commit.addActionListener(listener); + x2Cancel = new JButton("Cancel Changes"); + x2Cancel.addActionListener(listener); + x2ButtonPanel.add(x2Commit); + x2ButtonPanel.add(x2Cancel); + doublePanel.add(Box.createVerticalGlue()); + doublePanel.add(x2ButtonPanel); + panel.add(doublePanel, "DOUBLE"); + + //switcher.show(panel, "EMPTY"); + return panel; + } + + public void showDialog() { + if (dialog == null) { + Window w = SwingUtilities.windowForComponent(this); + if (w instanceof Frame) { + dialog = new JDialog((Frame)w, true); + } + else if (w instanceof Dialog) { + dialog = new JDialog((Dialog)w, true); + } + else { + dialog = new JDialog(); + dialog.setModal(true); + } + dialog.setContentPane(contentPanel); + dialog.pack(); + dialog.setResizable(false); + dialog.addWindowListener(listener); + } + dialog.setVisible(true); + } + + CommandBlock commandBlock; + + public CommandBlock getCommandBlock() { + if (enable.isSelected()) { + return commandBlock; + } + return null; + } + + public void setCommandBlock(CommandBlock commandBlock) { + if (commandBlock == null) { + commandBlock = new CommandBlock(); + enable.setSelected(false); + setTopEnabled(false); + } + else { + enable.setSelected(true); + setTopEnabled(true); + } + this.commandBlock = commandBlock; + commandBlockTreeModel.root = commandBlock; + commandBlockTreeModel.fireTreeChanged(); + } + + public boolean stopCellEditing() { + fireEditingStopped(); + dialog.setVisible(false); + return true; + } + + public void cancelCellEditing() { + dialog.setVisible(false); + fireEditingCanceled(); + } + + private class MultiPurposeListener extends WindowAdapter implements ActionListener { + final JPopupMenu newCommandMenu = new JPopupMenu("new command"); + { + newCommandMenu.add("SET").addActionListener(this); + newCommandMenu.add("INVOKE").addActionListener(this); + newCommandMenu.add("IF").addActionListener(this); + newCommandMenu.add("ELSEIF").addActionListener(this); + newCommandMenu.add("ELSE").addActionListener(this); + } + public void actionPerformed(ActionEvent e) { + Object source = e.getSource(); + String command = e.getActionCommand(); + if (source == CommandBlockEditor.this) { + showDialog(); + } + else if (source == commitChanges) { + stopCellEditing(); + } + else if (source == cancelEdit) { + cancelCellEditing(); + } + else if (source == enable) { + switcher.show(editorPanel, "EMPTY"); + setTopEnabled(enable.isSelected()); + } + else if (source == newCommand) { + newCommandMenu.show(newCommand, newCommand.getWidth(), 0); + } + else if (source == editCommand) { + TreePath selection = commandTree.getSelectionPath(); + if (selection != null && selection.getPathCount() != 1) { + Object o = selection.getLastPathComponent(); + if (o instanceof CommandBlock.SetCommand) { + CommandBlock.SetCommand c = (CommandBlock.SetCommand)o; + x2Field1Label.setText("Property"); + x2Field1.setText(c.id); + x2Field2Label.setText("Value"); + x2Field2.setText(c.value); + switcher.show(editorPanel, "DOUBLE"); + setEditing(true); + } + else if (o instanceof CommandBlock.InvokeCommand) { + CommandBlock.InvokeCommand c = (CommandBlock.InvokeCommand)o; + x2Field1Label.setText("Method"); + x2Field1.setText(c.target); + x2Field2Label.setText("Arguments (comma separated)"); + if (c.args != null) { + String args = Arrays.asList(c.args).toString(); + x2Field2.setText(args.substring(1, args.length() - 1)); + } + else { + x2Field2.setText(""); + } + switcher.show(editorPanel, "DOUBLE"); + setEditing(true); + } + else if (o instanceof CommandBlock.ElseCommand) { + return; + } + else if (o instanceof CommandBlock.IfCommand) { + CommandBlock.IfCommand c = (CommandBlock.IfCommand)o; + x1FieldLabel.setText("Test"); + x1Field.setText(c.test); + switcher.show(editorPanel, "SINGLE"); + setEditing(true); + } + } + } + else if (source == removeCommand) { + TreePath[] selection = commandTree.getSelectionPaths(); + if (selection != null) { + removeCommands(selection); + } + } + else if (source == moveUpCommand) { + TreePath selection = commandTree.getSelectionPath(); + if (selection == null || selection.getPathCount() == 1) { + return; + } + CommandBlock.Command c = (CommandBlock.Command)selection.getLastPathComponent(); + CommandBlock parent = c.getParent(); + int index = parent.indexOf(c); + if (index == 0) { + return; + } + else { + removeCommands(new TreePath[]{selection}); + parent.insertCommand(c, index - 1); + commandBlockTreeModel.fireTreeNodeInserted(selection.getParentPath().getPath(), index - 1, c); + } + } + else if (source == moveDownCommand) { + TreePath selection = commandTree.getSelectionPath(); + if (selection == null || selection.getPathCount() == 1) { + return; + } + CommandBlock.Command c = (CommandBlock.Command)selection.getLastPathComponent(); + CommandBlock parent = c.getParent(); + int index = parent.indexOf(c); + if (index == parent.commandList.size()-1) { + return; + } + else { + removeCommands(new TreePath[]{selection}); + parent.insertCommand(c, index + 1); + commandBlockTreeModel.fireTreeNodeInserted(selection.getParentPath().getPath(), index + 1, c); + } + } + else if (source == x1Commit) { + TreePath selection = commandTree.getSelectionPath(); + Object node = selection.getLastPathComponent(); + CommandBlock.IfCommand c = (CommandBlock.IfCommand)node; + c.test = x1Field.getText(); + setEditing(false); + } + else if (source == x2Commit) { + TreePath selection = commandTree.getSelectionPath(); + Object node = selection.getLastPathComponent(); + if (node instanceof CommandBlock.SetCommand) { + CommandBlock.SetCommand c = (CommandBlock.SetCommand)node; + c.id = x2Field1.getText(); + c.value = x2Field2.getText(); + setEditing(false); + } + else { + CommandBlock.InvokeCommand c = (CommandBlock.InvokeCommand)node; + c.target = x2Field1.getText(); + c.args = x2Field2.getText().split("\\s*,\\s*"); + setEditing(false); + } + } + else if (source == x1Cancel || source == x2Cancel) { + setEditing(false); + } + else if (command.equals("SET")) { + addCommand(new CommandBlock.SetCommand("property", "value")); + } + else if (command.equals("INVOKE")) { + addCommand(new CommandBlock.InvokeCommand("object.method", null)); + } + else if (command.equals("IF")) { + addCommand(new CommandBlock.IfCommand("test")); + } + else if (command.equals("ELSEIF")) { + addCommand(new CommandBlock.ElseIfCommand("test")); + } + else if (command.equals("ELSE")) { + addCommand(new CommandBlock.ElseCommand()); + } + } + + public void windowClosing(WindowEvent e) { + cancelCellEditing(); + } + private void setEditing(boolean b) { + b = !b; + if (b) { + switcher.show(editorPanel, "EMPTY"); + } + setTopEnabled(b); + enable.setEnabled(b); + TreePath selection = commandTree.getSelectionPath(); + CommandBlock.Command c = (CommandBlock.Command)selection.getLastPathComponent(); + int index = c.getParent().indexOf(c); + commandBlockTreeModel.fireTreeNodesChanged(selection.getParentPath().getPath(), index, c); + } + } + + private void setTopEnabled(boolean b) { + commandTree.setEnabled(b); + newCommand.setEnabled(b); + editCommand.setEnabled(b); + removeCommand.setEnabled(b); + moveUpCommand.setEnabled(b); + moveDownCommand.setEnabled(b); + } + + public static void main(String[] args) { + + CommandBlockEditor editor = new CommandBlockEditor(); + + CommandBlock block = new CommandBlock(); + block.addCommand(new CommandBlock.SetCommand("property", "value")); + block.addCommand(new CommandBlock.InvokeCommand("fred", new String[] {"arg1", "arg2"})); + CommandBlock.IfCommand ifc = new CommandBlock.IfCommand("test"); + ifc.addCommand(new CommandBlock.SetCommand("fred", "larry")); + block.addCommand(ifc); + editor.setCommandBlock(block); + + JFrame frame = new JFrame(); + frame.setContentPane(editor); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.pack(); + frame.setVisible(true); + } + + public void addCellEditorListener(CellEditorListener l) { + listenerList.add(CellEditorListener.class, l); + } + + public Object getCellEditorValue() { + return getCommandBlock(); + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + //this.table = table; + setCommandBlock((CommandBlock)value); + return this; + } + + public boolean isCellEditable(EventObject anEvent) { + return true; + } + + public void removeCellEditorListener(CellEditorListener l) { + listenerList.remove(CellEditorListener.class, l); + } + + public boolean shouldSelectCell(EventObject anEvent) { + return true; + } + + private void fireEditingCanceled() { + ChangeEvent e = null; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==CellEditorListener.class) { + if (e == null) { + e = new ChangeEvent(this); + } + ((CellEditorListener)listeners[i+1]).editingCanceled(e); + } + } + } + + private void fireEditingStopped() { + ChangeEvent e = null; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==CellEditorListener.class) { + if (e == null) { + e = new ChangeEvent(this); + } + ((CellEditorListener)listeners[i+1]).editingStopped(e); + } + } + } + + /** Gets the property value as text. + * + * @return The property value as a human editable string. + *

Returns null if the value can't be expressed as an editable string. + *

If a non-null value is returned, then the PropertyEditor should + * be prepared to parse that string back in setAsText(). + */ + public String getAsText() { + return "dflkjd"; + } + + /** A PropertyEditor may choose to make available a full custom Component + * that edits its property value. It is the responsibility of the + * PropertyEditor to hook itself up to its editor Component itself and + * to report property value changes by firing a PropertyChange event. + *

+ * The higher-level code that calls getCustomEditor may either embed + * the Component in some larger property sheet, or it may put it in + * its own individual dialog, or ... + * + * @return A java.awt.Component that will allow a human to directly + * edit the current property value. May be null if this is + * not supported. + */ + public java.awt.Component getCustomEditor() { + return this; + } + + /** This method is intended for use when generating Java code to set + * the value of the property. It should return a fragment of Java code + * that can be used to initialize a variable with the current property + * value. + *

+ * Example results are "2", "new Color(127,127,34)", "Color.orange", etc. + * + * @return A fragment of Java code representing an initializer for the + * current value. + */ + public String getJavaInitializationString() { + return "???"; + } + + /** If the property value must be one of a set of known tagged values, + * then this method should return an array of the tags. This can + * be used to represent (for example) enum values. If a PropertyEditor + * supports tags, then it should support the use of setAsText with + * a tag value as a way of setting the value and the use of getAsText + * to identify the current value. + * + * @return The tag values for this property. May be null if this + * property cannot be represented as a tagged value. + * + */ + public String[] getTags() { + return null; + } + + /** Gets the property value. + * + * @return The value of the property. Primitive types such as "int" will + * be wrapped as the corresponding object type such as "java.lang.Integer". + */ + public Object getValue() { + return commandBlock; + } + + /** Determines whether this property editor is paintable. + * + * @return True if the class will honor the paintValue method. + */ + public boolean isPaintable() { + return false; + } + + /** Paint a representation of the value into a given area of screen + * real estate. Note that the propertyEditor is responsible for doing + * its own clipping so that it fits into the given rectangle. + *

+ * If the PropertyEditor doesn't honor paint requests (see isPaintable) + * this method should be a silent noop. + *

+ * The given Graphics object will have the default font, color, etc of + * the parent container. The PropertyEditor may change graphics attributes + * such as font and color and doesn't need to restore the old values. + * + * @param gfx Graphics object to paint into. + * @param box Rectangle within graphics object into which we should paint. + */ + public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) { + } + + /** Set the property value by parsing a given String. May raise + * java.lang.IllegalArgumentException if either the String is + * badly formatted or if this kind of property can't be expressed + * as text. + * @param text The string to be parsed. + */ + public void setAsText(String text) throws java.lang.IllegalArgumentException { + } + + /** Set (or change) the object that is to be edited. Primitive types such + * as "int" must be wrapped as the corresponding object type such as + * "java.lang.Integer". + * + * @param value The new target object to be edited. Note that this + * object should not be modified by the PropertyEditor, rather + * the PropertyEditor should create a new object to hold any + * modified value. + */ + public void setValue(Object value) { + setCommandBlock((CommandBlock)value); + } + + /** Determines whether this property editor supports a custom editor. + * + * @return True if the propertyEditor can provide a custom editor. + */ + public boolean supportsCustomEditor() { + return true; + } + + private static class CommandBlockTreeModel implements TreeModel { + + private EventListenerList eventListenerList; + private CommandBlock root; + + CommandBlockTreeModel(CommandBlock root) { + this.root = root; + } + + /** Adds a listener for the TreeModelEvent + * posted after the tree changes. + * + * @param l the listener to add + * @see #removeTreeModelListener + */ + public void addTreeModelListener(TreeModelListener l) { + if (eventListenerList == null) { + eventListenerList = new EventListenerList(); + } + eventListenerList.add(TreeModelListener.class, l); + } + + /** Returns the child of parent at index index + * in the parent's + * child array. parent must be a node previously obtained + * from this data source. This should not return null + * if index + * is a valid index for parent (that is index >= 0 && + * index < getChildCount(parent)). + * + * @param parent a node in the tree, obtained from this data source + * @return the child of parent at index index + */ + public Object getChild(Object parent, int index) { + if (parent instanceof CommandBlock) { + return ((CommandBlock)parent).commandList.get(index); + } + else { + return null; + } + } + + /** Returns the number of children of parent. + * Returns 0 if the node + * is a leaf or if it has no children. parent must be a node + * previously obtained from this data source. + * + * @param parent a node in the tree, obtained from this data source + * @return the number of children of the node parent + */ + public int getChildCount(Object parent) { + if (parent instanceof CommandBlock) { + return ((CommandBlock)parent).commandList.size(); + } + else { + return 0; + } + } + + /** Returns the index of child in parent. If parent + * is null or child is null, + * returns -1. + * + * @param parent a note in the tree, obtained from this data source + * @param child the node we are interested in + * @return the index of the child in the parent, or -1 if either + * child or parent are null + */ + public int getIndexOfChild(Object parent, Object child) { + if (parent instanceof CommandBlock) { + return ((CommandBlock)parent).commandList.indexOf(child); + } + return -1; + } + + /** Returns the root of the tree. Returns null + * only if the tree has no nodes. + * + * @return the root of the tree + */ + public Object getRoot() { + return root; + } + + /** Returns true if node is a leaf. + * It is possible for this method to return false + * even if node has no children. + * A directory in a filesystem, for example, + * may contain no files; the node representing + * the directory is not a leaf, but it also has no children. + * + * @param node a node in the tree, obtained from this data source + * @return true if node is a leaf + */ + public boolean isLeaf(Object node) { + return !(node instanceof CommandBlock); + } + + /** Removes a listener previously added with + * addTreeModelListener. + * + * @see #addTreeModelListener + * @param l the listener to remove + */ + public void removeTreeModelListener(TreeModelListener l) { + if (eventListenerList != null) { + eventListenerList.remove(TreeModelListener.class, l); + } + } + + protected void fireTreeChanged() { + TreeModelEvent evt = null; + Object[] listeners = eventListenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i-=2) { + if (listeners[i] == TreeModelListener.class) { + if (evt == null) { + evt = new TreeModelEvent(this, new Object[]{root}); + } + ((TreeModelListener)listeners[i+1]).treeStructureChanged(evt); + } + } + } + + protected void fireTreeNodesChanged(Object[] path, int index, Object child) { + TreeModelEvent evt = null; + Object[] listeners = eventListenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i-=2) { + if (listeners[i] == TreeModelListener.class) { + if (evt == null) { + evt = new TreeModelEvent(this, path, new int[]{index}, new Object[]{child}); + } + ((TreeModelListener)listeners[i+1]).treeNodesChanged(evt); + } + } + } + + protected void fireTreeNodeInserted(Object[] path, int index, Object child) { + TreeModelEvent evt = null; + Object[] listeners = eventListenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i-=2) { + if (listeners[i] == TreeModelListener.class) { + if (evt == null) { + evt = new TreeModelEvent(this, path, new int[]{index}, new Object[]{child}); + } + ((TreeModelListener)listeners[i+1]).treeNodesInserted(evt); + } + } + } + + protected void fireTreeNodesRemoved(TreePath parent, int[] indices, Object[] children) { + TreeModelEvent evt = null; + Object[] listeners = eventListenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i-=2) { + if (listeners[i] == TreeModelListener.class) { + if (evt == null) { + evt = new TreeModelEvent(this, parent, indices, children); + } + ((TreeModelListener)listeners[i+1]).treeNodesRemoved(evt); + } + } + } + + protected void fireTreeStructureChanged(Object[] path) { + TreeModelEvent evt = null; + Object[] listeners = eventListenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i-=2) { + if (listeners[i] == TreeModelListener.class) { + if (evt == null) { + evt = new TreeModelEvent(this, path); + } + ((TreeModelListener)listeners[i+1]).treeStructureChanged(evt); + } + } + } + + Object[] getPathToNode(Object node) { + if (!(node instanceof CommandBlock.Command)) { + return new Object[]{node}; + } + else { + CommandBlock.Command command = (CommandBlock.Command)node; + CommandBlock parent = command.getParent(); + int count = 2; + while (parent instanceof CommandBlock.Command) { + parent = ((CommandBlock.Command)parent).getParent(); + count ++; + } + Object[] path = new Object[count]; + parent = command.getParent(); + path[count - 1] = node; + path[count - 2] = parent; + int index = count - 2; + while (parent instanceof CommandBlock.Command) { + index --; + parent = ((CommandBlock.Command)parent).getParent(); + path[index] = parent; + } + System.out.println(Arrays.asList(path)); + return path; + } + } + + /** Messaged when the user has altered the value for the item identified + * by path to newValue. + * If newValue signifies a truly new value + * the model should post a treeNodesChanged event. + * + * @param path path to the node that the user has altered + * @param newValue the new value from the TreeCellEditor + */ + public void valueForPathChanged(TreePath path, Object newValue) { + } + + } + + private static class CommandRenderer extends JLabel implements TreeCellRenderer { + + Color textForeground; + + Color textBackground; + + Color selectionForeground; + + Color selectionBackground; + + Border focusedBorder; + + Border unfocusedBorder; + + CommandRenderer() { + setOpaque(true); + textForeground = UIManager.getColor("Tree.textForeground"); + textBackground = UIManager.getColor("Tree.textBackground"); + selectionForeground = UIManager.getColor("Tree.selectionForeground"); + selectionBackground = UIManager.getColor("Tree.selectionBackground"); + focusedBorder = new LineBorder(UIManager.getColor("Tree.selectionBorderColor")); + unfocusedBorder = new LineBorder(textBackground); + } + + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + setForeground(selected ? selectionForeground : textForeground); + setBackground(selected ? selectionBackground : textBackground); + setBorder(hasFocus ? focusedBorder : unfocusedBorder); + if (!(value instanceof CommandBlock.Command)) { + setText("[Command Block]"); + } + else { + setText(value.toString()); + } + setEnabled(tree.isEnabled()); + return this; + } + + } +} diff --git a/dasCore/src/main/java/org/das2/dasml/DOMBuilder.java b/dasCore/src/main/java/org/das2/dasml/DOMBuilder.java new file mode 100644 index 000000000..d7bafcf68 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/DOMBuilder.java @@ -0,0 +1,210 @@ +/* + * Serializer.java + * + * Created on April 28, 2006, 4:49 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.dasml; + +import org.das2.graph.DasCanvasComponent; +import org.das2.DasApplication; +import org.das2.NameContext; +import org.das2.beans.AccessLevelBeanInfo; +import org.das2.beans.BeansUtil; +import org.das2.system.DasLogger; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.monitor.NullProgressMonitor; +import java.beans.*; +import java.beans.PropertyDescriptor; +import java.lang.reflect.*; +import java.util.*; +import java.util.logging.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * New stateful serialize utility that uses names when available. + * @author Jeremy + */ +public class DOMBuilder { + + Object bean; + HashMap serializedObjects; + NameContext nameContext; + + /** Creates a new instance of Serializer */ + public DOMBuilder( Object bean ) { + this.bean= bean; + } + + /** + * returns name or null. + */ + private String getBeanName( Object bean ) { + try { + PropertyDescriptor[] pds= BeansUtil.getPropertyDescriptors(bean.getClass()); + for ( int i=0; i0 ) monitor.setTaskSize( propertyNameList.length ); + monitor.started(); + + for ( int i=0; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.xml.sax.*; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.util.*; +import java.util.regex.Pattern; + +/** + * A validator for the dasML language developed for the University of + * Iowa Space Plasma Wave Group. This class is used as a pre-processor + * to (hopefully) provide clear and helpful error messages. + * + * Warning: This class is not thread-safe. Unexpected results can occur + * if multiple threads use an instance of this class concurrently. + * + * @author Edward West + */ +public class DasMLValidator extends DefaultHandler { + + public static Pattern INTEGER_PATTERN = Pattern.compile("(0|[1-9][0-9]*)"); + + public static Pattern WINDOW_POSITION_PATTERN = Pattern.compile("\\((0|[1-9][0-9]*),(0|[1-9][0-9]*)\\)"); + + public static Pattern FLOAT_PATTERN = Pattern.compile("-?[0-9]*(\\.[0-9]*)?([eE]-?[0-9]+)?"); + + /** + * Instance of the SAXParserFactory that this class uses to create + * instances of SAXParser. + */ + private static SAXParserFactory factory; + + /** + * Static initialization block to property initialize factory + */ + static { + factory = SAXParserFactory.newInstance(); + factory.setValidating(true); + } + + /** + * Instance of SAXParser used by this validator + * to parse documents. + */ + private SAXParser parser; + + /** + * Instance of ErrorHandler that the error events + * from the SAXParser are delegated to. This member is + * only valid during a call to validate() + */ + private ErrorHandler errorHandler; + + /** + * The last error encountered by this validator. + */ + private SAXException lastError; + + /** + * Locator used to locate the position in the + * XML document that where certain events have taken place. + */ + private Locator locator; + + /** + * Mapping of 'name' attributes to 'type' of element (element name) + */ + private Map typeMap; + + /** + * A list of TypeCheck that are to be processed once the whole + * document is loaded. + */ + private List typeCheckList; + + /** Creates a new instance of DasMLValidator */ + public DasMLValidator() throws ParserConfigurationException, SAXException { + parser = factory.newSAXParser(); + typeMap = new HashMap(); + typeCheckList = new LinkedList(); + } + + /** + * Parses and validates a dasML document. All errors are + * passed to the ErrorHandler instance specified. SAXExceptions + * thrown by the underlying parser are caught and suppressed by + * this method. If an application needs access to the errors, + * an ErrorHandler must be provided. + * + * @param source The source of the XML document + * @param errorHandler The ErrorHandler instance that will receive + * error messages from the parser. This can be null + * @return true if the document is a valid dasML document. + * @throws IOException if the there is an error while reading the document. + */ + public boolean validate(InputSource source, ErrorHandler errorHandler) throws java.io.IOException { + this.errorHandler = errorHandler; + if (this == errorHandler) throw new IllegalArgumentException("cannot pass an instance of DasMLValidator to its own validate() method"); + lastError = null; + try { + typeMap.clear(); + typeCheckList.clear(); + parser.parse(source, this); + } + catch (SAXException se) { + //Save a reference to the error and return false + lastError = se; + } + + return lastError == null; + } + + /** + * Returns the last error encountered by this validator + * or null if no error has been found. This method + * will only return an error if the last call to + * validate(InputSource, ErrorHandler) returned false. + * If an application wishes to have access to warnings + * and non-fatal errors then an ErrorHandler must be provided. + */ + public SAXException getLastError() { + return lastError; + } + + /** Report a fatal XML parsing error. + * + * @param e The error information encoded as an exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ErrorHandler#fatalError + * @see org.xml.sax.SAXParseException + */ + public void fatalError(SAXParseException e) throws SAXException { + if (errorHandler != null) { + errorHandler.fatalError(e); + } + throw e; + } + + /** Receive a Locator object for document events. + * + * @param locator A locator for all SAX document events. + * @see org.xml.sax.ContentHandler#setDocumentLocator + * @see org.xml.sax.Locator + */ + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + /** Receive notification of a recoverable parser error. + * + * @param e The warning information encoded as an exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ErrorHandler#warning + * @see org.xml.sax.SAXParseException + */ + public void error(SAXParseException e) throws SAXException { + if (errorHandler != null) { + errorHandler.error(e); + } + lastError = e; + } + + /** Receive notification of a parser warning. + * + * @param e The warning information encoded as an exception. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ErrorHandler#warning + * @see org.xml.sax.SAXParseException + */ + public void warning(SAXParseException e) throws SAXException { + if (errorHandler != null) { + errorHandler.warning(e); + } + lastError = e; + } + + /** Receive notification of the beginning of the document. + * + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#startDocument + */ + public void startDocument() throws SAXException { + } + + /** Receive notification of the start of an element. + * + * @param qName The element type name. + * @param attributes The specified or defaulted attributes. + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#startElement + */ + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + String name = attributes.getValue("name"); + if (name != null) { + if (typeMap.containsKey(name)) + errorInternal("An element with the name " + name + " already exists. " + + "The values of name attributes must be unique."); + typeMap.put(name, qName); + } + if (qName.equals("window")) + checkWindow(attributes); + else if (qName.equals("form")) + checkForm(attributes); + else if (qName.equals("textfield")) + checkTextfield(attributes); + else if (qName.equals("checkbox")) + checkCheckbox(attributes); + else if (qName.equals("if")) + ;//CHECK EXPRESSION + else if (qName.equals("elseif")) + ;//CHECK EXPRESSION + else if (qName.equals("update")) //KLUDGE - Will be removed once is implemented + typeCheckList.add(new TypeCheck("update", + "target", + "spectrogram", + attributes.getValue("target"), + locator)); + else if (qName.equals("radiobutton")) + checkRadiobutton(attributes); + else if (qName.equals("panel")) + checkPanel(attributes); + else if (qName.equals("glue")) + checkGlue(attributes); + else if (qName.equals("canvas")) + checkCanvas(attributes); + else if (qName.equals("row")) + checkRowColumn("row", attributes); + else if (qName.equals("column")) + checkRowColumn("column", attributes); + else if (qName.equals("spectrogram")) + checkSpectrogram(attributes); + else if (qName.equals("xAxis")) + checkXAxis(attributes); + else if (qName.equals("yAxis")) + checkYAxis(attributes); + else if (qName.equals("zAxis")) + checkZAxis(attributes); + else if (qName.equals("axis")) + checkAxis(attributes); + else if (qName.equals("timeaxis")) + checkTimeaxis(attributes); + else if (qName.equals("attachedaxis")) + checkAttachedaxis(attributes); + else if (qName.equals("colorbar")) + checkColorbar(attributes); + } + + /** + * Checks the attribute values for a colorbar element + */ + private void checkColorbar(Attributes attributes) throws SAXException { + String minimum = attributes.getValue("minimum"); + if (!FLOAT_PATTERN.matcher(minimum).matches() || minimum.charAt(0)=='-') + errorInternal("The minimum attribute of a colorbar element must be a positive number"); + String maximum = attributes.getValue("maximum"); + if (!FLOAT_PATTERN.matcher(maximum).matches() || maximum.charAt(0)=='-') + errorInternal("The maximum attribute of a colorbar element must be a positive number"); + String row = attributes.getValue("row"); + if (row != null) { + typeCheckList.add(new TypeCheck("colorbar", "row", "row", row, locator)); + } + else if (!insideSpectrogram) { + errorInternal("The \"row\" attribute of a \"colorbar\" element must be specified" + + " if the element is not nested in a \"spectrogram\" element"); + } + String column = attributes.getValue("column"); + if (column != null) { + typeCheckList.add(new TypeCheck("colorbar", "column", "column", column, locator)); + } + else if (!insideSpectrogram) { + errorInternal("The \"column\" attribute of a \"colorbar\" element must be specified" + + " if the element is not nested in a \"spectrogram\" element"); + } + String log = attributes.getValue("log"); + if (!(log.equals("true") || log.equals("false"))) + errorInternal("The log attribute of a colorbar must be either 'true' or 'false'"); + } + + private boolean hasXAxis = false; + private boolean hasYAxis = false; + private boolean hasZAxis = false; + private boolean insideSpectrogram = false; + + /** + * Checks the attribute values for a spectrogram element + */ + private void checkSpectrogram(Attributes attributes) throws SAXParseException { + String row = attributes.getValue("row"); + typeCheckList.add(new TypeCheck("spectrogram", "row", "row", row, locator)); + String column = attributes.getValue("column"); + typeCheckList.add(new TypeCheck("spectrogram", "column", "column", column, locator)); + + String xAxis = attributes.getValue("xAxis"); + if (xAxis != null) { + typeCheckList.add(new TypeCheck("spectrogram", "xAxis", "axis|timeaxis|attachedaxis", xAxis, locator)); + hasXAxis = true; + } + + String yAxis = attributes.getValue("yAxis"); + if (yAxis != null) { + typeCheckList.add(new TypeCheck("spectrogram", "yAxis", "axis|timeaxis|attachedaxis", yAxis, locator)); + hasYAxis = true; + } + + String colorbar = attributes.getValue("colorbar"); + if (colorbar != null) { + typeCheckList.add(new TypeCheck("spectrogram", "colorbar", "colorbar", colorbar, locator)); + hasZAxis = true; + } + + insideSpectrogram = true; + } + + private void endCheckSpectrogram() throws SAXException { + + if (!hasXAxis) { + errorInternal("No xAxis specified. Spectrograms require an xAxis to be specified"); + } + if (!hasYAxis) { + errorInternal("No yAxis specified. Spectrograms required a yAxis to be specified"); + } + if (!hasZAxis) { + errorInternal("No zAxis specified. Spectrograms required a zAxis to be specified"); + } + + hasXAxis = false; + hasYAxis = false; + hasZAxis = false; + insideSpectrogram = false; + } + + private void checkXAxis(Attributes attributes) throws SAXException { + hasXAxis = true; + } + + private void checkYAxis(Attributes attributes) throws SAXException { + hasYAxis = true; + } + + private void checkZAxis(Attributes attributes) throws SAXException { + hasZAxis = true; + } + + /** + * Checks the attribute values for a attachedaxis element + */ + private void checkAttachedaxis(Attributes attributes) throws SAXException { + String ref = attributes.getValue("ref"); + typeCheckList.add(new TypeCheck("attachedaxis", "ref", "axis|timeaxis", ref, locator)); + String row = attributes.getValue("row"); + if (row != null) { + typeCheckList.add(new TypeCheck("attachedaxis", "row", "row", row, locator)); + } + else if (!insideSpectrogram) { + errorInternal("The \"row\" attribute of an \"attachedaxis\" element must be specified" + + " if the element is not nested in a \"spectrogram\" element"); + } + String column = attributes.getValue("column"); + if (column != null) { + typeCheckList.add(new TypeCheck("attachedaxis", "column", "column", column, locator)); + } + else if (!insideSpectrogram) { + errorInternal("The \"column\" attribute of an \"attachedaxis\" element must be specified" + + " if the element is not nested in a \"spectrogram\" element"); + } + String orientation = attributes.getValue("orientation"); + if (!(orientation.equals("horizontal") || orientation.equals("vertical"))) + errorInternal("The orientation attibute of an attachedaxis element must be either 'horizontal' or 'vertical'"); + } + + /** + * Checks the attribute values for a timeaxis element + */ + private void checkTimeaxis(Attributes attributes) throws SAXException { + String showTca = attributes.getValue("showTca"); + if (!(showTca.equals("true") || showTca.equals("false"))) + errorInternal("The showTca attribute of a timeaxis element must be either 'true' or 'false'"); + String row = attributes.getValue("row"); + if (row != null) { + typeCheckList.add(new TypeCheck("timeaxis", "row", "row", row, locator)); + } + else if (!insideSpectrogram) { + errorInternal("The \"row\" attribute of a \"timeaxis\" element must be specified" + + " if the element is not nested in a \"spectrogram\" element"); + } + String column = attributes.getValue("column"); + if (column != null) { + typeCheckList.add(new TypeCheck("timeaxis", "column", "column", column, locator)); + } + else if (!insideSpectrogram) { + errorInternal("The \"column\" attribute of a \"timeaxis\" element must be specified" + + " if the element is not nested in a \"spectrogram\" element"); + } + String orientation = attributes.getValue("orientation"); + if (!(orientation.equals("horizontal") || orientation.equals("vertical"))) + errorInternal("The orientation attibute of an axis element must be either 'horizontal' or 'vertical'"); + if (showTca.equals("true") && orientation.equals("vertical")) + errorInternal("Vertical axes cannot diplay time correlated annotations"); + } + + /** + * Checks the attribute values for an axis element + */ + private void checkAxis(Attributes attributes) throws SAXException { + String log = attributes.getValue("log"); + if (!(log.equals("true") || log.equals("false"))) + errorInternal("The log attribute of an axis element must be either 'true' or 'false'"); + String dataMinimum = attributes.getValue("dataMinimum"); + if (!FLOAT_PATTERN.matcher(dataMinimum).matches()) + errorInternal("The dataMinimum attribute of an axis element must be a valid number'"); + String dataMaximum = attributes.getValue("dataMaximum"); + if (!FLOAT_PATTERN.matcher(dataMinimum).matches()) + errorInternal("The dataMaximum attribute of an axis element must be a valid number'"); + String row = attributes.getValue("row"); + if (row != null) { + typeCheckList.add(new TypeCheck("axis", "row", "row", row, locator)); + } + else if (!insideSpectrogram) { + errorInternal("The \"row\" attribute of an \"axis\" element must be specified" + + " if the element is not nested in a \"spectrogram\" element"); + } + String column = attributes.getValue("column"); + if (column != null) { + typeCheckList.add(new TypeCheck("axis", "column", "column", column, locator)); + } + else if (!insideSpectrogram) { + errorInternal("The \"column\" attribute of an \"axis\" element must be specified" + + " if the element is not nested in a \"spectrogram\" element"); + } + String orientation = attributes.getValue("orientation"); + if (!(orientation.equals("horizontal") || orientation.equals("vertical"))) + errorInternal("The orientation attibute of an axis element must be either 'horizontal' or 'vertical'"); + } + + /** + * Checks the attribute values for a row or column element + */ + private void checkRowColumn(String tagName, Attributes attributes) throws SAXException { + String minimum = attributes.getValue("minimum"); + if (!FLOAT_PATTERN.matcher(minimum).matches() || minimum.charAt(0)=='-') + errorInternal("The minimum attribute of a " + tagName + " element must be a positive number"); + String maximum = attributes.getValue("maximum"); + if (!FLOAT_PATTERN.matcher(maximum).matches() || maximum.charAt(0)=='-') + errorInternal("The maximum attribute of a " + tagName + " element must be a positive number"); + } + + /** + * Checks the attribute values for a canvas element + */ + private void checkCanvas(Attributes attributes) throws SAXException { + String width = attributes.getValue("width"); + if (!INTEGER_PATTERN.matcher(width).matches()) + errorInternal("The width attribute of a canvas element must be a positive integer"); + String height = attributes.getValue("height"); + if (!INTEGER_PATTERN.matcher(height).matches()) + errorInternal("The height attribute of a canvas element must be a positive integer"); + } + + /** + * Checks the attribute values for a glue element + */ + private void checkGlue(Attributes attributes) throws SAXException { + String direction = attributes.getValue("direction"); + if (!(direction.equals("horizontal") || direction.equals("vertical"))) + errorInternal("The direction attribute of a glue element must be either 'horizontal' or 'vertical'"); + } + + /** + * Checks the attribute values for a panel element + */ + private void checkPanel(Attributes attributes) throws SAXException { + String direction = attributes.getValue("direction"); + if (!(direction.equals("horizontal") || direction.equals("vertical"))) + errorInternal("The direction attribute of a panel element must be either 'horizontal' or 'vertical'"); + String border = attributes.getValue("border"); + if (!(border.equals("true") || border.equals("false"))) + errorInternal("The border attribute of a panel element must be either 'true' or 'false'"); + } + + /** + * Checks the attribute values for a checkbox element + */ + private void checkRadiobutton(Attributes attributes) throws SAXException { + String group = attributes.getValue("group"); + typeCheckList.add(new TypeCheck("radiobutton", + "group", + "buttongroup", + group, + locator)); + String selected = attributes.getValue("selected"); + if (!(selected.equals("true") || selected.equals("false"))) + errorInternal("The selected attribute of a radiobutton element must be either 'true' or 'false'"); + } + + /** + * Checks the attribute values for a checkbox element + */ + private void checkCheckbox(Attributes attributes) throws SAXException { + String selected = attributes.getValue("selected"); + if (!(selected.equals("true") || selected.equals("false"))) + errorInternal("The selected attribute of a checkbox element must be either 'true' or 'false'"); + } + + /** + * Checks the attribute values for a textfield element + */ + private void checkTextfield(Attributes attributes) throws SAXException { + String length = attributes.getValue("length"); + if (!INTEGER_PATTERN.matcher(length).matches()) + errorInternal("The length attribute of textfield elements must be a positive integer"); + } + + /** + * Checks the attribute values for a form element + */ + private void checkForm(Attributes attributes) throws SAXException { + String alignment = attributes.getValue("alignment"); + if (!(alignment.equals("left") || alignment.equals("center") || alignment.equals("right"))) + errorInternal("The alignment attribute of a form element must be 'left', 'center', or 'right'"); + } + + /** + * Checks the attribute values for a window element + */ + private void checkWindow(Attributes attributes) throws SAXException { + String width = attributes.getValue("width"); + if (!INTEGER_PATTERN.matcher(width).matches()) + errorInternal("The width attribute of a window element must be a positve integer."); + String height = attributes.getValue("height"); + if (!INTEGER_PATTERN.matcher(height).matches()) + errorInternal("The height attribute of a window element must be a positive integer."); + String location = attributes.getValue("location"); + if (!WINDOW_POSITION_PATTERN.matcher(location).matches()) + errorInternal("The location attribute of a window element must be a pair of the form (x,y)"); + String visible = attributes.getValue("visible"); + if (!visible.equals("true") && !visible.equals("false")) + errorInternal("The visible attribute of a window element must be either 'true' or 'false'"); + } + + /** Receive notification of the end of the document. + * + * @exception org.xml.sax.SAXException Any SAX exception, possibly + * wrapping another exception. + * @see org.xml.sax.ContentHandler#endDocument + */ + public void endDocument() throws SAXException { + Iterator iterator = typeCheckList.iterator(); + SAXParseException exception = null; + while (iterator.hasNext()) { + TypeCheck check = (TypeCheck)iterator.next(); + String type = (String)typeMap.get(check.value); + if (type == null) { + exception = new SAXParseException("No element of type \"" + check.type + "\" with attribute " + + "name=\"" + check.value + "\" exists.", + check); + error(exception); + } + if (!Pattern.matches(check.type, type)) { + exception = new SAXParseException("Element '" + check.elementName + "', " + + "attribute '" + check.attributeName + "' : " + + type + " expected, but found " + type + + " (" + check.value + ")", + check); + error(exception); + } + } + if (exception != null) throw exception; + } + + /** + * This class encapsulates the information necessary + * to check that the value of an attribute references + * an element of the property type. + */ + private static class TypeCheck implements Locator { + public String elementName; + public String attributeName; + public String type; + public String value; + private int lineNumber; + private int columnNumber; + private String publicId; + private String systemId; + public TypeCheck(String elementName, String attributeName, String type, String value, Locator locator) { + this.elementName = elementName; + this.attributeName = attributeName; + this.type = type; + this.value = value; + this.lineNumber = locator.getLineNumber(); + this.columnNumber = locator.getColumnNumber(); + this.publicId = locator.getPublicId(); + this.systemId = locator.getSystemId(); + } + public int getColumnNumber() { return columnNumber; } + public int getLineNumber() { return lineNumber; } + public String getPublicId() { return publicId; } + public String getSystemId() { return systemId; } + + } + + private void errorInternal(String message) throws SAXException { + lastError = new SAXParseException(message, locator); + error((SAXParseException)lastError); + } + + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("usage: java org.das2.dasml.DasMLValidator "); + return; + } + + ErrorHandler errorHandler = new ErrorHandler() { + public void warning(SAXParseException spe) throws SAXException { + org.das2.util.DasDie.println("Line " + spe.getLineNumber() + ", " + spe.getMessage()); + } + public void error(SAXParseException spe) throws SAXException { + org.das2.util.DasDie.println("Line " + spe.getLineNumber() + ", " + spe.getMessage()); + } + public void fatalError(SAXParseException spe) throws SAXException { + org.das2.util.DasDie.println("Line " + spe.getLineNumber() + ", " + spe.getMessage()); + } + }; + + try { + String path = new java.io.File(args[0]).getCanonicalPath(); + DasMLValidator validator = new DasMLValidator(); + if (validator.validate(new InputSource("file://" + path), errorHandler)) { + org.das2.util.DasDie.println("No errors"); + } + } + catch (ParserConfigurationException pce) { + org.das2.util.DasDie.println(pce.getMessage()); + } + catch (SAXException se) { + org.das2.util.DasDie.println(se.getMessage()); + } + catch (java.io.IOException ioe) { + org.das2.util.DasDie.println(ioe.getMessage()); + } + + } + + public void endElement(String uri, String localName, String qName) throws SAXException { + if (qName.equals("spectrogram")) { + endCheckSpectrogram(); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/DataFormatException.java b/dasCore/src/main/java/org/das2/dasml/DataFormatException.java new file mode 100644 index 000000000..6a3dfcffd --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/DataFormatException.java @@ -0,0 +1,37 @@ +/* File: DataFormatException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +/** + * + * @author Edward West + */ +public class DataFormatException extends Exception { + public DataFormatException(String message) { + super(message); + } + public DataFormatException() { + super(); + } +} diff --git a/dasCore/src/main/java/org/das2/dasml/DefaultComponentDnDSupport.java b/dasCore/src/main/java/org/das2/dasml/DefaultComponentDnDSupport.java new file mode 100644 index 000000000..bce7655e6 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/DefaultComponentDnDSupport.java @@ -0,0 +1,59 @@ +/* File: DefaultComponentDnDSupport.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +/** + * + * @author eew + */ +class DefaultComponentDnDSupport extends org.das2.util.DnDSupport { + + DefaultComponentDnDSupport(java.awt.Component c) { + this(c, java.awt.dnd.DnDConstants.ACTION_NONE); + } + + /** Creates a new instance of ComponentDnDSupport */ + DefaultComponentDnDSupport(java.awt.Component c, int action) { + super(c, action, null); + } + + protected int canAccept(java.awt.datatransfer.DataFlavor[] flavors, int x, int y, int action) { + return -1; + } + + protected void done() { + } + + protected boolean importData(java.awt.datatransfer.Transferable t, int x, int y, int action) { + return false; + } + + protected java.awt.datatransfer.Transferable getTransferable(int x, int y, int action) { + return null; + } + + protected void exportDone(java.awt.datatransfer.Transferable t, int action) { + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormBase.java b/dasCore/src/main/java/org/das2/dasml/FormBase.java new file mode 100644 index 000000000..65d6df333 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormBase.java @@ -0,0 +1,712 @@ +/* File: FormBase.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.DasApplication; +import org.das2.beans.BeansUtil; +import org.das2.datum.Datum; +import org.das2.util.DasExceptionHandler; +//import org.apache.xml.serialize.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.swing.*; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.*; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.MethodDescriptor; +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.*; +import java.util.List; +import org.das2.DasException; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; + +/** This class displays a Java form that is generated from an XML Document that is provided as input. + * + * @author Edward West + */ +public class FormBase extends JTabbedPane implements FormComponent { + + /** The factory object used for creating DOM parsers. */ + private static DocumentBuilderFactory domFactory; + + private DasApplication application = DasApplication.getDefaultApplication(); + + /** static initialization block for this class */ + static { + domFactory = DocumentBuilderFactory.newInstance(); + URL schemaLocation = FormBase.class.getResource("schema/dasML.xsd"); + if (schemaLocation != null) { + domFactory.setAttribute( + "http://apache.org/xml/features/validation/schema-full-checking", + Boolean.TRUE); + domFactory.setAttribute( + "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", + schemaLocation.toExternalForm()); + domFactory.setValidating(true); + domFactory.setNamespaceAware(true); + } + else { + //TODO: Report this error, also maybe try to recover. + } + domFactory.setCoalescing(true); + domFactory.setIgnoringElementContentWhitespace(true); + } + + /**Initialization commands*/ + private CommandBlock initBlock; + + List windowList = new ArrayList(); + + private JDesktopPane desktop; + + boolean editable; + + private boolean onHover = false; + + /** Setup for DnD support */ + { + DnDSupport dndSupport = new DnDSupport(); + DropTarget dropTarget = new DropTarget(this, dndSupport); + setDropTarget(dropTarget); + } + + /** Creates a FormBase object + * + * @param url A uniform resouce locator pointing to the XML document to parse. + */ + public FormBase(URL url, ErrorHandler eh, boolean editable) throws IOException, SAXException { + this(url.openStream(), eh, editable); + } + + public FormBase(InputStream in, ErrorHandler eh, boolean editable) throws IOException, SAXException { + this(new InputStreamReader(in), eh, editable); + } + + public FormBase(Reader reader, ErrorHandler eh, boolean editable) throws IOException, SAXException { + this.editable = editable; + + try { + Document document = parseDasML(reader, eh); + createFormFromTree(document); + registerComponent(); + } + catch (ParserConfigurationException pce) { + throw new IllegalStateException("DOM parser not configured properly: " + pce.getMessage()); + } + catch (org.das2.DasException de) { + throw new SAXException(de); + } + } + + public FormBase(boolean editable) { + this.editable = editable; + } + + static Document parseDasML(Reader reader, ErrorHandler eh) throws ParserConfigurationException, SAXException, IOException { + InputSource source = new InputSource(); + source.setCharacterStream(reader); + DocumentBuilder builder; + synchronized (domFactory) { + builder = domFactory.newDocumentBuilder(); + } + builder.setErrorHandler(eh); + return builder.parse(source); + } + + /** Sets up the Java form using a DOM document tree. + * + * @param doc The DOM document tree. + */ + private void createFormFromTree(Document doc) throws SAXException { + try { + Element das2 = doc.getDocumentElement(); + if (!das2.getTagName().equals("das2")) + ;//TODO: do some sort of error handling here. + NodeList children = das2.getChildNodes(); + int childCount = children.getLength(); + for (int index = 0; index < childCount; index++) { + Node node = children.item(index); + if (node instanceof Element) { + if (node.getNodeName().equals("form")) { + FormTab form = new FormTab((Element)node, this); + addForm(form); + } + else if (node.getNodeName().equals("window")) { + FormWindow window = new FormWindow((Element)node, this); + addWindow(window); + } + else if (node.getNodeName().equals("init")) { + initBlock = processInitElement((Element)node); + } + else { + //TODO: error message. + } + } + else { + //TODO: nothing, only interested in elements. + } + } + } + catch (DasException dne) { + throw new SAXException(dne); + } + catch (ParsedExpressionException pee) { + throw new SAXException(pee); + } + } + + private int getTabInsertionIndex() { + int index = 0; + int count = getComponentCount(); + while (index < count && getComponent(index) instanceof FormTab) { + index++; + } + return index; + } + + public void addForm(FormTab form) { + synchronized (getTreeLock()) { + if (getEditingMode()) { + int index = getTabInsertionIndex(); + insertTab(form.getLabel(), null, form, null, index); + } + else { + addTab(form.getLabel(), form); + } + } + form.setEditingMode(getEditingMode()); + } + + public void addWindow(FormWindow window) { + if (window.form != null) { + window.form.removeWindow(window); + } + window.form = this; + windowList.add(window); + boolean editingMode = getEditingMode(); + window.setEditingMode(editingMode); + if (editingMode) { + if (desktop == null) { + desktop = new JDesktopPane(); + add(desktop, "Windows"); + } + window.pack(); + desktop.add(window.getInternalFrame()); + } + this.firePropertyChange("window", null, window); + } + + public void removeWindow(FormWindow window) { + if (windowList.contains(window)) { + windowList.remove(window); + if (getEditingMode()) { + desktop.remove(SwingUtilities.getAncestorOfClass(JInternalFrame.class, window)); + } + firePropertyChange("window", window, null); + } + } + + /** Process a <action> element. + * + * @param element The DOM tree node that represents the element + */ + private CommandBlock processActionElement(Element element) { + return new CommandBlock(element, this); + } + + /** Process a <glue> element. + * + * @param element The DOM tree node that represents the element + */ + Component processGlueElement(Element element) { + String direction = element.getAttribute("direction"); + if (direction.equals("horizontal")) + return Box.createHorizontalGlue(); + else return Box.createVerticalGlue(); + } + + private CommandBlock processInitElement(Element element) throws SAXException { + return new CommandBlock(element, this); + } + + + + /** + * Writes the XML representation of this form to the specified + * byte stream + * + * @param out the specified byte stream + */ + public void serialize(OutputStream out) throws IOException { + try { + DocumentBuilder builder = domFactory.newDocumentBuilder(); + Document document = builder.newDocument(); + document.appendChild(getDOMElement(document)); + + DOMImplementationLS ls = (DOMImplementationLS) + document.getImplementation().getFeature("LS", "3.0"); + LSOutput output = ls.createLSOutput(); + output.setEncoding("UTF-8"); + output.setByteStream(out); + ls.createLSSerializer().write(document, output); + out.close(); + + /* + String method = org.apache.xml.serialize.Method.XML; + OutputFormat format = new OutputFormat(method, "UTF-8", true); + format.setLineWidth(0); + XMLSerializer serializer = new XMLSerializer(out, format); + serializer.serialize(document); + */ + } + catch (ParserConfigurationException pce) { + IOException ioe = new IOException(pce.getMessage()); + ioe.initCause(pce); + throw ioe; + } + } + + private boolean isValidType(Class type) { + return type.isPrimitive() + || type == String.class + || type == Datum.class + || org.das2.datum.Datum.class.isAssignableFrom(type) + || Number.class.isAssignableFrom(type); + } + + + public Object checkValue(String name, Class type, String tag) throws org.das2.DasPropertyException, org.das2.DasNameException { + try { + Object obj = application.getNameContext().get(name); + if (obj == null) { + throw new org.das2.DasNameException(name + " must be defined before it is used"); + } + if (!type.isInstance(obj)) { + throw new org.das2.DasPropertyException(org.das2.DasPropertyException.TYPE_MISMATCH, name, null); + } + return obj; + } + catch (InvocationTargetException ite) { + throw new RuntimeException(ite); + } + } + + public Object invoke(String name, String[] args) throws org.das2.DasPropertyException, DataFormatException, ParsedExpressionException, InvocationTargetException { + int lastDot = name.lastIndexOf('.'); + if (lastDot == -1) throw new DataFormatException("No object associated with method name" + name); + String objectName = name.substring(0, lastDot); + String methodName = name.substring(lastDot+1); + org.das2.util.DasDie.println("object name: " + objectName); + org.das2.util.DasDie.println("method name: " + methodName); + Object o = application.getNameContext().get(objectName); + Method method = null; + try { + BeanInfo info = BeansUtil.getBeanInfo(o.getClass()); + MethodDescriptor[] methodDescriptors = info.getMethodDescriptors(); + for (int i = 0; i <= methodDescriptors.length; i++) { + if (i == methodDescriptors.length) + throw new org.das2.DasPropertyException(org.das2.DasPropertyException.NOT_DEFINED, methodName, objectName); + if (!methodDescriptors[i].getName().equals(methodName)) continue; + //if (methodDescriptors[i].getMethod().getParameterTypes().length != args.length) continue; + method = methodDescriptors[i].getMethod(); + break; + } + Class[] parameterTypes = method.getParameterTypes(); + Object[] argValues = new Object[args.length]; + for (int i = 0; i < parameterTypes.length; i++) { + argValues[i] = application.getNameContext().parseValue(args[i], parameterTypes[i]); + } + return method.invoke(o, argValues); + } + catch (IntrospectionException ie) { + throw new DataFormatException(ie.getMessage()); + } + catch (InvocationTargetException ite) { + throw new DataFormatException(ite.getTargetException().getMessage()); + } + catch (IllegalAccessException iae) { + throw new DataFormatException(iae.getMessage()); + } + } + + + public boolean getEditingMode() { + return editable; + } + + public void setEditingMode(boolean b) { + if (editable != b) { + editable = b; + int componentCount = getComponentCount(); + for (int i = 0; i < componentCount; i++) { + Component c = getComponent(i); + if (c instanceof FormTab) { + ((FormTab)c).setEditingMode(b); + } + } + if (windowList.size() > 0) { + if (b) { + if (desktop == null) { + desktop = new JDesktopPane(); + } + add(desktop, "Windows"); + } + else { + remove(desktop); + } + for (Iterator i = windowList.iterator(); i.hasNext();) { + FormWindow window = (FormWindow)i.next(); + window.setEditingMode(b); + if (b) { + window.pack(); + JInternalFrame it = window.getInternalFrame(); + if (it.getParent() != desktop) { + desktop.add(it); + } + } + } + } + revalidate(); + repaint(); + if (b) { + firePropertyChange("editable", Boolean.FALSE, Boolean.TRUE); + } + else { + firePropertyChange("editable", Boolean.TRUE, Boolean.FALSE); + } + } + } + + public FormBase getForm() { + return this; + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("das2"); + for (int index = 0; index < getComponentCount(); index++) { + FormComponent child = (FormComponent)getComponent(index); + element.appendChild(child.getDOMElement(document)); + } + if (!editable) { + for (Iterator i = windowList.iterator(); i.hasNext();) { + FormComponent child = (FormComponent)i.next(); + element.appendChild(child.getDOMElement(document)); + } + } + return element; + } + + /** Paints the component's border. + *

+ * If you override this in a subclass you should not make permanent + * changes to the passed in Graphics. For example, you + * should not alter the clip Rectangle or modify the + * transform. If you need to do these operations you may find it + * easier to create a new Graphics from the passed in + * Graphics and manipulate it. + * + * @param g the Graphics context in which to paint + * + * @see #paint + * @see #setBorder + */ + protected void paintBorder(Graphics g) { + super.paintBorder(g); + if (onHover) { + Graphics2D g2 = (Graphics2D)g.create(); + Stroke thick = new BasicStroke(3.0f); + g2.setStroke(thick); + g2.setColor(Color.GRAY); + g2.drawRect(1, 1, getWidth() - 2, getHeight() - 2); + g2.dispose(); + } + } + + public org.das2.util.DnDSupport getDnDSupport() { + return null; + } + + public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { + return false; + } + + public String getDasName() { + return null; + } + + public List getWindowList() { + return Collections.unmodifiableList(windowList); + } + + public void setDasName(String name) throws org.das2.DasNameException { + throw new org.das2.DasNameException(); + } + + public void deregisterComponent() { + for (int index = 0; index < getComponentCount(); index++) { + Component c = getComponent(index); + if (c instanceof FormComponent) { + ((FormComponent)c).deregisterComponent(); + } + } + for (Iterator i = windowList.iterator(); i.hasNext();) { + FormWindow w = (FormWindow)i.next(); + w.deregisterComponent(); + } + } + + public org.das2.DasApplication getDasApplication() { + return application; + } + + public void registerComponent() throws org.das2.DasException { + for (int index = 0; index < getComponentCount(); index++) { + Component c = getComponent(index); + if (c instanceof FormComponent) { + ((FormComponent)c).registerComponent(); + } + } + for (Iterator i = windowList.iterator(); i.hasNext();) { + FormWindow w = (FormWindow)i.next(); + w.registerComponent(); + } + } + + private class DnDSupport implements DropTargetListener { + + private final Set acceptableFlavors = new HashSet(Arrays.asList(new DataFlavor[] { + TransferableFormComponent.TAB_FLAVOR, + TransferableFormComponent.WINDOW_FLAVOR + })); + + /** Called while a drag operation is ongoing, when the mouse pointer enters + * the operable part of the drop site for the DropTarget + * registered with this listener. + * + * @param dtde the DropTargetDragEvent + */ + public void dragEnter(DropTargetDragEvent dtde) { + if (canAccept(dtde.getCurrentDataFlavors())) { + dtde.acceptDrag(dtde.getSourceActions()); + onHover = true; + repaint(); + } + } + + /** Called while a drag operation is ongoing, when the mouse pointer has + * exited the operable part of the drop site for the + * DropTarget registered with this listener. + * + * @param dte the DropTargetEvent + */ + public void dragExit(DropTargetEvent dte) { + onHover = false; + repaint(); + } + + /** Called when a drag operation is ongoing, while the mouse pointer is still + * over the operable part of the drop site for the DropTarget + * registered with this listener. + * + * @param dtde the DropTargetDragEvent + */ + public void dragOver(DropTargetDragEvent dtde) { + } + + /** Called when the drag operation has terminated with a drop on + * the operable part of the drop site for the DropTarget + * registered with this listener. + *

+ * This method is responsible for undertaking + * the transfer of the data associated with the + * gesture. The DropTargetDropEvent + * provides a means to obtain a Transferable + * object that represents the data object(s) to + * be transfered.

+ * From this method, the DropTargetListener + * shall accept or reject the drop via the + * acceptDrop(int dropAction) or rejectDrop() methods of the + * DropTargetDropEvent parameter. + *

+ * Subsequent to acceptDrop(), but not before, + * DropTargetDropEvent's getTransferable() + * method may be invoked, and data transfer may be + * performed via the returned Transferable's + * getTransferData() method. + *

+ * At the completion of a drop, an implementation + * of this method is required to signal the success/failure + * of the drop by passing an appropriate + * boolean to the DropTargetDropEvent's + * dropComplete(boolean success) method. + *

+ * Note: The data transfer should be completed before the call to the + * DropTargetDropEvent's dropComplete(boolean success) method. + * After that, a call to the getTransferData() method of the + * Transferable returned by + * DropTargetDropEvent.getTransferable() is guaranteed to + * succeed only if the data transfer is local; that is, only if + * DropTargetDropEvent.isLocalTransfer() returns + * true. Otherwise, the behavior of the call is + * implementation-dependent. + *

+ * @param dtde the DropTargetDropEvent + */ + public void drop(DropTargetDropEvent dtde) { + boolean success = false; + if (canAccept(dtde.getCurrentDataFlavors())) { + Transferable t = dtde.getTransferable(); + if (t.isDataFlavorSupported(TransferableFormComponent.COMPONENT_FLAVOR)) { + dtde.acceptDrop(dtde.getDropAction()); + success = acceptComponent(t); + } + else if (t.isDataFlavorSupported(TransferableFormComponent.DASML_FRAGMENT_FLAVOR)) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + success = acceptFragment(t); + } + dtde.dropComplete(success); + } + else { + dtde.rejectDrop(); + } + onHover = false; + repaint(); + } + + private boolean acceptFragment(Transferable t) { + boolean success = false; + try { + String data = (String)t.getTransferData(TransferableFormComponent.DASML_FRAGMENT_FLAVOR); + Document document = FormBase.parseDasML(new StringReader(data), null); + Element root = document.getDocumentElement(); + if (root.getTagName().equals("form")) { + FormTab tab = new FormTab(root, FormBase.this); + addForm(tab); + success = true; + revalidate(); + } + else if (root.getTagName().equals("window")) { + FormWindow window = new FormWindow(root, FormBase.this); + addWindow(window); + success = true; + revalidate(); + } + } + catch (org.das2.dasml.ParsedExpressionException pee) { + pee.printStackTrace(); + } + + catch (org.das2.DasException de) { + de.printStackTrace(); + } catch (UnsupportedFlavorException ufe) { + //Allow to fall through. + //exception is handled by allowing success to remain false + } + catch (IOException ioe) { + DasExceptionHandler.handle(ioe); + //Allow to fall through. + //exception is handled by allowing success to remain false + } + catch (ParserConfigurationException pce) { + DasExceptionHandler.handle(pce); + //Allow to fall through. + //exception is handled by allowing success to remain false + } + catch (SAXException se) { + DasExceptionHandler.handle(se); + //Allow to fall through. + //exception is handled by allowing success to remain false + } + return success; + } + + private boolean acceptComponent(Transferable t) { + boolean success = false; + try { + Component c = (Component)t.getTransferData(TransferableFormComponent.COMPONENT_FLAVOR); + if (c instanceof FormTab) { + addForm((FormTab)c); + success = true; + revalidate(); + } + else if (c instanceof FormWindow) { + addWindow((FormWindow)c); + success = true; + revalidate(); + } + else { + System.out.println(c); + } + } + catch (UnsupportedFlavorException ufe) { + //Allow to fall through. + //exception is handled by allowing success to remain false + } + catch (IOException ioe) { + //Allow to fall through. + //exception is handled by allowing success to remain false + } + return success; + } + + /** Called if the user has modified + * the current drop gesture. + *

+ * @param dtde the DropTargetDragEvent + */ + public void dropActionChanged(DropTargetDragEvent dtde) { + if (onHover) { + dtde.acceptDrag(dtde.getDropAction()); + } + } + + private boolean canAccept(DataFlavor[] flavors) { + for (int i = 0; i < flavors.length; i++) { + if (acceptableFlavors.contains(flavors[i])) { + return true; + } + } + return false; + } + + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormButton.java b/dasCore/src/main/java/org/das2/dasml/FormButton.java new file mode 100644 index 000000000..1503fdf08 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormButton.java @@ -0,0 +1,228 @@ +/* File: FormButton.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import org.das2.DasPropertyException; +import org.das2.components.propertyeditor.Editable; + +/** + * This class is provided to override the Java Beans properties of + * the JButton class. + * + * @author Edward West + */ +public class FormButton extends JButton implements Editable, FormComponent { + + CommandAction commandAction; + + CommandBlock commandBlock; + + private boolean editable; + + private String dasName; + + protected org.das2.util.DnDSupport dndSupport; + + public FormButton(String name, String label) { + super(label); + if (name == null) { + name = "button_" + Integer.toHexString(System.identityHashCode(this)); + } + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + } + + /** Creates a new instance of FormButton */ + FormButton(Element element, FormBase form) + throws org.das2.DasPropertyException, ParsedExpressionException { + + String name = element.getAttribute("name"); + String label = element.getAttribute("label"); + boolean enabled = element.getAttribute("enabled").equals("true"); + + setText(label); + setEnabled(enabled); + + if (!name.equals("")) { + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + } + + NodeList children = element.getChildNodes(); + int childCount = children.getLength(); + for (int index = 0; index < childCount; index++) { + Node node = children.item(index); + if (node instanceof Element && node.getNodeName().equals("action")) { + Element actionElement = (Element)node; + commandBlock = new CommandBlock(actionElement, form); + commandAction = new CommandAction(commandBlock); + addActionListener(commandAction); + } + } + } + + public CommandBlock getFormAction() { + return commandBlock; + } + + public void setFormAction(CommandBlock cb) { + if (cb == commandBlock) { + return; + } + if (commandBlock != null) { + removeActionListener(commandAction); + } + if (cb == null) { + commandAction = null; + commandBlock = null; + } + else { + commandBlock = cb; + commandAction = new CommandAction(commandBlock); + addActionListener(commandAction); + } + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("button"); + element.setAttribute("name", getDasName()); + element.setAttribute("label", getText()); + element.setAttribute("enabled", String.valueOf(isEnabled())); + if (commandBlock != null) { + Element actionElement = document.createElement("action"); + commandBlock.appendDOMElements(actionElement); + element.appendChild(actionElement); + } + return element; + } + + public String getDasName() { + return dasName; + } + + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = getDasApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + public FormBase getForm() { + FormComponent parent = (FormComponent)getParent(); + if (parent == null) { + return null; + } + return parent.getForm(); + } + + public boolean getEditingMode() { + return editable; + } + + public void setEditingMode(boolean b) { + editable = b; + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new DefaultComponentDnDSupport(this); + } + return dndSupport; + } + + public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { + return false; + } + + public void deregisterComponent() { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + try { + if (nc.get(getDasName()) == this) { + nc.remove(getDasName()); + } + } + catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } + catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + } + + public DasApplication getDasApplication() { + java.awt.Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent)p).getDasApplication(); + } + else { + return null; + } + } + + public void registerComponent() throws DasException { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + nc.put(getDasName(), this); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormButtonBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormButtonBeanInfo.java new file mode 100644 index 000000000..9dd27469f --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormButtonBeanInfo.java @@ -0,0 +1,44 @@ +/* File: FormButtonBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +/** + * Bean info class for the FormRadioButton class + */ +public class FormButtonBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, "getDasName", "setDasName", null), + new Property("enabled", AccessLevel.DASML, "isEnabled", "setEnabled", null), + new Property("label", AccessLevel.ALL, "getText", "setText", null), + new Property("formAction", AccessLevel.ALL, "getFormAction", "setFormAction", CommandBlockEditor.class) + }; + + public FormButtonBeanInfo() { + super(properties, FormButton.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormCheckBox.java b/dasCore/src/main/java/org/das2/dasml/FormCheckBox.java new file mode 100644 index 000000000..883a38037 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormCheckBox.java @@ -0,0 +1,226 @@ +/* File: FormCheckBox.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import org.das2.DasPropertyException; +import org.das2.components.propertyeditor.Editable; + +/** + * This class is provided to override the Java Beans properties of + * the JCheckBox class. + * + * @author Edward West + */ +public class FormCheckBox extends JCheckBox implements Editable, FormComponent { + + CommandAction commandAction; + + CommandBlock commandBlock; + + private String dasName; + + protected org.das2.util.DnDSupport dndSupport; + + private boolean editable; + + public FormCheckBox(String name, String label) { + super(label); + if (name == null) { + name = "checkbox_" + Integer.toHexString(System.identityHashCode(this)); + } + try { + setDasName(name); + } + catch(org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + } + + FormCheckBox(Element element, FormBase form) + throws org.das2.DasPropertyException, ParsedExpressionException, + org.das2.DasNameException { + + String name = element.getAttribute("name"); + String label = element.getAttribute("label"); + boolean enabled = element.getAttribute("enabled").equals("true"); + boolean selected = element.getAttribute("selected").equals("true"); + + setText(label); + setEnabled(enabled); + setSelected(selected); + + try { + setDasName(name); + } + catch(org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + + NodeList children = element.getChildNodes(); + int childCount = children.getLength(); + for (int index = 0; index < childCount; index++) { + Node node = children.item(index); + if (node instanceof Element && node.getNodeName().equals("action")) { + commandBlock = new CommandBlock((Element)node, form); + commandAction = new CommandAction(commandBlock); + addActionListener(commandAction); + } + } + } + + public CommandBlock getFormAction() { + return commandBlock; + } + + public void setFormAction(CommandBlock cb) { + if (cb == commandBlock) { + return; + } + if (commandBlock != null) { + removeActionListener(commandAction); + } + if (cb == null) { + commandAction = null; + commandBlock = null; + } + else { + commandBlock = cb; + commandAction = new CommandAction(commandBlock); + addActionListener(commandAction); + } + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("checkbox"); + element.setAttribute("name", getDasName()); + element.setAttribute("enabled", String.valueOf(isEnabled())); + element.setAttribute("selected", String.valueOf(isSelected())); + element.setAttribute("label", getText()); + if (commandBlock != null) { + Element commandElement = document.createElement("action"); + commandBlock.appendDOMElements(element); + } + return element; + } + + public String getDasName() { + return dasName; + } + + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = getDasApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + public FormBase getForm() { + FormComponent parent = (FormComponent)getParent(); + if (parent == null) { + return null; + } + return parent.getForm(); + } + + public boolean getEditingMode() { + return editable; + } + + public void setEditingMode(boolean b) { + editable = b; + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new DefaultComponentDnDSupport(this); + } + return dndSupport; + } + + public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { + return false; + } + + public void deregisterComponent() { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + try { + if (nc.get(getDasName()) == this) { + nc.remove(getDasName()); + } + } + catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } + catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + } + + public DasApplication getDasApplication() { + java.awt.Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent)p).getDasApplication(); + } + else { + return null; + } + } + + public void registerComponent() throws DasException { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + nc.put(getDasName(), this); + } + } +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormCheckBoxBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormCheckBoxBeanInfo.java new file mode 100644 index 000000000..b183a80b1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormCheckBoxBeanInfo.java @@ -0,0 +1,45 @@ +/* File: FormCheckBoxBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +/** + * Bean info class for the FormRadioButton class + */ +public class FormCheckBoxBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, "getDasName", "setDasName", null), + new Property("enabled", AccessLevel.DASML, "isEnabled", "setEnabled", null), + new Property("selected", AccessLevel.DASML, "isSelected", "setSelected", null), + new Property("label", AccessLevel.ALL, "getText", "setText", null), + new Property("formAction", AccessLevel.ALL, "getFormAction", "setFormAction", CommandBlockEditor.class) + }; + + public FormCheckBoxBeanInfo() { + super(properties, FormCheckBox.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormChoice.java b/dasCore/src/main/java/org/das2/dasml/FormChoice.java new file mode 100644 index 000000000..2f71ab1ba --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormChoice.java @@ -0,0 +1,248 @@ +/* File: FormChoice.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import org.das2.DasPropertyException; +import org.das2.components.propertyeditor.Editable; + +/** + * Drop down list for making single selections. + */ +public class FormChoice extends JComboBox implements Editable, FormComponent, OptionList { + + protected org.das2.util.DnDSupport dndSupport; + + private boolean editable; + + private String dasName; + + public FormChoice(String name) { + if (name == null) { + name = "choice_" + Integer.toString(System.identityHashCode(this)); + } + try { + setDasName(name); + } + catch(org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + } + + FormChoice(Element element, FormBase form) + throws org.das2.DasPropertyException,org.das2.DasNameException, + ParsedExpressionException { + + super(); + + String name = element.getAttribute("name"); + + NodeList children = element.getChildNodes(); + int childCount = children.getLength(); + for (int i = 0; i < childCount; i++) { + Node node = children.item(i); + if (node instanceof Element && node.getNodeName().equals("option")) { + processOptionElement((Element)node); + } + else if (node instanceof Element && node.getNodeName().equals("action")) { + CommandBlock cb = new CommandBlock((Element)node, form); + CommandAction action = new CommandAction(cb); + addActionListener(action); + } + } + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + } + + public java.awt.Dimension getMinimumSize() { + return getPreferredSize(); + } + + public java.awt.Dimension getMaximumSize() { + return getPreferredSize(); + } + + private void processOptionElement(Element element) { + boolean selected = element.getAttribute("selected").equals("true"); + ListOption option = new ListOption(element); + addItem(option); + if (selected) setSelectedItem(option); + } + + public String getSelectedValue() { + ListOption selected = (ListOption)getSelectedItem(); + if (selected == null) { + return null; + } + return selected.getValue(); + } + + public void addOption(ListOption option) { + addItem(option); + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("choice"); + element.setAttribute("name", getDasName()); + element.setAttribute("enabled", String.valueOf(isEnabled())); + for (int index = 0; index < getItemCount(); index++) { + ListOption option = (ListOption)getItemAt(index); + element.appendChild(option.getDOMElement(document)); + } + return element; + } + + public ListOption[] getOptions() { + ListModel model = getModel(); + ListOption[] options = new ListOption[model.getSize()]; + for (int index = 0; index < options.length; index++) { + options[index] = (ListOption)model.getElementAt(index); + } + return options; + } + + public void setOptions(ListOption[] options) { + setModel(new DefaultComboBoxModel(options)); + if (options.length == 0) { + setSelectedItem(null); + } + else { + setSelectedItem(options[0]); + for (int index = 0; index < options.length; index++) { + if (options[index].isSelected()) { + setSelectedItem(options[index]); + } + } + } + } + + public Object getPrototypeDisplayValue() { + if (this.getItemCount() == 0) { + return "XXXXXXXXXXXX"; + } + return null; + } + + public String getDasName() { + return dasName; + } + + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = getDasApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + public FormBase getForm() { + FormComponent parent = (FormComponent)getParent(); + if (parent == null) { + return null; + } + return parent.getForm(); + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new DefaultComponentDnDSupport(this); + } + return dndSupport; + } + + public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { + return false; + } + + public boolean getEditingMode() { + return editable; + } + + public void setEditingMode(boolean b) { + editable = b; + } + public void deregisterComponent() { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + try { + if (nc.get(getDasName()) == this) { + nc.remove(getDasName()); + } + } + catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } + catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + } + + public DasApplication getDasApplication() { + java.awt.Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent)p).getDasApplication(); + } + else { + return null; + } + } + + public void registerComponent() throws DasException { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + nc.put(getDasName(), this); + } + } +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormChoiceBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormChoiceBeanInfo.java new file mode 100644 index 000000000..2035feb50 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormChoiceBeanInfo.java @@ -0,0 +1,45 @@ +/* File: FormChoiceBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +/** + * BeanInfo class for FormChoice + */ +public class FormChoiceBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, "getDasName", "setDasName", null), + new Property("selectedValue", AccessLevel.DASML, "getSelectedValue", null, null), + new Property("selectedIndex", AccessLevel.DASML, "getSelectedIndex", "setSelectedIndex", null), + new Property("enabled", AccessLevel.DASML, "isEnabled", "setEnabled", null), + new Property("options", AccessLevel.ALL, "getOptions", "setOptions", OptionListEditor.class) + }; + + public FormChoiceBeanInfo() { + super(properties, FormChoice.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormComponent.java b/dasCore/src/main/java/org/das2/dasml/FormComponent.java new file mode 100644 index 000000000..799a79787 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormComponent.java @@ -0,0 +1,62 @@ +/* File: FormComponent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.DasNameException; +import java.awt.event.MouseEvent; +import org.das2.util.DnDSupport; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * + * @author eew + */ +public interface FormComponent { + + Element getDOMElement(Document document); + + FormBase getForm(); + + boolean getEditingMode(); + + void setEditingMode(boolean b); + + DnDSupport getDnDSupport(); + + boolean startDrag(int x, int y, int action, MouseEvent evt); + + String getDasName(); + + void setDasName(String name) throws DasNameException; + + DasApplication getDasApplication(); + + void registerComponent() throws DasException; + + void deregisterComponent(); + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormContainer.java b/dasCore/src/main/java/org/das2/dasml/FormContainer.java new file mode 100644 index 000000000..33ed5fdfd --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormContainer.java @@ -0,0 +1,624 @@ +/* File: FormContainer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.graph.DasCanvas; +import org.das2.graph.dnd.TransferableCanvas; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; +import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.util.List; +import org.das2.DasNameException; +import org.das2.components.propertyeditor.Editable; + +/** + * A subclass of JPanel to override the default Beans properties of + * a JPanel + * + * @author Edward West + */ +public abstract class FormContainer extends JPanel implements Editable, FormComponent { + + float horizontalComponentAlignment = JComponent.LEFT_ALIGNMENT; + final float verticalComponentAlignment = JComponent.TOP_ALIGNMENT; + + boolean onHover = false; + org.das2.util.DnDSupport dndSupport; + List flavorList; + int dropPosition; + boolean editable; + + /** + * The axis along which child components will be laid out. + */ + private int axis = BoxLayout.X_AXIS; + + /** + * The titled displayed along the panel border. + */ + private String borderTitle = ""; + + /** + * If true, the panel has an etched border. + */ + private boolean hasBorder = false; + + /** + * Empty constructor for use with super classes. + */ + protected FormContainer() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + setBorder(new NoBorder()); + } + + /** + * Returns true if this panel has a border + */ + public boolean hasBorder() { + return hasBorder; + } + + /** + * If the given boolean is true then the panel will be + * given a border, if is does not already have one. If the given + * boolean is false then the panel's border will be + * removed, if it has one. + */ + public void setHasBorder(boolean b) { + if (hasBorder != b) { + hasBorder = b; + if (hasBorder) { + EtchedBorder etchedBorder = new EtchedBorder(); + TitledBorder titledBorder = new TitledBorder(etchedBorder, borderTitle, TitledBorder.LEFT, TitledBorder.TOP); + setBorder(titledBorder); + } + else { + setBorder(new NoBorder()); + } + } + } + + /** + * Returns the title that is displayed along the top of this panels border + * (if it has one). + */ + public String getBorderTitle() { + return borderTitle; + } + + /** + * Set the title that is displayed along the top of this panels border + * (if it has one) to the given String. + */ + public void setBorderTitle(String s) { + if (!s.equals(borderTitle)) { + borderTitle = s; + if (hasBorder) { + EtchedBorder etchedBorder = new EtchedBorder(); + TitledBorder titledBorder = new TitledBorder(etchedBorder, borderTitle, TitledBorder.LEFT, TitledBorder.TOP); + setBorder(titledBorder); + } + } + } + + /** Adds the specified component to this container at the specified + * index. This method also notifies the layout manager to add + * the component to this container's layout using the specified + * constraints object via the addLayoutComponent + * method. The constraints are + * defined by the particular layout manager being used. For + * example, the BorderLayout class defines five + * constraints: BorderLayout.NORTH, + * BorderLayout.SOUTH, BorderLayout.EAST, + * BorderLayout.WEST, and BorderLayout.CENTER. + * + *

Note that if the component already exists + * in this container or a child of this container, + * it is removed from that container before + * being added to this container. + *

+ * This is the method to override if a program needs to track + * every add request to a container as all other add methods defer + * to this one. An overriding method should + * usually include a call to the superclass's version of the method: + *

+ *

+ * super.addImpl(comp, constraints, index) + *
+ *

+ * @param comp the component to be added + * @param constraints an object expressing layout constraints + * for this component + * @param index the position in the container's list at which to + * insert the component, where -1 + * means append to the end + * @exception IllegalArgumentException if index is invalid + * @exception IllegalArgumentException if adding the container's parent + * to itself + * @exception IllegalArgumentException if adding a window to a container + * @see java.awt.Container#add(Component) + * @see java.awt.Container#add(Component, int) + * @see java.awt.Container#add(Component, java.lang.Object) + * @see java.awt.LayoutManager + * @see java.awt.LayoutManager2 + */ + protected void addImpl(Component comp, Object constraints, int index) { + if (comp instanceof JComponent) { + ((JComponent)comp).setAlignmentX(horizontalComponentAlignment); + ((JComponent)comp).setAlignmentY(verticalComponentAlignment); + } + super.addImpl(comp, constraints, index); + if (comp instanceof FormComponent) { + FormComponent fc = (FormComponent)comp; + org.das2.util.DnDSupport childDnDSupport = fc.getDnDSupport(); + if (childDnDSupport != null) { + childDnDSupport.setParent(dndSupport); + } + fc.setEditingMode(getEditingMode()); + } + packFormWindowAnscestor(); + } + + private void packFormWindowAnscestor() { + if (isDisplayable()) { + if (this instanceof FormWindow) { + ((FormWindow)this).pack(); + } + else { + FormWindow fw = (FormWindow)SwingUtilities.getAncestorOfClass(FormWindow.class, this); + if (fw != null) { + fw.pack(); + } + } + } + } + + public void removeAll() { + int ncomponents = getComponentCount(); + for (int index = ncomponents-1; index >= 0; index--) { + remove(index); + } + } + + public void remove(int index) { + super.remove(index); + packFormWindowAnscestor(); + } + + public void remove(Component c) { + super.remove(c); + packFormWindowAnscestor(); + } + + public void setDirection(Orientation direction) { + if (direction == Orientation.HORIZONTAL) { + if (axis != BoxLayout.X_AXIS) { + axis = BoxLayout.X_AXIS; + setLayout(new BoxLayout(this, axis)); + getForm().validate(); + } + } + else if (axis != BoxLayout.Y_AXIS) { + axis = BoxLayout.Y_AXIS; + setLayout(new BoxLayout(this, axis)); + revalidate(); + } + } + + public Orientation getDirection() { + if (axis == BoxLayout.X_AXIS) return Orientation.HORIZONTAL; + if (axis == BoxLayout.Y_AXIS) return Orientation.VERTICAL; + throw new AssertionError("Invalid value for axis"); + } + + /** + * Returns the FormBase object this component is associated with, or null + */ + public FormBase getForm() { + FormComponent parent = (FormComponent)getParent(); + if (parent == null) { + return null; + } + return parent.getForm(); + } + + /** + * Returns true if this component is in an editing state. + * @return true if this component is in an editing state. + */ + public boolean getEditingMode() { + return editable; + } + + public void paint(Graphics g) { + super.paint(g); + if (onHover) { + Graphics2D g2 = (Graphics2D)g.create(); + Stroke thick = new BasicStroke(3.0f); + g2.setStroke(thick); + g2.setPaint(Color.GRAY); + g2.drawRect(1, 1, getWidth() - 2, getHeight() - 2); + g2.setPaint(Color.ORANGE); + if (getDirection() == Orientation.HORIZONTAL) { + g2.drawLine(dropPosition, 4, dropPosition, getHeight() - 4); + } + else { + g2.drawLine(4, dropPosition, getHeight() - 4, dropPosition); + } + g2.dispose(); + } + } + + public void setEditingMode(boolean b) { + if (editable == b) return; + editable = b; + int componentCount = getComponentCount(); + for (int i = 0; i < componentCount; i++) { + if (getComponent(i) instanceof FormComponent) { + ((FormComponent)getComponent(i)).setEditingMode(b); + } + } + } + + private int[] getInsertionPositions() { + int componentCount = getComponentCount(); + int[] positions = new int[componentCount + 1]; + if (getDirection() == Orientation.HORIZONTAL) { + positions[0] = 2; + for (int i = 1; i <= componentCount; i++) { + Component c = getComponent(i - 1); + positions[i] = c.getX() + c.getWidth() + 1; + } + } + else { + positions[0] = 2; + for (int i = 1; i <= componentCount; i++) { + Component c = getComponent(i - 1); + positions[i] = c.getY() + c.getHeight() + 1; + } + } + return positions; + } + + private int getInsertionPosition(int p) { + int[] positions = getInsertionPositions(); + int insertionPosition = 0; + int dp = Integer.MAX_VALUE; + for (int i = 0; i < positions.length; i++) { + int delta = Math.abs(p - positions[i]); + if (delta < dp) { + dp = delta; + insertionPosition = positions[i]; + } + } + return insertionPosition; + } + + private int getInsertionIndex(int p) { + int[] positions = getInsertionPositions(); + int insertionIndex = 0; + int dp = Integer.MAX_VALUE; + for (int i = 0; i < positions.length; i++) { + int delta = Math.abs(p - positions[i]); + if (delta < dp) { + dp = delta; + insertionIndex = i; + } + } + return insertionIndex; + } + + public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { + for (int i = 0; i < getComponentCount(); i++) { + if (getComponent(i).getBounds().contains(x, y)) { + dndSupport.startDrag(x, y, action, evt); + return true; + } + } + return false; + } + + public Dimension getPreferredSize() { + if (getComponentCount() == 0) { + return new Dimension(100, 100); + } + else { + return super.getPreferredSize(); + } + } + + public Dimension getMinimumSize() { + if (getComponentCount() == 0) { + return new Dimension(100, 100); + } + else { + return super.getMinimumSize(); + } + } + + public Dimension getMaximumSize() { + Dimension pref = getPreferredSize(); + Dimension max = super.getMaximumSize(); + max.width = Math.max(max.width, 100); + max.height = pref.height; + return max; + } + + public String getDasName() { + return null; + } + + public void setDasName(String name) throws org.das2.DasNameException { + throw new org.das2.DasNameException(); + } + + public void deregisterComponent() { + for (int index = 0; index < getComponentCount(); index++) { + Component c = getComponent(index); + if (c instanceof FormComponent) { + ((FormComponent)c).deregisterComponent(); + } + } + } + + public DasApplication getDasApplication() { + Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent)p).getDasApplication(); + } + else { + return null; + } + } + + public void registerComponent() throws DasException { + try { + for (int index = 0; index < getComponentCount(); index++) { + Component c = getComponent(index); + if (c instanceof FormComponent) { + ((FormComponent)c).registerComponent(); + } + } + } + catch (DasNameException dne) { + deregisterComponent(); + throw dne; + } + } + + class NoBorder extends EmptyBorder { + + Color color = Color.GRAY; + + NoBorder() { + super(5, 5, 5, 5); + } + + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if (editable) { + g.setColor(color); + g.drawRect(x + 2, y + 2, width - 4, height - 4); + } + } + } + + protected class ContainerDnDSupport extends org.das2.util.DnDSupport { + + ContainerDnDSupport(org.das2.util.DnDSupport parent) { + super(FormContainer.this, java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE, parent); + } + + protected int canAccept(DataFlavor[] flavors, int x, int y, int action) { + if (flavorList != null && getEditingMode()) { + for (int i = 0; i < flavors.length; i++) { + if (flavorList.contains(flavors[i])) { + onHover = true; + if (getDirection() == Orientation.HORIZONTAL) { + dropPosition = getInsertionPosition(x); + } + else { + dropPosition = getInsertionPosition(y); + } + repaint(); + return action; + } + } + } + return -1; + } + + protected void done() { + onHover = false; + repaint(); + } + + protected boolean importData(Transferable t, int x, int y, int action) { + boolean success = false; + try { + int insertionIndex; + if (getDirection() == Orientation.HORIZONTAL) { + insertionIndex = getInsertionIndex(x); + } + else { + insertionIndex = getInsertionIndex(y); + } + Component c = null; + if (t.isDataFlavorSupported(TransferableFormComponent.COMPONENT_FLAVOR)) { + c = (Component)t.getTransferData(TransferableFormComponent.COMPONENT_FLAVOR); + } + else if (t.isDataFlavorSupported(TransferableCanvas.CANVAS_FLAVOR)) { + c = (Component)t.getTransferData(TransferableCanvas.CANVAS_FLAVOR); + } + else if (t.isDataFlavorSupported(TransferableFormComponent.DASML_FRAGMENT_FLAVOR)) { + c = getComponentFromDasMLFragment((String)t.getTransferData(TransferableFormComponent.DASML_FRAGMENT_FLAVOR)); + } + if (c != null) { + if (!(c instanceof FormTab) && !(c instanceof FormWindow)) { + if (c != FormContainer.this && !SwingUtilities.isDescendingFrom(FormContainer.this, c)) { + if (c.getParent() == FormContainer.this) { + int cIndex = -1; + int componentCount = getComponentCount(); + for (int i = 0; i < componentCount; i++) { + if (getComponent(i) == c) { + cIndex = i; + break; + } + } + if (insertionIndex > cIndex) { + insertionIndex--; + } + remove(cIndex); + add(c, insertionIndex); + success = true; + } + else { + add(c, insertionIndex); + success = true; + } + } + } + } + } + catch (UnsupportedFlavorException ufe) { + } + catch (IOException ioe) { + } + if (success) { + revalidate(); + } + return success; + } + + private Component getComponentFromDasMLFragment(String dasML) { + try { + Document document = FormBase.parseDasML(new java.io.StringReader(dasML), null); + Element element = document.getDocumentElement(); + String tag = element.getTagName(); + if (tag.equals("panel")) { + return new FormPanel(element, getForm()); + } + else if (tag.equals("radiobutton")) { + return new FormRadioButton(element, getForm()); + } + else if (tag.equals("textfield")) { + return new FormTextField(element, getForm()); + } + else if (tag.equals("text")) { + return new FormText(element); + } + else if (tag.equals("button")) { + return new FormButton(element, getForm()); + } + else if (tag.equals("checkbox")) { + return new FormCheckBox(element, getForm()); + } + else if (tag.equals("buttongroup")) { + return new FormRadioButtonGroup(element, getForm()); + } + else if (tag.equals("canvas")) { + return DasCanvas.processCanvasElement(element, getForm()); + } + else if (tag.equals("choice")) { + return new FormChoice(element, getForm()); + } + } + catch (javax.xml.parsers.ParserConfigurationException pce) { + throw new RuntimeException(pce); + } + catch (org.xml.sax.SAXException se) { + throw new RuntimeException(se); + } + + catch (org.das2.DasException de) { + org.das2.util.DasExceptionHandler.handle(de); + } catch (org.das2.dasml.ParsedExpressionException pee) { + org.das2.util.DasExceptionHandler.handle(pee); + } + catch (IOException ioe) { + org.das2.util.DasExceptionHandler.handle(ioe); + } + catch ( java.text.ParseException ex ) { + org.das2.util.DasExceptionHandler.handle(ex); + } + return null; + } + + protected Transferable getTransferable(int x, int y, int action) { + for (int i = 0; i < getComponentCount(); i++) { + Component c = getComponent(i); + if (c.getBounds().contains(x, y)) { + if (c instanceof DasCanvas) { + return new TransferableCanvas((DasCanvas)c); + } + else if (c instanceof FormPanel) { + return new TransferableFormComponent((FormPanel)c); + } + else if (c instanceof FormText) { + return new TransferableFormComponent((FormText)c); + } + else if (c instanceof FormTextField) { + return new TransferableFormComponent((FormTextField)c); + } + else if (c instanceof FormButton) { + return new TransferableFormComponent((FormButton)c); + } + else if (c instanceof FormCheckBox) { + return new TransferableFormComponent((FormCheckBox)c); + } + else if (c instanceof FormRadioButtonGroup) { + return new TransferableFormComponent((FormRadioButtonGroup)c); + } + else if (c instanceof FormRadioButton) { + return new TransferableFormComponent((FormRadioButton)c); + } + else if (c instanceof FormTab) { + return new TransferableFormComponent((FormTab)c); + } + else if (c instanceof FormChoice) { + return new TransferableFormComponent((FormChoice)c); + } + else if (c instanceof FormList) { + return new TransferableFormComponent((FormList)c); + } + } + } + return null; + } + + protected void exportDone(Transferable t, int action) { + } + + } +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormList.java b/dasCore/src/main/java/org/das2/dasml/FormList.java new file mode 100644 index 000000000..433422876 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormList.java @@ -0,0 +1,320 @@ +/* File: FormList.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import javax.swing.event.MouseInputListener; +import javax.swing.plaf.basic.BasicListUI; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.util.ArrayList; +import java.util.List; +import org.das2.DasPropertyException; +import org.das2.components.propertyeditor.Editable; + +public class FormList extends JList implements Editable, FormComponent { + + private String delimiter = " "; + + protected org.das2.util.DnDSupport dndSupport; + + private String dasName; + + private boolean editable; + + public FormList(String name) { + try { + setDasName(name); + } + catch(org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + } + + FormList(Element element, FormBase form) + throws org.das2.DasPropertyException,org.das2.DasNameException, org.das2.DasException , + ParsedExpressionException { + + super(new OptionListModel()); + + String name = element.getAttribute("name"); + String selectionMode = element.getAttribute("selectionMode"); + + if (selectionMode.equals("single")) { + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + } + else if (selectionMode.equals("multiple")) { + setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + } + + setDelimiter(element.getAttribute("delimiter")); + setEnabled(element.getAttribute("enabled").equals("true")); + + NodeList children = element.getChildNodes(); + int childCount = children.getLength(); + for (int i = 0; i < childCount; i++) { + Node node = children.item(i); + if (node instanceof Element && node.getNodeName().equals("option")) { + processOptionElement((Element)node); + } + } + setMinimumSize(getPreferredSize()); + setMaximumSize(getPreferredSize()); + try { + setDasName(name); + } + catch(org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + } + + private void processOptionElement(Element element) { + ListOption option = new ListOption(element); + ((OptionListModel)getModel()).list.add(option); + boolean selected = element.getAttribute("selected").equals("true"); + if (selected) addSelectionInterval(getModel().getSize()-1, getModel().getSize()-1); + } + + + public void addMouseListener(MouseListener l) { + if (l instanceof BasicListUI.MouseInputHandler) { + l = new CtrlDownMouseInputListener((MouseInputListener)l); + } + super.addMouseListener(l); + } + + public void addMouseMotionListener(MouseMotionListener l) { + if (l instanceof BasicListUI.MouseInputHandler) { + l = new CtrlDownMouseInputListener((MouseInputListener)l); + } + super.addMouseMotionListener(l); + } + + public void addItem(ListOption o) { + ((OptionListModel)getModel()).list.add(o); + } + + public ListOption getItem(int index) { + return (ListOption)((OptionListModel)getModel()).list.get(index); + } + + public int getItemCount() { + return ((OptionListModel)getModel()).list.size(); + } + + public void setDelimiter(String delimiter) { + this.delimiter = delimiter; + } + + public String getDelimiter() { + return delimiter; + } + + public String getSelected() { + Object[] o = getSelectedValues(); + if (o.length == 0) return ""; + String result = ((ListOption)o[0]).getValue(); + for (int i = 1; i < o.length; i++) { + result = result + delimiter + ((ListOption)o[i]).getValue(); + } + return result; + } + + private static class CtrlDownMouseEvent extends MouseEvent { + private static final int CTRL_YES = CTRL_MASK | CTRL_DOWN_MASK; + private static final int SHIFT_NO = -(SHIFT_MASK | SHIFT_DOWN_MASK)-1; + public CtrlDownMouseEvent(MouseEvent e) { + super(e.getComponent(), e.getID(), e.getWhen(), + (e.getModifiers() | CTRL_YES) & SHIFT_NO, + e.getX(), e.getY(), e.getClickCount(), + e.isPopupTrigger(), e.getButton()); + } + } + + private static class CtrlDownMouseInputListener extends MouseInputAdapter { + private MouseInputListener listener; + public CtrlDownMouseInputListener(MouseInputListener listener) { + this.listener = listener; + } + public void mousePressed(MouseEvent e) { + listener.mousePressed(new CtrlDownMouseEvent(e)); + } + public void mouseReleased(MouseEvent e) { + listener.mouseReleased(new CtrlDownMouseEvent(e)); + } + public void mouseDragged(MouseEvent e) { + listener.mouseDragged(new CtrlDownMouseEvent(e)); + } + } + + void setSelected(Object item, boolean b) { + OptionListModel model = (OptionListModel)getModel(); + int index = 0; + while (index < model.list.size() && model.list.get(index) != item) { + index++; + } + if (model.list.get(index) != item) return; + if (b) { + addSelectionInterval(index, index); + } + else { + removeSelectionInterval(index, index); + } + } + + + public Element getDOMElement(Document document) { + Element element = document.createElement("list"); + element.setAttribute("name", getDasName()); + element.setAttribute("delimiter", delimiter); + element.setAttribute("enabled", (isEnabled() ? "true" : "false")); + for (int index = 0; index < getItemCount(); index++) { + element.appendChild(getItem(0).getDOMElement(document)); + } + return element; + } + + private static class OptionListModel extends AbstractListModel { + + List list = new ArrayList(); + + /** Returns the value at the specified index.s + * @param index the requested index + * @return the value at index + */ + public Object getElementAt(int index) { + return list.get(index); + } + + /** + * Returns the length of the list. + * @return the length of the list + */ + public int getSize() { + return list.size(); + } + + } + + public FormBase getForm() { + FormComponent parent = (FormComponent)getParent(); + if (parent == null) { + return null; + } + return parent.getForm(); + } + + public boolean getEditingMode() { + return editable; + } + + public void setEditingMode(boolean b) { + editable = b; + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new DefaultComponentDnDSupport(this); + } + return dndSupport; + } + + public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { + return false; + } + + public String getDasName() { + return dasName; + } + + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = getDasApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + public void deregisterComponent() { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + try { + if (nc.get(getDasName()) == this) { + nc.remove(getDasName()); + } + } + catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } + catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + } + + public DasApplication getDasApplication() { + java.awt.Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent)p).getDasApplication(); + } + else { + return null; + } + } + + public void registerComponent() throws DasException { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + nc.put(getDasName(), this); + } + } +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormListBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormListBeanInfo.java new file mode 100644 index 000000000..cf0e70eda --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormListBeanInfo.java @@ -0,0 +1,43 @@ +/* File: FormListBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +/** + * BeanInfo class for FormList + */ +public class FormListBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, "getDasName", "setDasName", null), + new Property("selected", AccessLevel.DASML, "getSelected", null, null), + new Property("delimiter", AccessLevel.DASML, "getDelimiter", "setDelimiter", null), + new Property("enabled", AccessLevel.DASML, "isEnabled", "setEnabled", null) + }; + + public FormListBeanInfo() { + super(properties, FormList.class); + } +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormPanel.java b/dasCore/src/main/java/org/das2/dasml/FormPanel.java new file mode 100644 index 000000000..966500761 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormPanel.java @@ -0,0 +1,186 @@ +/* File: FormPanel.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.graph.DasCanvas; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import org.das2.components.propertyeditor.Editable; + +/** + * A subclass of JPanel to override the default Beans properties of + * a JPanel + * + * @author Edward West + */ +public class FormPanel extends FormContainer implements Editable, FormComponent { + + /** Initializer for flavorList */ + { + DataFlavor[] flavors = { + TransferableFormComponent.BUTTONGROUP_FLAVOR, + TransferableFormComponent.BUTTON_FLAVOR, + TransferableFormComponent.CHECKBOX_FLAVOR, + TransferableFormComponent.CHOICE_FLAVOR, + //TransferableFormComponent.LIST_FLAVOR, + TransferableFormComponent.PANEL_FLAVOR, + TransferableFormComponent.TEXTFIELD_FLAVOR, + TransferableFormComponent.TEXT_FLAVOR, + org.das2.graph.dnd.TransferableCanvas.CANVAS_FLAVOR + }; + flavorList = java.util.Arrays.asList(flavors); + } + + + /** + * Empty constructor for use with super classes. + */ + public FormPanel() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + setBorder(new NoBorder()); + } + + /** + * Constructs a FormPanel object associated with the given + * FormBase instance and initialized with the values in the given + * Element instance. + */ + FormPanel(Element element, FormBase form) + throws org.das2.DasException, + ParsedExpressionException, org.xml.sax.SAXException, java.text.ParseException { + + super(); + + String alignment = element.getAttribute("alignment"); + if (alignment.equals("left")) { + horizontalComponentAlignment = JComponent.LEFT_ALIGNMENT; + } + else if (alignment.equals("right")) { + horizontalComponentAlignment = JComponent.RIGHT_ALIGNMENT; + } + else { + horizontalComponentAlignment = JComponent.CENTER_ALIGNMENT; + } + + String direction = element.getAttribute("direction"); + BoxLayout layout; + if (direction.equals("horizontal")) { + layout = new BoxLayout(this, BoxLayout.X_AXIS); + } + else { + layout = new BoxLayout(this, BoxLayout.Y_AXIS); + } + setLayout(layout); + + NodeList children = element.getChildNodes(); + int length = children.getLength(); + for (int index = 0; index < length; index++) { + Node node = children.item(index); + if (node instanceof Element) { + String tagName = node.getNodeName(); + if (tagName.equals("panel")) { + FormPanel p = new FormPanel((Element)node, form); + add(p); + } + else if (tagName.equals("text") || tagName.equals("info")) { + FormText text = new FormText((Element)node); + add(text); + } + else if (tagName.equals("textfield")) { + FormTextField textfield = new FormTextField((Element)node, form); + add(textfield); + } + else if (tagName.equals("button")) { + FormButton button = new FormButton((Element)node, form); + add(button); + } + else if (tagName.equals("checkbox")) { + FormCheckBox checkbox = new FormCheckBox((Element)node, form); + add(checkbox); + } + else if (tagName.equals("list")) { + FormList list = new FormList((Element)node, form); + add(list); + } + else if (tagName.equals("choice")) { + FormChoice choice = new FormChoice((Element)node, form); + add(choice); + } + else if (tagName.equals("glue")) { + add(form.processGlueElement((Element)node)); + } + else if (tagName.equals("buttongroup")) { + add(new FormRadioButtonGroup((Element)node, form)); + } + else if (tagName.equals("canvas")) { + DasCanvas canvas = DasCanvas.processCanvasElement((Element)node, form); + add(canvas); + } + else { + //DO NOTHING RIGHT NOW + } + } + else { + //TODO: do some sort of error handling here. + } + } + + setHasBorder(element.getAttribute("border").equals("true")); + setBorderTitle(element.getAttribute("border-title")); + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("panel"); + element.setAttribute("border", Boolean.toString(hasBorder())); + element.setAttribute("border-title", getBorderTitle()); + for (int index = 0; index < getComponentCount(); index++) { + Component comp = getComponent(index); + if (comp instanceof FormComponent) { + FormComponent formComponent = (FormComponent)comp; + Element child = formComponent.getDOMElement(document); + element.appendChild(child); + } + else if (comp instanceof DasCanvas) { + DasCanvas canvas = (DasCanvas)comp; + Element child = canvas.getDOMElement(document); + element.appendChild(child); + } + } + return element; + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new ContainerDnDSupport(null); + } + return dndSupport; + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormPanelBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormPanelBeanInfo.java new file mode 100644 index 000000000..5679b53f0 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormPanelBeanInfo.java @@ -0,0 +1,43 @@ +/* File: FormPanelBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +/** + * Bean info class for the FormPanel class + */ +public class FormPanelBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("border", AccessLevel.ALL, "hasBorder", "setHasBorder", null), + new Property("borderTitle", AccessLevel.ALL, "getBorderTitle", "setBorderTitle", null), + new Property("direction", AccessLevel.ALL, "getDirection", "setDirection", null) + }; + + public FormPanelBeanInfo() { + super(properties, FormPanel.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormRadioButton.java b/dasCore/src/main/java/org/das2/dasml/FormRadioButton.java new file mode 100644 index 000000000..1ed6dead9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormRadioButton.java @@ -0,0 +1,190 @@ +/* File: FormRadioButton.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.swing.*; +import org.das2.DasPropertyException; +import org.das2.components.propertyeditor.Editable; + +public class FormRadioButton extends JRadioButton implements Editable, FormComponent { + + private String dasName; + + private String value; + + protected org.das2.util.DnDSupport dndSupport; + + private boolean editable; + + public FormRadioButton(String name, String label) { + super(label); + if (name == null) { + name = "radiobutton_" + Integer.toHexString(System.identityHashCode(this)); + } + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + } + + FormRadioButton(Element element, FormBase form) + throws org.das2.DasPropertyException, ParsedExpressionException, + org.das2.DasNameException { + + String name = element.getAttribute("name"); + String label = element.getAttribute("label"); + boolean selected = element.getAttribute("selected").equals("true"); + boolean enabled = element.getAttribute("enabled").equals("true"); + + setText(label); + setSelected(selected); + setEnabled(enabled); + + if (!name.equals("")) { + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + } + + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("radiobutton"); + element.setAttribute("name", getDasName()); + element.setAttribute("selected", String.valueOf(isSelected())); + element.setAttribute("enabled", String.valueOf(isEnabled())); + element.setAttribute("label", getText()); + return element; + } + + public FormBase getForm() { + FormComponent parent = (FormComponent)getParent(); + if (parent == null) { + return null; + } + return parent.getForm(); + } + + public boolean getEditingMode() { + return editable; + } + + public void setEditingMode(boolean b) { + editable = b; + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new DefaultComponentDnDSupport(this); + } + return dndSupport; + } + + public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { + return false; + } + + public String getDasName() { + return dasName; + } + + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = getDasApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + public void deregisterComponent() { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + try { + if (nc.get(getDasName()) == this) { + nc.remove(getDasName()); + } + } + catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } + catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + } + + public DasApplication getDasApplication() { + java.awt.Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent)p).getDasApplication(); + } + else { + return null; + } + } + + public void registerComponent() throws DasException { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + nc.put(getDasName(), this); + } + } +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormRadioButtonBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormRadioButtonBeanInfo.java new file mode 100644 index 000000000..d1eaef662 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormRadioButtonBeanInfo.java @@ -0,0 +1,45 @@ +/* File: FormRadioButtonBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +/** + * Bean info class for the FormRadioButton class + */ +public class FormRadioButtonBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, "getDasName", "setDasName", null), + new Property("value", AccessLevel.DASML, "getValue", "setValue", null), + new Property("selected", AccessLevel.DASML, "isSelected", "setSelected", null), + new Property("enabled", AccessLevel.DASML, "isEnabled", "setEnabled", null), + new Property("label", AccessLevel.ALL, "getLabel", "setLabel", null) + }; + + public FormRadioButtonBeanInfo() { + super(properties, FormRadioButton.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormRadioButtonGroup.java b/dasCore/src/main/java/org/das2/dasml/FormRadioButtonGroup.java new file mode 100644 index 000000000..ed1d6f8ac --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormRadioButtonGroup.java @@ -0,0 +1,129 @@ +/* File: FormRadioButtonGroup.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import org.das2.components.propertyeditor.Editable; + +public class FormRadioButtonGroup extends FormContainer implements Editable, FormComponent { + + /** + * The button group the radio buttons will be associated with. + */ + private ButtonGroup group; + + /** Initializer for flavorList */ + { + DataFlavor[] flavors = { + TransferableFormComponent.RADIOBUTTON_FLAVOR + }; + flavorList = java.util.Arrays.asList(flavors); + } + + public FormRadioButtonGroup() { + setDirection(Orientation.HORIZONTAL); + setHasBorder(false); + group = new ButtonGroup(); + } + + FormRadioButtonGroup(Element element, FormBase form) + throws org.das2.DasPropertyException, ParsedExpressionException, + org.das2.DasNameException, org.xml.sax.SAXException { + + group = new ButtonGroup(); + + String alignment = element.getAttribute("alignment"); + if (alignment.equals("left")) { + horizontalComponentAlignment = JComponent.LEFT_ALIGNMENT; + } + else if (alignment.equals("right")) { + horizontalComponentAlignment = JComponent.RIGHT_ALIGNMENT; + } + else { + horizontalComponentAlignment = JComponent.CENTER_ALIGNMENT; + } + + String direction = element.getAttribute("direction"); + BoxLayout layout; + if (direction.equals("horizontal")) { + setDirection(Orientation.HORIZONTAL); + } + else { + setDirection(Orientation.VERTICAL); + } + + NodeList children = element.getChildNodes(); + int length = children.getLength(); + for (int index = 0; index < length; index++) { + Node node = children.item(index); + if (node instanceof Element) { + String tagName = node.getNodeName(); + if (tagName.equals("radiobutton")) { + FormRadioButton radiobutton = new FormRadioButton((Element)node, form); + radiobutton.setAlignmentX(horizontalComponentAlignment); + add(radiobutton); + } + } + } + + setHasBorder(element.getAttribute("border").equals("true")); + setBorderTitle(element.getAttribute("border-title")); + } + + protected void addImpl(Component comp, Object constraints, int index) { + if (comp instanceof FormRadioButton) { + FormRadioButton radiobutton = (FormRadioButton)comp; + super.addImpl(radiobutton, constraints, index); + group.add(radiobutton); + } + else { + throw new IllegalArgumentException("Only FormRadioButton instances allowed"); + } + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("group"); + for (int index = 0; index < getComponentCount(); index++) { + FormComponent comp = (FormComponent)getComponent(index); + Element child = comp.getDOMElement(document); + element.appendChild(child); + } + return element; + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new ContainerDnDSupport(null); + } + return dndSupport; + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormRadioButtonGroupBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormRadioButtonGroupBeanInfo.java new file mode 100644 index 000000000..2ac0045c3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormRadioButtonGroupBeanInfo.java @@ -0,0 +1,43 @@ +/* File: FormRadioButtonGroupBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +/** + * BeanInfo class for FormRadioButtonGroup + */ +public class FormRadioButtonGroupBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("border", AccessLevel.DASML, "hasBorder", "setHasBorder", null), + new Property("borderTitle", AccessLevel.DASML, "getBorderTitle", "setBorderTitle", null), + new Property("direction", AccessLevel.ALL, "getDirection", "setDirection", null) + }; + + public FormRadioButtonGroupBeanInfo() { + super(properties, FormRadioButtonGroup.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormTab.java b/dasCore/src/main/java/org/das2/dasml/FormTab.java new file mode 100644 index 000000000..a59d3bc69 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormTab.java @@ -0,0 +1,284 @@ +/* File: FormTab.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.DasPropertyException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import org.das2.util.DasExceptionHandler; + +/** + * + * @author eew + */ +public class FormTab extends FormContainer { + + private String label; + + private String dasName; + + /** Initializer for flavorList */ + { + java.awt.datatransfer.DataFlavor[] flavors = { + TransferableFormComponent.BUTTONGROUP_FLAVOR, + TransferableFormComponent.BUTTON_FLAVOR, + TransferableFormComponent.CHECKBOX_FLAVOR, + TransferableFormComponent.CHOICE_FLAVOR, + //TransferableFormComponent.LIST_FLAVOR, + TransferableFormComponent.PANEL_FLAVOR, + TransferableFormComponent.TEXTFIELD_FLAVOR, + TransferableFormComponent.TEXT_FLAVOR, + org.das2.graph.dnd.TransferableCanvas.CANVAS_FLAVOR + }; + flavorList = java.util.Arrays.asList(flavors); + } + + /** Creates a new instance of Form */ + public FormTab(String name, String label) { + super(); + setDirection(Orientation.VERTICAL); + if (name == null) { + name = "form_" + Integer.toHexString(System.identityHashCode(this)); + } + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + this.label = label; + dndSupport = new ContainerDnDSupport(null); + } + + FormTab(Element element, FormBase form) + throws DasException, + ParsedExpressionException, org.xml.sax.SAXException { + + String alignment = element.getAttribute("alignment"); + if (alignment.equals("left")) { + horizontalComponentAlignment = JComponent.LEFT_ALIGNMENT; + } + else if (alignment.equals("right")) { + horizontalComponentAlignment = JComponent.RIGHT_ALIGNMENT; + } + else { + horizontalComponentAlignment = JComponent.CENTER_ALIGNMENT; + } + String name = element.getAttribute("name"); + String label = element.getAttribute("label"); + setDirection(Orientation.VERTICAL); + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + if (label.equals("")) { + setLabel(name); + } + else { + setLabel(label); + } + + NodeList children = element.getChildNodes(); + int length = children.getLength(); + for (int index = 0; index < length; index++) { + Node node = children.item(index); + if (node instanceof Element) { + String tagName = node.getNodeName(); + if (tagName.equals("panel")) { + try { + FormPanel p = new FormPanel((Element)node, form); + add(p); + } catch ( java.text.ParseException ex ) { + DasExceptionHandler.handle(ex); + } + } + else if (tagName.equals("text") || tagName.equals("info")) { + FormText text = new FormText((Element)node); + add(text); + } + else if (tagName.equals("textfield")) { + FormTextField textfield = new FormTextField((Element)node, form); + add(textfield); + } + else if (tagName.equals("button")) { + FormButton button = new FormButton((Element)node, form); + add(button); + } + else if (tagName.equals("checkbox")) { + FormCheckBox checkbox = new FormCheckBox((Element)node, form); + add(checkbox); + } + else if (tagName.equals("list")) { + FormList list = new FormList((Element)node, form); + add(list); + } + else if (tagName.equals("choice")) { + FormChoice choice = new FormChoice((Element)node, form); + add(choice); + } + else if (tagName.equals("glue")) { + add(form.processGlueElement((Element)node)); + } + else if (tagName.equals("buttongroup")) { + add(new FormRadioButtonGroup((Element)node, form)); + } + else if (tagName.equals("canvas")) { + try { + org.das2.graph.DasCanvas canvas = org.das2.graph.DasCanvas.processCanvasElement((Element)node, form); + canvas.setAlignmentX(horizontalComponentAlignment); + add(canvas); + } + + catch (org.das2.DasException dne) { + DasExceptionHandler.handle(dne); + } catch ( java.text.ParseException ex ) { + DasExceptionHandler.handle(ex); + } + } + else { + //DO NOTHING RIGHT NOW + } + } + else { + //TODO: do some sort of error handling here. + } + } + dndSupport = new ContainerDnDSupport(null); + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + FormBase form = getForm(); + if (form != null) { + form.setTitleAt(getForm().indexOfComponent(this), label); + } + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("form"); + element.setAttribute("name", getDasName()); + element.setAttribute("label", getLabel()); + for (int index = 0; index < getComponentCount(); index++) { + java.awt.Component comp = getComponent(index); + if (comp instanceof FormComponent) { + FormComponent formComponent = (FormComponent)comp; + Element child = formComponent.getDOMElement(document); + element.appendChild(child); + } + else if (comp instanceof org.das2.graph.DasCanvas) { + org.das2.graph.DasCanvas canvas = (org.das2.graph.DasCanvas)comp; + Element child = canvas.getDOMElement(document); + element.appendChild(child); + } + } + return element; + } + + public String getDasName() { + return dasName; + } + + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = getDasApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new ContainerDnDSupport(null); + } + return dndSupport; + } + + public void deregisterComponent() { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + try { + if (nc.get(getDasName()) == this) { + nc.remove(getDasName()); + } + } + catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } + catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + super.deregisterComponent(); + } + + public final DasApplication getDasApplication() { + java.awt.Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent)p).getDasApplication(); + } + else { + return null; + } + } + + public void registerComponent() throws DasException { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + nc.put(getDasName(), this); + } + super.registerComponent(); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormTabBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormTabBeanInfo.java new file mode 100644 index 000000000..c946afc12 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormTabBeanInfo.java @@ -0,0 +1,42 @@ +/* File: FormTabBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +/** + * Bean info class for the FormPanel class + */ +public class FormTabBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, "getDasName", "setDasName", null), + new Property("label", AccessLevel.ALL, "getLabel", "setLabel", null) + }; + + public FormTabBeanInfo() { + super(properties, FormTab.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormText.java b/dasCore/src/main/java/org/das2/dasml/FormText.java new file mode 100644 index 000000000..9596ee671 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormText.java @@ -0,0 +1,129 @@ +/* File: FormText.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.DasApplication; +import org.das2.DasException; +import org.w3c.dom.*; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import org.das2.components.propertyeditor.Editable; + +/** + * + * @author eew + */ +public class FormText extends JTextArea implements Editable, FormComponent { + + protected org.das2.util.DnDSupport dndSupport; + + private boolean editingMode; + + public FormText() { + super(""); + super.setEditable(false); + setOpaque(false); + setBorder(new EmptyBorder(5,5,5,5)); + } + + FormText(Element element) { + this(); + Text text = getElementContent(element); + if (text != null) { + setText(text.getData()); + } + } + + private static Text getElementContent(Element element) { + NodeList children = element.getChildNodes(); + for (int index = 0; index < children.getLength(); index++) { + Node node = children.item(index); + if (node instanceof Text) { + return (Text)node; + } + } + return null; + } + + public Dimension getMaximumSize() { + return getPreferredSize(); + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("text"); + Text text = document.createTextNode(getText()); + element.appendChild(text); + return element; + } + + public FormBase getForm() { + FormComponent parent = (FormComponent)getParent(); + if (parent == null) { + return null; + } + return parent.getForm(); + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new DefaultComponentDnDSupport(this); + } + return dndSupport; + } + + public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { + return false; + } + + public void setEditingMode(boolean b) { editingMode = b; } + + public boolean getEditingMode() { return editingMode; } + + public String getDasName() { + return null; + } + + public void setDasName(String name) throws org.das2.DasNameException { + throw new org.das2.DasNameException(); + } + + public void deregisterComponent() { + } + + public DasApplication getDasApplication() { + Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent)p).getDasApplication(); + } + else { + return null; + } + } + + public void registerComponent() throws DasException { + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormTextBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormTextBeanInfo.java new file mode 100644 index 000000000..159381bb9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormTextBeanInfo.java @@ -0,0 +1,41 @@ +/* File: FormTextBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +/** + * Bean info class for the FormTextField class + */ +public class FormTextBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("content", AccessLevel.ALL, "getText", "setText", null) + }; + + public FormTextBeanInfo() { + super(properties, FormText.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormTextField.java b/dasCore/src/main/java/org/das2/dasml/FormTextField.java new file mode 100644 index 000000000..4733f38ba --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormTextField.java @@ -0,0 +1,192 @@ +/* File: FormTextField.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +import javax.swing.*; +import org.das2.DasPropertyException; +import org.das2.components.propertyeditor.Editable; + +/** + * A subclass of JTextField to override the default Beans properties of + * a JTextField + * + * @author Edward West + */ +public class FormTextField extends JTextField implements Editable, FormComponent { + + private String dasName; + + protected org.das2.util.DnDSupport dndSupport; + + private boolean editingMode; + + public FormTextField(String name) { + super(10); + if (name == null) { + name="textfield_" + Integer.toHexString(System.identityHashCode(this)); + } + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + setMinimumSize(getPreferredSize()); + setMaximumSize(getPreferredSize()); + } + + public FormTextField(Element element, FormBase form) + throws org.das2.DasPropertyException,org.das2.DasNameException, org.das2.DasException, + ParsedExpressionException { + + String name = element.getAttribute("name"); + + int length = Integer.parseInt(element.getAttribute("length")); + setColumns(length); + setMinimumSize(getPreferredSize()); + setMaximumSize(getPreferredSize()); + + NodeList children = element.getChildNodes(); + int childCount = children.getLength(); + for (int i = 0; i < childCount; i++) { + if (children.item(i) instanceof Text) { + Text text = (Text)children.item(i); + setText(text.getData()); + break; + } + } + + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("textfield"); + Text text = document.createTextNode(getText()); + element.appendChild(text); + element.setAttribute("name", getDasName()); + element.setAttribute("enabled", (isEnabled() ? "true" : "false")); + element.setAttribute("length", Integer.toString(getColumns())); + return element; + } + + public FormBase getForm() { + FormComponent parent = (FormComponent)getParent(); + if (parent == null) { + return null; + } + return parent.getForm(); + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new DefaultComponentDnDSupport(this); + } + return dndSupport; + } + + public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { + return false; + } + + public void setEditingMode(boolean b) { editingMode = b; } + public boolean getEditingMode() { return editingMode; } + + public String getDasName() { + return dasName; + } + + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = getDasApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + public void deregisterComponent() { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + try { + if (nc.get(getDasName()) == this) { + nc.remove(getDasName()); + } + } + catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } + catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + } + + public DasApplication getDasApplication() { + java.awt.Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent)p).getDasApplication(); + } + else { + return null; + } + } + + public void registerComponent() throws DasException { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + nc.put(getDasName(), this); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormTextFieldBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormTextFieldBeanInfo.java new file mode 100644 index 000000000..331a45f6f --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormTextFieldBeanInfo.java @@ -0,0 +1,43 @@ +/* File: FormTextFieldBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +/** + * Bean info class for the FormTextField class + */ +public class FormTextFieldBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, "getDasName", "setDasName", null), + new Property("enabled", AccessLevel.DASML, "isEnabled", "setEnabled", null), + new Property("text", AccessLevel.DASML, "getText", "setText", null) + }; + + public FormTextFieldBeanInfo() { + super(properties, FormTextField.class); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormWindow.java b/dasCore/src/main/java/org/das2/dasml/FormWindow.java new file mode 100644 index 000000000..8fe9f67db --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormWindow.java @@ -0,0 +1,437 @@ +/* File: FormWindow.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.DasPropertyException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import java.awt.*; +import org.das2.components.propertyeditor.Editable; +import org.das2.util.DasExceptionHandler; + +/** + * @author eew + */ +public class FormWindow extends FormContainer implements Editable, FormComponent { + + FormBase form; + + JDialog dialog; + + JInternalFrame internalFrame; + + String title = ""; + + int windowWidth = -1; + + int windowHeight = -1; + + boolean shouldBeVisible = false; + + private String dasName; + + { + flavorList = java.util.Arrays.asList(new java.awt.datatransfer.DataFlavor[]{ + TransferableFormComponent.BUTTONGROUP_FLAVOR, + TransferableFormComponent.BUTTON_FLAVOR, + TransferableFormComponent.CHECKBOX_FLAVOR, + TransferableFormComponent.CHOICE_FLAVOR, + //TransferableFormComponent.LIST_FLAVOR, + TransferableFormComponent.PANEL_FLAVOR, + TransferableFormComponent.TEXTFIELD_FLAVOR, + TransferableFormComponent.TEXT_FLAVOR, + org.das2.graph.dnd.TransferableCanvas.CANVAS_FLAVOR + }); + setLayout(new BorderLayout()); + } + + public FormWindow(String name, String title) { + this(name, title, -1, -1); + } + + public FormWindow(String name, String title, int width, int height) { + this.title = title; + this.windowWidth = width; + this.windowHeight = height; + if (name == null) { + name = "window_" + Integer.toHexString(System.identityHashCode(this)); + } + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + dndSupport = new ContainerDnDSupport(null); + } + + /** Creates a new instance of FormWindow */ + FormWindow(Element element, FormBase form) + throws DasException, + ParsedExpressionException, org.xml.sax.SAXException { + + this.form = form; + + String name = element.getAttribute("name"); + String title = element.getAttribute("title"); + String alignment = element.getAttribute("alignment"); + int width = Integer.parseInt(element.getAttribute("width")); + int height = Integer.parseInt(element.getAttribute("height")); + java.awt.Point location = parsePoint(element.getAttribute("location")); + boolean visible = element.getAttribute("visible").equals("true"); + + NodeList children = element.getChildNodes(); + for (int index = 0; index < children.getLength(); index++) { + Node node = children.item(index); + if (node instanceof Element && node.getNodeName().equals("panel")) { + try { + FormPanel panel = new FormPanel((Element)node, form); + add(panel); + } catch ( java.text.ParseException ex ) { + DasExceptionHandler.handle(ex); + } + } + else if (node instanceof Element && node.getNodeName().equals("canvas")) { + try { + org.das2.graph.DasCanvas canvas = org.das2.graph.DasCanvas.processCanvasElement((Element)node, form); + add(canvas); + } catch ( java.text.ParseException ex ) { + DasExceptionHandler.handle(ex); + } + } + } + + setTitle(title); + windowWidth = width; + windowHeight = height; + setWindowVisible(visible); + try { + setDasName(name); + } + catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + dndSupport = new ContainerDnDSupport(null); + } + + private static java.awt.Point parsePoint(String str) { + int commaIndex = str.indexOf(','); + return new java.awt.Point(Integer.parseInt(str.substring(1, commaIndex)), + Integer.parseInt(str.substring(commaIndex+1, str.length()-1))); + } + + public FormBase getForm() { + return form; + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("window"); + element.setAttribute("name", getDasName()); + element.setAttribute("width", String.valueOf(getWidth())); + element.setAttribute("height", String.valueOf(getHeight())); + element.setAttribute("title", title); + element.setAttribute("visible", String.valueOf(isVisible())); + if (getComponentCount() > 0) { + Component comp = getComponent(0); + if (comp instanceof FormComponent) { + FormComponent child = (FormComponent)comp; + element.appendChild(child.getDOMElement(document)); + } + else if (comp instanceof org.das2.graph.DasCanvas) { + org.das2.graph.DasCanvas child = (org.das2.graph.DasCanvas)comp; + element.appendChild(child.getDOMElement(document)); + } + } + return element; + } + + public String getDasName() { + return dasName; + } + + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = getDasApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + public boolean isWindowVisible() { + if (getEditingMode()) { + return shouldBeVisible; + } + else { + return (dialog == null ? false : dialog.isVisible()); + } + } + + public void setWindowVisible(boolean b) { + boolean oldValue = isWindowVisible(); + if (oldValue == b) { + return; + } + shouldBeVisible = b; + if (!getEditingMode() && b) { + if (dialog == null) { + initDialog(); + } + dialog.setVisible(b); + } + firePropertyChange("visible", oldValue, b); + } + + public void setEditingMode(boolean b) { + if (getEditingMode() == b) { + return; + } + if (b) { + if (dialog != null) { + shouldBeVisible = dialog.isVisible(); + dialog.setVisible(false); + } + else { + shouldBeVisible = false; + } + maybeInitializeInternalFrame(); + internalFrame.setContentPane(this); + } + else { + if (dialog != null) { + dialog.setContentPane(this); + dialog.pack(); + dialog.setVisible(shouldBeVisible); + } + else if (shouldBeVisible) { + initDialog(); + dialog.pack(); + dialog.setVisible(shouldBeVisible); + } + } + super.setEditingMode(b); + } + + public Dimension getPreferredSize() { + if (windowWidth == -1 || windowHeight == -1) { + return super.getPreferredSize(); + } + else { + return new Dimension(windowWidth, windowHeight); + } + } + + /** Getter for property title. + * @return Value of property title. + * + */ + public String getTitle() { + return title; + } + + /** Setter for property title. + * @param title New value of property title. + * + */ + public void setTitle(String title) { + if (this.title == title || (this.title != null && this.title.equals(title))) { + return; + } + String oldValue = this.title; + this.title = title; + if (getEditingMode() && internalFrame != null) { + internalFrame.setTitle(title); + } + firePropertyChange("title", oldValue, title); + } + + public void pack() { + if (getEditingMode()) { + maybeInitializeInternalFrame(); + internalFrame.pack(); + } + else { + if (dialog == null) { + initDialog(); + } + dialog.pack(); + } + } + + public Dimension getWindowSize() { + return new Dimension(windowWidth, windowHeight); + } + + public void setWindowSize(int width, int height) { + int oldWidth = windowWidth; + int oldHeight = windowHeight; + if (width != windowWidth) { + windowWidth = width; + firePropertyChange("width", oldWidth, width); + } + if (height != windowHeight) { + windowHeight = height; + firePropertyChange("height", oldHeight, height); + } + if (height != windowHeight || width != windowWidth) { + pack(); + } + } + + public void setWindowWidth(int width) { + setWindowSize(width, windowHeight); + } + + public int getWindowWidth() { + return windowWidth; + } + + public void setWindowHeight(int height) { + setWindowSize(windowWidth, height); + } + + public int getWindowHeight() { + return windowHeight; + } + + private void initDialog() { + Window w = SwingUtilities.getWindowAncestor(form); + if (w instanceof Frame) { + dialog = new JDialog((Frame)w); + } + else if (w instanceof Dialog) { + dialog = new JDialog((Dialog)w); + } + else { + dialog = new JDialog(); + } + dialog.setContentPane(this); + dialog.setTitle(title); + } + + private void maybeInitializeInternalFrame() { + if (internalFrame == null) { + internalFrame = new InternalFrame(); + internalFrame.setTitle(title); + internalFrame.setVisible(true); + internalFrame.setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE); + if (getEditingMode()) { + internalFrame.setContentPane(this); + } + internalFrame.pack(); + } + } + + public org.das2.util.DnDSupport getDnDSupport() { + if (dndSupport == null) { + dndSupport = new ContainerDnDSupport(null); + } + return dndSupport; + } + + protected void addImpl(Component c, Object constraints, int index) { + if (getComponentCount() >= 1) throw new IllegalArgumentException("Only one component allowed"); + super.addImpl(c, constraints, index); + if (c instanceof JComponent) { + ((JComponent)c).setAlignmentY(JComponent.TOP_ALIGNMENT); + } + } + + JInternalFrame getInternalFrame() { + if (getEditingMode()) { + maybeInitializeInternalFrame(); + return internalFrame; + } + return null; + } + + public void deregisterComponent() { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + try { + if (nc.get(getDasName()) == this) { + nc.remove(getDasName()); + } + } + catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } + catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + super.deregisterComponent(); + } + + public DasApplication getDasApplication() { + if (form != null) { + return form.getDasApplication(); + } + else { + return null; + } + } + + public void registerComponent() throws org.das2.DasException { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + nc.put(getDasName(), this); + } + super.registerComponent(); + } + + public class InternalFrame extends JInternalFrame { + InternalFrame() { + super(null, true, false, false, false); + } + public FormWindow getWindow() { + return FormWindow.this; + } + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/FormWindowBeanInfo.java b/dasCore/src/main/java/org/das2/dasml/FormWindowBeanInfo.java new file mode 100644 index 000000000..e21fd42a2 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/FormWindowBeanInfo.java @@ -0,0 +1,65 @@ +/* File: FormWindowBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.beans.AccessLevelBeanInfo; + +import java.beans.MethodDescriptor; + +/** + * + * @author eew + */ +public class FormWindowBeanInfo extends AccessLevelBeanInfo { + + private static Property[] properties = { + new Property("name", AccessLevel.ALL, "getDasName", "setDasName", null), + new Property("visible", AccessLevel.DASML, "isWindowVisible", "setWindowVisible", null), + new Property("title", AccessLevel.ALL, "getTitle", "setTitle", null) + }; + + private static MethodDescriptor[] methods; + + static { + methods = new MethodDescriptor[1]; + try { + methods[0] = new MethodDescriptor(FormWindow.class.getMethod("pack")); + } + catch (NoSuchMethodException nsme) { + IllegalStateException ise = new IllegalStateException(nsme.getMessage()); + ise.initCause(nsme); + throw ise; + } + + } + + public FormWindowBeanInfo() { + super(properties, FormWindow.class); + } + + public MethodDescriptor[] getMethodDescriptors() { + return methods; + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/ListOption.java b/dasCore/src/main/java/org/das2/dasml/ListOption.java new file mode 100644 index 000000000..79a6593c4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/ListOption.java @@ -0,0 +1,107 @@ +/* File: ListOption.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.awt.*; + +public class ListOption { + private String label; + private String value; + private boolean selected; + + ItemSelectable selectable; + + public ListOption(FormBase form, String label, String value) { + this.label = label; + this.value = value; + } + + ListOption(Element element) { + label = element.getAttribute("label"); + value = element.getAttribute("value"); + } + + ListOption(String label, String value) { + this.label = label; + this.value = value; + } + + public String toString() { + return getLabel(); + } + + public String getLabel() { + return label; + } + + public String getValue() { + return value; + } + + public void setLabel(String label) { + this.label = label; + } + + public void setValue(String value) { + this.value = value; + } + + public boolean isSelected() { + if (selectable != null) { + Object[] items = selectable.getSelectedObjects(); + for (int index = 0; index < items.length; index++) { + if (items[index] == this) { + return true; + } + } + } + return false; + } + + public void setSelected(boolean b) { + if (selectable instanceof FormChoice) { + if (b) { + FormChoice choice = (FormChoice)selectable; + choice.setSelectedItem(this); + } + } + else if (selectable instanceof FormList) { + FormList list = (FormList)selectable; + list.setSelected(this, b); + } + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("option"); + element.setAttribute("label", label); + element.setAttribute("value", value); + element.setAttribute("selected", String.valueOf(isSelected())); + return element; + } + +} + diff --git a/dasCore/src/main/java/org/das2/dasml/OptionList.java b/dasCore/src/main/java/org/das2/dasml/OptionList.java new file mode 100644 index 000000000..1f2aa9998 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/OptionList.java @@ -0,0 +1,33 @@ +/* File: OptionList.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +/** + * + * @author eew + */ +public interface OptionList { + ListOption[] getOptions(); + void setOptions(ListOption[] options); +} diff --git a/dasCore/src/main/java/org/das2/dasml/OptionListEditor.java b/dasCore/src/main/java/org/das2/dasml/OptionListEditor.java new file mode 100644 index 000000000..21d6dbbe1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/OptionListEditor.java @@ -0,0 +1,598 @@ +/* File: OptionListEditor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.event.*; +import java.awt.*; +import java.awt.event.*; + +/** + * + * @author eew + */ +public class OptionListEditor extends JButton implements OptionList, javax.swing.table.TableCellEditor, java.beans.PropertyEditor { + + EventListenerList listenerList = new EventListenerList(); + + ListOption editing; + + JDialog dialog; + + JPanel editorPanel; + + JLabel labelLabel; + + JLabel valueLabel; + + JTextField labelField; + + JTextField valueField; + + JList optionList; + + AllPurposeListener listener = new AllPurposeListener(); + + JButton add; + JButton moveUp; + JButton moveDown; + JButton delete; + JButton commitChanges; + JButton cancelEdit; + + private final ListModel EMPTY_MODEL = new ListModel() { + public void addListDataListener(ListDataListener l) {} + public void removeListDataListener(ListDataListener l) {} + public Object getElementAt(int index) { + return ""; + } + public int getSize() { + return 1; + } + }; + + /** Creates a new instance of FormChoiceEditor */ + public OptionListEditor() { + super("edit"); + addActionListener(listener); + + labelLabel = new JLabel("label:", JLabel.LEFT); + labelField = new JTextField(20); + labelField.addFocusListener(listener); + valueLabel = new JLabel("value:", JLabel.LEFT); + valueField = new JTextField(20); + valueField.addFocusListener(listener); + optionList = new JList(EMPTY_MODEL); + optionList.setVisibleRowCount(10); + optionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + optionList.addListSelectionListener(listener); + + editorPanel = new JPanel(new BorderLayout()); + editorPanel.setBorder(new EmptyBorder(5,5,5,5)); + editorPanel.add(new JScrollPane(optionList, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); + + JPanel rightPanel = new JPanel(); + rightPanel.setBorder(new EmptyBorder(0, 5, 0, 0)); + rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); + add = new JButton("Add"); + moveUp = new JButton("Move up"); + moveDown = new JButton("Move down"); + delete = new JButton("Delete"); + add.addActionListener(listener); + moveUp.addActionListener(listener); + moveDown.addActionListener(listener); + delete.addActionListener(listener); + Dimension size = moveDown.getPreferredSize(); + add.setPreferredSize(size); + add.setMaximumSize(size); + moveUp.setPreferredSize(size); + moveUp.setMaximumSize(size); + moveDown.setPreferredSize(size); + moveDown.setMaximumSize(size); + delete.setPreferredSize(size); + delete.setMaximumSize(size); + rightPanel.add(add); + rightPanel.add(moveUp); + rightPanel.add(moveDown); + rightPanel.add(delete); + editorPanel.add(rightPanel, BorderLayout.EAST); + + JPanel buttonPanel = new JPanel(); + commitChanges = new JButton("Commit changes"); + commitChanges.addActionListener(listener); + cancelEdit = new JButton("Cancel Edit"); + cancelEdit.addActionListener(listener); + buttonPanel.add(commitChanges); + buttonPanel.add(cancelEdit); + + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS)); + labelLabel.setAlignmentX(JComponent.LEFT_ALIGNMENT); + labelField.setAlignmentX(JComponent.LEFT_ALIGNMENT); + valueLabel.setAlignmentX(JComponent.LEFT_ALIGNMENT); + valueField.setAlignmentX(JComponent.LEFT_ALIGNMENT); + buttonPanel.setAlignmentX(JComponent.LEFT_ALIGNMENT); + bottomPanel.add(labelLabel); + bottomPanel.add(labelField); + bottomPanel.add(valueLabel); + bottomPanel.add(valueField); + bottomPanel.add(buttonPanel); + editorPanel.add(bottomPanel, BorderLayout.SOUTH); + } + + public void showDialog() { + if (dialog == null) { + Window w = SwingUtilities.windowForComponent(this); + if (w instanceof Dialog) { + dialog = new JDialog((Dialog)w, true); + } + else if (w instanceof Frame) { + dialog = new JDialog((Frame)w, true); + } + else { + dialog = new JDialog(); + dialog.setModal(true); + } + dialog.setContentPane(editorPanel); + dialog.pack(); + dialog.setResizable(false); + dialog.addWindowListener(listener); + } + dialog.setVisible(true); + } + + private void addOption() { + DefaultListModel model = getListModel(); + model.addElement(new ListOption("label", "value")); + optionList.setSelectedIndex(model.getSize() - 1); + } + + private DefaultListModel getListModel() { + return (DefaultListModel)optionList.getModel(); + } + + private void moveUp(int index) { + if (index >= 0) { + if (index == 0) { + return; + } + DefaultListModel model = getListModel(); + Object option = model.getElementAt(index); + model.removeElementAt(index); + model.insertElementAt(option, index - 1); + optionList.setSelectedIndex(index - 1); + } + } + + private void moveDown(int index) { + if (index >= 0) { + DefaultListModel model = getListModel(); + if (index == model.getSize()) { + return; + } + Object option = model.getElementAt(index); + model.removeElementAt(index); + model.insertElementAt(option, index + 1); + optionList.setSelectedIndex(index + 1); + } + } + + private void deleteOption(int index) { + if (index >= 0) { + DefaultListModel model = getListModel(); + model.removeElementAt(index); + optionList.clearSelection(); + } + } + + private class AllPurposeListener implements ActionListener, ListSelectionListener, FocusListener, WindowListener { + + /** Invoked when an action occurs. + */ + public void actionPerformed(ActionEvent e) { + Object source = e.getSource(); + if (source == OptionListEditor.this) { + showDialog(); + } + else if (source == add) { + addOption(); + } + else if (source == moveUp) { + moveUp(optionList.getSelectedIndex()); + } + else if (source == moveDown) { + moveDown(optionList.getSelectedIndex()); + } + else if (source == delete) { + deleteOption(optionList.getSelectedIndex()); + } + else if (source == commitChanges) { + stopCellEditing(); + } + else if (source == cancelEdit) { + cancelCellEditing(); + } + } + + /** + * Called whenever the value of the selection changes. + * @param e the event that characterizes the change. + */ + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting()) { + return; + } + int index = optionList.getSelectedIndex(); + if (index >= 0) { + editing = (ListOption)getListModel().getElementAt(index); + labelField.setText(editing.getLabel()); + valueField.setText(editing.getValue()); + } + else { + editing = null; + labelField.setText(""); + valueField.setText(""); + } + } + + /** Invoked when a component gains the keyboard focus. + */ + public void focusGained(FocusEvent e) {} + + /** Invoked when a component loses the keyboard focus. + */ + public void focusLost(FocusEvent e) { + if (editing != null) { + if (e.getComponent() == labelField) { + editing.setLabel(labelField.getText()); + optionList.repaint(); + } + else if (e.getComponent() == valueField) { + editing.setValue(valueField.getText()); + } + } + } + + public void windowClosing(WindowEvent e) { + fireEditingCanceled(); + } + + public void windowClosed(WindowEvent e) {} + public void windowActivated(WindowEvent e) {} + public void windowDeactivated(WindowEvent e) {} + public void windowDeiconified(WindowEvent e) {} + public void windowIconified(WindowEvent e) {} + public void windowOpened(WindowEvent e) {} + + } + + public static void main(String[] args) { + + JPanel content = new JPanel(new BorderLayout()); + + FormBase form = new FormBase(true); + FormChoice choice = new FormChoice("fred"); + choice.addOption(new ListOption("One", "1")); + choice.addOption(new ListOption("Two", "2")); + + OptionListEditor editor = new OptionListEditor(); + editor.setOptions(choice.getOptions()); + + content.add(choice, BorderLayout.CENTER); + content.add(editor, BorderLayout.SOUTH); + + + + JFrame frame = new JFrame(); + frame.setContentPane(content); + frame.pack(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } + + public ListOption[] getOptions() { + ListModel model = getListModel(); + ListOption[] options = new ListOption[model.getSize()]; + for (int index = 0; index < options.length; index++) { + options[index] = (ListOption)model.getElementAt(index); + } + return options; + } + + public void setOptions(ListOption[] options) { + DefaultListModel model = new DefaultListModel(); + model.ensureCapacity(options.length); + for (int index = 0; index < options.length; index++) { + model.addElement(options[index]); + } + optionList.setModel(model); + } + + /** Adds a listener to the list that's notified when the editor + * stops, or cancels editing. + * + * @param l the CellEditorListener + */ + public void addCellEditorListener(CellEditorListener l) { + listenerList.add(CellEditorListener.class, l); + } + + /** Tells the editor to cancel editing and not accept any partially + * edited value. + */ + public void cancelCellEditing() { + fireEditingCanceled(); + dialog.setVisible(false); + } + + /** Returns the value contained in the editor. + * @return the value contained in the editor + */ + public Object getCellEditorValue() { + return getOptions(); + } + + /** Sets an initial value for the editor. This will cause + * the editor to stopEditing and lose any partially + * edited value if the editor is editing when this method is called.

+ * + * Returns the component that should be added to the client's + * Component hierarchy. Once installed in the client's + * hierarchy this component will then be able to draw and receive + * user input. + * + * @param table the JTable that is asking the + * editor to edit; can be null + * @param value the value of the cell to be edited; it is + * up to the specific editor to interpret + * and draw the value. For example, if value is + * the string "true", it could be rendered as a + * string or it could be rendered as a check + * box that is checked. null + * is a valid value + * @param isSelected true if the cell is to be rendered with + * highlighting + * @param row the row of the cell being edited + * @param column the column of the cell being edited + * @return the component for editing + */ + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + ListOption[] options = (ListOption[])value; + if (dialog != null && dialog.isVisible()) { + dialog.setVisible(false); + } + setOptions(options); + return this; + } + + /** Asks the editor if it can start editing using anEvent. + * anEvent is in the invoking component coordinate system. + * The editor can not assume the Component returned by + * getCellEditorComponent is installed. This method + * is intended for the use of client to avoid the cost of setting up + * and installing the editor component if editing is not possible. + * If editing can be started this method returns true. + * + * @param anEvent the event the editor should use to consider + * whether to begin editing or not + * @return true if editing can be started + * @see #shouldSelectCell + */ + public boolean isCellEditable(java.util.EventObject anEvent) { + return true; + } + + /** Removes a listener from the list that's notified + * + * @param l the CellEditorListener + */ + public void removeCellEditorListener(CellEditorListener l) { + listenerList.remove(CellEditorListener.class, l); + } + + /** Returns true if the editing cell should be selected, false otherwise. + * Typically, the return value is true, because is most cases the editing + * cell should be selected. However, it is useful to return false to + * keep the selection from changing for some types of edits. + * eg. A table that contains a column of check boxes, the user might + * want to be able to change those checkboxes without altering the + * selection. (See Netscape Communicator for just such an example) + * Of course, it is up to the client of the editor to use the return + * value, but it doesn't need to if it doesn't want to. + * + * @param anEvent the event the editor should use to start + * editing + * @return true if the editor would like the editing cell to be selected; + * otherwise returns false + * @see #isCellEditable + */ + public boolean shouldSelectCell(java.util.EventObject anEvent) { + return true; + } + + /** Tells the editor to stop editing and accept any partially edited + * value as the value of the editor. The editor returns false if + * editing was not stopped; this is useful for editors that validate + * and can not accept invalid entries. + * + * @return true if editing was stopped; false otherwise + */ + public boolean stopCellEditing() { + fireEditingStopped(); + dialog.setVisible(false); + return true; + } + + private void fireEditingCanceled() { + ChangeEvent e = null; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==CellEditorListener.class) { + if (e == null) { + e = new ChangeEvent(this); + } + ((CellEditorListener)listeners[i+1]).editingCanceled(e); + } + } + } + + private void fireEditingStopped() { + ChangeEvent e = null; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==CellEditorListener.class) { + if (e == null) { + e = new ChangeEvent(this); + } + ((CellEditorListener)listeners[i+1]).editingStopped(e); + } + } + } + + /** Gets the property value as text. + * + * @return The property value as a human editable string. + *

Returns null if the value can't be expressed as an editable string. + *

If a non-null value is returned, then the PropertyEditor should + * be prepared to parse that string back in setAsText(). + */ + public String getAsText() { + return null; + } + + /** A PropertyEditor may choose to make available a full custom Component + * that edits its property value. It is the responsibility of the + * PropertyEditor to hook itself up to its editor Component itself and + * to report property value changes by firing a PropertyChange event. + *

+ * The higher-level code that calls getCustomEditor may either embed + * the Component in some larger property sheet, or it may put it in + * its own individual dialog, or ... + * + * @return A java.awt.Component that will allow a human to directly + * edit the current property value. May be null if this is + * not supported. + */ + public java.awt.Component getCustomEditor() { + return this; + } + + /** This method is intended for use when generating Java code to set + * the value of the property. It should return a fragment of Java code + * that can be used to initialize a variable with the current property + * value. + *

+ * Example results are "2", "new Color(127,127,34)", "Color.orange", etc. + * + * @return A fragment of Java code representing an initializer for the + * current value. + */ + public String getJavaInitializationString() { + return "???"; + } + + /** If the property value must be one of a set of known tagged values, + * then this method should return an array of the tags. This can + * be used to represent (for example) enum values. If a PropertyEditor + * supports tags, then it should support the use of setAsText with + * a tag value as a way of setting the value and the use of getAsText + * to identify the current value. + * + * @return The tag values for this property. May be null if this + * property cannot be represented as a tagged value. + * + */ + public String[] getTags() { + return null; + } + + /** Gets the property value. + * + * @return The value of the property. Primitive types such as "int" will + * be wrapped as the corresponding object type such as "java.lang.Integer". + */ + public Object getValue() { + return getOptions(); + } + + /** Determines whether this property editor is paintable. + * + * @return True if the class will honor the paintValue method. + */ + public boolean isPaintable() { + return false; + } + + /** Paint a representation of the value into a given area of screen + * real estate. Note that the propertyEditor is responsible for doing + * its own clipping so that it fits into the given rectangle. + *

+ * If the PropertyEditor doesn't honor paint requests (see isPaintable) + * this method should be a silent noop. + *

+ * The given Graphics object will have the default font, color, etc of + * the parent container. The PropertyEditor may change graphics attributes + * such as font and color and doesn't need to restore the old values. + * + * @param gfx Graphics object to paint into. + * @param box Rectangle within graphics object into which we should paint. + */ + public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) { + } + + /** Set the property value by parsing a given String. May raise + * java.lang.IllegalArgumentException if either the String is + * badly formatted or if this kind of property can't be expressed + * as text. + * @param text The string to be parsed. + */ + public void setAsText(String text) throws java.lang.IllegalArgumentException { + throw new IllegalArgumentException(); + } + + /** Set (or change) the object that is to be edited. Primitive types such + * as "int" must be wrapped as the corresponding object type such as + * "java.lang.Integer". + * + * @param value The new target object to be edited. Note that this + * object should not be modified by the PropertyEditor, rather + * the PropertyEditor should create a new object to hold any + * modified value. + */ + public void setValue(Object value) { + setOptions((ListOption[])value); + } + + /** Determines whether this property editor supports a custom editor. + * + * @return True if the propertyEditor can provide a custom editor. + */ + public boolean supportsCustomEditor() { + return true; + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/Orientation.java b/dasCore/src/main/java/org/das2/dasml/Orientation.java new file mode 100644 index 000000000..ffd383525 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/Orientation.java @@ -0,0 +1,67 @@ +/* File: Orientation.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import org.das2.components.propertyeditor.Enumeration; + +/** + * + * @author eew + */ +public final class Orientation implements Enumeration { + + public static final Orientation HORIZONTAL = new Orientation("horizontal"); + + public static final Orientation VERTICAL = new Orientation("vertical"); + + private String description; + + private Orientation(String description) { + this.description = description; + } + + public String toString() { + return description; + } + + public static Orientation valueOf(String str) { + if (str.equals("vertical")) { + return VERTICAL; + } + if (str.equals("horizontal")) { + return HORIZONTAL; + } + throw new IllegalArgumentException("Orientation must be either 'horizontal' or 'vertical'"); + } + + /** An icon can be provided that will be shown in a list + * along with the textual description of the element. + * This method should return null if there + * is no icon available. + */ + public javax.swing.Icon getListIcon() { + return null; + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/ParsedExpression.java b/dasCore/src/main/java/org/das2/dasml/ParsedExpression.java new file mode 100644 index 000000000..bd43e1fe2 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/ParsedExpression.java @@ -0,0 +1,734 @@ +/* File: ParsedExpression.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This class encapsulates a pre-parsed expression. + * + * @author Edward West + */ +public class ParsedExpression { + + private static final Pattern SIMPLE_NAME_PATTERN = Pattern.compile("[A-Za-z_][A-Za-z0-9_-]*"); + + private static final Pattern INT_PATTERN = Pattern.compile("-?(0|[1-9][0-9]*)"); + + private static final Pattern FLOAT_PATTERN = Pattern.compile("-?[0-9]*(\\.[0-9]*)?([eE]-?[0-9]+)?"); + + private static final Pattern PAREN_PATTERN = Pattern.compile("\\(([^\\(\\)])\\)"); + + private static final Pattern EQUALITY_PATTERN = Pattern.compile("\\b(eq|ne)\\b"); + + private static final Pattern COMPARISON_PATTERN = Pattern.compile("\\b(lt|le|gt|ge)\\b"); + + private static final Pattern OR_PATTERN = Pattern.compile("\\bor\\b"); + + private static final Pattern AND_PATTERN = Pattern.compile("\\band\\b"); + + private static final Pattern NOT_PATTERN = Pattern.compile("\\Anot\\b"); + + static final int ID_LOAD = 0; + static final int ID_ALOAD = 1; + static final int ID_STORE = 2; + static final int ID_ASTORE = 3; + static final int ID_ADD = 4; + static final int ID_SUBTRACT = 5; + static final int ID_MULTIPLY = 6; + static final int ID_DIVIDE = 7; + static final int ID_NEGATE = 8; + static final int ID_EQ = 9; + static final int ID_NE = 10; + static final int ID_GT = 11; + static final int ID_LT = 12; + static final int ID_GE = 13; + static final int ID_LE = 14; + static final int ID_OR = 15; + static final int ID_AND = 16; + static final int ID_NOT = 17; + + private static class Op { + static final Op LOAD = new Op(ID_LOAD); + static final Op ALOAD = new Op(ID_ALOAD); + static final Op STORE = new Op(ID_STORE); + static final Op ADD = new Op(ID_ADD); + static final Op SUBTRACT = new Op(ID_SUBTRACT); + static final Op MULTIPLY = new Op(ID_MULTIPLY); + static final Op DIVIDE = new Op(ID_DIVIDE); + static final Op NEGATE = new Op(ID_NEGATE); + static final Op EQ = new Op(ID_EQ); + static final Op NE = new Op(ID_NE); + static final Op GT = new Op(ID_GT); + static final Op LT = new Op(ID_LT); + static final Op GE = new Op(ID_GE); + static final Op LE = new Op(ID_LE); + static final Op OR = new Op(ID_OR); + static final Op AND = new Op(ID_AND); + static final Op NOT = new Op(ID_NOT); + int id; + private Op(int id) {this.id = id;} + } + + private List list; + + private String expression; + + public ParsedExpression(String expression) throws ParsedExpressionException { + this.expression = expression; + list = new LinkedList(); + if (!parseExpression(expression, list)) throw new ParsedExpressionException("Invalid expression"); + } + + public String toString() { + return expression; + } + + private static boolean parseExpression(String s, List queue) { + return parseOrExpression(s, queue); + } + + private static boolean parseOrExpression(String expression, List queue) { + if (expression.length() < 1) return false; + Matcher matcher = OR_PATTERN.matcher(expression); + while (matcher.find()) { + String andExpression = expression.substring(matcher.end()).trim(); + String orExpression = expression.substring(0, matcher.start()).trim(); + List leftList = new LinkedList(); + List rightList = new LinkedList(); + if (parseAndExpression(andExpression, rightList) + && parseOrExpression(orExpression, leftList)) { + queue.addAll(leftList); + queue.addAll(rightList); + queue.add(Op.OR); + return true; + } + } + return parseAndExpression(expression, queue); + } + + private static boolean parseAndExpression(String expression, List queue) { + if (expression.length() < 1) return false; + Matcher matcher = AND_PATTERN.matcher(expression); + while (matcher.find()) { + String notExpression = expression.substring(matcher.end()).trim(); + String andExpression = expression.substring(0, matcher.start()).trim(); + List leftList = new LinkedList(); + List rightList = new LinkedList(); + if (parseNotExpression(notExpression, rightList) + && parseAndExpression(andExpression, leftList)) { + queue.addAll(leftList); + queue.addAll(rightList); + queue.add(Op.AND); + return true; + } + } + return parseNotExpression(expression, queue); + } + + private static boolean parseNotExpression(String expression, List queue) { + if (expression.length() < 1) return false; + Matcher matcher = NOT_PATTERN.matcher(expression); + if (matcher.find()) { + String equalityExpression = expression.substring(matcher.end()).trim(); + List list = new LinkedList(); + if (parseEqualityExpression(equalityExpression, list)) { + queue.addAll(list); + queue.add(Op.NOT); + return true; + } + else return false; + } + return parseEqualityExpression(expression, queue); + } + + private static boolean parseEqualityExpression(String expression, List queue) { + if (expression.length() < 1) return false; + Matcher matcher = EQUALITY_PATTERN.matcher(expression); + while (matcher.find()) { + String relationalExpression = expression.substring(matcher.end()).trim(); + String equalityExpression = expression.substring(0, matcher.start()).trim(); + List leftList = new LinkedList(); + List rightList = new LinkedList(); + if (parseRelationalExpression(relationalExpression, rightList) + && parseEqualityExpression(equalityExpression, leftList)) { + queue.addAll(leftList); + queue.addAll(rightList); + String op = matcher.group(1); + if (op.equals("eq")) { + queue.add(Op.EQ); + } + else { + queue.add(Op.NE); + } + return true; + } + } + return parseRelationalExpression(expression, queue); + } + + private static boolean parseRelationalExpression(String expression, List queue) { + if (expression.length() < 1) return false; + Matcher matcher = COMPARISON_PATTERN.matcher(expression); + while (matcher.find()) { + String additiveExpression = expression.substring(matcher.end()).trim(); + String relationalExpression = expression.substring(0, matcher.start()).trim(); + List leftList = new LinkedList(); + List rightList = new LinkedList(); + if (parseAdditiveExpression(additiveExpression, rightList) + && parseRelationalExpression(relationalExpression, leftList)) { + queue.addAll(leftList); + queue.addAll(rightList); + String op = matcher.group(1); + if (op.equals("lt")) { + queue.add(Op.LT); + } + else if (op.equals("le")) { + queue.add(Op.LE); + } + else if (op.equals("gt")) { + queue.add(Op.GT); + } + else { + queue.add(Op.GE); + } + return true; + } + } + return parseAdditiveExpression(expression, queue); + } + + private static boolean parseAdditiveExpression(String expression, List queue) { + if (expression.length() < 1) return false; + int index = Math.max(expression.lastIndexOf('+'), + expression.lastIndexOf('-')); + while (index >= 0) { + String multiplicativeExpression = expression.substring(index + 1).trim(); + String additiveExpression = expression.substring(0, index).trim(); + List leftList = new LinkedList(); + List rightList = new LinkedList(); + if (parseMultiplicativeExpression(multiplicativeExpression, rightList) + && parseAdditiveExpression(additiveExpression, leftList)) { + queue.addAll(leftList); + queue.addAll(rightList); + queue.add(expression.charAt(index) == '+' ? Op.ADD : Op.SUBTRACT); + return true; + } + index = Math.max(expression.lastIndexOf('+', index-1), expression.lastIndexOf('-', index-1)); + } + return parseMultiplicativeExpression(expression, queue); + } + + private static boolean parseMultiplicativeExpression(String expression, List queue) { + if (expression.length() < 1) return false; + int index = Math.max(expression.lastIndexOf('*'), expression.lastIndexOf('/')); + while (index >= 0) { + String unaryExpression = expression.substring(index + 1).trim(); + String multiplicativeExpression = expression.substring(0, index).trim(); + List leftList = new LinkedList(); + List rightList = new LinkedList(); + if (parseUnaryExpression(unaryExpression, rightList) + && parseMultiplicativeExpression(multiplicativeExpression, leftList)) { + queue.addAll(leftList); + queue.addAll(rightList); + queue.add(expression.charAt(index) == '*' ? Op.MULTIPLY : Op.DIVIDE); + return true; + } + } + return parseUnaryExpression(expression, queue); + } + + private static boolean parseUnaryExpression(String expression, List queue) { + return parseNegateExpression(expression, queue); + } + + private static boolean parseNegateExpression(String expression, List queue) { + if (expression.length() < 1) return false; + if (expression.charAt(0) == '-') { + List list = new LinkedList(); + boolean result = parseSimpleExpression(expression.substring(1).trim(), list); + if (result) { + queue.addAll(list); + queue.add(Op.NEGATE); + return true; + } + else return false; + } + return parseSimpleExpression(expression, queue); + } + + private static boolean parseSimpleExpression(String expression, List queue) { + if (expression.length() < 1) return false; + if (expression.startsWith("(") && expression.endsWith(")")) { + List list = new LinkedList(); + boolean result = parseExpression(expression.substring(1, expression.length()-1).trim(), list); + if (result) { + queue.addAll(list); + return true; + } + else { + return false; + } + } + if (expression.startsWith("${") && expression.endsWith("}")) { + List list = new LinkedList(); + boolean result = parseArrayAccess(expression.substring(2, expression.length() - 1).trim(), list); + if (result) { + queue.addAll(list); + } + return result; + } + Matcher matcher = INT_PATTERN.matcher(expression); + if (matcher.matches()) { + queue.add(new Integer(expression)); + return true; + } + matcher = FLOAT_PATTERN.matcher(expression); + if (matcher.matches()) { + queue.add(new Double(expression)); + return true; + } + if (expression.equals("true")) { + queue.add(Boolean.TRUE); + return true; + } + if (expression.equals("false")) { + queue.add(Boolean.FALSE); + return true; + } + return false; + } + + private static boolean parseArrayAccess(String expression, List queue) { + expression = expression.trim(); + if (expression.length() < 1) return false; + if (SIMPLE_NAME_PATTERN.matcher(expression).matches()) { + queue.add(null); + queue.add(expression); + queue.add(Op.LOAD); + return true; + } + else if (expression.charAt(expression.length() - 1) == ']') { + int index = expression.lastIndexOf('['); + while (index >= 0) { + int dotIndex = expression.lastIndexOf('.', index); + String arrayAccess = expression.substring(0, dotIndex); + String simpleName = expression.substring(dotIndex + 1, index).trim(); + String indexExpression = expression.substring(index + 1, expression.length() - 2).trim(); + if (!SIMPLE_NAME_PATTERN.matcher(simpleName).matches()) { + return false; + } + List leftList = new LinkedList(); + List rightList = new LinkedList(); + boolean rightResult = parseExpression(indexExpression, rightList); + if (rightResult) { + boolean leftResult = parseArrayAccess(arrayAccess, leftList); + if (leftResult) { + queue.addAll(leftList); + queue.addAll(rightList); + queue.add(Op.ALOAD); + return true; + } + } + } + } + else { + int index = expression.lastIndexOf('.'); + while (index > 0) { + String arrayAccess = expression.substring(0, index); + String simpleName = expression.substring(index + 1); + if (!SIMPLE_NAME_PATTERN.matcher(simpleName).matches()) { + return false; + } + List leftList = new LinkedList(); + boolean leftResult = parseArrayAccess(arrayAccess, leftList); + if (leftResult) { + queue.addAll(leftList); + queue.add(simpleName); + queue.add(Op.LOAD); + return true; + } + } + } + return false; + } + + public Object evaluate(org.das2.NameContext nc) throws ParsedExpressionException, org.das2.DasPropertyException { + try { + return evaluate(this.list, nc); + } + catch (InvocationTargetException ite) { + Throwable t = ite.getTargetException(); + if (t instanceof DataFormatException) { + ParsedExpressionException pee = new ParsedExpressionException(t.getMessage()); + pee.initCause(t); + throw pee; + } + throw new RuntimeException(ite); + } + } + + private static Object evaluate(List list, org.das2.NameContext nc) throws ParsedExpressionException, InvocationTargetException, org.das2.DasPropertyException { + if (list.size() == 0) { + throw new RuntimeException("empty expression"); + } + if (list.size() == 1 && !(list.get(0) instanceof Op)) { + return list.get(0); + } + List stack = new ArrayList(list.size()); + for (Iterator i = list.iterator(); i.hasNext();) { + Object o = i.next(); + if (o instanceof Op) { + Op op = (Op)o; + evaluate(stack, op.id, nc); + } + else { + push(stack, o); + } + } + if (stack.size() != 1) { + throw new IllegalArgumentException("Invalid expression: " + list); + } + return pop(stack); + } + + private static void evaluate(List stack, int id, org.das2.NameContext nc) throws InvocationTargetException, ParsedExpressionException, org.das2.DasPropertyException { + switch(id) { + case ID_LOAD: + load(stack, nc); + break; + case ID_ALOAD: + aload(stack, nc); + break; + case ID_STORE: + store(stack, nc); + break; + case ID_ASTORE: + astore(stack, nc); + break; + case ID_NEGATE: + negate(stack); + break; + case ID_ADD: + add(stack); + break; + case ID_SUBTRACT: + subtract(stack); + break; + case ID_MULTIPLY: + multiply(stack); + break; + case ID_DIVIDE: + divide(stack); + break; + case ID_EQ: + eq(stack); + break; + case ID_NE: + ne(stack); + break; + case ID_GT: + gt(stack); + break; + case ID_LT: + lt(stack); + break; + case ID_GE: + ge(stack); + break; + case ID_LE: + le(stack); + break; + case ID_OR: + or(stack); + break; + case ID_AND: + and(stack); + break; + default: throw new IllegalStateException(); + } + } + + private static Object pop(List stack) { + return stack.remove(stack.size() - 1); + } + + private static Boolean popBoolean(List stack) { + return (Boolean)stack.remove(stack.size() - 1); + } + + private static String popString(List stack) { + return (String)stack.remove(stack.size() - 1); + } + + private static Integer popInteger(List stack) { + return (Integer)stack.remove(stack.size() - 1); + } + + private static Double popDouble(List stack) { + return (Double)stack.remove(stack.size() - 1); + } + + private static Number popNumber(List stack) { + return (Number)stack.remove(stack.size() - 1); + } + + private static Comparable popComparable(List stack) { + return (Comparable)stack.remove(stack.size() - 1); + } + + private static void push(List stack, Object o) { + stack.add(o); + } + + private static Class widest(Class c1, Class c2) { + if (c1.getSuperclass() != Number.class || c2.getSuperclass() != Number.class) { + throw new IllegalArgumentException("(" + c1.getName() + ", " + c2.getName()); + } + else if (c1 == Double.class || c2 == Double.class) { + return Double.class; + } + else if (c1 == Float.class || c2 == Float.class) { + return Float.class; + } + else if (c1 == Long.class || c2 == Long.class) { + return Long.class; + } + else if (c1 == Integer.class || c2 == Integer.class) { + return Integer.class; + } + else if (c1 == Short.class || c2 == Short.class) { + return Short.class; + } + else { + return Byte.class; + } + + } + + private static void load(List stack, org.das2.NameContext nc) throws org.das2.DasPropertyException, InvocationTargetException, ParsedExpressionException { + String pname = popString(stack); + Object source = pop(stack); + Object value; + if (source == null) { + value = nc.get(pname); + } + else { + value = nc.getPropertyValue(source, pname); + } + push(stack, value); + } + + private static void aload(List stack, org.das2.NameContext nc) throws org.das2.DasPropertyException, InvocationTargetException, ParsedExpressionException { + Integer index = popInteger(stack); + String pname = popString(stack); + Object source = pop(stack); + push(stack, nc.getIndexedPropertyValue(source, pname, index.intValue())); + } + + private static void store(List stack, org.das2.NameContext nc) throws org.das2.DasPropertyException, InvocationTargetException, ParsedExpressionException { + Object value = pop(stack); + String pname = popString(stack); + Object dest = pop(stack); + nc.setPropertyValue(dest, pname, value); + } + + private static void astore(List stack, org.das2.NameContext nc) throws org.das2.DasPropertyException, InvocationTargetException, ParsedExpressionException { + Object value = pop(stack); + Integer index = popInteger(stack); + String pname = popString(stack); + Object dest = pop(stack); + nc.setIndexedPropertyValue(dest, pname, index.intValue(), value); + } + + private static void negate(List stack) { + Double a = popDouble(stack); + push(stack, new Double(-a.doubleValue())); + } + + private static void add(List stack) { + Number b = popNumber(stack); + Number a = popNumber(stack); + Class widest = widest(a.getClass(), b.getClass()); + Number result; + if (widest == Double.class) { + result = new Double(a.doubleValue() + b.doubleValue()); + } + else if (widest == Float.class) { + result = new Float(a.floatValue() + b.floatValue()); + } + else if (widest == Long.class) { + result = new Long(a.longValue() + b.longValue()); + } + else if (widest == Integer.class) { + result = new Integer(a.intValue() + b.intValue()); + } + else if (widest == Short.class) { + result = new Short((short)(a.shortValue() + b.shortValue())); + } + else { + result = new Byte((byte)(a.byteValue() + b.byteValue())); + } + push(stack, result); + } + + private static void subtract(List stack) { + Number b = popNumber(stack); + Number a = popNumber(stack); + Class widest = widest(a.getClass(), b.getClass()); + Number result; + if (widest == Double.class) { + result = new Double(a.doubleValue() - b.doubleValue()); + } + else if (widest == Float.class) { + result = new Float(a.floatValue() - b.floatValue()); + } + else if (widest == Long.class) { + result = new Long(a.longValue() - b.longValue()); + } + else if (widest == Integer.class) { + result = new Integer(a.intValue() - b.intValue()); + } + else if (widest == Short.class) { + result = new Short((short)(a.shortValue() - b.shortValue())); + } + else { + result = new Byte((byte)(a.byteValue() - b.byteValue())); + } + push(stack, result); + } + + private static void multiply(List stack) { + Number b = popNumber(stack); + Number a = popNumber(stack); + Class widest = widest(a.getClass(), b.getClass()); + Number result; + if (widest == Double.class) { + result = new Double(a.doubleValue() * b.doubleValue()); + } + else if (widest == Float.class) { + result = new Float(a.floatValue() * b.floatValue()); + } + else if (widest == Long.class) { + result = new Long(a.longValue() * b.longValue()); + } + else if (widest == Integer.class) { + result = new Integer(a.intValue() * b.intValue()); + } + else if (widest == Short.class) { + result = new Short((short)(a.shortValue() * b.shortValue())); + } + else { + result = new Byte((byte)(a.byteValue() * b.byteValue())); + } + push(stack, result); + } + + private static void divide(List stack) { + Number b = popNumber(stack); + Number a = popNumber(stack); + Class widest = widest(a.getClass(), b.getClass()); + Number result; + if (widest == Double.class) { + result = new Double(a.doubleValue() / b.doubleValue()); + } + else if (widest == Float.class) { + result = new Float(a.floatValue() / b.floatValue()); + } + else if (widest == Long.class) { + result = new Long(a.longValue() / b.longValue()); + } + else if (widest == Integer.class) { + result = new Integer(a.intValue() / b.intValue()); + } + else if (widest == Short.class) { + result = new Short((short)(a.shortValue() / b.shortValue())); + } + else { + result = new Byte((byte)(a.byteValue() / b.byteValue())); + } + push(stack, result); + } + + private static void eq(List stack) { + Object b = pop(stack); + Object a = pop(stack); + Boolean result = (a == b || (a != null && a.equals(b)) ? Boolean.TRUE : Boolean.FALSE); + push(stack, result); + } + + private static void ne(List stack) { + Object b = pop(stack); + Object a = pop(stack); + push(stack, Boolean.valueOf(a == b || (a != null && a.equals(b)))); + } + + private static void gt(List stack) { + Comparable b = popComparable(stack); + Comparable a = popComparable(stack); + push(stack, Boolean.valueOf(a.compareTo(b) > 0)); + } + + private static void lt(List stack) { + Comparable b = popComparable(stack); + Comparable a = popComparable(stack); + push(stack, Boolean.valueOf(a.compareTo(b) < 0)); + } + + private static void ge(List stack) { + Comparable b = popComparable(stack); + Comparable a = popComparable(stack); + push(stack, Boolean.valueOf(a.compareTo(b) >= 0)); + } + + private static void le(List stack) { + Comparable b = popComparable(stack); + Comparable a = popComparable(stack); + push(stack, Boolean.valueOf(a.compareTo(b) <= 0)); + } + + private static void or(List stack) { + Boolean b = popBoolean(stack); + Boolean a = popBoolean(stack); + push(stack, Boolean.valueOf(a.booleanValue() || b.booleanValue())); + } + + private static void and(List stack) { + Boolean b = popBoolean(stack); + Boolean a = popBoolean(stack); + push(stack, Boolean.valueOf(a.booleanValue() && b.booleanValue())); + } + + private static void not(List stack) { + Boolean a = popBoolean(stack); + push(stack, Boolean.valueOf(!a.booleanValue())); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/ParsedExpressionException.java b/dasCore/src/main/java/org/das2/dasml/ParsedExpressionException.java new file mode 100644 index 000000000..d4afdb77b --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/ParsedExpressionException.java @@ -0,0 +1,37 @@ +/* File: ParsedExpressionException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +/** + * + * @author eew + */ +public class ParsedExpressionException extends java.lang.Exception { + + /** Creates a new instance of ParsedExpressionFormat */ + public ParsedExpressionException(String message) { + super(message); + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/SerializeUtil.java b/dasCore/src/main/java/org/das2/dasml/SerializeUtil.java new file mode 100644 index 000000000..7d019b707 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/SerializeUtil.java @@ -0,0 +1,322 @@ +/* + * SerializeUtil.java + * + * Created on June 21, 2005, 9:55 AM + * + * To change this template, choose Tools | Options and locate the template under + * the Source Creation and Management node. Right-click the template and choose + * Open. You can then make changes to the template in the Source Editor. + */ + +package org.das2.dasml; + +import org.das2.graph.DasCanvasComponent; +import org.das2.beans.AccessLevelBeanInfo; +import org.das2.beans.BeansUtil; +import org.das2.system.DasLogger; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.monitor.NullProgressMonitor; +import java.beans.*; +import java.beans.PropertyDescriptor; +import java.lang.reflect.*; +import java.text.*; +import java.util.*; +import java.util.logging.*; +import org.w3c.dom.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * + * @author Jeremy + */ +public class SerializeUtil { + + static Set toStringSet; + static { + toStringSet= new HashSet(); + toStringSet.add( Boolean.class ); + toStringSet.add( Short.class ); + toStringSet.add( Integer.class ); + toStringSet.add( Long.class ); + toStringSet.add( Float.class ); + toStringSet.add( Double.class ); + toStringSet.add( String.class ); + } + + public static org.w3c.dom.Element getDOMElement( Document document, Object object ) { + return getDOMElement( document, object, new NullProgressMonitor() ); + } + + public static org.w3c.dom.Element getDOMElement( Document document, Object object, ProgressMonitor monitor ) { + Logger log= DasLogger.getLogger( DasLogger.SYSTEM_LOG ); + + try { + String elementName= object.getClass().getName(); + elementName= elementName.replaceAll("\\$", "\\_dollar_"); + + Element element=null; + try { + element= document.createElement(elementName); + } catch ( Exception e ) { + System.err.println(e); + throw new RuntimeException(e); + } + + BeanInfo info = BeansUtil.getBeanInfo(object.getClass()); + + AccessLevelBeanInfo alInfo= BeansUtil.asAccessLevelBeanInfo( info, object.getClass() ); + + PropertyDescriptor[] properties = alInfo.getPropertyDescriptors( AccessLevelBeanInfo.PersistenceLevel.PERSISTENT ); + String[] propertyNameList= BeansUtil.getPropertyNames( properties ); + + HashMap nameMap= new HashMap(); + + for ( int i=0; i0 ) monitor.setTaskSize( propertyNameList.length ); + monitor.started(); + + for ( int i=0; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dasml; + +//import org.apache.xml.serialize.*; +import org.w3c.dom.Document; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.io.StringWriter; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; + +/** + * + * @author eew + */ +public class TransferableFormComponent implements Transferable { + + public static final DataFlavor COMPONENT_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormComponent"); + public static final DataFlavor PANEL_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormPanel"); + public static final DataFlavor TEXT_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormText"); + public static final DataFlavor TEXTFIELD_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormTextField"); + public static final DataFlavor BUTTON_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormButton"); + public static final DataFlavor CHECKBOX_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormCheckBox"); + public static final DataFlavor BUTTONGROUP_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormRadioButtonGroup"); + public static final DataFlavor RADIOBUTTON_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormRadioButton"); + public static final DataFlavor TAB_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormTab"); + public static final DataFlavor CHOICE_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormChoice"); + public static final DataFlavor LIST_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormList"); + public static final DataFlavor WINDOW_FLAVOR = createJVMLocalDataFlavor("org.das2.dasml.FormWindow"); + public static final DataFlavor DASML_FRAGMENT_FLAVOR; + static { + try { + DASML_FRAGMENT_FLAVOR = new DataFlavor("x-text/dasml-fragment;class=java.lang.String"); + } + catch (ClassNotFoundException cnfe) { + throw new RuntimeException(cnfe); + } + } + + private final FormComponent formComponent; + private final DataFlavor moreSpecificDataFlavor; + private DataFlavor[] flavorList; + + public TransferableFormComponent(FormPanel panel) { + formComponent = panel; + moreSpecificDataFlavor = PANEL_FLAVOR; + } + + public TransferableFormComponent(FormText text) { + formComponent = text; + moreSpecificDataFlavor = TEXT_FLAVOR; + } + + public TransferableFormComponent(FormTextField textField) { + formComponent = textField; + moreSpecificDataFlavor = TEXTFIELD_FLAVOR; + } + + public TransferableFormComponent(FormButton button) { + formComponent = button; + moreSpecificDataFlavor = BUTTON_FLAVOR; + } + + public TransferableFormComponent(FormCheckBox checkBox) { + formComponent = checkBox; + moreSpecificDataFlavor = CHECKBOX_FLAVOR; + } + + public TransferableFormComponent(FormRadioButtonGroup buttonGroup) { + formComponent = buttonGroup; + moreSpecificDataFlavor = BUTTONGROUP_FLAVOR; + } + + public TransferableFormComponent(FormRadioButton radioButton) { + formComponent = radioButton; + moreSpecificDataFlavor = RADIOBUTTON_FLAVOR; + } + + public TransferableFormComponent(FormTab form) { + formComponent = form; + moreSpecificDataFlavor = TAB_FLAVOR; + } + + public TransferableFormComponent(FormChoice choice) { + formComponent = choice; + moreSpecificDataFlavor = CHOICE_FLAVOR; + } + + public TransferableFormComponent(FormList list) { + formComponent = list; + moreSpecificDataFlavor = LIST_FLAVOR; + } + + public TransferableFormComponent(FormWindow window) { + formComponent = window; + moreSpecificDataFlavor = WINDOW_FLAVOR; + } + + /** Returns an object which represents the data to be transferred. The class + * of the object returned is defined by the representation class of the flavor. + * + * @param flavor the requested flavor for the data + * @see DataFlavor#getRepresentationClass + * @exception IOException if the data is no longer available + * in the requested flavor. + * @exception UnsupportedFlavorException if the requested data flavor is + * not supported. + */ + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, java.io.IOException { + if (flavor.equals(moreSpecificDataFlavor) || flavor.equals(COMPONENT_FLAVOR)) { + return formComponent; + } + else if (flavor.equals(DataFlavor.stringFlavor) + || flavor.equals(DASML_FRAGMENT_FLAVOR)) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.newDocument(); + document.appendChild(formComponent.getDOMElement(document)); + StringWriter writer = new StringWriter(); + + DOMImplementationLS ls = (DOMImplementationLS) + document.getFeature("LS", "3.0"); + LSOutput output = ls.createLSOutput(); + output.setEncoding("UTF-8"); + output.setCharacterStream(writer); + LSSerializer serializer = ls.createLSSerializer(); + serializer.write(document, output); + + /* + OutputFormat format = new OutputFormat(Method.XML, "UTF-8", true); + format.setOmitXMLDeclaration(true); + format.setOmitDocumentType(true); + XMLSerializer serializer = new XMLSerializer(writer, format); + serializer.serialize(document); + */ + + return writer.toString(); + } + catch (ParserConfigurationException pce) { + throw new RuntimeException(pce); + } + } + throw new UnsupportedFlavorException(flavor); + } + + /** Returns an array of DataFlavor objects indicating the flavors the data + * can be provided in. The array should be ordered according to preference + * for providing the data (from most richly descriptive to least descriptive). + * @return an array of data flavors in which this data can be transferred + */ + public DataFlavor[] getTransferDataFlavors() { + if (flavorList == null) { + flavorList = new DataFlavor[] { + moreSpecificDataFlavor, + COMPONENT_FLAVOR, + DASML_FRAGMENT_FLAVOR, + DataFlavor.stringFlavor, + }; + } + return flavorList; + } + + /** Returns whether or not the specified data flavor is supported for + * this object. + * @param flavor the requested flavor for the data + * @return boolean indicating whether or not the data flavor is supported + */ + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(COMPONENT_FLAVOR) + || flavor.equals(moreSpecificDataFlavor) + || flavor.equals(DataFlavor.stringFlavor); + } + + private static DataFlavor createJVMLocalDataFlavor(String classname) { + try { + String mimeType = DataFlavor.javaJVMLocalObjectMimeType + + ";class="+classname; + return new DataFlavor(mimeType); + } + catch (ClassNotFoundException cnfe) { + throw new RuntimeException(cnfe); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/dasml/package.html b/dasCore/src/main/java/org/das2/dasml/package.html new file mode 100644 index 000000000..08bd7f7dc --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/package.html @@ -0,0 +1,4 @@ + + Package for implementing dasml, a language for describing das2 applications. +Note dasml has fallen out of use. + diff --git a/dasCore/src/main/java/org/das2/dasml/schema/action.xsd b/dasCore/src/main/java/org/das2/dasml/schema/action.xsd new file mode 100644 index 000000000..2b5d41167 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/schema/action.xsd @@ -0,0 +1 @@ + diff --git a/dasCore/src/main/java/org/das2/dasml/schema/canvas.xsd b/dasCore/src/main/java/org/das2/dasml/schema/canvas.xsd new file mode 100644 index 000000000..acea1e52c --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/schema/canvas.xsd @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dasCore/src/main/java/org/das2/dasml/schema/commandblock.xsd b/dasCore/src/main/java/org/das2/dasml/schema/commandblock.xsd new file mode 100644 index 000000000..b99eafa13 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/schema/commandblock.xsd @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dasCore/src/main/java/org/das2/dasml/schema/commandfragment.xsd b/dasCore/src/main/java/org/das2/dasml/schema/commandfragment.xsd new file mode 100644 index 000000000..1a09e828a --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/schema/commandfragment.xsd @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dasCore/src/main/java/org/das2/dasml/schema/dasML.xsd b/dasCore/src/main/java/org/das2/dasml/schema/dasML.xsd new file mode 100644 index 000000000..27056fb67 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/schema/dasML.xsd @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dasCore/src/main/java/org/das2/dasml/schema/dasMLtypes.xsd b/dasCore/src/main/java/org/das2/dasml/schema/dasMLtypes.xsd new file mode 100644 index 000000000..9e764e53d --- /dev/null +++ b/dasCore/src/main/java/org/das2/dasml/schema/dasMLtypes.xsd @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dasCore/src/main/java/org/das2/dataset/AbstractDataSet.java b/dasCore/src/main/java/org/das2/dataset/AbstractDataSet.java new file mode 100755 index 000000000..e59d99641 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/AbstractDataSet.java @@ -0,0 +1,177 @@ +/* File: AbstractDataSet.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 27, 2003, 9:32 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.Datum; + +import java.util.*; + +/** + * + * @author eew + */ +public abstract class AbstractDataSet implements DataSet { + + private Map properties; + + private double[] xTags; + + private Units xUnits; + + private Units yUnits; + + /** Creates a new instance of AbstractDataSet + * The properties map must only have keys of type String. + * @param xTags values of the x tags for this data set in xUnits + * @param xUnits the units of the x tags for this data set + * @param yUnits the units of the y tags/values for this data set + * @param properties map of property names and values + * @throws IllegalArgumentException if properties has one or more keys + * that is not a String + */ + protected AbstractDataSet(double[] xTags, Units xUnits, Units yUnits, Map properties) throws IllegalArgumentException { + for (Iterator i = properties.keySet().iterator(); i.hasNext();) { + if (!(i.next() instanceof String)) { + throw new IllegalArgumentException("Non-String key found in property map"); + } + } + this.properties = new HashMap(properties); + this.xTags = (double[])xTags.clone(); + this.xUnits = xUnits; + this.yUnits = yUnits; + } + + private AbstractDataSet() { + } + + /** Returns the value of the property that name represents + * @param name String name of the property requested + * @return the value of the property that name represents + */ + public Object getProperty(String name) { + return properties.get(name); + } + + protected boolean hasProperty(String name) { + return properties.containsKey(name); + } + + public Map getProperties() { + return new HashMap( properties ); + } + + public int getXLength() { + return xTags.length; + } + + public Datum getXTagDatum(int i) { + return Datum.create(xTags[i], getXUnits()); + } + + public double getXTagDouble(int i, Units units) { + double xTag = xTags[i]; + xTag = getXUnits().getConverter(units).convert(xTag); + return xTag; + } + + public int getXTagInt(int i, Units units) { + return (int)Math.round(getXTagDouble(i, units)); + } + + /** Returns the Units object representing the unit type of the x tags + * for this data set. + * @return the x units + */ + public Units getXUnits() { + return xUnits; + } + + /** Returns the Units object representing the unit type of the y tags + * or y values for this data set. + * @return the y units + */ + public Units getYUnits() { + return yUnits; + } + + /** A DataSet implementation that share properties, yUnits and + * yUnits with the instance of AbstractDataSet it is associated with. + * This class is provided so that sub-classes of AbstractDataSet can + * extend this class when creating views of their data without having + * to copy the immutable data AbstractDataSet contains. + */ + protected abstract class ViewDataSet extends AbstractDataSet implements DataSet { + + protected ViewDataSet() {} + + /** Returns the value of the property that name represents + * @param name String name of the property requested + * @return the value of the property that name represents + */ + public Object getProperty(String name) { + return properties.get(name); + } + + public Map getProperties() { + return new HashMap(properties); + } + + public int getXLength() { + return xTags.length; + } + + public Datum getXTagDatum(int i) { + return Datum.create(xTags[i], getXUnits()); + } + + public double getXTagDouble(int i, Units units) { + double xTag = xTags[i]; + xTag = getXUnits().getConverter(units).convert(xTag); + return xTag; + } + + public int getXTagInt(int i, Units units) { + return (int)Math.round(getXTagDouble(i, units)); + } + + /** Returns the Units object representing the unit type of the x tags + * for this data set. + * @return the x units + */ + public Units getXUnits() { + return xUnits; + } + + /** Returns the Units object representing the unit type of the y tags + * or y values for this data set. + * @return the y units + */ + public Units getYUnits() { + return yUnits; + } + + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/AbstractDataSetCache.java b/dasCore/src/main/java/org/das2/dataset/AbstractDataSetCache.java new file mode 100644 index 000000000..72f1e6535 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/AbstractDataSetCache.java @@ -0,0 +1,189 @@ +/* File: DataSetCache.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.components.propertyeditor.Displayable; +import org.das2.datum.Datum; +import org.das2.DasApplication; +import org.das2.system.DasLogger; +import java.text.*; +import java.util.*; +import java.util.logging.Logger; +/** + * Keeps keep track of cache statistics and to give consistent + * log messages, and provides the Entry class. + * + * @author jbf + */ +public abstract class AbstractDataSetCache implements DataSetCache { + + protected class Entry implements Displayable { + + protected DataSetDescriptor dsd; + protected CacheTag cacheTag; + protected DataSet data; + protected int nhits; + protected long birthTime; + protected long lastAccess; + + Entry() { + this( null, null, null ); + } + + Entry( DataSetDescriptor dsd, CacheTag cacheTag, DataSet data ) { + this.dsd= dsd; + this.cacheTag= cacheTag; + this.data= data; + this.nhits= 0; + this.birthTime= System.currentTimeMillis(); + this.lastAccess= birthTime; + } + + /* + * returns the dataSet attached to the Entry, and updates the lastAccess time + */ + protected DataSet getData() { + this.lastAccess= System.currentTimeMillis(); + return data; + } + + protected boolean satifies( Entry entry ) { + boolean result= ( this.dsd!=null) && ( entry.dsd!=null); + result= result && ( this.dsd.equals( entry.dsd ) ); + result= result && ( cacheTag.contains(entry.cacheTag) ); + return result; + } + + public String toString() { + long sizeBytes= DataSetUtil.guessSizeBytes(this.data); + String sizeBytesString= " ("+NumberFormat.getIntegerInstance().format(sizeBytes)+" bytes)"; + return dsd.toString() + " " + cacheTag + " ["+nhits+" hits]"+sizeBytesString; + } + + public javax.swing.Icon getListIcon() { + return null; + } + + public String getListLabel() { + return toString(); + } + + public CacheTag getCacheTag() { + return this.cacheTag; + } + + } + + private static final Logger logger= DasLogger.getLogger(DasLogger.SYSTEM_LOG); + + public int hits=0; + public int misses=0; + + abstract public void store( DataSetDescriptor dsd, CacheTag cacheTag, DataSet data ); + + abstract boolean haveStoredImpl( DataSetDescriptor dsd, CacheTag cacheTag ); + + public boolean haveStored( DataSetDescriptor dsd, CacheTag cacheTag ) { + boolean result= haveStoredImpl( dsd, cacheTag ); + if ( result ) { + logger.fine("cache hit "+dsd+" "+cacheTag); + hits++; + } else { + logger.fine("cache miss "+dsd+" "+cacheTag); + misses++; + } + return result; + } + + /** + * return a measure of the utility of the entry, presumably so that it may be + * swapped out when a resource limit is met. + */ + protected long cacheValue( Entry e ) { + return e.lastAccess; + } + + abstract DataSet retrieveImpl( DataSetDescriptor dsd, CacheTag cacheTag ); + + public DataSet retrieve( DataSetDescriptor dsd, CacheTag cacheTag ) { + return retrieveImpl( dsd, cacheTag ); + } + + /** + * reset the internal state of the cache + */ + abstract public void reset(); + + /** + * return the DataSet described by the set of DataSets if possible. + * @throws IllegalArgumentException if a subset is not continous, + * non-overlapping, and of the same resolution. Removes + * elements from the list that are not needed for the set. + */ + public DataSet coalese( List result ) { + Collections.sort( result, new Comparator() { + public int compare( Object o1, Object o2 ) { + return ((Entry)o1).cacheTag.range.compareTo( ((Entry)o2).cacheTag.range ); + } + } ); + + Entry e0= (Entry)result.get(0); + CacheTag ct= e0.cacheTag; + Datum t1= ct.range.max(); + Datum resolution= ct.resolution; + + DataSet ds= e0.data; + + // check for continuity and non-overlap + for ( int i=1; i + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.Datum; + +import java.util.*; + +/** + * + * @author Edward West + */ +public abstract class AbstractTableDataSet extends AbstractDataSet implements DataSet, TableDataSet { + + private Units zUnits; + + protected List tableProperties; + + /** Creates a new instance of AbstractTableDataSet */ + public AbstractTableDataSet(double[] xTags, Units xUnits, Units yUnits, Units zUnits, Map properties) { + super(xTags, xUnits, yUnits, properties); + this.zUnits = zUnits; + this.tableProperties= null; + } + + public Units getZUnits() { + return zUnits; + } + + public VectorDataSet getXSlice(int i) { + return new XSliceDataSet(this, i); + } + + public VectorDataSet getYSlice(int j, int table) { + return new YSliceDataSet(this, j, table); + } + + public String toString() { + return TableUtil.toString(this); + } + + public Object getProperty(int table, String name) { + if ( tableProperties!=null ) { + return tableProperties.get(table).get(name); + } else { + return null; + } + } + + protected static class XSliceDataSet extends AbstractDataSet.ViewDataSet implements VectorDataSet{ + + private int iIndex; + private TableDataSet ds; + + protected XSliceDataSet(AbstractDataSet ds, int i) { + ds.super(); + this.ds = (TableDataSet)ds; + this.iIndex = i; + } + + public DataSet getPlanarView(String planeID) { + return new XSliceDataSet( (AbstractDataSet)ds.getPlanarView(planeID), iIndex ); + } + + public String[] getPlaneIds() { + return ds.getPlaneIds(); + } + + public Datum getDatum(int i) { + return ds.getDatum(iIndex, i); + } + + public double getDouble(int i, Units units) { + return ds.getDouble(iIndex, i, units); + } + + public int getInt(int i, Units units) { + return ds.getInt(iIndex, i, units); + } + + public Datum getXTagDatum(int i) { + int table = ds.tableOfIndex(iIndex); + return ds.getYTagDatum(table, i); + } + + public int getXLength() { + int table = ds.tableOfIndex(iIndex); + return ds.getYLength(table); + } + + public Units getXUnits() { + return ds.getYUnits(); + } + + public double getXTagDouble(int i, Units units) { + int table = ds.tableOfIndex(iIndex); + return ds.getYTagDouble(table, i, units); + } + + public Units getYUnits() { + return ds.getZUnits(); + } + + public int getXTagInt(int i, Units units) { + int table = ds.tableOfIndex(iIndex); + return ds.getYTagInt(table, i, units); + } + + public Object getProperty(String name) { + return null; + } + + public String toString() { + return VectorUtil.toString(this); + } + + public Map getProperties() { + return new HashMap(); + } + + } + + protected static class YSliceDataSet extends AbstractDataSet.ViewDataSet implements VectorDataSet { + + private final int table; + private final int jIndex; + private final TableDataSet ds; + + protected YSliceDataSet(AbstractDataSet ds, int jIndex, int table) { + ds.super(); + this.jIndex = jIndex; + this.table = table; + this.ds = (TableDataSet)ds; + } + + public DataSet getPlanarView(String planeID) { + return new YSliceDataSet( (AbstractDataSet)ds.getPlanarView(planeID), jIndex, table ); + } + + public String[] getPlaneIds() { + return ds.getPlaneIds(); + } + + public Datum getDatum(int i) { + int offset = ds.tableStart(table); + return ds.getDatum(i + offset, jIndex); + } + + public double getDouble(int i, Units units) { + int offset = ds.tableStart(table); + return ds.getDouble(i + offset, jIndex, units); + } + + public int getInt(int i, Units units) { + int offset = ds.tableStart(table); + return ds.getInt(i + offset, jIndex, units); + } + + public Datum getXTagDatum(int i) { + int offset = ds.tableStart(table); + return ds.getXTagDatum(i + offset); + } + + public int getXLength() { + return ds.tableEnd(table) - ds.tableStart(table); + } + + public double getXTagDouble(int i, Units units) { + int offset = ds.tableStart(table); + return ds.getXTagDouble(i + offset, units); + } + + public Units getYUnits() { + return ds.getZUnits(); + } + + public int getXTagInt(int i, Units units) { + int offset = ds.tableStart(table); + return ds.getXTagInt(i + offset, units); + } + + public Object getProperty(String name) { + return null; + } + + public Map getProperties() { + return new HashMap(); + } + + } +} diff --git a/dasCore/src/main/java/org/das2/dataset/AbstractVectorDataSet.java b/dasCore/src/main/java/org/das2/dataset/AbstractVectorDataSet.java new file mode 100755 index 000000000..56e2740c4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/AbstractVectorDataSet.java @@ -0,0 +1,59 @@ +/* File: AbstractVectorDataSet.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 27, 2003, 10:31 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; + +import java.util.*; + +/** Abstract implementation of the VectorDataSet interface provided to make + * implementation of concrete base classes easier. Subclasses only need to + * implement:

    + *
  • {@link VectorDataSet#getDatum(int)}
  • + *
  • {@link VectorDataSet#getDouble(int, org.das2.datum.Units)}
  • + *
  • {@link VectorDataSet#getInt(int, org.das2.datum.Units)}
  • + *
  • {@link DataSet#getPlanarView(java.lang.String)}
  • + * + * @author Edward West + */ +public abstract class AbstractVectorDataSet extends AbstractDataSet implements DataSet, VectorDataSet { + + /** Creates a new instance of AbstractVectorDataSet + * The properties map must only have keys of type String. + * @param xTags values of the x tags for this data set in xUnits + * @param xUnits the units of the x tags for this data set + * @param yUnits the units of the y tags/values for this data set + * @param properties map of property names and values + * @throws IllegalArgumentException if properties has one or more keys + * that is not a String + */ + protected AbstractVectorDataSet(double[] xTags, Units xUnits, Units yUnits, Map properties) throws IllegalArgumentException { + super(xTags, xUnits, yUnits, properties); + } + + public String toString() { + return VectorUtil.toString(this); + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/AppendTableDataSet.java b/dasCore/src/main/java/org/das2/dataset/AppendTableDataSet.java new file mode 100644 index 000000000..3255c99b2 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/AppendTableDataSet.java @@ -0,0 +1,239 @@ +/* + * AppendTableDataSet.java + * + * Created on September 27, 2005, 2:12 PM + * + * + */ + +package org.das2.dataset; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Jeremy + */ +public class AppendTableDataSet implements TableDataSet { + TableDataSet[] tableDataSets; + int[] firstIndexs; + int[] firstTables; + + public AppendTableDataSet( TableDataSet tds1, TableDataSet tds2 ) { + List tableDataSetsList= new ArrayList(); + List firstIndexsList= new ArrayList(); + List firstTablesList= new ArrayList(); + + if ( tds1 instanceof AppendTableDataSet && tds2 instanceof AppendTableDataSet ) { + throw new IllegalStateException("not implemented"); + } else if ( tds1 instanceof AppendTableDataSet ) { + AppendTableDataSet atds1= (AppendTableDataSet)tds1; + tableDataSetsList.addAll( box( atds1.tableDataSets ) ); + firstIndexsList.addAll( box( atds1.firstIndexs ) ); + firstTablesList.addAll( box( atds1.firstTables ) ); + tableDataSetsList.add( tds2 ); + firstIndexsList.add( new Integer( atds1.firstIndexs[atds1.tableDataSets.length]+tds2.getXLength() ) ); + firstTablesList.add( new Integer( atds1.firstTables[atds1.tableDataSets.length]+tds2.getXLength() ) ); + + tableDataSets= (TableDataSet[])tableDataSetsList.toArray( new TableDataSet[ tableDataSetsList.size() ] ); + firstIndexs= unbox( firstIndexsList ); + firstTables= unbox( firstTablesList ); + + } else if ( tds2 instanceof AppendTableDataSet ) { + throw new IllegalStateException("not implemented"); + + } else { + tableDataSets= new TableDataSet[2]; + tableDataSets[0]= tds1; + tableDataSets[1]= tds2; + firstIndexs= new int[3]; + firstIndexs[0]= 0; + firstIndexs[1]= tds1.getXLength(); + firstIndexs[2]= firstIndexs[1] + tds2.getXLength(); + firstTables= new int[3]; + firstTables[0]= 0; + firstTables[1]= tds1.tableCount(); + firstTables[2]= firstTables[1] + tds2.tableCount(); + } + } + + private int[] unbox( List intList ) { + int[] result= new int[intList.size()]; + for ( int i=0; i=i ) return itds; + } + return firstIndexs.length-1; + } + + private int tdsTable( int itable ) { + for ( int itds=0; itds=itable ) return itds; + } + return firstIndexs.length-1; + } + + public org.das2.datum.Datum getDatum(int i, int j) { + int itds= tdsIndex(i); + return tableDataSets[itds].getDatum( i-firstIndexs[itds], j ); + } + + public double getDouble(int i, int j, org.das2.datum.Units units) { + int itds= tdsIndex(i); + return tableDataSets[itds].getDouble( i-firstIndexs[itds], j, units ); + } + + public double[] getDoubleScan(int i, org.das2.datum.Units units) { + int itds= tdsIndex(i); + return tableDataSets[itds].getDoubleScan( i-firstIndexs[itds], units ); + } + + public int getInt(int i, int j, org.das2.datum.Units units) { + int itds= tdsIndex(i); + return tableDataSets[itds].getInt( i-firstIndexs[itds], j, units ); + } + + public DataSet getPlanarView(String planeID) { + TableDataSet[] tdsPlane= new TableDataSet[tableDataSets.length]; + for ( int i=0; i "+outputBins[i]+"\n" ); + } + if ( length==0 ) { + result.append( "(no rebinning)\n" ); + } else if ( length>30 ) { + result.append( "("+(length-30)+" more)" ); + } + return result.toString(); + } + } + + private static DatumRange[] getXTagRanges( DataSet ds, int i0, int i1 ) { + Datum tagWidth= DataSetUtil.guessXTagWidth(ds).divide(2); + DatumRange[] result= new DatumRange[ i1-i0 ]; + for ( int i=0; i1 ) throw new IllegalArgumentException( "null yRebinDescriptor not allowed for non-simple table datasets." ); + ybd= getIdentityBinDescriptor( tds.getYLength(itable) ); + } + logger.finest("apply rebinning"); + + logger.finest("ybd.length="+ybd.length); + + int x0= tds.tableStart(itable); + if ( nearestNeighbor ) { + for ( int i=0; i weights[xbd.outputBins[i]][ybd.outputBins[j]] ) { + sum[xbd.outputBins[i]][ybd.outputBins[j]]= z; + weights[xbd.outputBins[i]][ybd.outputBins[j]]= w * w2; + } + } + } + } else { + for ( int i=0; i0. ) { + sum[i][j]/= weights[i][j]; + } else { + sum[i][j]= fill; + } + } + } + } + + logger.finest("calculate dataset"); + double[][][] zValues = {sum,weights}; + + int[] tableOffsets = {0}; + Units[] zUnits = {tds.getZUnits(), Units.dimensionless}; + String[] planeIDs = {"", DataSet.PROPERTY_PLANE_WEIGHTS }; + + Map properties= new HashMap(ds.getProperties()); + + if ( ddx!=null ) properties.put( DataSet.PROPERTY_X_TAG_WIDTH, ddx.binWidthDatum() ); + if ( ddy!=null ) properties.put( DataSet.PROPERTY_Y_TAG_WIDTH, ddy.binWidthDatum() ); + + double[] xTags; + if (ddx != null) { + xTags = ddx.binCenters(); + } else { + xTags = new double[nx]; + for (int i = 0; i < nx; i++) { + xTags[i] = tds.getXTagDouble(i, tds.getXUnits()); + } + } + double[][] yTags; + if (ddy != null) { + yTags = new double[][]{ddy.binCenters()}; + } else { + yTags = new double[1][ny]; + for (int j = 0; j < ny; j++) { + yTags[0][j] = tds.getYTagDouble(0, j, tds.getYUnits()); + } + } + + TableDataSet result= new DefaultTableDataSet( xTags, ddx.getUnits(), yTags, ddy.getUnits(), zValues, zUnits, planeIDs, tableOffsets, properties ); + + logger.finest("done, exiting AverageNoInterpolateTableRebinner.rebin"); + return result; + } + + public boolean isNearestNeighbor( ) { + return this.nearestNeighbor; + } + + public void setNearestNeighbor( boolean v ) { + this.nearestNeighbor= v; + } + + +} diff --git a/dasCore/src/main/java/org/das2/dataset/AveragePeakTableRebinner.java b/dasCore/src/main/java/org/das2/dataset/AveragePeakTableRebinner.java new file mode 100755 index 000000000..ed6b3135a --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/AveragePeakTableRebinner.java @@ -0,0 +1,163 @@ +/* File: TableAveragePeakRebinner.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on November 12, 2003, 4:25 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.Datum; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author Edward West + */ +public class AveragePeakTableRebinner implements DataSetRebinner { + + /* adds additional planes for debugging */ + private boolean debug= false; + + /** Creates a new instance of TableAveragePeakRebinner */ + public AveragePeakTableRebinner() { + } + + @Override + public DataSet rebin( + DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) throws IllegalArgumentException { + + if (!(ds instanceof TableDataSet)) { + throw new IllegalArgumentException(); + } + + TableDataSet tds = (TableDataSet)ds; + + if ( ddY==null && tds.tableCount()==0 ) { + throw new IllegalArgumentException( "empty table and null RebinDescriptor for Y, so result YTags are undefined." ); + } + + TableDataSet weights = (TableDataSet)ds.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); + TableDataSet peaks = (TableDataSet)ds.getPlanarView(DataSet.PROPERTY_PLANE_PEAKS); + + long timer= System.currentTimeMillis(); + + int nx= (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + int ny= (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); + + double[][] averageData= new double[nx][ny]; + double[][] averageWeights= new double[nx][ny]; + double[][] peakData = new double[nx][ny]; + + AverageTableRebinner.average(tds, weights, averageData, averageWeights, ddX, ddY); + + double[] xTags; + double[] xTagMin, xTagMax; + if (ddX != null) { + xTags = ddX.binCenters(); + xTagMin = ddX.binStops(); + xTagMax = ddX.binStarts(); + for ( int i=0; i-1 && ibin + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.dataset; + +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.datum.DatumRangeUtil; +import org.das2.datum.UnitsUtil; +import org.das2.DasException; +import org.das2.system.DasLogger; +import java.util.*; +import java.util.logging.*; + +/** + * + * @author Edward West + */ +public class AverageTableRebinner implements DataSetRebinner { + + private static Logger logger = DasLogger.getLogger(DasLogger.DATA_OPERATIONS_LOG); + /** + * Holds value of property interpolate. + */ + private boolean interpolate = true; + private boolean enlargePixels = true; + + public static enum Interpolate { + + None, Linear, NearestNeighbor + } + + /** Creates a new instance of TableAverageRebinner */ + public AverageTableRebinner() { + } + + @Override + public DataSet rebin( + DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) throws IllegalArgumentException, DasException { + logger.finest("enter AverageTableRebinner.rebin"); + + if(override != null) + throw new UnsupportedOperationException("This rebinner does not "+ + "yet know how to override dataset properties."); + + if (ds == null) { + throw new NullPointerException("null data set"); + } + if (!(ds instanceof TableDataSet)) { + throw new IllegalArgumentException("Data set must be an instanceof TableDataSet: " + ds.getClass().getName()); + } + TableDataSet tds = (TableDataSet) ds; + TableDataSet weights = (TableDataSet) ds.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); + if (ddX != null && tds.getXLength() > 0) { + double start = tds.getXTagDouble(0, ddX.getUnits()); + double end = tds.getXTagDouble(tds.getXLength() - 1, ddX.getUnits()); + if (start > ddX.end) { + throw new NoDataInIntervalException("data starts after range"); + } else if (end < ddX.start) { + throw new NoDataInIntervalException("data ends before range"); + } + } + + long timer = System.currentTimeMillis(); + + Units xunits = ddX.getUnits(); + + int nx = (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + int ny = (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); + + logger.finest("Allocating rebinData and rebinWeights: " + nx + " x " + ny); + + double[][] rebinData = new double[nx][ny]; + double[][] rebinWeights = new double[nx][ny]; + + average(tds, weights, rebinData, rebinWeights, ddX, ddY); + if (interpolate) { + doBoundaries2RL(tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType); + doBoundaries2TB(tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType); + doCorners(tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType); + } + + double[] xTags; + double[] xTagMin; + double[] xTagMax; + + if (ddX != null) { + xTags = ddX.binCenters(); + xTagMin = ddX.binStops(); + xTagMax = ddX.binStarts(); + for (int i = 0; i < tds.getXLength(); i++) { + double xt = tds.getXTagDouble(i, xunits); + int ibin = ddX.whichBin(xt, xunits); + if (ibin > -1 && ibin < nx) { + xTagMin[ibin] = Math.min(xTagMin[ibin], xt); + xTagMax[ibin] = Math.max(xTagMax[ibin], xt); + } + } + } else { + xTags = new double[nx]; + for (int i = 0; i < nx; i++) { + xTags[i] = tds.getXTagDouble(i, tds.getXUnits()); + } + xTagMin = xTags; + xTagMax = xTags; + } + + + double[][] yTags; + if (ddY != null) { + yTags = new double[][]{ddY.binCenters()}; + } else { + yTags = new double[1][ny]; + for (int j = 0; j < ny; j++) { + yTags[0][j] = tds.getYTagDouble(0, j, tds.getYUnits()); + } + } + + Units resultXUnits = ddX == null ? tds.getXUnits() : ddX.getUnits(); + Units resultYUnits = ddY == null ? tds.getYUnits() : ddY.getUnits(); + + if (this.interpolate) { + Datum xTagWidth = (Datum) ds.getProperty("xTagWidth"); + if (xTagWidth == null) { + xTagWidth = DataSetUtil.guessXTagWidth(tds); + } + double xTagWidthDouble = xTagWidth.doubleValue(ddX.getUnits().getOffsetUnits()); + Datum yTagWidth = (Datum) ds.getProperty("yTagWidth"); + + if (ddX != null) { + fillInterpolateX(rebinData, rebinWeights, xTags, xTagMin, xTagMax, xTagWidthDouble, interpolateType); + } + if (ddY != null) { + /* Note the yTagMin,yTagMax code doesn't work here, because of the + * multiple tables. So here, we'll just up yTagWidth to be twice + * the pixel cadence. When a new data model is introduced, + * this should be revisited. + */ + if (yTagWidth == null && interpolateType == Interpolate.NearestNeighbor) { + yTagWidth = TableUtil.guessYTagWidth(tds); + } + fillInterpolateY(rebinData, rebinWeights, ddY, yTagWidth, interpolateType); + } + } else if (enlargePixels) { + enlargePixels(rebinData, rebinWeights); + } + + double[][][] zValues = {rebinData, rebinWeights}; + + int[] tableOffsets = {0}; + Units[] zUnits = {tds.getZUnits(), Units.dimensionless}; + String[] planeIDs = {"", DataSet.PROPERTY_PLANE_WEIGHTS}; + + Map properties = new HashMap(ds.getProperties()); + + if (ddX != null) { + properties.put(DataSet.PROPERTY_X_TAG_WIDTH, ddX.binWidthDatum()); + } + if (ddY != null) { + properties.put(DataSet.PROPERTY_Y_TAG_WIDTH, ddY.binWidthDatum()); + } + TableDataSet result = new DefaultTableDataSet(xTags, resultXUnits, yTags, resultYUnits, zValues, zUnits, planeIDs, tableOffsets, properties); + logger.finest("done, AverageTableRebinner.rebin"); + return result; + } + + static void doBoundaries2RL(TableDataSet tds, TableDataSet weights, double[][] rebinData, double[][] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY, Interpolate interpolateType) { + Units yunits = tds.getYUnits(); + Units zunits = tds.getZUnits(); + Units wunits = Units.dimensionless; + TableDataSet wds = WeightsTableDataSet.create(tds); + for (int i = 0; i < 2; i++) { + int ix = i == 0 ? 0 : ddX.numberOfBins() - 1; + Datum xx = i == 0 ? ddX.binCenter(0) : ddX.binCenter(ix); + + int i0 = DataSetUtil.getPreviousColumn(tds, xx); + int i1 = DataSetUtil.getNextColumn(tds, xx); + + int itable = tds.tableOfIndex(i0); + if (itable == tds.tableOfIndex(i1) && (i1 != i0)) { + DatumRange dr = new DatumRange(tds.getXTagDatum(i0), tds.getXTagDatum(i1)); + if (dr.width().gt(DataSetUtil.guessXTagWidth(tds).multiply(0.9))) { + double alpha = DatumRangeUtil.normalize(dr, xx); + if ( interpolateType==Interpolate.NearestNeighbor ) { + alpha= alpha < 0.5 ? 0.0 : 1.0; + } + int ny = ddY == null ? tds.getYLength(itable) : ddY.numberOfBins(); + for (int j = 0; j < tds.getYLength(itable); j++) { + int jj = ddY == null ? j : ddY.whichBin(tds.getYTagDouble(itable, j, yunits), yunits); + if (jj >= 0 && jj < ny) { + if (rebinWeights[ix][jj] > 0.0) { + continue; + } + if (wds.getDouble(i0, j, wunits) * wds.getDouble(i1, j, wunits) == 0.) { + continue; + } + rebinData[ix][jj] = (1 - alpha) * tds.getDouble(i0, j, zunits) + + alpha * tds.getDouble(i1, j, zunits); + rebinWeights[ix][jj] = 1.0; + } + } + } + } + } + + } + + static void doBoundaries2TB(TableDataSet tds, TableDataSet weights, double[][] rebinData, double[][] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY, Interpolate interpolateType) { + + if (ddY == null) { + return; + } + + Units yunits = tds.getYUnits(); + Units zunits = tds.getZUnits(); + Units xunits = tds.getXUnits(); + Units wunits = Units.dimensionless; + + TableDataSet wds = WeightsTableDataSet.create(tds); + for (int itable = 0; itable < tds.tableCount(); itable++) { + for (int i = 0; i < 2; i++) { + int iy = i == 0 ? 0 : ddY.numberOfBins() - 1; + Datum yy = i == 0 ? ddY.binCenter(0) : ddY.binCenter(iy); + + int j0 = TableUtil.getPreviousRow(tds, itable, yy); + int j1 = TableUtil.getNextRow(tds, itable, yy); + + if (j1 != j0) { + + DatumRange dr; + dr = new DatumRange(tds.getYTagDatum(itable, j0), tds.getYTagDatum(itable, j1)); + Datum dsWidth = TableUtil.guessYTagWidth(tds, itable); + if (ddY.isLog()) { + Units u = dr.getUnits(); + double d = dr.min().doubleValue(u); + double d0 = Math.log(dr.min().doubleValue(u) / d); + double d1 = Math.log(dr.max().doubleValue(u) / d); + dr = new DatumRange(d0, d1, Units.logERatio); + yy = Units.logERatio.createDatum(Math.log(yy.doubleValue(u) / d)); + // TODO: infinity + } + DatumRange xdr = new DatumRange(ddX.binCenter(0), ddX.binCenter(ddX.numberOfBins() - 1)); + double alpha = DatumRangeUtil.normalize(dr, yy); + if ( interpolateType==Interpolate.NearestNeighbor ) { + alpha= alpha < 0.5 ? 0.0 : 1.0; + } + int nx = ddX.numberOfBins(); + for (int ix = tds.tableStart(itable); ix < tds.tableEnd(itable); ix++) { + int ii = ddX.whichBin(tds.getXTagDouble(ix, xunits), xunits); + if (ii >= 0 && ii < nx) { + if (rebinWeights[ii][iy] > 0.0) { + continue; + } + if (wds.getDouble(ix, j0, wunits) * wds.getDouble(ix, j1, wunits) == 0.) { + continue; + } + rebinData[ii][iy] = (1 - alpha) * tds.getDouble(ix, j0, zunits) + alpha * tds.getDouble(ix, j1, zunits); + rebinWeights[ii][iy] = 1.0; + } + } + } + } + } + } + + static void doCorners(TableDataSet tds, TableDataSet weights, double[][] rebinData, double[][] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY, Interpolate interpolateType) { + if (ddY == null) { + return; + } + Units yunits = tds.getYUnits(); + Units zunits = tds.getZUnits(); + Units xunits = tds.getXUnits(); + Units wunits = Units.dimensionless; + TableDataSet wds = WeightsTableDataSet.create(tds); + for (int i = 0; i < 2; i++) { + int ix = i == 0 ? 0 : ddX.numberOfBins() - 1; + Datum xx = ddX.binCenter(ix); + int i0 = DataSetUtil.getPreviousColumn(tds, xx); + int i1 = DataSetUtil.getNextColumn(tds, xx); + + int itable = tds.tableOfIndex(i0); + if (itable != tds.tableOfIndex(i1)) { + continue; + } + + if (i0 == i1) { + continue; + } + + DatumRange xdr = new DatumRange(tds.getXTagDatum(i0), tds.getXTagDatum(i1)); + double xalpha = DatumRangeUtil.normalize(xdr, xx); + if (interpolateType == Interpolate.NearestNeighbor) { + xalpha = xalpha < 0.5 ? 0.0 : 1.0; + } + + for (int j = 0; j < 2; j++) { + int iy = j == 0 ? 0 : ddY.numberOfBins() - 1; + Datum yy = ddY.binCenter(iy); + + int j0 = TableUtil.getPreviousRow(tds, itable, yy); + int j1 = TableUtil.getNextRow(tds, itable, yy); + + if (j0 != j1) { + DatumRange ydr = new DatumRange(tds.getYTagDatum(itable, j0), tds.getYTagDatum(itable, j1)); + if (xdr.width().lt(DataSetUtil.guessXTagWidth(tds).multiply(1.1))) { + DatumRange xdr1 = new DatumRange(ddX.binCenter(0), ddX.binCenter(ddX.numberOfBins() - 1)); + double yalpha = DatumRangeUtil.normalize(ydr, yy); + if (interpolateType == Interpolate.NearestNeighbor) { + yalpha = yalpha < 0.5 ? 0.0 : 1.0; + } + if (rebinWeights[ix][iy] > 0.0) { + continue; + } + if (wds.getDouble(i1, j1, wunits) * + wds.getDouble(i0, j0, wunits) * + wds.getDouble(i1, j0, wunits) * + wds.getDouble(i0, j1, wunits) == 0.) { + continue; + } + rebinData[ix][iy] = + tds.getDouble(i1, j1, zunits) * xalpha * yalpha + + tds.getDouble(i0, j0, zunits) * (1 - xalpha) * (1 - yalpha) + + tds.getDouble(i1, j0, zunits) * xalpha * (1 - yalpha) + + tds.getDouble(i0, j1, zunits) * (1 - xalpha) * yalpha; + rebinWeights[ix][iy] = 1.0; + } + } + } + } + } + + static void average(TableDataSet tds, TableDataSet weights, double[][] rebinData, double[][] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY) { + double[] ycoordinate; + int nTables; + Units xUnits, zUnits; + int nx, ny; + + xUnits = tds.getXUnits(); + zUnits = tds.getZUnits(); + + nx = (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + ny = (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); + + + if (ddY != null) { + ycoordinate = ddY.binCenters(); + } else { + ycoordinate = new double[tds.getYLength(0)]; + for (int j = 0; j < ycoordinate.length; j++) { + ycoordinate[j] = tds.getDouble(0, j, zUnits); + } + } + + nTables = tds.tableCount(); + for (int iTable = 0; iTable < nTables; iTable++) { + int[] ibiny = new int[tds.getYLength(iTable)]; + for (int j = 0; j < ibiny.length; j++) { + if (ddY != null) { + ibiny[j] = ddY.whichBin(tds.getYTagDouble(iTable, j, tds.getYUnits()), tds.getYUnits()); + } else { + ibiny[j] = j; + } + } + for (int i = tds.tableStart(iTable); i < tds.tableEnd(iTable); i++) { + int ibinx; + if (ddX != null) { + ibinx = ddX.whichBin(tds.getXTagDouble(i, xUnits), xUnits); + } else { + ibinx = i; + } + + if (ibinx >= 0 && ibinx < nx) { + for (int j = 0; j < tds.getYLength(iTable); j++) { + double z = tds.getDouble(i, j, zUnits); + double w = weights == null + ? (zUnits.isFill(z) ? 0. : 1.) + : weights.getDouble(i, j, Units.dimensionless); + if (ibiny[j] >= 0 && ibiny[j] < ny) { + rebinData[ibinx][ibiny[j]] += z * w; + rebinWeights[ibinx][ibiny[j]] += w; + } + } + } + } + } + multiplyWeights(rebinData, rebinWeights, zUnits.getFillDouble()); + } + + private final static double linearlyInterpolate(int i0, double z0, int i1, double z1, int i) { + double r = ((double) (i - i0)) / (i1 - i0); + return z0 + r * (z1 - z0); + } + + private final static void multiplyWeights(double[][] data, double[][] weights, double fill) { + for (int i = 0; i < data.length; i++) { + for (int j = 0; j < data[i].length; j++) { + if (weights[i][j] > 0.0) { + data[i][j] = data[i][j] / weights[i][j]; + } else { + data[i][j] = fill; + } + } + } + } + + static void fillInterpolateX( + final double[][] data, final double[][] weights, final double[] xTags, + double[] xTagMin, double[] xTagMax, final double xSampleWidth, + Interpolate interpolateType + ) { + + final int nx = xTags.length; + final int ny = data[0].length; + final int[] i1 = new int[nx]; + final int[] i2 = new int[nx]; + double a1; + double a2; + + for (int j = 0; j < ny; j++) { + int ii1 = -1; + int ii2 = -1; + for (int i = 0; i < nx; i++) { + if (weights[i][j] > 0. && ii1 == (i - 1)) { // ho hum another valid point + i1[i] = -1; + i2[i] = -1; + ii1 = i; + } else if (weights[i][j] > 0. && ii1 == -1) { // first valid point + i1[i] = -1; + i2[i] = -1; + ii1 = i; + if (interpolateType == Interpolate.NearestNeighbor) { + for (int jjj = 0; jjj < i; jjj++) { + i2[jjj] = ii1; + } + } + } else if (weights[i][j] > 0. && ii1 < (i - 1)) { // bracketed a gap, interpolate + if (ii1 > -1) { + i1[i] = -1; + i2[i] = -1; + ii2 = i; + for (int ii = ii1 + 1; ii < i; ii++) { + i1[ii] = ii1; + i2[ii] = ii2; + } + ii1 = i; + } + } else { + i1[i] = -1; + i2[i] = -1; + } + } + if (interpolateType == Interpolate.NearestNeighbor && ii1 > -1) { + for (int jjj = ii1; jjj < nx; jjj++) { + i1[jjj] = ii1; + } + } + if (interpolateType == Interpolate.NearestNeighbor) { + + for (int i = 0; i < nx; i++) { + if (Math.min( + i1[i] == -1 ? Double.MAX_VALUE : (xTags[i] - xTagMin[i1[i]]), + i2[i] == -1 ? Double.MAX_VALUE : (xTagMax[i2[i]] - xTags[i])) < xSampleWidth / 2 + ) { + + int idx = -1; + if (i1[i] == -1) { + if (i2[i] == -1) { + continue; + } + idx = i2[i]; + } else if (i2[i] == -1) { + idx = i1[i]; + } else { + a2 = ((xTags[i] - xTagMax[i1[i]]) / (xTagMin[i2[i]] - xTags[i1[i]])); + if (a2 < 0.5) { + idx = i1[i]; + } else { + idx = i2[i]; + } + } + data[i][j] = data[idx][j]; + weights[i][j] = weights[idx][j]; + } + } + } else { + for (int i = 0; i < nx; i++) { + if (i1[i] > -1 && i2[i] > -1 && (xTagMin[i2[i]] - xTagMax[i1[i]]) <= xSampleWidth * 1.5) { + a2 = ((xTags[i] - xTagMax[i1[i]]) / (xTagMin[i2[i]] - xTags[i1[i]])); + a1 = 1. - a2; + data[i][j] = data[i1[i]][j] * a1 + data[i2[i]][j] * a2; + weights[i][j] = weights[i1[i]][j] * a1 + weights[i2[i]][j] * a2; //approximate + + } + } + } + } + } + + static void fillInterpolateY(final double[][] data, final double[][] weights, RebinDescriptor ddY, Datum yTagWidth, Interpolate interpolateType) { + + final int nx = data.length; + final int ny = ddY.numberOfBins(); + final int[] i1 = new int[ny]; + final int[] i2 = new int[ny]; + final double[] yTagTemp = new double[ddY.numberOfBins()]; + double a1; + double a2; + + final double[] yTags = ddY.binCenters(); + final Units yTagUnits = ddY.getUnits(); + final boolean log = ddY.isLog(); + + if (log) { + for (int j = 0; j < ny; j++) { + yTagTemp[j] = Math.log(yTags[j]); + } + } else { + for (int j = 0; j < ny; j++) { + yTagTemp[j] = yTags[j]; + } + } + + double ySampleWidth; + double fudge = 1.5; + if (interpolateType == Interpolate.NearestNeighbor) { + fudge = 1.1; + } + if (yTagWidth == null) { + double d = Double.MAX_VALUE / 4; // avoid roll-over when *1.5 + ySampleWidth = d; + } else { + if (UnitsUtil.isRatiometric(yTagWidth.getUnits())) { + double p = yTagWidth.doubleValue(Units.logERatio); + ySampleWidth = p * fudge; + } else { + double d = yTagWidth.doubleValue(yTagUnits.getOffsetUnits()); + ySampleWidth = d * fudge; + } + } + + for (int i = 0; i < nx; i++) { + int ii1 = -1; + int ii2 = -1; + for (int j = 0; j < ny; j++) { + if (weights[i][j] > 0. && ii1 == (j - 1)) { // ho hum another valid point + + i1[j] = -1; + i2[j] = -1; + ii1 = j; + } else if (weights[i][j] > 0. && ii1 == -1) { // first valid point + + i1[j] = -1; + i2[j] = -1; + ii1 = j; + if (interpolateType == Interpolate.NearestNeighbor) { + for (int jjj = 0; jjj < j; jjj++) { + i2[jjj] = ii1; + } + } + } else if (weights[i][j] > 0. && ii1 < (j - 1)) { // bracketed a gap, interpolate + + if ((ii1 > -1)) { // need restriction on Y gap size + + i1[j] = -1; + i2[j] = -1; + ii2 = j; + for (int jj = j - 1; jj >= ii1; jj--) { + i1[jj] = ii1; + i2[jj] = ii2; + } + ii1 = j; + } + } else { + i1[j] = -1; + i2[j] = -1; + } + } + if (interpolateType == Interpolate.NearestNeighbor && ii1 > -1) { + for (int jjj = ii1; jjj < ny; jjj++) { + i1[jjj] = ii1; + } + } + if (interpolateType == Interpolate.NearestNeighbor) { + for (int j = 0; j < ny; j++) { + boolean doInterp; + if ( i1[j]!= -1 && i2[j] != -1) { + doInterp= ( yTagTemp[i2[j]]-yTagTemp[i1[j]] ) < ySampleWidth*2; + } else if ( i1[j] != -1 ) { + doInterp= (yTagTemp[j] - yTagTemp[i1[j]]) < ySampleWidth / 2; + } else if ( i2[j] != -1 ) { + doInterp= (yTagTemp[i2[j]] - yTagTemp[j]) < ySampleWidth / 2; + } else { // If we reach this branch then both i1[j] and i2[j] equal -1 + doInterp= false; + } + if ( doInterp ) { + int idx; + if (i1[j] == -1) { + idx = i2[j]; + } else if (i2[j] == -1) { + idx = i1[j]; + } else { + a2 = ((yTagTemp[j] - yTagTemp[i1[j]]) / (yTagTemp[i2[j]] - yTagTemp[i1[j]])); + if (a2 < 0.5) { + idx = i1[j]; + } else { + idx = i2[j]; + } + } + data[i][j] = data[i][idx]; + weights[i][j] = weights[i][idx]; + + } + if ( i==1 && j==34 ) { + int jkk=0; + } + + } + } else { + for (int j = 0; j < ny; j++) { + if ((i1[j] != -1) && ((yTagTemp[i2[j]] - yTagTemp[i1[j]]) < ySampleWidth || i2[j] - i1[j] == 2)) { //kludge for bug 000321 + + a2 = ((yTagTemp[j] - yTagTemp[i1[j]]) / (yTagTemp[i2[j]] - yTagTemp[i1[j]])); + a1 = 1. - a2; + data[i][j] = data[i][i1[j]] * a1 + data[i][i2[j]] * a2; + weights[i][j] = weights[i][i1[j]] * a1 + weights[i][i2[j]] * a2; //approximate + + } + } + } + } + } + + private void enlargePixels(double[][] rebinData, double[][] rebinWeights) { + int enlargeSize = 5; + for (int aa = 0; aa < enlargeSize; aa++) { + for (int ii = 0; ii < rebinData.length - 1; ii++) { + for (int jj = 0; jj < rebinData[0].length; jj++) { + if (rebinWeights[ii][jj] == 0) { + rebinData[ii][jj] = rebinData[ii + 1][jj]; + rebinWeights[ii][jj] = rebinWeights[ii + 1][jj]; + } + } + } + for (int ii = rebinData.length - 1; ii > 0; ii--) { + for (int jj = 0; jj < rebinData[0].length; jj++) { + if (rebinWeights[ii][jj] == 0) { + rebinData[ii][jj] = rebinData[ii - 1][jj]; + rebinWeights[ii][jj] = rebinWeights[ii - 1][jj]; + } + } + } + for (int jj = 0; jj < rebinData[0].length - 1; jj++) { + for (int ii = 0; ii < rebinData.length; ii++) { + if (rebinWeights[ii][jj] == 0) { + rebinData[ii][jj] = rebinData[ii][jj + 1]; + rebinWeights[ii][jj] = rebinWeights[ii][jj + 1]; + } + } + } + for (int jj = rebinData[0].length - 1; jj > 0; jj--) { + for (int ii = 0; ii < rebinData.length; ii++) { + if (rebinWeights[ii][jj] == 0) { + rebinData[ii][jj] = rebinData[ii][jj - 1]; + rebinWeights[ii][jj] = rebinWeights[ii][jj - 1]; + } + } + } + } + } + + /** + * Getter for property interpolate. + * @return Value of property interpolate. + */ + public boolean isInterpolate() { + return this.interpolate; + } + + /** + * Setter for property interpolate. + * @param interpolate New value of property interpolate. + */ + public void setInterpolate(boolean interpolate) { + this.interpolate = interpolate; + } + + public void setEnlargePixels(boolean enlargePixels) { + this.enlargePixels = enlargePixels; + } + + public boolean isEnlargePixels() { + return enlargePixels; + } + protected Interpolate interpolateType = Interpolate.Linear; + public static final String PROP_INTERPOLATETYPE = "interpolateType"; + + public Interpolate getInterpolateType() { + return interpolateType; + } + + public void setInterpolateType(Interpolate interpolateType) { + Interpolate oldInterpolateType = this.interpolateType; + this.interpolateType = interpolateType; + propertyChangeSupport.firePropertyChange(PROP_INTERPOLATETYPE, oldInterpolateType, interpolateType); + } + private java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); + + public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { + propertyChangeSupport.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { + propertyChangeSupport.removePropertyChangeListener(listener); + } +} diff --git a/dasCore/src/main/java/org/das2/dataset/CacheTag.java b/dasCore/src/main/java/org/das2/dataset/CacheTag.java new file mode 100755 index 000000000..f6a8bc28d --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/CacheTag.java @@ -0,0 +1,116 @@ +package org.das2.dataset; + +import org.das2.datum.DatumRange; +import org.das2.datum.Datum; +import org.das2.datum.DatumUtil; + +/** + * CacheTags are used to represent the coverage of datasets stored in a cache, and are + * the reference used to decide if a cache entry is capable of satisfying a data request. + * + */ +public class CacheTag { + + public static final String INTRINSIC = "intrinsic"; + + DatumRange range; + Datum resolution; + + /** + * Appends two CacheTags, when possible. The new range covers the ranges, and the resolution is the lower of the two. + * @param tag1 a CacheTag + * @param tag2 a CacheTag that is adjacent to tag1 + * @return a CacheTag that covers both CacheTags precisely. + */ + public static CacheTag append( CacheTag tag1, CacheTag tag2 ) { + Datum res; + if ( tag1.resolution==null && tag2.resolution==null ) { + res= null; + } else { + if ( tag1.resolution!=null && tag2.resolution!=null ) { + res= tag1.resolution.gt( tag2.resolution ) ? tag1.resolution : tag2.resolution; + } else { + res= tag1.resolution==null ? tag2.resolution : tag1.resolution; + } + } + + Datum min; + Datum max; + if ( !tag1.range.intersects(tag2.range) ) { + if ( tag2.range.min().lt( tag1.range.min() ) ) { + CacheTag temp= tag1; + tag1= tag2; + tag2= temp; + } + if ( tag1.range.max().lt(tag2.range.min()) ) { + throw new IllegalArgumentException("cache tags cannot be appended, they are not adjacent"); + } + min= tag1.range.min(); + max= tag2.range.max(); + } else { + min= tag1.range.min().lt( tag2.range.min() ) ? tag1.range.min() : tag2.range.min(); + max= tag1.range.max().gt( tag2.range.max() ) ? tag1.range.max() : tag2.range.max(); + } + + DatumRange range= new DatumRange( min, max ); + return new CacheTag( range, res ); + } + + /** + * Constructs a new CacheTag. + * @param start the beginning of the interval. + * @param end the end of the interval. + * @param resolution the highest resolution request that can be provided. + */ + public CacheTag(Datum start, Datum end, Datum resolution) { + this( new DatumRange( start, end ), resolution ); + } + + /** + * Constucts a new CacheTag. + * @param range the interval covered by the CacheTag. + * @param resolution the highest resolution request that can be provided. + */ + public CacheTag( DatumRange range, Datum resolution) { + this.range= range; + this.resolution= resolution; + } + + /** + * Returns a string representation of the object. + * @return a human consumable representation of the cache tag. This should fit on + * one line as it is used to list cache contents. + */ + public String toString() { + return range + " @ " + ( resolution==null ? INTRINSIC : ""+DatumUtil.asOrderOneUnits(resolution) ); + } + + /** + * Indicates if the cache tag the the capability of satifying the given cache tag. If the tag has a lower (courser) resolution and its bounds are within + * this CacheTag. + * @return true if the tag has a lower resolution and its bounds are within + * this CacheTag. + * @param tag a CacheTag to test. + */ + public boolean contains( CacheTag tag ) { + return ( this.range.contains( tag.range ) + && ( this.resolution==null + || ( tag.resolution!=null && this.resolution.le( tag.resolution ) ) + ) ); + } + + /** + * the range covered by the cache tag. + */ + public DatumRange getRange() { + return this.range; + } + + /** + * the resolution of the cache tag, which may be null. + */ + public Datum getResolution() { + return this.resolution; + } +} + diff --git a/dasCore/src/main/java/org/das2/dataset/ClippedTableDataSet.java b/dasCore/src/main/java/org/das2/dataset/ClippedTableDataSet.java new file mode 100755 index 000000000..bc9b73be1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/ClippedTableDataSet.java @@ -0,0 +1,266 @@ +/* + * ClippedTableDataSet.java + * + * Created on February 16, 2004, 12:19 PM + */ + +package org.das2.dataset; + +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import java.util.*; + +/** + * + * @author Jeremy + */ +public class ClippedTableDataSet implements TableDataSet { + + /* + * clippedTableDataSet + * + * TableDataSet that is a view of a section of a TableDataSet including an X and Y range, + * but not much more. + */ + + TableDataSet source; + + int xoffset; + int xlength; + + int[] yoffsets; + int[] ylengths; + + int tableOffset; + int tableCount; + + void calculateXOffset( Datum xmin, Datum xmax ) { + xoffset= DataSetUtil.getPreviousColumn(source, xmin); + int ix1= DataSetUtil.getNextColumn(source, xmax ); + xlength= ix1- xoffset+1; + } + + void calculateTableOffset() { + tableOffset= -99; + for ( int itable=0; itable xoffset ) { + tableOffset= itable; + } + if ( tableOffset!=-99 + && source.tableEnd(itable) >= xoffset+xlength ) { + tableCount= itable - tableOffset + 1; + } + } + } + + void calculateYOffsets( Datum ymin, Datum ymax ) { + yoffsets= new int[tableCount]; + ylengths= new int[tableCount]; + for ( int itable=tableOffset; itable 1 ) { + throw new IllegalArgumentException( "this ClippedTableDataSet constructor requires that there be only one table" ); + } + if ( source.getXLength() < xoffset+xlength ) { + throw new IllegalArgumentException( "xoffset + xlength greater than the number of XTags in source" ); + } + if ( source.getYLength(0) < yoffset+ylength ) { + throw new IllegalArgumentException( "yoffset + ylength greater than the number of YTags in source" ); + } + if ( yoffset<0 || xoffset<0 ) { + throw new IllegalArgumentException( "yoffset("+yoffset+") or xoffset("+xoffset+") is negative" ); + } + this.source= source; + this.xoffset= xoffset; + this.xlength= xlength; + this.tableOffset= 0; + this.tableCount= 1; + this.yoffsets= new int[] { yoffset }; + this.ylengths= new int[] { ylength }; + } + + private ClippedTableDataSet( TableDataSet source, int xoffset, int xlength, + int [] yoffsets, int [] ylengths, int tableOffset, int tableCount ) { + if ( source==null ) { + throw new IllegalArgumentException("source is null"); + } + this.source= source; + this.xoffset= xoffset; + this.xlength= xlength; + this.yoffsets= yoffsets; + this.ylengths= ylengths; + this.tableOffset= tableOffset; + this.tableCount= tableCount; + } + + + public Datum getDatum(int i, int j) { + return source.getDatum( i+xoffset, j+yoffsets[tableOfIndex(i)] ); + } + + public double getDouble(int i, int j, Units units) { + return source.getDouble( i+xoffset, j+yoffsets[tableOfIndex(i)], units ); + } + + public double[] getDoubleScan(int i, Units units) { + int table = tableOfIndex(i); + int yLength = getYLength(table); + double[] array = new double[yLength]; + double[] sourceArray = source.getDoubleScan(i + xoffset, units); + System.arraycopy(sourceArray, yoffsets[table], array, 0, yLength); + return array; + } + + public DatumVector getScan(int i) { + int table = tableOfIndex(i); + int yLength = getYLength(table); + return source.getScan(i+xoffset).getSubVector(yoffsets[table], yoffsets[table] + yLength); + } + + public int getInt(int i, int j, Units units) { + return source.getInt( i+xoffset, j+yoffsets[tableOfIndex(i)], units ); + } + + public DataSet getPlanarView(String planeID) { + TableDataSet sourcePlane= (TableDataSet)source.getPlanarView(planeID); + if (sourcePlane==null) { + return null; + } else { + return new ClippedTableDataSet(sourcePlane, + xoffset,xlength,yoffsets,ylengths,tableOffset,tableCount); + } + } + + public String[] getPlaneIds() { + return source.getPlaneIds(); + } + + public Map getProperties() { + return source.getProperties(); + } + + public Object getProperty( String name ) { + return source.getProperty( name ); + } + + public int getXLength() { + return xlength; + } + + public VectorDataSet getXSlice(int i) { + int itable= source.tableOfIndex(i+xoffset); + DatumRange dr= new DatumRange( source.getYTagDatum(itable,yoffsets[itable]), source.getYTagDatum(itable,yoffsets[itable]+ylengths[itable]) ); + return new ClippedVectorDataSet( source.getXSlice( i+xoffset ), dr ); + } + + public Datum getXTagDatum(int i) { + return source.getXTagDatum( i+xoffset ); + } + + public double getXTagDouble(int i, Units units) { + return source.getXTagDouble( i+xoffset, units ); + } + + public int getXTagInt(int i, Units units) { + return source.getXTagInt( i+xoffset, units ); + } + + public Units getXUnits() { + return source.getXUnits(); + } + + public int getYLength(int table) { + return ylengths[table]; + } + + public VectorDataSet getYSlice(int j, int table) { + return source.getYSlice( j+yoffsets[table], table+tableOffset ); + } + + public Datum getYTagDatum(int table, int j) { + return source.getYTagDatum( table+tableOffset, j+yoffsets[table] ); + } + + public double getYTagDouble(int table, int j, Units units) { + return source.getYTagDouble( table+tableOffset, j+yoffsets[table], units ); + } + + public int getYTagInt(int table, int j, Units units) { + return source.getYTagInt( table+tableOffset, j+yoffsets[table], units ); + } + + public org.das2.datum.Units getYUnits() { + return source.getYUnits(); + } + + public org.das2.datum.Units getZUnits() { + return source.getZUnits(); + } + + public int tableCount() { + return tableCount; + } + + public int tableEnd(int table) { + int i= source.tableEnd(table+tableOffset) - xoffset; + if ( i>getXLength() ) { + return getXLength(); + } else { + return i; + } + } + + public int tableOfIndex(int i) { + return source.tableOfIndex( i+xoffset ) - tableOffset; + } + + public int tableStart(int table) { + int i= source.tableStart(table+tableOffset) - xoffset; + if ( i<0 ) { + return 0; + } else { + return i; + } + } + + public String toString() { + return "ClippedTableDataSet " + TableUtil.toString(this); + } + + public DatumVector getYTags(int table) { + double[] tags = new double[getYLength(table)]; + Units yUnits = getYUnits(); + for (int j = 0; j < tags.length; j++) { + tags[j] = getYTagDouble(table, j, yUnits); + } + return DatumVector.newDatumVector(tags, yUnits); + } + + public Object getProperty(int table, String name) { + return source.getProperty(table, name); + } + + +} diff --git a/dasCore/src/main/java/org/das2/dataset/ClippedVectorDataSet.java b/dasCore/src/main/java/org/das2/dataset/ClippedVectorDataSet.java new file mode 100644 index 000000000..891bdeeb0 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/ClippedVectorDataSet.java @@ -0,0 +1,88 @@ +/* + * ClippedVectorDataSet.java + * + * Created on April 18, 2005, 5:38 PM + */ + +package org.das2.dataset; + +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.Datum; + +/** + * + * @author Jeremy + */ +public class ClippedVectorDataSet implements VectorDataSet { + + int xoffset; + int xlength; + VectorDataSet source; + + /** Creates a new instance of ClippedVectorDataSet */ + public ClippedVectorDataSet( VectorDataSet source, DatumRange xclip ) { + xoffset= DataSetUtil.getPreviousColumn( source, xclip.min() ); + xlength= DataSetUtil.getNextColumn( source, xclip.max() ) - xoffset; + this.source= source; + } + + public ClippedVectorDataSet( VectorDataSet source, int xoffset, int xlength ) { + this.xoffset= xoffset; + this.xlength= xlength; + this.source= source; + } + + public Datum getDatum(int i) { + return source.getDatum(i+xoffset); + } + + public double getDouble(int i, Units units) { + return source.getDouble(i+xoffset,units); + } + + public int getInt(int i, Units units) { + return source.getInt(i+xoffset,units); + } + + public DataSet getPlanarView(String planeID) { + return new ClippedVectorDataSet( (VectorDataSet)source.getPlanarView(planeID), xoffset, xlength ); + } + + public String[] getPlaneIds() { + return source.getPlaneIds(); + } + + public java.util.Map getProperties() { + return source.getProperties(); + } + + public Object getProperty(String name) { + return source.getProperty(name); + } + + public int getXLength() { + return xlength; + } + + public Datum getXTagDatum(int i) { + return source.getXTagDatum( i+xoffset ); + } + + public double getXTagDouble(int i, Units units) { + return source.getXTagDouble( i+xoffset, units ); + } + + public int getXTagInt(int i, Units units) { + return source.getXTagInt( i+xoffset, units ); + } + + public Units getXUnits() { + return source.getXUnits(); + } + + public Units getYUnits() { + return source.getYUnits(); + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/ConstantDataSetDescriptor.java b/dasCore/src/main/java/org/das2/dataset/ConstantDataSetDescriptor.java new file mode 100755 index 000000000..6628d6c4f --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/ConstantDataSetDescriptor.java @@ -0,0 +1,78 @@ +/* File: ConstantDataSetDescriptor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.DasException; +import org.das2.datum.Datum; +import org.das2.util.monitor.ProgressMonitor; + +/** + * This class wraps a DataSet to use where DataSetDescriptors are required. This + * trivially returns the wrapped DataSet regardless of the getDataSet parameters. + * Originally this class was used with Renderers, which needed a DataSetDescriptor + * for their operation. Now that they interact only with DataSets, and this + * class should not be used with them, use Renderer.setDataSet instead. + * + * @author eew + * + */ +public class ConstantDataSetDescriptor extends DataSetDescriptor { + + DataSet ds; + + /** Creates a new instance of ConstantDataSetDescriptor */ + public ConstantDataSetDescriptor(DataSet ds) { + super(); + if (ds == null) throw new NullPointerException("DataSet parameter cannot be null"); + this.ds = ds; + } + + // this is never called because we override getDataSet + public DataSet getDataSetImpl(Datum start, Datum end, Datum resolution, ProgressMonitor monitor) throws DasException { + return ds; + } + + public DataSet getDataSet( Datum start, Datum end, Datum resolution, ProgressMonitor monitor) throws DasException { + return ds; + } + + public org.das2.datum.Units getXUnits() { + return ds.getXUnits(); + } + + public void requestDataSet(Datum start, Datum end, Datum resolution, ProgressMonitor monitor, Object lockObject) { + DataSetUpdateEvent dsue= null; + try { + DataSet ds= getDataSet(start, end, resolution, monitor); + dsue= new DataSetUpdateEvent( (Object)this, ds ); + fireDataSetUpdateEvent(dsue); + } catch ( DasException e ) { + dsue= new DataSetUpdateEvent( (Object)this,e); + fireDataSetUpdateEvent(dsue); + } + return; + + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/DataRequestThread.java b/dasCore/src/main/java/org/das2/dataset/DataRequestThread.java new file mode 100644 index 000000000..912d2785a --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/DataRequestThread.java @@ -0,0 +1,188 @@ +/* File: DataRequestThread.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.util.monitor.ProgressMonitor; +import org.das2.DasException; +import org.das2.dataset.DataRequestor; +import org.das2.datum.Datum; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * + * @author Edward West + */ +public class DataRequestThread extends Thread { + + private static int threadCount = 0; + + private Object lock = new String("DATA_REQUEST_LOCK"); + + private DataRequest currentRequest; + + private List queue = Collections.synchronizedList(new LinkedList()); + + /** Creates a new instance of DataRequestThread */ + public DataRequestThread() { + //Set the name for debugging purposes. + setName("DataRequest_" + threadCount++); + setDaemon(true); + start(); //Start it up + } + + /** + * Begins a data reqest operation that will be executed + * in a separate thread and pass the resulting DataSet to the + * org.das2.event.DataRequestListener#finished(org.das2.event.DataRequestEvent) + * finished() method of the DataRequestor + * specified. + * + * @param dsd the DataSetDescriptor used to obtain + * the DataSet + * @param start the start of the requested time interval + * @param end the end of the requested time interval + * @param resolution the requested resolution of the data set + * @param requestor DataRequestor that is notified + * when the data loading operation is complete. + */ + public void request(DataSetDescriptor dsd, + Datum start, Datum end, Datum resolution, DataRequestor requestor, ProgressMonitor monitor) + throws InterruptedException { + + requestInternal(new DataRequest(dsd, start, end, resolution, requestor, monitor)); + + } + /** + * Begins a data reqest operation that will be executed + * in a separate thread and pass the resulting DataSet to the + * org.das2.event.DataRequestListener#finished(org.das2.event.DataRequestEvent) + * finished() method of the DataRequestor + * specified. + * + * This method does not return until after the data loading is complete + * or the request had been canceled. + * + * @param dsd the DataSetDescriptor used to obtain + * the DataSet + * @param params extra parameters passed to the + * DataSetDescriptor#getDataSet(org.das2.util.Datum,org.das2.util.Datum,org.das2.util.Datum,org.das2.util.monitor.ProgressMonitor) + * getDataSet() method. (TODO: these are ignored) + * @param start the start of the requested time interval + * @param end the end of the requested time interval + * @param resolution the requested resolution of the data set + * @param requestor DataRequestor that is notified + * when the data loading operation is complete. + */ + public void requestAndWait(DataSetDescriptor dsd, Object params, + Datum start, Datum end, Datum resolution, DataRequestor requestor, ProgressMonitor monitor) + throws InterruptedException { + + DataRequest request = new DataRequest(dsd, start, end, resolution, requestor, monitor); + + //Wait till thread is done loading + synchronized (request) { + requestInternal(request); + request.wait(); + } + } + + private void requestInternal(DataRequest request) { + queue.add(request); + + //Notify loading thread if it is waiting on object lock. + synchronized (lock) { + lock.notify(); + } + } + + public void cancelCurrentRequest() throws InterruptedException { + synchronized (lock) { + if (this.isAlive()) { + this.interrupt(); + lock.wait(); + } + } + } + + public void run() { + if (Thread.currentThread() != this) { + throw new IllegalStateException( + "This method should not be invoked directly"); + } + + while (true) { + while (!queue.isEmpty()) { + currentRequest = (DataRequest)queue.remove(0); + try { + DataSet ds = currentRequest.dsd.getDataSet( + currentRequest.start, + currentRequest.end, + currentRequest.resolution, + currentRequest.monitor); + currentRequest.requestor.finished(ds); + } + catch (DasException e) { + currentRequest.requestor.exception(e); + } + finally { + synchronized (currentRequest) { + currentRequest.notifyAll(); + } + } + } + synchronized (lock) { + try { + lock.wait(); + } + catch(InterruptedException ie) { + return; + } + } + } + } + + private static class DataRequest { + DataSetDescriptor dsd; + Datum start; + Datum end; + Object params; + Datum resolution; + DataRequestor requestor; + ProgressMonitor monitor; + DataRequest(DataSetDescriptor dsd, Datum start, + Datum end, Datum resolution, + DataRequestor requestor, ProgressMonitor monitor) { + this.dsd = dsd; + this.start = start; + this.end = end; + this.resolution = resolution; + this.requestor = requestor; + this.monitor = monitor; + } + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/DataRequestor.java b/dasCore/src/main/java/org/das2/dataset/DataRequestor.java new file mode 100644 index 000000000..e5d490c8c --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/DataRequestor.java @@ -0,0 +1,36 @@ +/* File: DataRequestor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +/** + * + * @author Edward West + */ +public interface DataRequestor extends java.util.EventListener { + + void finished(DataSet ds); + + void exception(Exception e); + +} diff --git a/dasCore/src/main/java/org/das2/dataset/DataSet.java b/dasCore/src/main/java/org/das2/dataset/DataSet.java new file mode 100644 index 000000000..91c13637b --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/DataSet.java @@ -0,0 +1,193 @@ +/* File: DataSet.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 24, 2003, 11:23 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.Datum; +import java.util.*; + +/** General interface for objects encapsulating a data set + * + * @author Edward West + */ +public interface DataSet { + + /** + * CacheTag object describing the start, end, and resolution of the dataset. + */ + final static String PROPERTY_CACHE_TAG= "cacheTag"; + + /** + * Long estimating the size of the dataset in memory. For example, if a dataset is + * backed by a local file, then zero for this indicates no penalty for storing this + * dataset. + */ + final static String PROPERTY_SIZE_BYTES= "sizeBytes"; + + /** + * Datum which is the nominal distance between successive xTags. This is used for example to prevent + * interpolation between distant measurements + */ + final static String PROPERTY_X_TAG_WIDTH= "xTagWidth"; + + /** + * Datum, see xTagWidth + */ + final static String PROPERTY_Y_TAG_WIDTH= "yTagWidth"; + + /** + * DatumRange useful for setting scales + */ + final static String PROPERTY_X_RANGE="xRange"; + + /** + * DatumRange useful for setting scales + */ + final static String PROPERTY_Y_RANGE="yRange"; + + /** + * DatumRange useful for setting scales + */ + final static String PROPERTY_Z_RANGE="zRange"; + + /** + * suggest render method to use. These are + * canonical: + * spectrogram + * symbolLine + * stackedHistogram + */ + final static String PROPERTY_RENDERER="renderer"; + + /** + * String "log" or "linear" + */ + final static String PROPERTY_Y_SCALETYPE="yScaleType"; + + /** + * String "log" or "linear" + */ + final static String PROPERTY_Z_SCALETYPE="zScaleType"; + + final static String PROPERTY_X_LABEL="xLabel"; + + final static String PROPERTY_Y_LABEL="yLabel"; + + final static String PROPERTY_Z_LABEL="zLabel"; + + /** + * Boolean assuring that the dataset is monotonic in X. This allows + * some optimizations to be made. + */ + final static String PROPERTY_X_MONOTONIC="xMonotonic"; + + /** + * dataset containing the peaks when available + */ + final static String PROPERTY_PLANE_PEAKS= "peaks"; + + /** + * dataset containing the weights when available + */ + final static String PROPERTY_PLANE_WEIGHTS= "weights"; + + /** + * DatumFormatter for formatting data in the dataset. + */ + public static String PROPERTY_FORMATTER= "formatter"; + + /** Returns the property value associated with the string name + * @param name the name of the property requested + * @return the property value for name or null + */ + Object getProperty(String name); + + /** Returns all dataset properties in a Map. + * @return a Map of all properties. + */ + Map getProperties(); + + /** Returns the Units object representing the unit type of the x tags + * for this data set. + * @return the x units + */ + Units getXUnits(); + + /** Returns the Units object representing the unit type of the y tags + * or y values for this data set. + * @return the y units + */ + Units getYUnits(); + + /** Returns the value of the x tag at the given index i as a + * Datum. + * @param i the index of the requested x tag + * @return the value of the x tag at the given index i as a + * Datum. + */ + Datum getXTagDatum(int i); + + /** Returns the value of the x tag at the given index i as a + * double in the given units. XTags must be + * monotonically increasing with i. + * @return the value of the x tag at the given index i as a + * double. + * @param units the units of the returned value + * @param i the index of the requested x tag + */ + double getXTagDouble(int i, Units units); + + /** Returns the value of the x tag at the given index i as an + * int in the given units. XTags must be + * monotonically increasing with i. + * @return the value of the x tag at the given index i as an + * int. + * @param units the units of the returned value. + * @param i the index of the requested x tag + */ + int getXTagInt(int i, Units units); + + /** Returns the number of x tags in this data set. XTags must be + * monotonically increasing with i. + * @return the number of x tags in this data set. + */ + int getXLength(); + + /** Returns a DataSet with the specified view as the primary + * view. + * @param planeID the String id of the requested plane. + * @return the specified view, as a DataSet + */ + //TODO: consider throwing IllegalArgumentException if the plane doesn't exist. + // we have methods to query for the plane names. + DataSet getPlanarView(String planeID); + + /** + * Returns a list of auxillary planes (e.g. weights, peaks) for the dataset. + * Note that the default plane, "" may or may not be returned, based on + * implementations. + */ + public String[] getPlaneIds(); + +} diff --git a/dasCore/src/main/java/org/das2/dataset/DataSetCache.java b/dasCore/src/main/java/org/das2/dataset/DataSetCache.java new file mode 100644 index 000000000..7c060e985 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/DataSetCache.java @@ -0,0 +1,38 @@ +/* File: DataSetCache.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; +/** + * + * @author jbf + */ +public interface DataSetCache { + + public void store( DataSetDescriptor dsd, CacheTag cacheTag, DataSet data ); + + public boolean haveStored( DataSetDescriptor dsd, CacheTag cacheTag ); + + public DataSet retrieve( DataSetDescriptor dsd, CacheTag cacheTag ); + + public void reset(); +} diff --git a/dasCore/src/main/java/org/das2/dataset/DataSetConsumer.java b/dasCore/src/main/java/org/das2/dataset/DataSetConsumer.java new file mode 100644 index 000000000..bc9ee0f6a --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/DataSetConsumer.java @@ -0,0 +1,32 @@ +/* File: DataSetConsumer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +/** + * + * @author jbf + */ +public interface DataSetConsumer { + public DataSet getConsumedDataSet(); +} diff --git a/dasCore/src/main/java/org/das2/dataset/DataSetDescriptor.java b/dasCore/src/main/java/org/das2/dataset/DataSetDescriptor.java new file mode 100755 index 000000000..967a807b4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/DataSetDescriptor.java @@ -0,0 +1,406 @@ +/* File: DataSetDescriptor.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 22, 2003, 12:49 PM by __FULLNAME__ <__EMAIL__> + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.dataset; + +import org.das2.components.propertyeditor.Displayable; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.client.DasServer; +import org.das2.client.DataSetDescriptorNotAvailableException; +import org.das2.client.StreamDataSetDescriptor; +import org.das2.client.NoSuchDataSetException; +import org.das2.stream.StreamDescriptor; +import org.das2.DasIOException; +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.util.monitor.NullProgressMonitor; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.system.DasLogger; +import org.das2.system.RequestProcessor; + +import org.das2.util.URLBuddy; +import java.net.*; +import java.util.*; +import java.util.regex.*; +import java.lang.reflect.*; +import java.util.logging.Logger; +import javax.swing.event.*; + +/** + * DataSetDescriptors are a source from where datasets are produced. These uniquely identify a data set + * that is parameteric. Typically, the parameter is time, so for example, there might be a DataSetDescriptor + * for "discharge of the Iowa River measured at Iowa City." Clients of the class get + * DataSets from the DataSetDescriptor via the getDataSet( Start, End, Resolution ) method. So for + * example, you might ask what is the discharge from June 1 to August 31st, 2005, at a resolution of + * 1 day. Presently, it's implicit that this means to give bin averages of the data. + * + *

    DataSetDescriptors are identified with a URL-like string: + *

    http://www-pw.physics.uiowa.edu/das/das2Server?das2_1/cluster/wbd/r_wbd_dsn_cfd&spacecraft%3Dc1%26antenna%3DEy

    + * + *

    The protocol of the string indicates how the DataSetDescriptor is to be constructed, and presently + * there are: + *

    + *   http     a das2Server provides the specification of the datasetdescriptor.
    + *   class    refers to a loadable java class that is an instanceof DataSetDescriptor and
    + *            has the method newDataSetDescriptor( Map params ) throws DasException
    + *
    + *

    + * @author jbf + */ +public abstract class DataSetDescriptor implements Displayable { + + protected Map properties = new HashMap(); + private boolean defaultCaching = true; + private DataSetCache dataSetCache; + private String dataSetID; + private EventListenerList listenerList; + + protected DataSetDescriptor(final String dataSetID) { + dataSetCache = DasApplication.getDefaultApplication().getDataSetCache(); + this.dataSetID = dataSetID; + } + + protected DataSetDescriptor() { + dataSetCache = DasApplication.getDefaultApplication().getDataSetCache(); + dataSetID = "class:"+this.getClass().getName(); + } + private static final Logger logger = DasLogger.getLogger(DasLogger.GRAPHICS_LOG); + + /** + * getDataSetImpl implements the getDataSet for this DataSetDescriptor implementation. The + * getDataSet call of the abstract DataSetDescriptor uses this routine to satisfy requests and + * fill its cache. This caching may be disabled via setDefaultCaching. To satisfy the request, + * a DataSet should be returned with an x tag range that contains start and end, with a + * resolution finer than that requested. + * + * @param start beginning of range for the request. + * @param end end of the range for the request. + * @param resolution the resolution requirement for the reqeust. null may be used to request the finest resolution available or intrinic resolution. + */ + protected abstract DataSet getDataSetImpl(Datum start, Datum end, Datum resolution, ProgressMonitor monitor) throws DasException; + + /** + * @return the x units of the DataSetDescriptor that parameterize the data. This is used to identify dataSet requests. + */ + public abstract Units getXUnits(); + + /** + * Requests that a dataSet be loaded, and that the dataSet be returned via a DataSetUpdate event. + * The @param lockObject is an object that is dependent on the load, for example, the DasCanvas, + * and will be passed in to the request processor. If the dataSet is available in interactive time, + * then the dataSetUpdate may be fired on the same thread as the request is made. + */ + public void requestDataSet(final Datum start, final Datum end, final Datum resolution, + final ProgressMonitor monitor, Object lockObject) { + + Runnable request = new Runnable() { + + public void run() { + logger.fine("requestDataSet: " + start + " " + end + " " + resolution); + try { + DataSet ds = getDataSet(start, end, resolution, monitor); + if (ds == null) { + throw new NoDataInIntervalException(new DatumRange(start, end).toString()); + } + DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)DataSetDescriptor.this, ds); + dsue.setMonitor(monitor); + fireDataSetUpdateEvent(dsue); + } catch (DasException e) { + DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)DataSetDescriptor.this, e); + dsue.setMonitor(monitor); + fireDataSetUpdateEvent(dsue); + } + } + + public String toString() { + return "loadDataSet " + new DatumRange(start, end); + } + }; + logger.fine("submit data request"); + + CacheTag tag = new CacheTag(start, end, resolution); + if (dataSetCache.haveStored(this, tag)) { + request.run(); + } else { + RequestProcessor.invokeLater(request, lockObject); + } + + } + + /** + * Request the dataset, and the dataset is returned only to the listener. + * + * @param lockObject object that is waiting for the result of this load, used to block other tasks which use that object. + */ + public void requestDataSet(final Datum start, final Datum end, final Datum resolution, + final ProgressMonitor monitor, Object lockObject, final DataSetUpdateListener listener) { + + if (lockObject == null) { + lockObject = listener; + } + + if (this instanceof ConstantDataSetDescriptor) { + try { + DataSet ds = getDataSet(null, null, null, null); + DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)this, ds); + dsue.setMonitor(monitor); + } catch (DasException e) { + DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)DataSetDescriptor.this, e); + dsue.setMonitor(monitor); + listener.dataSetUpdated(dsue); + } + } else { + Runnable request = new Runnable() { + + public void run() { + logger.fine("request data from dsd: " + start + " " + end + " " + resolution); + try { + DataSet ds = getDataSet(start, end, resolution, monitor); + DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)DataSetDescriptor.this, ds); + dsue.setMonitor(monitor); + listener.dataSetUpdated(dsue); + } catch (DasException e) { + DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)DataSetDescriptor.this, e); + dsue.setMonitor(monitor); + listener.dataSetUpdated(dsue); + } + } + + public String toString() { + return "loadDataSet " + new DatumRange(start, end); + } + }; + RequestProcessor.invokeLater(request, lockObject); + } + + } + + /** + * Retrieve the dataset for this interval and resolution. The contract for this function is that + * identical start,end,resolution parameters will yield an identical dataSet, except for when an + * DataSetUpdate has been fired in the meantime. + * + * null for the data resolution indicates that the data should be returned at its "intrinsic resolution" + * if such a resolution exists. + */ + public DataSet getDataSet(Datum start, Datum end, Datum resolution, ProgressMonitor monitor) throws DasException { + if (monitor == null) { + monitor = new NullProgressMonitor(); + } + + CacheTag tag = null; + if (defaultCaching) { + tag = new CacheTag(start, end, resolution); + DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG).fine("getDataSet " + this + " " + tag); + } + + if (defaultCaching && dataSetCache.haveStored(this, tag)) { + return dataSetCache.retrieve(this, tag); + } else { + try { + DataSet ds = getDataSetImpl(start, end, resolution, monitor); + if (ds != null) { + if (ds.getProperty("cacheTag") != null) { + tag = (CacheTag) ds.getProperty("cacheTag"); + } + if (defaultCaching) { + dataSetCache.store(this, tag, ds); + } + } + return ds; + } catch (DasException e) { + throw e; + } finally { + monitor.finished(); + } + } + } + + /** + * clear any state that's developed, in particular any data caches. Note + * this currently deletes all cached datasets, regardless of the DataSetDescriptor + * that produced them. + */ + public void reset() { + dataSetCache.reset(); + } + + /** + * defaultCaching means that the abstract DataSetDescriptor is allowed to handle + * repeat getDataSet calls by returning a cached dataset. If a dataSetUpdate event + * is thrown, the defaultCache is reset. + * + * Use caution when using this. Note that caching may only be turned off + * with this call. + */ + public void setDefaultCaching(boolean value) { + if (value == false) { + defaultCaching = value; + } + } + + public void addDataSetUpdateListener(DataSetUpdateListener listener) { + if (listenerList == null) { + listenerList = new EventListenerList(); + } + listenerList.add(DataSetUpdateListener.class, listener); + } + + public void removeDataSetUpdateListener(DataSetUpdateListener listener) { + if (listenerList == null) { + listenerList = new EventListenerList(); + } + listenerList.remove(DataSetUpdateListener.class, listener); + } + + protected void fireDataSetUpdateEvent(DataSetUpdateEvent event) { + if (listenerList == null) { + return; + } + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == DataSetUpdateListener.class) { + ((DataSetUpdateListener) listeners[i + 1]).dataSetUpdated(event); + } + } + } + + /** + * @return the string that uniquely identifies this dataset. + */ + public String getDataSetID() { + return this.dataSetID; + } + private static final Pattern CLASS_ID = Pattern.compile("class:([a-zA-Z0-9_\\.]+)(?:\\?(.*))?"); + private static final Pattern EXEC_ID = Pattern.compile("exec:(.+)\\?(.+)"); + private static final Pattern NAME_VALUE = Pattern.compile("([_0-9a-zA-Z%+.-]+)=([_0-9a-zA-Z%+.-]+)"); + + /** + * creates a DataSetDescriptor for the given identification string. The identification + * string is a URL-like string, for example http://www-pw.physics.uiowa.edu/das/das2Server?das2_1/cluster/wbd/r_wbd_dsn_cfd&spacecraft%3Dc1%26antenna%3DEy + * The protocol of the string indicates how the DataSetDescriptor is to be constructed, and presently + * there are: + *
    +     *   http     a das2Server provides the specification of the DataSetDescriptor, and a
    +     *            StreamDataSetDescriptor is created.
    +     *   class    refers to a loadable java class that is an instanceof DataSetDescriptor and
    +     *            has the method newDataSetDescriptor( Map params )
    +     *
    + * Note that DataSetDescriptors are stateless, the same DataSetDescriptor object + * may be returned to multiple clients. + */ + public static DataSetDescriptor create(final String dataSetID) throws DasException { + java.util.regex.Matcher classMatcher = CLASS_ID.matcher(dataSetID); + java.util.regex.Matcher execMatcher = EXEC_ID.matcher(dataSetID); + DataSetDescriptor result; + if (classMatcher.matches()) { + result = createFromClassName(dataSetID, classMatcher); + } else if (execMatcher.matches()) { + result = createFromExecutable(dataSetID, execMatcher); + } else { + try { + result = createFromServerAddress(new URL(dataSetID)); + //result = DasServer.createDataSetDescriptor(new URL(dataSetID)); + } catch (MalformedURLException mue) { + throw new DasIOException(mue.getMessage()); + } + } + result.dataSetID = dataSetID; + return result; + } + + private static DataSetDescriptor createFromServerAddress(final URL url) throws DasException { + DasServer server = DasServer.create(url); + StreamDescriptor sd = server.getStreamDescriptor(url); + return new StreamDataSetDescriptor(sd, server.getStandardDataStreamSource(url)); + } + private static DataSetDescriptor createFromExecutable(String dataSetID, Matcher matcher) { + String executable = matcher.group(1); + String[] paramPatterns = matcher.group(2).split("&"); + return new ExecutableDataSetDescriptor(executable, paramPatterns); + } + + private static DataSetDescriptor createFromClassName(final String dataSetID, final Matcher matcher) throws DasException { + try { + String className = matcher.group(1); + String argString = matcher.group(2); + Map argMap = argString == null ? Collections.EMPTY_MAP : URLBuddy.parseQueryString(argString); + Class dsdClass = Class.forName(className); + Method method = dsdClass.getMethod("newDataSetDescriptor", new Class[]{java.util.Map.class}); + if (!Modifier.isStatic(method.getModifiers())) { + throw new NoSuchDataSetException("newDataSetDescriptor must be static"); + } + return (DataSetDescriptor) method.invoke(null, new Object[]{argMap}); + } catch (ClassNotFoundException cnfe) { + DataSetDescriptorNotAvailableException dsdnae = + new DataSetDescriptorNotAvailableException(cnfe.getMessage()); + dsdnae.initCause(cnfe); + throw dsdnae; + } catch (NoSuchMethodException nsme) { + DataSetDescriptorNotAvailableException dsdnae = + new DataSetDescriptorNotAvailableException(nsme.getMessage()); + dsdnae.initCause(nsme); + throw dsdnae; + } catch (InvocationTargetException ite) { + DataSetDescriptorNotAvailableException dsdnae = + new DataSetDescriptorNotAvailableException(ite.getTargetException().getMessage()); + dsdnae.initCause(ite.getTargetException()); + throw dsdnae; + } catch (IllegalAccessException iae) { + DataSetDescriptorNotAvailableException dsdnae = + new DataSetDescriptorNotAvailableException(iae.getMessage()); + dsdnae.initCause(iae); + throw dsdnae; + } + } + + protected void setProperties(Map properties) { + this.properties.putAll(properties); + } + + /** + * Returns the value of the property with the specified name + * + * @param name The name of the property requested + * @return The value of the requested property as an Object + */ + public Object getProperty(String name) { + return properties.get(name); + } + + public javax.swing.Icon getListIcon() { + return null; + } + + public String getListLabel() { + return this.dataSetID; + } + + /** + * @return the DataSetCache object used to store cached copies of the + * DataSets created by this object. + */ + public DataSetCache getDataSetCache() { + return this.dataSetCache; + } +} diff --git a/dasCore/src/main/java/org/das2/dataset/DataSetRebinner.java b/dasCore/src/main/java/org/das2/dataset/DataSetRebinner.java new file mode 100755 index 000000000..c1f9f30cf --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/DataSetRebinner.java @@ -0,0 +1,49 @@ +/* File: DataSetRebinner.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on November 5, 2003, 10:28 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import java.util.Map; +import org.das2.DasException; + +/** + * + * @author Edward West + */ +public interface DataSetRebinner { + + /** Rebin a dataset into a 2-D grid + * + * Datasets are immutable, so you can't alter them in place + * @param ds The dataset to rebin + * @param x bin size and spacing information for the X direction + * @param y bin size and spacing information for the Y direction + * @param override a map of dataset properties to override, may be null. + * property names and values for this map match those given in DataSet.java + * @return a new dataset + * @throws IllegalArgumentException + * @throws DasException + */ + DataSet rebin(DataSet ds, RebinDescriptor x, RebinDescriptor y, Map override) + throws IllegalArgumentException, DasException; +} diff --git a/dasCore/src/main/java/org/das2/dataset/DataSetStreamProducer.java b/dasCore/src/main/java/org/das2/dataset/DataSetStreamProducer.java new file mode 100644 index 000000000..07faaf5d3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/DataSetStreamProducer.java @@ -0,0 +1,294 @@ +/* + * DataSetStreamProducer.java + * + * Created on October 12, 2007, 10:11 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.dataset; + +import org.das2.datum.Datum; +import org.das2.datum.DatumVector; +import org.das2.datum.UnitsUtil; +import org.das2.stream.DataTransferType; +import org.das2.stream.PacketDescriptor; +import org.das2.stream.StreamDescriptor; +import org.das2.stream.StreamException; +import org.das2.stream.StreamMultiYDescriptor; +import org.das2.stream.StreamProducer; +import org.das2.stream.StreamXDescriptor; +import org.das2.stream.StreamYScanDescriptor; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.channels.Channels; +import java.nio.channels.WritableByteChannel; +import java.util.Iterator; +import java.util.Map; + +/** + * Configurable class for serializing a DataSet into a das2Stream. This class + * handles both VectorDataSets and TableDataSets, and uses java beans properties + * to control how the stream is produced. This code subsumes the functionality + * of TableUtil.dumpToDas2Stream and VectorUtil.dumpToDas2Stream. + * + * @author jbf + */ +public class DataSetStreamProducer { + + /** Creates a new instance of DataSetStreamProducer */ + public DataSetStreamProducer() { + } + + /** + * convenient method for writing to an OutputStream. Simply + * uses Channels.newChannel to create a WritableByteChannel. + */ + public void writeStream( OutputStream out ) { + this.writeStream( Channels.newChannel(out) ); + } + + /** + * writes the stream to the Channel. + */ + public void writeStream( WritableByteChannel out ) { + if ( dataSet instanceof VectorDataSet ) { + writeVectorDataSetStream( out ); + } else { + writeTableDataSetStream( out ); + } + } + + private void writeTableDataSetStream( WritableByteChannel out ) { + TableDataSet tds= (TableDataSet)dataSet; + + if (tds.getXLength() == 0) { + try { + out.close(); + } catch (IOException ioe) { + //Do nothing. + } + return; + } + try { + StreamProducer producer = new StreamProducer(out); + StreamDescriptor sd = new StreamDescriptor(); + + Map properties= tds.getProperties(); + if ( properties!=null) { + for ( Iterator i= properties.keySet().iterator(); i.hasNext(); ) { + String key= (String)i.next(); + sd.setProperty(key, properties.get(key)); + } + } + + if ( compressed ) { + sd.setCompression( "deflate" ); + } + + producer.streamDescriptor(sd); + + DataTransferType xTransferType; + DataTransferType yTransferType; + + if ( asciiTransferTypes ) { + if ( UnitsUtil.isTimeLocation(tds.getXUnits()) ) { + xTransferType= DataTransferType.getByName("time24"); + } else { + xTransferType= DataTransferType.getByName("ascii10"); + } + yTransferType= DataTransferType.getByName("ascii10"); + } else { + xTransferType= DataTransferType.getByName("sun_real8"); + yTransferType= DataTransferType.getByName("sun_real4"); + } + + PacketDescriptor pd = new PacketDescriptor(); + + StreamXDescriptor xDescriptor = new StreamXDescriptor(); + xDescriptor.setUnits(tds.getXUnits()); + xDescriptor.setDataTransferType(xTransferType); + + pd.setXDescriptor(xDescriptor); + + String[] planeIds= DataSetUtil.getAllPlaneIds(tds); + + DatumVector[] yValues = new DatumVector[planeIds.length]; + + for ( int j=0; j2 ) { + Units units= table.getXUnits(); + double min= table.getXTagDouble(1,units) - table.getXTagDouble( 0,units) ; + for ( int i=2; i(-(insertion point) - 1). (See Arrays.binarySearch) + */ + public static int xTagBinarySearch( DataSet ds, Datum datum, int low, int high ) { + Units units= datum.getUnits(); + double key= datum.doubleValue(units); + while (low <= high) { + int mid = (low + high) >> 1; + double midVal = ds.getXTagDouble(mid,units); + int cmp; + if (midVal < key) { + cmp = -1; // Neither val is NaN, thisVal is smaller + } else if (midVal > key) { + cmp = 1; // Neither val is NaN, thisVal is larger + } else { + long midBits = Double.doubleToLongBits(midVal); + long keyBits = Double.doubleToLongBits(key); + cmp = (midBits == keyBits ? 0 : // Values are equal + (midBits < keyBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN) + 1)); // (0.0, -0.0) or (NaN, !NaN) + } + + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid - 1; + else + return mid; // key found + } + return -(low + 1); // key not found. + } + + /** Searches the xtags of the specified data set for the specified datum + * using the binary search algorithm. + * + * @param ds the data set to search + * @param datum the key to search for + * @return index of the search datum if it exists as an xtag in the + * data set; otherwise the insertion point. The insertion point is + * is defined as -1.0 if the datum is less that the first xtag + * in the data set, ds.getXLength() if the datum is + * larger than the last xtag, or + * (i + (datum - x0) / (x1 - x0) ) + * where x0 is the largest xtag smaller than datum, + * x1 is the smallest xtag larger than datum, and + * i is the index of x0. + */ + public static double columnFindex( DataSet ds, Datum datum ) { + int result = xTagBinarySearch( ds, datum, 0, ds.getXLength()-1); + + if (result >= -1) { + return result; + } + + result = ~result; + + if (result == ds.getXLength()) { + return result; + } + else { + Units u = ds.getXUnits(); + int lowerIndex = result-1; + double lower = ds.getXTagDouble(lowerIndex, u); + double upper = ds.getXTagDouble(result, u); + double key = datum.doubleValue(u); + return lowerIndex + (key - lower) / (upper - lower); + } + } + + public static int closestColumn( DataSet table, Datum datum ) { + int result= xTagBinarySearch( table, datum, 0, table.getXLength()-1 ); + if (result == -1) { + result = 0; //insertion point is 0 + } else if (result < 0) { + result= ~result; // usually this is the case + if ( result >= table.getXLength()-1 ) { + result= table.getXLength()-1; + } else { + double x= datum.doubleValue( datum.getUnits() ); + double x0= table.getXTagDouble(result-1, datum.getUnits() ); + double x1= table.getXTagDouble(result, datum.getUnits() ); + result= ( ( x-x0 ) / ( x1 - x0 ) < 0.5 ? result-1 : result ); + } + } + return result; + } + + public static int closestColumn( DataSet table, double x, Units units ) { + return closestColumn( table, units.createDatum(x) ); + } + + /** + * finds the dataset column closest to the value, starting the search at guessIndex. + * Handles monotonically increasing or decreasing tags. + * @param table + * @param xdatum + * @param guessIndex + * @return + */ + public static int closestColumn( DataSet table, Datum xdatum, int guessIndex ) { + int monotonicDir= 1; + int result= guessIndex; + int len= table.getXLength(); + if ( len==1 ) return 0; + + if ( result>=len-1 ) result=len-2; + if ( result<0 ) result=0; + + Units units= xdatum.getUnits(); + + if ( table.getXTagDouble(result, units) > table.getXTagDouble(result+1, units ) ) { + monotonicDir= -1; + } + + double x= xdatum.doubleValue(units); + if ( monotonicDir==1 ) { + while ( result<(len-1) && table.getXTagDouble(result,units) < x ) result++; + while ( result>0 && table.getXTagDouble(result,units) > x ) result--; + } else { + while ( result<(len-1) && table.getXTagDouble(result,units) > x ) result++; + while ( result>0 && table.getXTagDouble(result,units) < x ) result--; + } + // result should now point to the guy to the left of x, unless x>the last guy or 0 && ds.getXTagDatum(i).ge(datum) ) { + return i-1; + } else { + return i; + } + } + + /** + * returns the first column that is after the given datum. Note the + * if the datum identifies (==) an xtag, then the previous column is + * returned. + */ + public static int getNextColumn( DataSet ds, Datum datum ) { + int i= closestColumn( ds, datum ); + // TODO: consider the virtue of le + if ( i + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import org.das2.datum.UnitsConverter; +import org.das2.system.DasLogger; +import java.io.PrintStream; + +import java.util.*; +import java.text.MessageFormat; +import java.util.logging.Logger; + +/** + * + * @author Edward West + */ +public final class DefaultTableDataSet extends AbstractTableDataSet { + + /** + * Description of indices + * From left to right: + * 1. Index of the plane. 0 is primary + * 2. Index of the yscan within the dataset. + * 3. Index of the zValue within the yscan. + */ + private double[][][] tableData; + + private Units[] zUnits; + + private double[][] yTags; + final int tableCount; + + private int[] tableOffsets; + + private String[] planeIDs; + + private static final Logger logger= DasLogger.getLogger(); + + /** Creates a new instance of DefaultTableDataSet for tables where the + * table geometry changes, and the DataSet contains multiple planes. + */ + public DefaultTableDataSet(double[] xTags, Units xUnits, + double[][] yTags, Units yUnits, + double[][][] zValues, Units zUnits, + Map zValuesMap, Map zUnitsMap, + Map properties) { + super(xTags, xUnits, yUnits, zUnits, properties); + if (zValuesMap == null ^ zUnitsMap == null) { + throw new IllegalArgumentException("zValuesMap == null ^ zUnitsMap == null"); + } + if (zValuesMap != null && !zValuesMap.keySet().equals(zUnitsMap.keySet())) { + throw new IllegalArgumentException("mismatched keySets for zValuesMap and zUnitsMap"); + } + if (zValuesMap != null) { + for (Iterator it = zValuesMap.keySet().iterator(); it.hasNext();) { + if (!(it.next() instanceof String)) { + throw new IllegalArgumentException("Non-String key found in zValuesMap"); + } + } + } + int planeCount = 1 + (zValuesMap == null ? 0 : zValuesMap.size()); + this.tableData = new double[planeCount][][]; + this.tableData[0] = flatten(zValues); + this.yTags = copy(yTags); + this.tableCount= yTags.length; + this.zUnits = new Units[planeCount]; + this.zUnits[0] = zUnits; + this.planeIDs = new String[planeCount]; + this.planeIDs[0] = ""; + tableOffsets = computeTableOffsets(zValues, yTags); + if (zValuesMap != null) { + int index = 1; + for (Iterator it = new TreeMap(zValuesMap).entrySet().iterator(); it.hasNext();) { + Map.Entry entry = (Map.Entry)it.next(); + String key = (String)entry.getKey(); + double[][][] d = (double[][][])entry.getValue(); + double[][] f = flatten(d); + this.planeIDs[index] = key; + this.tableData[index] = f; + this.zUnits[index] = (Units)zUnitsMap.get(key); + } + } + } + + private double[][] flatten(double[][][] d) { + int sum = 0; + for(double[][] d1 : d){ + sum += d1.length; + } + double[][] flat = new double[sum][]; + int offset = 0; + for(double[][] d2 : d){ + for(double[] d1 : d2){ + flat[offset] = (double[]) d1.clone(); + offset++; + } + } + return flat; + } + + public static DefaultTableDataSet createSimple( double[] xTags, double[] yTags, double[][] zValues ) { + int nx= zValues.length; + int ny= zValues[0].length; + if ( xTags.length!=nx ) { + throw new IllegalArgumentException("xTags ("+xTags.length+") don't match zValues' first dimension ("+nx+","+ny+")." ); + } + if ( yTags.length!=ny ) { + throw new IllegalArgumentException("yTags ("+yTags.length+") don't match zValues' first dimension ("+nx+","+ny+")." ); + } + return new DefaultTableDataSet( xTags, Units.dimensionless, yTags, Units.dimensionless, zValues, Units.dimensionless, new HashMap() ); + } + + /** Creates a DefaultTableDataSet when the table geometry changes. */ + public DefaultTableDataSet(double[] xTags, Units xUnits, + double[] yTags, Units yUnits, + double[][] zValues, Units zUnits, + Map properties) { + this(xTags, xUnits, new double[][]{yTags}, yUnits, new double[][][]{zValues}, zUnits, + null, null, properties); + } + + DefaultTableDataSet(double[] xTags, Units xUnits, + double[][] yTags, Units yUnits, + double[][][] zValues, Units[] zUnits, + String[] planeIDs, int[] tableOffsets, + Map properties) { + super(xTags, xUnits, yUnits, zUnits[0], properties); + this.yTags = yTags; + this.tableCount= yTags.length; + this.tableData = zValues; + this.zUnits = zUnits; + this.planeIDs = planeIDs; + this.tableOffsets = tableOffsets; + } + + DefaultTableDataSet(double[] xTags, Units xUnits, + double[][] yTags, Units yUnits, + double[][][] zValues, Units[] zUnits, + String[] planeIDs, int[] tableOffsets, + Map properties, List tableProperties ) { + super(xTags, xUnits, yUnits, zUnits[0], properties); + this.yTags = yTags; + this.tableCount= yTags.length; + this.tableData = zValues; + this.zUnits = zUnits; + this.planeIDs = planeIDs; + this.tableOffsets = tableOffsets; + this.tableProperties= tableProperties; + } + + private static double[][][] copy(double[][][] d) { + double[][][] copy = new double[d.length][][]; + for (int index = 0; index < d.length; index++) { + copy[index] = copy(d[index]); + } + return copy; + } + + private static double[][] copy(double[][] d) { + double[][] copy = new double[d.length][]; + for (int index = 0; index < d.length; index++) { + copy[index] = (double[])d[index].clone(); + } + return copy; + } + + private static int[] computeTableOffsets(double[][][] tableData, double[][] yTags) { + int[] tableOffsets = new int[tableData.length]; + int currentOffset = 0; + for (int index = 0; index < tableOffsets.length; index++) { + tableOffsets[index] = currentOffset; + currentOffset += tableData[index].length; + } + return tableOffsets; + } + + @Override + public Datum getDatum(int i, int j) { + int table = tableOfIndex(i); + int yLength = yTags[table].length; + if (i < 0 || i >= tableData[0].length) { + IndexOutOfBoundsException ioobe = new IndexOutOfBoundsException + ("x index is out of bounds: " + i + " xLength: " + getXLength()); + logger.throwing(DefaultTableDataSet.class.getName(), + "getDatum(int,int)", ioobe); + throw ioobe; + } + if (j < 0 || j >= this.yTags[table].length) { + IndexOutOfBoundsException ioobe = new IndexOutOfBoundsException + ("y index is out of bounds: " + i + " yLength(" + table + "): " + getYLength(table)); + logger.throwing(DefaultTableDataSet.class.getName(), + "getDatum(int,int)", ioobe); + throw ioobe; + } + try { + double value = tableData[0][i][j]; + return Datum.create(value, zUnits[0]); + } catch (ArrayIndexOutOfBoundsException aioobe) { + throw aioobe; //This is just here so developers can put a breakpoint + //here if ArrayIndexOutOfBoundsException is thrown. + } + } + + @Override + public DatumVector getScan(int i) { + int table = tableOfIndex(i); + if (i < 0 || i >= tableData[0].length) { + IndexOutOfBoundsException ioobe = new IndexOutOfBoundsException + ("x index is out of bounds: " + i + " xLength: " + getXLength()); + logger.throwing(DefaultTableDataSet.class.getName(), + "getDatum(int,int)", ioobe); + throw ioobe; + } + try { + double[] values = tableData[0][i]; + return DatumVector.newDatumVector(values, 0, getYLength(table), zUnits[0]); + } catch (ArrayIndexOutOfBoundsException aioobe) { + throw aioobe; //This is just here so developers can put a breakpoint + //here if ArrayIndexOutOfBoundsException is thrown. + } + } + + @Override + public double getDouble(int i, int j, Units units) { + int table; + int yLength; + + if (i < 0 || i >= getXLength()) { + throw new IndexOutOfBoundsException("i: " + i + ", xLength: " + getXLength()); + } + + table = tableOfIndex(i); + yLength = yTags[table].length; + + if (j < 0 || j >= yLength) { + throw new IndexOutOfBoundsException("j: " + j + ", yLength: " + yLength); + } + double value = tableData[0][i][j]; + if (units == getZUnits()) { + return value; + } + return zUnits[0].getConverter(units).convert(value); + } + + @Override + public double[] getDoubleScan(int i, Units units) { + int table = tableOfIndex(i); + int yLength = yTags[table].length; + double[] values = tableData[0][i]; + double[] retValues = new double[yLength]; + if (units == getZUnits()) { + System.arraycopy(values, 0, retValues, 0, yLength); + } else { + UnitsConverter uc = zUnits[0].getConverter(units); + for (int j = 0; j < yLength; j++) { + retValues[j] = uc.convert(values[j]); + } + } + return retValues; + } + + @Override + public int getInt(int i, int j, Units units) { + return (int)Math.round(getDouble(i, j, units)); + } + + @Override + public DataSet getPlanarView(String planeID) { + int planeIndex = -1; + for (int index = 0; index < planeIDs.length; index++) { + if (planeIDs[index].equals(planeID)) { + planeIndex = index; + } + } + if (planeIndex == -1) { + return null; + } else { + return new PlanarViewDataSet(planeIndex); + } + } + + @Override + public String[] getPlaneIds() { + String[] result= new String[planeIDs.length]; + System.arraycopy( planeIDs, 0, result, 0, planeIDs.length ); + return result; + } + + @Override + public int getYLength(int table) { + return yTags[table].length; + } + + @Override + public Datum getYTagDatum(int table, int j) { + double value = yTags[table][j]; + return Datum.create(value, getYUnits()); + } + + @Override + public double getYTagDouble(int table, int j, Units units) { + double value = yTags[table][j]; + if (units == getYUnits()) { + return value; + } + return getYUnits().getConverter(units).convert(value); + } + + @Override + public int getYTagInt(int table, int j, Units units) { + return (int)Math.round(getYTagDouble(table, j, units)); + } + + @Override + public DatumVector getYTags(int table) { + return DatumVector.newDatumVector(yTags[table], 0, getYLength(table), getYUnits()); + } + + @Override + public int tableCount() { + return tableCount; + } + + @Override + public int tableEnd(int table) { + if (table == tableOffsets.length - 1) { + return getXLength(); + } else { + return tableOffsets[table + 1]; + } + } + + @Override + public int tableOfIndex(int i) { + if ( yTags.length>5 ) { + int table = Arrays.binarySearch(tableOffsets, i); + if (i >= getXLength()) { + throw new IndexOutOfBoundsException(i + " > " + getXLength()); + } + if (table < 0) { + table = (~table) - 1; + } + return table; + } else { + int result= tableCount-1; + while ( result>=0 && tableOffsets[result]>i ) result--; + return result; + } + } + + @Override + public int tableStart(int table) { + return tableOffsets[table]; + } + + public void dump(java.io.PrintStream out) { + MessageFormat tableFormat = new MessageFormat( + " {0,number,00}. Y Length: {1,number,000}, Start: {2,number,000}, End: {3,number,000}"); + MessageFormat planeFormat = new MessageFormat(" ID: {0}, Z Units: ''{1}''"); + out.println("============================================================"); + out.println(getClass().getName()); + out.println(" X Length: " + getXLength()); + out.println(" X Units: '" + getXUnits() + "'"); + out.print(" X Tags:"); + for (int index = 0; index < getXLength(); index ++) { + out.print(" "); + out.print(getXTagDouble(index, getXUnits())); + } + out.println(); + out.println(" Y Units: '" + getYUnits() + "'"); + out.println(" Z Units: '" + getZUnits() + "'"); + out.println(" Table Count: " + tableCount()); + Object[] args = new Object[4]; + for (int table = 0; table < tableCount(); table++) { + args[0] = table; + args[1] = yTags[table].length; + args[2] = tableStart(table); + args[3] = tableEnd(table); + String str = tableFormat.format(args); + out.println(str); + out.print(" Y Tags:"); + for (int index = 0; index < getYLength(table); index ++) { + out.print(" "); + out.print(getYTagDouble(table, index, getXUnits())); + } + out.println(); + out.println(" Z Values:"); + for (int j = 0; j < getYLength(table); j++) { + out.print(" "); + for (int i = tableStart(table); i < tableEnd(table); i++) { + out.print(getDouble(i, j, getZUnits())); + out.print("\t"); + } + out.println(); + } + } + out.println(" Plane Count: " + planeIDs.length); + for (int plane = 1; plane < planeIDs.length; plane++) { + args[0] = planeIDs[plane]; + args[1] = zUnits[plane]; + String str = planeFormat.format(args); + out.println(str); + } + out.println("============================================================"); + } + + @Override + public String toString() { + return "DefaultTableDataSet "+TableUtil.toString(this); + } + + public void printDebugInfo(PrintStream out) { + out.println("xLength: " + getXLength()); + out.println("tableCount: " + tableCount()); + for (int table = 0; table < tableCount(); table++) { + out.println("tableStart(" + table + "): " + tableStart(table)); + out.println("yLength: " + getYLength(table)); + for (int i = tableStart(table); i < tableEnd(table); i++) { + out.println(i + ": " + tableData[0][i].length); + } + out.println("tableEnd(" + table + "): " + tableEnd(table)); + } + } + + private final class PlanarViewDataSet extends AbstractDataSet.ViewDataSet implements TableDataSet { + + private final int index; + + private PlanarViewDataSet(int index) { + DefaultTableDataSet.this.super(); + this.index = index; + } + + @Override + public DataSet getPlanarView(String planeID) { + return planeID.equals("") ? this : null; + } + + // @Override + // public DataSet getPlanarView(int nPlaneIdx){ + // if(nPlaneIdx > ); + //return null; + // } + + @Override + public String[] getPlaneIds() { + return new String[0]; + } + + @Override + public Datum getDatum(int i, int j) { + int table = tableOfIndex(i); + int yLength = yTags[table].length; + double value = tableData[index][i][j]; + return Datum.create(value, zUnits[index]); + } + + @Override + public double getDouble(int i, int j, Units units) { + int table = tableOfIndex(i); + int yLength = yTags[table].length; + double value = tableData[index][i][j]; + return zUnits[index].getConverter(units).convert(value); + } + + @Override + public double[] getDoubleScan(int i, Units units) { + int table = tableOfIndex(i); + int yLength = yTags[table].length; + double[] values = tableData[index][i]; + double[] retValues = new double[yLength]; + if (units == getZUnits()) { + System.arraycopy(values, 0, retValues, 0, yLength); + } else { + UnitsConverter uc = zUnits[index].getConverter(units); + for (int j = 0; j < yLength; j++) { + retValues[j] = uc.convert(values[j]); + } + } + return retValues; + } + + @Override + public DatumVector getScan(int i) { + int table = tableOfIndex(i); + if (i < 0 || i >= tableData[0].length) { + IndexOutOfBoundsException ioobe = new IndexOutOfBoundsException + ("x index is out of bounds: " + i + " xLength: " + getXLength()); + logger.throwing(DefaultTableDataSet.class.getName(), + "getDatum(int,int)", ioobe); + throw ioobe; + } + try { + double[] values = tableData[index][i]; + return DatumVector.newDatumVector(values, 0, getYLength(table), zUnits[index]); + } catch (ArrayIndexOutOfBoundsException aioobe) { + throw aioobe; //This is just here so developers can put a breakpoint + //here if ArrayIndexOutOfBoundsException is thrown. + } + } + + @Override + public int getInt(int i, int j, Units units) { + return (int)Math.round(getDouble(i, j, units)); + } + + @Override + public VectorDataSet getXSlice(int i) { + return new XSliceDataSet(this, i); + } + + @Override + public int getYLength(int table) { + return DefaultTableDataSet.this.getYLength(table); + } + + @Override + public VectorDataSet getYSlice(int j, int table) { + return new YSliceDataSet(this, j, table); + } + + @Override + public Datum getYTagDatum(int table, int j) { + return DefaultTableDataSet.this.getYTagDatum(table, j); + } + + @Override + public double getYTagDouble(int table, int j, Units units) { + return DefaultTableDataSet.this.getYTagDouble(table, j, units); + } + + @Override + public int getYTagInt(int table, int j, Units units) { + return DefaultTableDataSet.this.getYTagInt(table, j, units); + } + + @Override + public Units getZUnits() { + return DefaultTableDataSet.this.zUnits[index]; + } + + @Override + public int tableCount() { + return DefaultTableDataSet.this.tableCount(); + } + + @Override + public int tableEnd(int table) { + return DefaultTableDataSet.this.tableEnd(table); + } + + @Override + public int tableOfIndex(int i) { + return DefaultTableDataSet.this.tableOfIndex(i); + } + + @Override + public int tableStart(int table) { + return DefaultTableDataSet.this.tableStart(table); + } + + @Override + public Object getProperty(String name) { + Object result= DefaultTableDataSet.this.getProperty(planeIDs[index] + "." + name); + if ( result==null ) result= DefaultTableDataSet.this.getProperty(name); + return result; + } + + @Override + public Object getProperty(int table, String name) { + Object result= DefaultTableDataSet.this.getProperty(table,planeIDs[index] + "." + name); + if ( result==null ) result= DefaultTableDataSet.this.getProperty(table,name); + return result; + } + + @Override + public DatumVector getYTags(int table) { + return DefaultTableDataSet.this.getYTags(table); + } + + @Override + public String toString() { + return TableUtil.toString(this); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/DefaultVectorDataSet.java b/dasCore/src/main/java/org/das2/dataset/DefaultVectorDataSet.java new file mode 100755 index 000000000..c30170c34 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/DefaultVectorDataSet.java @@ -0,0 +1,237 @@ +/* File: DefaultVectorDataSet.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 27, 2003, 11:18 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.Datum; + +import java.util.*; + +/** + * + * @author Edward West + */ +public final class DefaultVectorDataSet extends AbstractVectorDataSet implements DataSet, VectorDataSet { + + private double[][] yValues; + + private String[] planeIDs; + + private Units[] yUnits; + + /** Creates a new instance of DefaultVectorDataSet + * @param xTags array of double x tag values + * @param xUnits units for the x tags + * @param yValues array of double y values + * @param yUnits untis for the y values + * @param properties map of String property names to their values + */ + public DefaultVectorDataSet(double[] xTags, Units xUnits, + double[] yValues, Units yUnits, + Map properties) { + this(xTags, xUnits, yValues, yUnits, null, null, properties); + } + + /** Creates a new instance of DefaultVectorDataSet + * The keys for the properties, yValuesMap, and unitsMap parameter must + * consist solely of String values. If any of these key sets contain + * non-string values an IllegalArgumentException will be thrown. + * The key set of the yUnitsMap parameter must match exactly the key set + * of the yValuesMap. If their key sets do not exactly match, an + * IllegalArgumentException will be thrown. + * @param xTags array of double x tag values + * @param xUnits units for the x tags + * @param yValues array of double y values + * @param yUnits untis for the y values + * @param yValuesMap map of String plane IDs to their y values + * @param properties map of String property names to their values + */ + public DefaultVectorDataSet(double[] xTags, Units xUnits, + double[] yValues, Units yUnits, + Map yValuesMap, Map yUnitsMap, + Map properties) { + super(xTags, xUnits, yUnits, properties); + if (yValuesMap == null ^ yUnitsMap == null) { + throw new IllegalArgumentException("yValuesMap == null ^ yUnitsMap == null"); + } + if ( yValuesMap!=null ) { + if (!yValuesMap.keySet().equals(yUnitsMap.keySet())) { + throw new IllegalArgumentException("mismatched keySets for yValuesMap and yUnitsMap"); + } + for (Iterator it = yValuesMap.keySet().iterator(); it.hasNext();) { + if (!(it.next() instanceof String)) { + throw new IllegalArgumentException("Non-String key found in yValuesMap"); + } + } + } + int planeCount = 1 + (yValuesMap == null ? 0 : yValuesMap.size()); + this.yValues = new double[planeCount][]; + this.yValues[0] = (double[])yValues.clone(); + this.planeIDs = new String[planeCount]; + this.planeIDs[0] = ""; + this.yUnits = new Units[planeCount]; + this.yUnits[0] = yUnits; + if (yValuesMap != null) { + int index = 1; + for (Iterator it = new TreeMap(yValuesMap).entrySet().iterator(); it.hasNext();) { + Map.Entry entry = (Map.Entry)it.next(); + String id = (String)entry.getKey(); + double[] values = (double[])entry.getValue(); + planeIDs[index] = id; + this.yValues[index] = (double[])values.clone(); + this.yUnits[index] = (Units)yUnitsMap.get(id); + } + } + } + + /** Creates a DefaultVectorData set without copying or verifying the + * data and units supplied. + */ + DefaultVectorDataSet(double[] xTags, Units xUnits, + double[][] yValues, Units[] yUnits, + String[] planeIDs, Map properties) { + super(xTags, xUnits, yUnits[0], properties); + this.yValues = yValues; + this.yUnits = yUnits; + this.planeIDs = planeIDs; + } + + /** Returns the Y value for the given index into the x tags as a + * Datum. + * @param i index of the x tag for the requested value. + * @return the value at index location i as a Datum + */ + public Datum getDatum(int i) { + return Datum.create(yValues[0][i], getYUnits()); + } + + /** Returns the Y value for the given index into the x tags as a + * double with the given units. + * @param i index of the x tag for the requested value. + * @param units the units the returned value should be coverted to. + * @return the value at index location i as a double. + */ + public double getDouble(int i, Units units) { + if (yUnits[0].isFill(yValues[0][i])) { + return units.getFillDouble(); + } + return getYUnits().getConverter(units).convert(yValues[0][i]); + } + + /** Returns the Y value for the given index into the x tags as a + * int with the given units. + * @param i index of the x tag for the requested value. + * @param units the units the returned value should be coverted to. + * @return the value at index location i as a int. + */ + public int getInt(int i, Units units) { + return (int)Math.round(getDouble(i, units)); + } + + /** Returns a DataSet with the specified view as the primary + * view. + * @param planeID the String id of the requested plane. + * @return the specified view, as a DataSet + */ + public DataSet getPlanarView(String planeID) { + int index = -1; + for (int i = 0; i < planeIDs.length; i++) { + if (planeIDs[i].equals(planeID)) { + index = i; + break; + } + } + if (index == -1) { + return null; + } + else { + return new PlanarViewDataSet(index); + } + } + + public String[] getPlaneIds() { + String[] result= new String[planeIDs.length]; + System.arraycopy( planeIDs, 0, result, 0, planeIDs.length ); + return result; + } + + private class PlanarViewDataSet extends AbstractDataSet.ViewDataSet implements VectorDataSet { + + private final int index; + + private PlanarViewDataSet(int index) { + DefaultVectorDataSet.this.super(); + this.index = index; + } + + public Datum getDatum(int i) { + return Datum.create(yValues[index][i], yUnits[index]); + } + + public double getDouble(int i, Units units) { + return yUnits[index].getConverter(units).convert(yValues[index][i]); + } + + public int getInt(int i, Units units) { + return (int)Math.round(getDouble(i, units)); + } + + public Units getYUnits() { + return yUnits[index]; + } + + public DataSet getPlanarView(String planeID) { + DataSet result= planeID.equals("") ? this : DefaultVectorDataSet.this.getPlanarView(planeID); + if (result==null ) return DefaultVectorDataSet.this.getPlanarView( planeIDs[index] + "."+planeID ); + return result; + } + + public String[] getPlaneIds() { + return new String[0] ; + } + + public Object getProperty(String name) { + String planeProp = planeIDs[index] + "." + name; + if (DefaultVectorDataSet.this.hasProperty(name)) { + return DefaultVectorDataSet.this.getProperty(planeProp); + } + else { + return DefaultVectorDataSet.this.getProperty(name); + } + } + + // TODO: this appears to have different logic than in ViewDataSet. This needs to be resolved. + + // public Map getProperties() { + // throw new IllegalStateException("unimplemented"); + // //return DefaultVectorDataSet.this.getProperty(planeIDs[index] + "." + name); + // } + + public String toString() { + return "DefaultVectorDataSet("+DefaultVectorDataSet.this.planeIDs[index]+") "+VectorUtil.toString(this); + } + + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/ExecutableDataSetDescriptor.java b/dasCore/src/main/java/org/das2/dataset/ExecutableDataSetDescriptor.java new file mode 100644 index 000000000..f8dfed2aa --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/ExecutableDataSetDescriptor.java @@ -0,0 +1,88 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.dataset; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.das2.DasException; +import org.das2.DasIOException; +import org.das2.client.DataSetStreamHandler; +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.system.DasLogger; +import org.das2.util.DasProgressMonitorInputStream; +import org.das2.util.StreamTool; +import org.das2.util.monitor.ProgressMonitor; + +/** DataSetDescriptor implementation + * + * @author eew + */ +public class ExecutableDataSetDescriptor extends DataSetDescriptor { + + private String exePath; + private String[] commandFmts; + + private static final Logger logger= DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG); + + public ExecutableDataSetDescriptor(String exePath, String[] commandFmts) { + this.exePath = exePath; + this.commandFmts = Arrays.copyOf(commandFmts, commandFmts.length); + } + + @Override + protected DataSet getDataSetImpl(Datum start, Datum end, Datum resolution, ProgressMonitor monitor) throws DasException { + InputStream in; + DataSet result; + + String[] command = new String[commandFmts.length+1]; + command[0] = exePath; + for (int iParam = 0; iParam < commandFmts.length; iParam++) { + String sParam = commandFmts[iParam]; + if (sParam.contains("%{start}")) { + sParam = sParam.replace("%{start}", start.toString()); + } + else if (sParam.contains("%{end}")) { + sParam = sParam.replace("%{end}", end.toString()); + } + else if (sParam.contains("%{resolution}")) { + sParam = sParam.replace("%{resolution}", + Double.toString(resolution.doubleValue(Units.seconds))); + } + command[iParam+1] = sParam; + } + ProcessBuilder builder = new ProcessBuilder(command); + builder.redirectError(ProcessBuilder.Redirect.INHERIT); + try { + Process p = builder.start(); + in = p.getInputStream(); + final DasProgressMonitorInputStream mpin = new DasProgressMonitorInputStream(in, monitor); + ReadableByteChannel channel = Channels.newChannel(mpin); + + DataSetStreamHandler handler = new DataSetStreamHandler(properties, monitor); + + StreamTool.readStream(channel, handler); + return handler.getDataSet(); + + } catch (IOException ex) { + throw new DasIOException(ex); + } + + } + + @Override + public Units getXUnits() { + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/FastTableDataSet.java b/dasCore/src/main/java/org/das2/dataset/FastTableDataSet.java new file mode 100644 index 000000000..d96cca901 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/FastTableDataSet.java @@ -0,0 +1,17 @@ +/* + * FastTableDataSet.java + * + * Created on July 11, 2005, 3:30 PM + * + * + */ + +package org.das2.dataset; + +/** + * + * @author Jeremy + */ +public class FastTableDataSet { + +} diff --git a/dasCore/src/main/java/org/das2/dataset/GapListDouble.java b/dasCore/src/main/java/org/das2/dataset/GapListDouble.java new file mode 100755 index 000000000..f0594e17f --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/GapListDouble.java @@ -0,0 +1,196 @@ +/* File: DoubleList.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on December 30, 2003, 4:26 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +/** + * List implementation that allows for efficient insertion into the middle of the list. + * @author Edward West + */ +class GapListDouble { + + private static final int INITIAL_ARRAY_SIZE = 128; + private double[] array = new double[INITIAL_ARRAY_SIZE]; + private int gapStart = 0; + private int gapEnd = INITIAL_ARRAY_SIZE; + + /** + * Constructor for the class, which is only used within the package. + */ + GapListDouble() { + } + + /** + * inserts the double into the list. + * @param d the double to be added. + * @return the index of the element within the list. + */ + public int add(double d) { + int index = indexOf(d); + if (index < 0) { + index = ~index; + } + if (isFull()) { + resizeArray(); + } + if (index != gapStart) { + moveGap(index); + } + array[gapStart] = d; + gapStart++; + return index; + } + + /** + * Returns the element at the specified position in this list. + * @param index index of element to return. + * @return the element at the specified position in this list. + */ + public double get(int index) { + if (index < gapStart) { + return array[index]; + } + else { + return array[index + (gapEnd - gapStart)]; + } + } + + /** + * Find the index of the number, or insertion index if the number is not in the list. + * @param d number to look for + * @return index of the search key, if it is contained in the list; otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the list: the index of the first element greater than the key, or list.size(), if all elements in the list are less than the specified key. Note that this guarantees that the return value will be >= 0 if and only if the key is found. + */ + public int indexOf(double d) { + if (gapStart != 0 && array[gapStart - 1] >= d) { + return binarySearch(d, array, 0, gapStart); + } + else if (gapEnd != array.length && array[gapEnd] < d) { + int index = binarySearch(d, array, gapEnd, array.length); + if (index >= 0) { + return index - (gapEnd - gapStart); + } + else { + return ~( ~index - (gapEnd - gapStart)); + } + } + else { + return ~gapStart; + } + } + + /** + * true is the list is empty + * @return true if the list is empty + */ + public boolean isEmpty() { + return gapStart == 0 && gapEnd == array.length; + } + + private boolean isFull() { + return gapStart == gapEnd; + } + + /** + * Returns the number of elements in this collection. + * @return the number of elements in the list. + */ + public int size() { + return gapStart + array.length - gapEnd; + } + + /** + * returns the list in an array. + * @return double array of length size(), containing the list. + */ + public double[] toArray() { + double[] out = new double[size()]; + System.arraycopy(array, 0, out, 0, gapStart); + System.arraycopy(array, gapEnd, out, gapStart, array.length - gapEnd); + return out; + } + + /** + * returns a string representation of the object. + * @return a string representation of the object. + */ + public String toString() { + if (isEmpty()) { + return "[]"; + } + StringBuffer buffer = new StringBuffer("["); + int size = size(); + for (int i = 0; i < size-1; i++) { + buffer.append(get(i)).append(", "); + } + buffer.append(get(size - 1)).append("]"); + return buffer.toString(); + } + + private void resizeArray() { + double[] temp = new double[array.length << 1]; + System.arraycopy(array, 0, temp, 0, gapStart); + int l2 = array.length - gapEnd; + System.arraycopy(array, gapEnd, temp, temp.length - l2, l2); + array = temp; + gapEnd = temp.length - l2; + } + + private void moveGap(int position) { + if (position < gapStart) { + int chunkSize = gapStart - position; + int gapSize = gapEnd - gapStart; + System.arraycopy(array, position, array, position + gapSize, chunkSize); + gapStart = position; + gapEnd = gapStart + gapSize; + } + else if (position > gapStart) { + int chunkSize = position - gapStart; + int gapSize = gapEnd - gapStart; + System.arraycopy(array, gapEnd, array, gapStart, chunkSize); + gapStart = position; + gapEnd = gapStart + gapSize; + } + } + + /** + * index of the search key, if it is contained in the list; otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the list: the index of the first element greater than the key, or list.size(), if all elements in the list are less than the specified key. Note that this guarantees that the return value will be >= 0 if and only if the key is found. + */ + private static int binarySearch(final double d, final double[] array, final int start, final int end) { + int low = start; + int high = end-1; + while (low <= high) { + int mid = (low + high) >> 1; + if (array[mid] < d) { + low = mid + 1; + } + else if (array[mid] > d) { + high = mid - 1; + } + else { + return mid; + } + } + return ~low; + } + +} \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/dataset/GenericQernalFactory.java b/dasCore/src/main/java/org/das2/dataset/GenericQernalFactory.java new file mode 100644 index 000000000..ac900b1bf --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/GenericQernalFactory.java @@ -0,0 +1,75 @@ +/* + * GenericQernalFactory.java + * + * Created on October 7, 2005, 1:58 PM + * + * + */ + +package org.das2.dataset; + +import org.das2.dataset.QernalTableRebinner.Qernal; +import org.das2.datum.Datum; + +/** + * + * @author Jeremy + */ +public class GenericQernalFactory implements QernalTableRebinner.QernalFactory { + class GenericQernal implements QernalTableRebinner.Qernal { + double[][] qernal; + int dx0,dx1; + int dy0,dy1; + int nx, ny; + GenericQernal( double[][] qernal, int dx0, int dy0, int nx, int ny ) { + this.qernal= qernal; + this.dx0= dx0; + this.dy0= dy0; + this.dy1= qernal[0].length-dy0-1; + this.dx1= qernal.length-dx0-1; + this.nx= nx; + this.ny= ny; + } + + public void apply( int x, int y, double value, double weight, double[][]ss, double[][]ww ) { + int x0,x1; + int y0,y1; + x0= x-dx0; + x1= x+dx1+1; + y0= y-dy0; + y1= y+dy1+1; + if (x0<0) x0=0; else if (x0>nx) x0=nx; // trim to visible portion + if (x1<0) x1=0; else if (x1>nx) x1=nx; + if (y0<0) y0=0; else if (y0>ny) y0=ny; + if (y1<0) y1=0; else if (y1>ny) y1=ny; + + for ( int i=x0; i ww[i][j] ) { + try { + double w= weight * qernal[i-x+dx0][j-y+dy0]; + ss[i][j]+= value * w; + ww[i][j]+= w; + } catch ( ArrayIndexOutOfBoundsException e ) { + throw new RuntimeException(e); + } + } + } + } + + } + } + + public QernalTableRebinner.Qernal getQernal( RebinDescriptor ddx, RebinDescriptor ddy, Datum xTagWidth, Datum yTagWidth ) { + double[][] qernal= new double[5][5]; + qernal[1][4]= 1.0; + qernal[3][4]= 1.0; + qernal[0][1]= 1.0; + qernal[4][1]= 1.0; + qernal[1][0]= 1.0; + qernal[2][0]= 1.0; + qernal[3][0]= 1.0; + return new GenericQernal( qernal, 2, 2, ddx.numberOfBins(), ddy.numberOfBins() ); + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/LimitCountDataSetCache.java b/dasCore/src/main/java/org/das2/dataset/LimitCountDataSetCache.java new file mode 100644 index 000000000..9875a510a --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/LimitCountDataSetCache.java @@ -0,0 +1,123 @@ +/* File: DataSetCache.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.DasApplication; +/** + * + * @author jbf + */ +public class LimitCountDataSetCache extends AbstractDataSetCache { + + protected Entry[] buffer; + + /** Creates a new instance of StandardDataStreamCache */ + public LimitCountDataSetCache( int count ) { + buffer= new Entry[count]; + } + + public void store( DataSetDescriptor dsd, CacheTag cacheTag, DataSet data ) { + Entry entry= new Entry( dsd, cacheTag, data ); + + int iMin=-1; + for (int i=buffer.length-1; i>=0; i--) { + if (buffer[i]==null) { + iMin= i; + } + } + if ( iMin==-1 ) { + long oldestAccess= Long.MAX_VALUE; + int oldest= -1; + for (int i=buffer.length-1; i>=0; i--) { + if ( buffer[i].lastAccess < oldestAccess ) { + oldest= i; + oldestAccess= buffer[i].lastAccess; + } + } + iMin= oldest; + } + + buffer[iMin]= entry; + }; + + private int findStored( DataSetDescriptor dsd, CacheTag cacheTag ) { + Entry entry= new Entry( dsd, cacheTag, null ); + + int iHit=-1; + for (int i=0; i this.totalSizeLimit ) return; + + synchronized (entries) { + while ( sizeBytes + this.totalSize > this.totalSizeLimit ) { + Entry e= leastValuableEntry(); + long s= DataSetUtil.guessSizeBytes( e.data ); + entries.remove(e); + totalSize-= s; + } + + entries.add( new Entry( dsd, cacheTag, data ) ); + totalSize+= sizeBytes; + } + } + + public Entry[] getEntries() { + return (Entry[]) entries.toArray( new Entry[ entries.size() ] ); + } + + public Entry getEntries( int i ) { + return (Entry)entries.get(i); + } + + public Datum getTotalSize() { + return Units.kiloBytes.createDatum( this.totalSize/1000., 0.1 ); + } + + public Datum getTotalSizeLimit() { + return Units.kiloBytes.createDatum( this.totalSizeLimit/1000., 0.1 ); + } + + public void setTotalSizeLimit( Datum d ) { + this.totalSizeLimit= (long)( d.doubleValue( Units.kiloBytes ) * 1000 ); + } + + public Datum getHitRate() { + //return ( this.hits + this.misses == 0 ) ? Units.percent.getFillDatum() : Units.percent.createDatum( this.hits * 100. / ( this.hits + this.misses ), 0.1 ); + return Units.percent.createDatum( this.hits * 100. / ( this.hits + this.misses ), 0.1 ); + } + + public String getDisplayString() { + StringBuffer result= new StringBuffer( "LimitSizeBytesDataSetCache with "+entries.size()+" datasets" ); + for ( int i=0; inx) x0=nx; // trim to visible portion + if (x1<0) x1=0; else if (x1>nx) x1=nx; + if (y0<0) y0=0; else if (y0>ny) y0=ny; + if (y1<0) y1=0; else if (y1>ny) y1=ny; + for ( int i=x0; i ww[i][j] ) { + ss[i][j]= value * weight; + ww[i][j]= weight; + } + } + } + } + } + + // special case for 1-pixel Quernal + class NNQernalOne implements QernalTableRebinner.Qernal { + int nx, ny; + private NNQernalOne( int nx, int ny ) { + this.nx= nx; + this.ny= ny; + } + public void apply( int x, int y, double value, double weight, double[][]ss, double[][]ww ) { + if ( x>=0 && x=0 && y ww[x][y] ) { + ss[x][y]= value * weight; + ww[x][y]= weight; + } + } + } + + public Qernal getQernal( RebinDescriptor ddx, RebinDescriptor ddy, Datum xTagWidth, Datum yTagWidth ) { + Datum d= ddx.binCenter(0); + int i= ddx.whichBin( d.add(xTagWidth).doubleValue(d.getUnits()), d.getUnits() ); + int dx0= i/2; + int dx1= i/2; + int dy0,dy1; + if ( UnitsUtil.isRatiometric(yTagWidth.getUnits()) ) { + if (!ddy.isLog() ) throw new IllegalArgumentException("need log axis"); + d= ddy.binCenter(0); + double f= yTagWidth.doubleValue( Units.log10Ratio ); + i= ddy.whichBin( d.multiply( DasMath.exp10(f) ).doubleValue(d.getUnits()), d.getUnits() ); + dy0= i/2; + dy1= (i+1)/2; + } else { + d= ddy.binCenter(0); + i= ddy.whichBin( d.add(yTagWidth).doubleValue(d.getUnits()), d.getUnits() ); + dy0= i/2; + dy1= (i+1)/2; + } + if ( dx0==0 && dx1==0 && dy0==0 && dy1==0 ) { + return new NNQernalOne( ddx.numberOfBins(), ddy.numberOfBins() ); + } else { + return new NNQernal( dx0, dx1, dy0, dy1, ddx.numberOfBins(), ddy.numberOfBins() ); + } + } +} diff --git a/dasCore/src/main/java/org/das2/dataset/NearestNeighborTableDataSet.java b/dasCore/src/main/java/org/das2/dataset/NearestNeighborTableDataSet.java new file mode 100755 index 000000000..1b6895075 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/NearestNeighborTableDataSet.java @@ -0,0 +1,326 @@ +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import org.das2.datum.UnitsUtil; +import java.util.*; + +public class NearestNeighborTableDataSet implements TableDataSet { + + TableDataSet source; + + Map m_override; /* Have to keep track of your overrides for getProperty */ + + int[] imap; + + int[][] jmap; + + int[] itableMap; + + RebinDescriptor ddX; + + RebinDescriptor ddY; + + NearestNeighborTableDataSet( + TableDataSet source, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) { + imap= new int[ddX.numberOfBins()]; + if ( ddY==null ) { + if ( source.tableCount()>1 ) { + throw new IllegalArgumentException(); + } + jmap= new int[source.tableCount()][source.getYLength(0)]; + } else { + jmap= new int[source.tableCount()][ddY.numberOfBins()]; + } + itableMap= new int[ddX.numberOfBins()]; + + this.ddX= ddX; + this.ddY= ddY; + this.source= source; + + if(override == null) + m_override = new HashMap<>(); + else + m_override = override; + + if ( source.getXLength()==0 ) { + for ( int i=0; i xTagWidth: Use xOverRide else use xTagWidth + + Datum xTagWidth= (Datum)source.getProperty(DataSet.PROPERTY_X_TAG_WIDTH); + if ( xTagWidth==null ) xTagWidth= DataSetUtil.guessXTagWidth(source); + + Datum xOverRideWidth = (Datum)m_override.get(DataSet.PROPERTY_X_TAG_WIDTH); + if(xOverRideWidth != null){ + xTagWidth = xTagWidth.compareTo(xOverRideWidth) < 0 ? + xOverRideWidth : xTagWidth ; + } + + Datum yTagWidth = (Datum)m_override.get(DataSet.PROPERTY_Y_TAG_WIDTH); + if(yTagWidth == null) + yTagWidth = (Datum)source.getProperty(DataSet.PROPERTY_Y_TAG_WIDTH); + + if ( yTagWidth==null ) yTagWidth= TableUtil.guessYTagWidth(source); + + DatumVector xx= ddX.binCentersDV(); + double[] yy; + if ( ddY==null ) { + yy= TableUtil.getYTagArrayDouble( source, 0, source.getYUnits() ); + } else { + yy= ddY.binCenters(); + } + + int itable0=-1; + int guess= 0; + for ( int i=0; i xTagWidth.doubleValue(xunits)/1.90 ) { + imap[i]=-1; + } else { + int itable= source.tableOfIndex(imap[i]); + itableMap[i]= itable; + if ( itable0!=itable ) { + if ( ddY==null ) { + for ( int j=0; j yTagWidth.doubleValue(Units.logERatio)/1.90 ) jmap[itable][j]=-1; + } else { + Datum yclose= source.getYTagDatum( itable, jmap[itable][j] ); + if ( Math.abs( yclose.subtract(yy[j],ddY.getUnits()).doubleValue(yunits)) > yTagWidth.doubleValue(yunits)/1.90 ) { + jmap[itable][j]= -1; + } + } + } + } + itable0= itable; + } + } + } + } + } + + @Override + public Datum getDatum(int i, int j) { + if ( imap[i]!=-1 && jmap[itableMap[i]][j]!=-1 ) { + return source.getDatum(imap[i], jmap[itableMap[i]][j]); + } else { + return source.getZUnits().createDatum(source.getZUnits().getFillDouble()); + } + } + + @Override + public double getDouble(int i, int j, Units units) { + try { + if ( imap[i]!=-1 && jmap[itableMap[i]][j]!=-1 ) { + return source.getDouble(imap[i], jmap[itableMap[i]][j], units); + } else { + return source.getZUnits().getFillDouble(); + } + } catch ( ArrayIndexOutOfBoundsException e ) { + System.err.println("here: "+e); + throw new RuntimeException(e); + } + } + + @Override + public int getInt(int i, int j, Units units) { + if ( imap[i]!=-1 && jmap[itableMap[i]][j]!=-1 ) { + return source.getInt(imap[i], jmap[itableMap[i]][j],units); + } else { + return source.getZUnits().getFillDatum().intValue(source.getZUnits()); + } + } + + @Override + public DataSet getPlanarView(String planeID) { + TableDataSet ds = (TableDataSet)source.getPlanarView(planeID); + if (ds != null) { + return new NearestNeighborTableDataSet(ds,ddX,ddY, null); + } else { + return null; + } + } + + @Override + public String[] getPlaneIds() { + return source.getPlaneIds(); + } + + @Override + public Object getProperty(String name) { + Object ret = m_override.get(name); + if(ret != null) return source.getProperty(name); + return ret; + } + + @Override + public Map getProperties() { + if(m_override.isEmpty()) return source.getProperties(); + + // Fun, now we get to merge + HashMap mRet = new HashMap<>(m_override); + Map srcProps = source.getProperties(); + for(Object key: srcProps.keySet()){ + mRet.put(key, srcProps.get(key)); + } + return mRet; + } + + @Override + public int getXLength() { + return imap.length; + } + + @Override + public VectorDataSet getXSlice(int i) { + return new XSliceDataSet(this,i); + } + + @Override + public VectorDataSet getYSlice(int j, int table) { + return new YSliceDataSet(this, j, table); + } + + @Override + public Datum getXTagDatum(int i) { + return ddX.getUnits().createDatum(getXTagDouble(i,ddX.getUnits())); + } + + @Override + public double getXTagDouble(int i, Units units) { + return ddX.binCenter(i,units); + } + + @Override + public int getXTagInt(int i, Units units) { + return (int)getXTagDouble(i,units); + } + + @Override + public Units getXUnits() { + return ddX.getUnits(); + } + + @Override + public int getYLength(int table) { + if ( ddY==null ) { + return source.getYLength(table); + } else { + return ddY.numberOfBins(); + } + } + + @Override + public Datum getYTagDatum(int table, int j) { + if ( ddY==null ) { + return source.getYTagDatum( table, j ); + } else { + return ddY.getUnits().createDatum(getYTagDouble(table,j,ddY.getUnits())); + } + } + + @Override + public double getYTagDouble(int table, int j, Units units) { + if ( ddY==null ) { + return source.getYTagDouble( table, j, units ); + } else { + return ddY.binCenter(j,units); + } + } + + @Override + public int getYTagInt(int table, int j, Units units) { + return (int)getYTagDouble( table, j, units); + } + + @Override + public Units getYUnits() { + if ( ddY==null ) { + return source.getYUnits(); + } else { + return ddY.getUnits(); + } + } + + @Override + public Units getZUnits() { + return source.getZUnits(); + } + + @Override + public int tableCount() { + return 1; + } + + @Override + public int tableEnd(int table) { + return ddX.numberOfBins(); + } + + @Override + public int tableOfIndex(int i) { + return 0; + } + + @Override + public int tableStart(int table) { + return 0; + } + + @Override + public String toString() { + return "NearestNeighborTableDataSet " + TableUtil.toString(this); + } + + @Override + public double[] getDoubleScan(int i, Units units) { + int yLength = getYLength(tableOfIndex(i)); + double[] array = new double[yLength]; + for (int j = 0; j < yLength; j++) { + array[j] = getDouble(i, j, units); + } + return array; + } + + @Override + public DatumVector getScan(int i) { + Units zUnits = getZUnits(); + return DatumVector.newDatumVector(getDoubleScan(i, zUnits), zUnits); + } + + @Override + public DatumVector getYTags(int table) { + double[] tags = new double[getYLength(table)]; + Units yUnits = getYUnits(); + for (int j = 0; j < tags.length; j++) { + tags[j] = getYTagDouble(table, j, yUnits); + } + return DatumVector.newDatumVector(tags, yUnits); + } + + @Override + public Object getProperty(int table, String name) { + return getProperty(name); + } + +} + diff --git a/dasCore/src/main/java/org/das2/dataset/NearestNeighborTableRebinner.java b/dasCore/src/main/java/org/das2/dataset/NearestNeighborTableRebinner.java new file mode 100755 index 000000000..2f4673ed7 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/NearestNeighborTableRebinner.java @@ -0,0 +1,49 @@ +/* File: TableAverageRebinner.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on November 5, 2003, 10:31 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import java.util.Map; + +/** + * + * @author Jeremy Faden + */ +public class NearestNeighborTableRebinner implements DataSetRebinner { + + /** Creates a new instance of TableAverageRebinner */ + public NearestNeighborTableRebinner() { + } + + @Override + public DataSet rebin( + DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) throws IllegalArgumentException { + if (!(ds instanceof TableDataSet)) { + throw new IllegalArgumentException(); + } + + return new NearestNeighborTableDataSet((TableDataSet)ds, ddX, ddY, override ); + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/NewAverageTableRebinner.java b/dasCore/src/main/java/org/das2/dataset/NewAverageTableRebinner.java new file mode 100644 index 000000000..07ed4f7c0 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/NewAverageTableRebinner.java @@ -0,0 +1,486 @@ +/* File: TableAverageRebinner.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on November 5, 2003, 10:31 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.datum.UnitsUtil; +import org.das2.DasException; +import org.das2.system.DasLogger; +import java.util.*; +import java.util.logging.*; + +/** + * not thread safe!!! + * @author Jeremy Faden + */ +public class NewAverageTableRebinner implements DataSetRebinner { + + private static final Logger logger = DasLogger.getLogger(DasLogger.DATA_OPERATIONS_LOG); + + /** + * Holds value of property interpolate. + */ + private boolean interpolate= true; + private boolean enlargePixels = true; + + /* not thread safe--ny is set in rebin and then used by the other routines. */ + private final int ny; + private final int nx; + TableDataSet tds; + RebinDescriptor ddX, ddY; + + /** Creates a new instance of TableAverageRebinner */ + public NewAverageTableRebinner( DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY ) { + if (ds == null) { + throw new NullPointerException("null data set"); + } + if (!(ds instanceof TableDataSet)) { + throw new IllegalArgumentException("Data set must be an instanceof TableDataSet: " + ds.getClass().getName()); + } + this.tds = (TableDataSet)ds; + this.ddX= ddX; + this.ddY= ddY; + nx= (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + ny= (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); + } + + @Override + public DataSet rebin( + DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) throws IllegalArgumentException, DasException { + if ( ds!=this.tds ) throw new IllegalArgumentException("already set for another dataset"); + if ( ddX!=this.ddX ) throw new IllegalArgumentException("already set for another X rebin descriptor"); + if ( ddY!=this.ddY ) throw new IllegalArgumentException("already set for another Y rebin descriptor"); + + if(override != null) + throw new UnsupportedOperationException("This rebinner does not "+ + "yet know how to override dataset properties."); + + TableDataSet weights = (TableDataSet)tds.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); + if (ddX != null && tds.getXLength() > 0) { + double start = tds.getXTagDouble(0, ddX.getUnits()); + double end = tds.getXTagDouble(tds.getXLength() - 1, ddX.getUnits()); + if (start > ddX.end ) { + throw new NoDataInIntervalException("data starts after range"); + } else if ( end < ddX.start ) { + throw new NoDataInIntervalException("data ends before range"); + } + } + + long timer= System.currentTimeMillis(); + + Units xunits= ddX.getUnits(); + + + + logger.finest("Allocating rebinData and rebinWeights: " + nx + " x " + ny); + + double[] rebinData= new double[nx*ny]; // Y's are adjacent + double[] rebinWeights= new double[nx*ny]; + + average(tds, weights, rebinData, rebinWeights, ddX, ddY); + + double[] xTags; + if (ddX != null) { + xTags = ddX.binCenters(); + } else { + xTags = new double[nx]; + for (int i = 0; i < nx; i++) { + xTags[i] = tds.getXTagDouble(i, tds.getXUnits()); + } + } + + double[] yTags; + if (ddY != null) { + yTags = ddY.binCenters(); + } else { + yTags = new double[ny]; + for (int j = 0; j < ny; j++) { + yTags[j] = tds.getYTagDouble(0, j, tds.getYUnits()); + } + } + + /* TODO: handle xTagWidth yTagWidth properties. Pass on unrelated properties on to the + * new dataset. + */ + Units resultXUnits= ddX==null ? tds.getXUnits() : ddX.getUnits(); + Units resultYUnits= ddY==null ? tds.getYUnits() : ddY.getUnits(); + + if ( this.interpolate ) { + Datum xTagWidth= (Datum)ds.getProperty("xTagWidth"); + if ( xTagWidth==null ) { + xTagWidth= DataSetUtil.guessXTagWidth(tds); + } + double xTagWidthDouble= xTagWidth.doubleValue(ddX.getUnits().getOffsetUnits()); + + Datum yTagWidth= (Datum)ds.getProperty("yTagWidth"); + + if ( ddX!=null ) fillInterpolateX(rebinData, rebinWeights, xTags, xTagWidthDouble ); + if ( ddY!=null ) fillInterpolateY(rebinData, rebinWeights, ddY, yTagWidth ); + } else if (enlargePixels) { + enlargePixels( rebinData, rebinWeights ); + } + + TableDataSet weightsTDS= new SimpleTableDataSet( xTags, yTags, rebinWeights, resultXUnits, resultYUnits, Units.dimensionless ); + TableDataSet result= new SimpleTableDataSet( xTags, yTags, rebinData, resultXUnits, resultYUnits, Units.dimensionless, DataSet.PROPERTY_PLANE_WEIGHTS, weightsTDS ); + + return result; + } + + private final int indexOf( int i, int j ) { + return i*ny + j; + } + + void average(TableDataSet tds, TableDataSet weights, double[] rebinData, double[] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY) { + double[] ycoordinate; + int nTables; + Units xUnits, zUnits; + + double[][] hInterpData, hInterpWeights, vInterpData, vInterpWeights; + int[][] hInterpIndex, vInterpIndex; + + xUnits = tds.getXUnits(); + zUnits= tds.getZUnits(); + + hInterpData = new double[nx][2]; + hInterpWeights = new double[nx][2]; + hInterpIndex = new int[nx][2]; + vInterpData = new double[2][ny]; + vInterpWeights = new double[2][ny]; + vInterpIndex = new int[2][ny]; + + for (int i = 0; i < hInterpIndex.length; i++) { + Arrays.fill(hInterpIndex[i], -1); + } + for (int i = 0; i < vInterpIndex.length; i++) { + Arrays.fill(vInterpIndex[i], -1); + } + + if (ddY != null) { + ycoordinate = ddY.binCenters(); + } else { + ycoordinate = new double[tds.getYLength(0)]; + for (int j = 0; j < ycoordinate.length; j++) { + ycoordinate[j] = tds.getDouble(0, j, zUnits ); + } + } + + nTables = tds.tableCount(); + for (int iTable = 0; iTable < nTables; iTable++) { + int yLength= tds.getYLength(iTable); + int [] ibiny= new int[tds.getYLength(iTable)]; + for (int j=0; j < ibiny.length; j++) { + if (ddY != null) { + ibiny[j]= ddY.whichBin(tds.getYTagDouble(iTable, j, tds.getYUnits()), tds.getYUnits()); + } else { + ibiny[j] = j; + } + } + for (int i=tds.tableStart(iTable); i < tds.tableEnd(iTable); i++) { + int ibinx; + if (ddX != null) { + ibinx= ddX.whichBin(tds.getXTagDouble(i, xUnits), xUnits); + } else { + ibinx = i; + } + + if (ibinx < 0) { + for (int j = 0; j < yLength; j++) { + if (ibiny[j] < 0 || ibiny[j] >= ny) { + continue; + } + double z = tds.getDouble(i, j, zUnits); + double w = weights == null + ? (zUnits.isFill(z) ? 0. : 1.) + : weights.getDouble(i, j, Units.dimensionless); + if (vInterpIndex[0][ibiny[j]] == -1 + || ibinx > vInterpIndex[0][ibiny[j]]) { + vInterpData[0][ibiny[j]] = z * w; + vInterpWeights[0][ibiny[j]] = w; + vInterpIndex[0][ibiny[j]] = ibinx; + } else if (ibinx == vInterpIndex[0][ibiny[j]]) { + vInterpData[0][ibiny[j]] += z * w; + vInterpWeights[0][ibiny[j]] += w; + } + } + } else if (ibinx >= nx) { + for (int j = 0; j < yLength; j++) { + if (ibiny[j] < 0 || ibiny[j] >= ny) { + continue; + } + double z = tds.getDouble(i, j, zUnits); + double w = weights == null + ? (zUnits.isFill(z) ? 0. : 1.) + : weights.getDouble(i, j, Units.dimensionless); + if (vInterpIndex[1][ibiny[j]] == -1 + || ibinx < vInterpIndex[1][ibiny[j]]) { + vInterpData[1][ibiny[j]] = z * w; + vInterpWeights[1][ibiny[j]] = w; + vInterpIndex[1][ibiny[j]] = ibinx; + } else { + vInterpData[1][ibiny[j]] += z * w; + vInterpWeights[1][ibiny[j]] += w; + } + } + } else { //if (ibinx>=0 && ibinx hInterpIndex[ibinx][0]) { + hInterpData[ibinx][0] = z * w; + hInterpWeights[ibinx][0] = w; + hInterpIndex[ibinx][0] = ibiny[j]; + } else if (ibiny[j] == hInterpIndex[ibinx][0]) { + hInterpData[ibinx][0] += z * w; + hInterpWeights[ibinx][0] += w; + } + } else if (ibiny[j] >= ny) { + if (hInterpIndex[ibinx][1] == -1 + || ibiny[j] < hInterpIndex[ibinx][1]) { + hInterpData[ibinx][1] = z * w; + hInterpWeights[ibinx][1] = w; + hInterpIndex[ibinx][1] = ibiny[j]; + } else if (ibiny[j] == hInterpIndex[ibinx][1]) { + hInterpData[ibinx][1] += z * w; + hInterpWeights[ibinx][1] += w; + } + } else { //if (ibiny[j] >= 0 && ibiny[j] < ny) { + rebinData[ indexOf( ibinx,ibiny[j] ) ] += z * w; + rebinWeights[ indexOf(ibinx,ibiny[j] ) ] += w; + } + } + } + } + } + + multiplyWeights(rebinData, rebinWeights, zUnits); + } + + private final double linearlyInterpolate(int i0, double z0, int i1, double z1, int i) { + double r = ((double)(i - i0))/(i1 - i0); + return z0 + r * (z1 - z0); + } + + private final void multiplyWeights(double[] data, double[] weights, Units zUnits) { + for (int index = 0; index < data.length; index++) { + if (weights[index] > 0.0) { + data[index] = data[index] / weights[index]; + } else { + data[index] = zUnits.getFillDouble(); + } + } + } + + void fillInterpolateX(final double[] data, final double[] weights, final double[] xTags, final double xSampleWidth) { + + final int[] i1= new int[nx]; + final int[] i2= new int[nx]; + double a1; + double a2; + + for (int j = 0; j < ny; j++) { + int ii1 = -1; + int ii2 = -1; + for (int i = 0; i < nx; i++) { + if (weights[ indexOf(i,j) ] > 0. && ii1 == (i-1)) { // ho hum another valid point + i1[i] = -1; + i2[i] = -1; + ii1 = i; + } else if (weights[ indexOf(i,j) ] > 0. && ii1 == -1) { // first valid point + i1[i] = -1; + i2[i] = -1; + ii1 = i; + } else if (weights[indexOf(i,j)] > 0. && ii1 < (i-1)) { // bracketed a gap, interpolate + if (ii1 > -1) { + i1[i] = -1; + i2[i] = -1; + for (int ii = i - 1; ii >= ii1; ii--) { + ii2 = i; + i1[ii] = ii1; + i2[ii] = ii2; + } + ii1 = i; + } + } else { + i1[i] = -1; + i2[i] = -1; + } + } + + for (int i = 0; i < nx; i++) { + + if ((i1[i] != -1) && (xTags[i2[i]] - xTags[i1[i]]) < xSampleWidth * 1.5 ) { + a2 = (float)((xTags[i] - xTags[i1[i]]) / (xTags[i2[i]] - xTags[i1[i]])); + a1 = 1.f - a2; + data[indexOf(i,j)] = data[indexOf(i1[i],j)] * a1 + data[indexOf(i2[i],j)] * a2; + weights[indexOf(i,j)] = weights[indexOf(i1[i],j)] * a1 + weights[indexOf(i2[i],j)] * a2; //approximate + } + } + } + } + + void fillInterpolateY(final double[] data, final double[] weights, RebinDescriptor ddY, Datum yTagWidth ) { + + final int[] i1= new int[ny]; + final int[] i2= new int[ny]; + final double [] y_temp= new double[ddY.numberOfBins()]; + float a1; + float a2; + + final double[] yTags= ddY.binCenters(); + final Units yTagUnits= ddY.getUnits(); + final boolean log= ddY.isLog(); + + if (log) { + for (int j=0; j 0. && ii1 == (j-1)) { // ho hum another valid point + i1[j] = -1; + i2[j] = -1; + ii1 = j; + } else if (weights[indexOf(i,j)] > 0. && ii1 == -1) { // first valid point + i1[j] = -1; + i2[j] = -1; + ii1=j; + } else if (weights[indexOf(i,j)] > 0. && ii1 < (j-1)) { // bracketed a gap, interpolate + if ((ii1 > -1)) { // need restriction on Y gap size + i1[j] = -1; + i2[j] = -1; + for (int jj=j-1; jj>=ii1; jj--) { + ii2 = j; + i1[jj] = ii1; + i2[jj] = ii2; + } + ii1 = j; + } + } else { + i1[j] = -1; + i2[j] = -1; + } + } + + + for (int j = 0; j < ny; j++) { + if ( (i1[j] != -1) && ( ( yTags[i2[j]] - yTags[i1[j]] ) < ySampleWidth[j] ) ) { + a2 = (float)((y_temp[j] - y_temp[i1[j]]) / (y_temp[i2[j]] - y_temp[i1[j]])); + a1 = 1.f - a2; + data[indexOf(i,j)] = data[indexOf(i,i1[j])] * a1 + data[indexOf(i,i2[j])] * a2; + weights[indexOf(i,j)] = weights[indexOf(i,i1[j])] * a1 + weights[indexOf(i,i2[j])] * a2; //approximate + } + } + } + } + + private void enlargePixels( double[] rebinData, double[] rebinWeights ) { + for ( int ii=0; ii0; ii-- ) { + for ( int jj=0; jj0; jj-- ) { + for ( int ii=0; ii + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +/** + * + * @author jbf + */ +public class NoDataInIntervalException extends org.das2.DasException { + + public NoDataInIntervalException(String msg) { + super(msg); + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/NoInterpolateQernalFactory.java b/dasCore/src/main/java/org/das2/dataset/NoInterpolateQernalFactory.java new file mode 100644 index 000000000..28b4e5034 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/NoInterpolateQernalFactory.java @@ -0,0 +1,73 @@ +/* + * NNQernalFactory.java + * + * Created on October 7, 2005, 12:06 PM + * + * + */ + +package org.das2.dataset; + +import org.das2.dataset.QernalTableRebinner.Qernal; +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.util.DasMath; + +/** + * + * @author Jeremy + */ +public class NoInterpolateQernalFactory implements QernalTableRebinner.QernalFactory { + + class EnlargeQernal implements QernalTableRebinner.Qernal { + int dx0,dx1; + int dy0,dy1; + int nx,ny; // number of elements in each dimension + private EnlargeQernal( int dx0, int dx1, int dy0, int dy1, int nx, int ny ) { + this.dx0= dx0; + this.dx1= dx1; + this.dy0= dy0; + this.dy1= dy1; + this.nx= nx; + this.ny= ny; + } + public void apply( int x, int y, double value, double weight, double[][]ss, double[][]ww ) { + int x0,x1; + int y0,y1; + x0= x-dx0; + x1= x+dx1+1; + y0= y-dy0; + y1= y+dy1+1; + if (x0<0) x0=0; else if (x0>nx) x0=nx; // trim to visible portion + if (x1<0) x1=0; else if (x1>nx) x1=nx; + if (y0<0) y0=0; else if (y0>ny) y0=ny; + if (y1<0) y1=0; else if (y1>ny) y1=ny; + for ( int i=x0; i=0 && x=0 && y + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +/** + * + * @author jbf + */ +public class NoKeyProvidedException extends org.das2.DasException { + //The requested data set requires a key, but none was provided. + + /** Creates a new instance of NoKeyProvidedException */ + public NoKeyProvidedException(String msg) { + super(msg); + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/NullDataSetCache.java b/dasCore/src/main/java/org/das2/dataset/NullDataSetCache.java new file mode 100644 index 000000000..76404442b --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/NullDataSetCache.java @@ -0,0 +1,37 @@ +/* + * NullDataSetCache.java + * + * Created on November 13, 2006, 11:45 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.dataset; + +/** + * DataSetCache that does no caching at all. This is useful for batch + * mode or when dataset caching is undesirable. + * + * @author jbf + */ +public class NullDataSetCache implements DataSetCache { + + public NullDataSetCache() { + } + + public void store(DataSetDescriptor dsd, CacheTag cacheTag, DataSet data) { + } + + public boolean haveStored(DataSetDescriptor dsd, CacheTag cacheTag) { + return false; + } + + public DataSet retrieve(DataSetDescriptor dsd, CacheTag cacheTag) { + throw new IllegalArgumentException("not found in cache"); + } + + public void reset() { + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/PeakTableRebinner.java b/dasCore/src/main/java/org/das2/dataset/PeakTableRebinner.java new file mode 100755 index 000000000..afa1fe07e --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/PeakTableRebinner.java @@ -0,0 +1,132 @@ +/* File: TablePeakRebinner.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on November 5, 2003, 10:31 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import java.util.Map; +import org.das2.datum.Units; + +/** + * + * @author Edward West + */ +public class PeakTableRebinner implements DataSetRebinner { + + public PeakTableRebinner() { + } + + + @Override + public DataSet rebin( + DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) throws IllegalArgumentException { + if (!(ds instanceof TableDataSet)) { + throw new IllegalArgumentException(); + } + + if(override != null) + throw new UnsupportedOperationException("This rebinner does not "+ + "yet know how to override dataset properties."); + + TableDataSet tds = (TableDataSet)ds; + long timer= System.currentTimeMillis(); + + int nx= (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + int ny= (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); + + double[][] rebinData= new double[nx][ny]; + double[][] rebinWeights= new double[nx][ny]; + + peaks(tds, rebinData, ddX, ddY); + + double[] xTags; + if (ddX != null) { + xTags = ddX.binCenters(); + } + else { + xTags = new double[tds.getXLength()]; + for (int i = 0; i < xTags.length; i++) { + xTags[i] = tds.getXTagDouble(i, tds.getXUnits()); + } + } + double[][] yTags; + if (ddY != null) { + yTags = new double[][]{ddY.binCenters()}; + } + else { + yTags = new double[0][tds.getYLength(0)]; + for (int j = 0; j < yTags[0].length; j++) { + yTags[0][j] = tds.getYTagDouble(0, j, tds.getYUnits()); + } + } + double[][][] zValues = {rebinData}; + int[] tableOffsets = {0}; + Units[] zUnits = {tds.getZUnits()}; + String[] planeIDs = {""}; + + return new DefaultTableDataSet(xTags, tds.getXUnits(), yTags, tds.getYUnits(), zValues, zUnits, planeIDs, tableOffsets, java.util.Collections.EMPTY_MAP); + } + + static void peaks(TableDataSet tds, double[][] rebinData, RebinDescriptor ddX, RebinDescriptor ddY) { + + int nx= (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + int ny= (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); + + for (int i = 0; i < rebinData.length; i++) { + java.util.Arrays.fill(rebinData[i], Double.NaN); + } + + int [] ibiny= new int[tds.getYLength(0)]; + for (int j=0; j < ibiny.length; j++) { + if (ddY != null) { + ibiny[j]= ddY.whichBin(tds.getYTagDouble(0, j, tds.getYUnits()), tds.getYUnits()); + } + else { + ibiny[j] = j; + } + } + + for (int i=0; i < tds.getXLength(); i++) { + int ibinx; + if (ddX != null) { + ibinx= ddX.whichBin(tds.getXTagDouble(i, tds.getXUnits()), tds.getXUnits()); + } + else { + ibinx = i; + } + if (ibinx>=0 && ibinx= 0 && ibiny[j] < ny) { + double value = tds.getDouble(i, j, tds.getZUnits()); + if (Double.isNaN(rebinData[ibinx][ibiny[j]])) { + rebinData[ibinx][ibiny[j]] = value; + } + else { + rebinData[ibinx][ibiny[j]] = Math.max(value, rebinData[ibinx][ibiny[j]]); + } + } + } + } + } + } +} diff --git a/dasCore/src/main/java/org/das2/dataset/QernalTableRebinner.java b/dasCore/src/main/java/org/das2/dataset/QernalTableRebinner.java new file mode 100644 index 000000000..456690b1e --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/QernalTableRebinner.java @@ -0,0 +1,168 @@ +/* + * QernalTableRebinner.java + * + * Created on October 7, 2005, 11:34 AM + * + * + */ + +package org.das2.dataset; + +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.system.DasLogger; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + + + +/** + * + * @author Jeremy + */ +public class QernalTableRebinner implements DataSetRebinner { + + interface QernalFactory { + Qernal getQernal( RebinDescriptor ddx, RebinDescriptor ddy, Datum xBinWidth, Datum yBinWidth ); + } + + interface Qernal { + void apply( int x, int y, double value, double weight, double[][] s, double[][]w ); + } + + Logger logger= DasLogger.getLogger(DasLogger.DATA_OPERATIONS_LOG); + QernalFactory factory; + + public QernalTableRebinner( QernalFactory factory ) { + this.factory= factory; + } + + public DataSet rebin( + DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) throws IllegalArgumentException, org.das2.DasException { + logger.finest("enter QernalTableRebinner.rebin"); + + if (ds == null) { + throw new NullPointerException("null data set"); + } + if (!(ds instanceof TableDataSet)) { + throw new IllegalArgumentException("Data set must be an instanceof TableDataSet: " + ds.getClass().getName()); + } + + if(override != null) + throw new UnsupportedOperationException("This rebinner does not "+ + "yet know how to override dataset properties."); + + TableDataSet tds = (TableDataSet)ds; + TableDataSet weights = (TableDataSet)ds.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); + if (ddX != null && tds.getXLength() > 0) { + double start = tds.getXTagDouble(0, ddX.getUnits()); + double end = tds.getXTagDouble(tds.getXLength() - 1, ddX.getUnits()); + if (start > ddX.end ) { + throw new NoDataInIntervalException("data starts after range"); + } else if ( end < ddX.start ) { + throw new NoDataInIntervalException("data ends before range"); + } + } + + Datum xBinWidth= DataSetUtil.guessXTagWidth(tds); + + long timer= System.currentTimeMillis(); + + Units xUnits= ddX.getUnits(); + Units zUnits= tds.getZUnits(); + + int nx= (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + int ny= (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); + + logger.finest("Allocating rebinData and rebinWeights: " + nx + " x " + ny); + + double[][] rebinData= new double[nx][ny]; + double[][] rebinWeights= new double[nx][ny]; + + int nTables = tds.tableCount(); + for (int iTable = 0; iTable < nTables; iTable++) { + Datum yBinWidth= TableUtil.guessYTagWidth(tds,iTable); + + Qernal qernal= factory.getQernal( ddX, ddY, xBinWidth, yBinWidth ); + + int [] ibiny= new int[tds.getYLength(iTable)]; + for (int j=0; j < ibiny.length; j++) { + if (ddY != null) { + ibiny[j]= ddY.whichBin(tds.getYTagDouble(iTable, j, tds.getYUnits()), tds.getYUnits()); + } else { + ibiny[j] = j; + } + } + + for (int i=tds.tableStart(iTable); i < tds.tableEnd(iTable); i++) { + int ibinx; + if (ddX != null) { + ibinx= ddX.whichBin( tds.getXTagDouble(i, xUnits), xUnits ); + } else { + ibinx = i; + } + + for (int j = 0; j < tds.getYLength(iTable); j++) { + double z = tds.getDouble(i,j,zUnits); + double w = weights == null + ? (zUnits.isFill(z) ? 0. : 1.) + : weights.getDouble(i, j, Units.dimensionless); + qernal.apply( ibinx, ibiny[j], z, w, rebinData, rebinWeights ); + } + } + } + + logger.finest("normalize sums by weights"); + for ( int i=0; i 0. ) { + rebinData[i][j]/= rebinWeights[i][j]; + } else { + rebinData[i][j]= zUnits.getFillDouble(); + } + } + } + + logger.finest( "create new DataSet" ); + + double[] xTags; + if (ddX != null) { + xTags = ddX.binCenters(); + } else { + xTags = new double[nx]; + for (int i = 0; i < nx; i++) { + xTags[i] = tds.getXTagDouble(i, tds.getXUnits()); + } + } + double[][] yTags; + if (ddY != null) { + yTags = new double[][]{ddY.binCenters()}; + } else { + yTags = new double[1][ny]; + for (int j = 0; j < ny; j++) { + yTags[0][j] = tds.getYTagDouble(0, j, tds.getYUnits()); + } + } + + Units resultXUnits= ddX==null ? tds.getXUnits() : ddX.getUnits(); + Units resultYUnits= ddY==null ? tds.getYUnits() : ddY.getUnits(); + + double[][][] zValues = {rebinData,rebinWeights}; + + int[] tableOffsets = {0}; + Units[] newZUnits = {tds.getZUnits(), Units.dimensionless}; + String[] planeIDs = {"", DataSet.PROPERTY_PLANE_WEIGHTS}; + + Map properties= new HashMap(ds.getProperties()); + + if ( ddX!=null ) properties.put( DataSet.PROPERTY_X_TAG_WIDTH, ddX.binWidthDatum() ); + if ( ddY!=null ) properties.put( DataSet.PROPERTY_Y_TAG_WIDTH, ddY.binWidthDatum() ); + + TableDataSet result= new DefaultTableDataSet( xTags, resultXUnits, yTags, resultYUnits, zValues, newZUnits, planeIDs, tableOffsets, properties ); + logger.finest("done, QernalTableRebinner.rebin"); + return result; + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/QuickVectorDataSet.java b/dasCore/src/main/java/org/das2/dataset/QuickVectorDataSet.java new file mode 100644 index 000000000..4ae00621b --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/QuickVectorDataSet.java @@ -0,0 +1,64 @@ +/* + * EZVectorDataSet.java + * + * Created on April 15, 2005, 2:59 PM + */ + +package org.das2.dataset; + +import org.das2.datum.Datum; +import org.das2.datum.Units; + +/** + * Abstract VectorDataSet that allows for defining a vector dataset by + * implementing a minimal portion of the api. + * @author Jeremy + */ +public abstract class QuickVectorDataSet implements VectorDataSet { + java.util.Map properties= new java.util.HashMap(); + String[] planeIds= new String[0]; + + public Datum getDatum(int i) { + Units yUnits= getYUnits(); + return yUnits.createDatum( getDouble(i,yUnits) ); + } + + abstract public double getDouble(int i, Units units ); + + public int getInt(int i, Units units) { + return (int) getDouble( i,units ); + } + + public DataSet getPlanarView(String planeID) { + return null; + } + + public String[] getPlaneIds() { + return planeIds; + } + + public java.util.Map getProperties() { + return null; + } + + public Object getProperty(String name) { + return null; + } + + abstract public int getXLength(); + + public Datum getXTagDatum(int i) { + Units xUnits= getXUnits(); + return xUnits.createDatum( getXTagDouble(i,xUnits) ); + } + + abstract public double getXTagDouble(int i, Units units); + + public int getXTagInt(int i, Units units) { + return (int)getXTagDouble( i, units ); + } + + abstract public Units getXUnits(); + + abstract public Units getYUnits(); +} \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/dataset/RebinDescriptor.java b/dasCore/src/main/java/org/das2/dataset/RebinDescriptor.java new file mode 100755 index 000000000..b273d9243 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/RebinDescriptor.java @@ -0,0 +1,251 @@ +/* File: RebinDescriptor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.DatumVector; +import org.das2.datum.UnitsConverter; +import org.das2.datum.Datum; +import org.das2.datum.Units; + +/** Nice Documentation... + * + * @author jbf + */ +public class RebinDescriptor { + + Units units; + protected double start; + protected double end; + protected int nBin; + protected boolean isLog = false; + + public static final int FIRSTORLAST=-2; // return the closest valid bin, first or last + public static final int MINUSONE= -3; // return sentinel -1. + public static final int EXTRAPOLATE= -4; // return negative or >nBin. + + private int outOfBoundsAction= EXTRAPOLATE; + + /** Creates a new instance of RebinDescriptor */ + private RebinDescriptor() { + } + + public RebinDescriptor(double start, double end, Units units, int nBin, boolean isLog) { + this.units= units; + if (isLog) { + this.start= Math.log(start); + this.end= Math.log(end); + } else { + this.start= start; + this.end= end; + } + this.nBin= nBin; + this.isLog= isLog; + } + + public RebinDescriptor( Datum start, Datum end, int nBin, boolean isLog) { + this(start.doubleValue(start.getUnits()),end.doubleValue(end.getUnits()),start.getUnits(),nBin,isLog); + if (start.getUnits()!=end.getUnits()) throw new IllegalArgumentException("start and end units differ"); + } + + public int numberOfBins() { + return nBin; + } + + public int whichBin( double x, Units units ) { + if ( units!=this.units ) { + x= Units.getConverter(units,this.units).convert(x); + } + int result=0; + if (isLog) x= Math.log(x); + if ((x=end) && outOfBoundsAction!=EXTRAPOLATE) { + switch (outOfBoundsAction) { + case FIRSTORLAST: + result= x= numberOfBins() ) { + throw new IllegalArgumentException("bin "+ibin+" is out of bounds"); + } + } + double result= start+((ibin)/(double)(nBin)*(end-start)); + UnitsConverter uc= this.units.getConverter(units); + if ( isLog ) { + return uc.convert(Math.exp(result)); + } else { + return uc.convert(result); + } + } + + public Datum binStop( int ibin ) { + return Datum.create( binStop( ibin, units ), units ); + } + + public double binStop( int ibin, Units units ) { + if ( this.outOfBoundsAction!=RebinDescriptor.EXTRAPOLATE ) { + if ( ibin<0 || ibin >= numberOfBins() ) { + throw new IllegalArgumentException("bin "+ibin+" is out of bounds"); + } + } + double result= start+((ibin+1)/(double)(nBin)*(end-start)); + UnitsConverter uc= this.units.getConverter(units); + if ( isLog ) { + return uc.convert(Math.exp(result)); + } else { + return uc.convert(result); + } + } + + /** So what freaking units are the return value in??? + * Anyone? Buler? + * @return a number in who knows what coordinate system + */ + public double[] binStarts() { + double [] result= new double[nBin]; + for (int i=0; i0 ) { + i0= 0; + ymin= units.createDatum(ddY.binStart(0, units)); + } + if ( i0< -10000000 ) { + throw new IllegalArgumentException( "ymin would result in impossibly large rebin descriptor (ymin="+ymin+" falls in bin number "+i0+")" ); + } + + int i1= dd.whichBin( ymax.doubleValue(units), units ); + if ( i1 10000000 ) { + throw new IllegalArgumentException( "ymax would result in impossibly large rebin descriptor (ymax="+ymax+" falls in bin number "+i0+")" ); + } + + int nbins= i1-i0+1; + + return new RebinDescriptor( units.createDatum(dd.binStart(i0,units)), units.createDatum(dd.binStop(i1,units)), nbins, dd.isLog() ); + } + + public double binWidth() { + return (end-start)/(double)nBin; + } + + public Datum binWidthDatum() { + return Datum.create( binWidth(), getUnits().getOffsetUnits() ); + } + + public boolean isLog() { + return isLog; + } + + public Units getUnits() { + return units; + } + + public String toString() { + return "["+units.createDatum(start)+" - "+units.createDatum(end)+" in "+nBin+" bins "+(isLog?"Log":"")+"]"; + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/SimpleDataSetCache.java b/dasCore/src/main/java/org/das2/dataset/SimpleDataSetCache.java new file mode 100644 index 000000000..eb8245cf3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/SimpleDataSetCache.java @@ -0,0 +1,90 @@ +/* File: DataSetCache.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.DasApplication; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +/** + * simply cache data by storing one per DataSetDescriptor. + * + * @author jbf + */ +public class SimpleDataSetCache extends AbstractDataSetCache { + + protected Map buffer; + + /** Creates a new instance of StandardDataStreamCache */ + public SimpleDataSetCache() { + buffer= new HashMap(); + } + + public void store( DataSetDescriptor dsd, CacheTag cacheTag, DataSet data ) { + Entry entry= new Entry( dsd, cacheTag, data ); + buffer.put( dsd, entry ); + }; + + public boolean haveStoredImpl( DataSetDescriptor dsd, CacheTag cacheTag ) { + Entry haveEntry= (Entry)buffer.get(dsd); + if ( haveEntry==null ) { + return false; + } else { + Entry entry= new Entry( dsd, cacheTag, null ); + return haveEntry.satifies(entry); + } + } + + public DataSet retrieveImpl( DataSetDescriptor dsd, CacheTag cacheTag ) { + Entry haveEntry= (Entry)buffer.get(dsd); + if ( haveEntry==null ) { + throw new IllegalArgumentException("Data not found in cache"); + } else { + Entry entry= new Entry( dsd, cacheTag, null ); + if ( haveEntry.satifies(entry) ) { + return haveEntry.getData(); + } else { + throw new IllegalArgumentException("Data not found in cache"); + } + } + } + + public void reset() { + buffer= new HashMap(); + } + + public String toString() { + StringBuffer result= new StringBuffer("\n---SimpleDataSetCache---\n"); + for ( Iterator i= buffer.keySet().iterator(); i.hasNext(); ) { + Object key= i.next(); + result.append( " " ); + result.append( buffer.get(key).toString() ); + result.append( "\n" ); + } + result.append( "------------------------\n" ); + return result.toString(); + } + + +} diff --git a/dasCore/src/main/java/org/das2/dataset/SimpleTableDataSet.java b/dasCore/src/main/java/org/das2/dataset/SimpleTableDataSet.java new file mode 100644 index 000000000..84abe1969 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/SimpleTableDataSet.java @@ -0,0 +1,176 @@ +/* + * WritableTableDataSet.java + * + * Created on September 2, 2004, 11:58 AM + */ + +package org.das2.dataset; + +import org.das2.datum.Datum; +import org.das2.datum.DatumVector; +import org.das2.datum.Units; +import java.util.*; +import java.util.Map; + +/** + * optimized TableDataSet where only 1-table data set is supported, + * and is backed by a 1-D array. This is to house the result of the rebin method + * used for spectrograms. + * + * @author Jeremy + */ +public class SimpleTableDataSet implements TableDataSet { + + double[] z; + double[] x; + double[] y; + final int nx; + final int ny; + Units xunits; + Units yunits; + Units zunits; + Map properties; + TableDataSet auxPlane; + String auxPlaneName; + + /* z must be stored with the y's adjacent */ + public SimpleTableDataSet( double[] x, double[]y, double[] z, Units xunits, Units yunits, Units zunits ) { + this.z= z; + this.x= x; + this.y= y; + this.nx= x.length; + this.ny= y.length; + this.xunits= xunits; + this.yunits= yunits; + this.zunits= zunits; + auxPlaneName= null; + } + + public SimpleTableDataSet( double[] x, double[]y, double[] z, Units xunits, Units yunits, Units zunits, String planeName, TableDataSet planeData ) { + this( x, y, z, xunits, yunits, zunits ); + auxPlaneName= planeName; + auxPlane= planeData; // TODO: check units and dimensions + } + + + private final int indexOf( int i, int j ) { + return i*ny + j; + } + + public Datum getDatum(int i, int j) { + return Datum.create( z[indexOf(i,j)], zunits ); + } + + public double getDouble(int i, int j, Units units) { + return zunits.convertDoubleTo(units,z[indexOf(i,j)]); + } + + public double[] getDoubleScan(int i, Units units) { + throw new UnsupportedOperationException(); + } + + public int getInt(int i, int j, Units units) { + throw new UnsupportedOperationException(); + } + + public DataSet getPlanarView(String planeID) { + if ( planeID.equals( auxPlaneName ) ) return auxPlane; else return null; + } + + public String[] getPlaneIds() { + return new String[0]; + } + + public Object getProperty(String name) { + return properties.get(name); + } + + public Object getProperty( int table, String name) { + return getProperty(name); + } + + public DatumVector getScan(int i) { + throw new UnsupportedOperationException(); + } + + public int getXLength() { + return x.length; + } + + public VectorDataSet getXSlice(int i) { + return new XSliceDataSet( this, i ); + } + + public Datum getXTagDatum(int i) { + return Datum.create( x[i], xunits ); + } + + public double getXTagDouble(int i, Units units) { + return xunits.convertDoubleTo( units, x[i] ); + } + + public int getXTagInt(int i, Units units) { + throw new UnsupportedOperationException(); + } + + public Units getXUnits() { + return xunits; + } + + public int getYLength(int table) { + return y.length; + } + + public VectorDataSet getYSlice(int j, int table) { + return new YSliceDataSet( this, j, table ); + } + + public Datum getYTagDatum(int table, int j) { + return Datum.create( y[j], yunits ); + } + + public double getYTagDouble(int table, int j, Units units) { + return yunits.convertDoubleTo(units,y[j]); + } + + public void setYTagDouble( int table, int j, double yvalue, Units units ) { + y[j]= units.convertDoubleTo( yunits, yvalue ); + } + + public int getYTagInt(int table, int j, Units units) { + throw new UnsupportedOperationException(); + } + + public DatumVector getYTags(int table) { + return DatumVector.newDatumVector(y,yunits); + } + + public Units getYUnits() { + return yunits; + } + + public Units getZUnits() { + return zunits; + } + + public int tableCount() { + return 1; + } + + public int tableEnd(int table) { + return x.length; + } + + public int tableOfIndex(int i) { + return 0; + } + + public int tableStart(int table) { + return 0; + } + + public Map getProperties() { + return new HashMap(properties); + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/SingleVectorDataSet.java b/dasCore/src/main/java/org/das2/dataset/SingleVectorDataSet.java new file mode 100644 index 000000000..3c2eeb06e --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/SingleVectorDataSet.java @@ -0,0 +1,81 @@ +/* + * SingleVectorDataSet.java + * + * Created on November 3, 2005, 1:29 PM + * + * + */ + +package org.das2.dataset; + +import org.das2.datum.Datum; +import org.das2.datum.Units; +import java.util.HashMap; + +/** + * + * @author Jeremy + */ +public class SingleVectorDataSet implements VectorDataSet { + Datum x; + Datum y; + HashMap properties; + + public SingleVectorDataSet( Datum x, Datum y, HashMap properties ) { + this.x= x; + this.y= y; + this.properties= new HashMap(properties); + } + + public org.das2.datum.Datum getDatum(int i) { + return y; + } + + public double getDouble(int i, org.das2.datum.Units units) { + return y.doubleValue(units); + } + + public int getInt(int i, org.das2.datum.Units units) { + return y.intValue(units); + } + + public DataSet getPlanarView(String planeID) { + return null; + } + + public String[] getPlaneIds() { + return new String[] { "" }; + } + + public int getXLength() { + return 1; + } + + public Datum getXTagDatum(int i) { + return x; + } + + public double getXTagDouble( int i, Units units ) { + return x.doubleValue(units); + } + + public int getXTagInt( int i, Units units ) { + return x.intValue(units); + } + + public java.util.Map getProperties() { + return new HashMap( properties ); + } + + public Object getProperty(String name) { + return properties.get(name); + } + + public Units getXUnits() { + return x.getUnits(); + } + + public Units getYUnits() { + return y.getUnits(); + } +} diff --git a/dasCore/src/main/java/org/das2/dataset/SyncUtil.java b/dasCore/src/main/java/org/das2/dataset/SyncUtil.java new file mode 100755 index 000000000..8f8186f0f --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/SyncUtil.java @@ -0,0 +1,166 @@ +/* + * SyncUtil.java + * + * Created on April 7, 2004, 1:11 PM + */ + +package org.das2.dataset; + +import org.das2.datum.Datum; +import org.das2.datum.Units; +import java.util.Map; + +/** + * + * @author Jeremy + */ +public class SyncUtil { + + private static int[] calculateImap( DataSet source, DataSet target ) { + int[] imap= new int[target.getXLength()]; + Units xunits= source.getXUnits(); + + Datum xTagWidth= (Datum)source.getProperty("xTagWidth"); + if ( xTagWidth==null ) xTagWidth= DataSetUtil.guessXTagWidth(source); + + for ( int i=0; i xTagWidth.doubleValue(xunits)/2. ) { + imap[i]=-1; + } + } + return imap; + } + + /* calculates imap when width tags are irregular, and possibly overlapping. + */ + private static int[] calculateImapForWidthTags( DataSet source, DataSet target ) { + int[] imap= new int[target.getXLength()]; + Units xunits= source.getXUnits(); + Units xoffsetUnits= xunits.getOffsetUnits(); + String widthsPlane= "xTagWidth"; + + VectorDataSet widthsDs= (VectorDataSet)source.getPlanarView(widthsPlane); + + int sourceLength= source.getXLength(); + + for ( int i=0; i + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; + +/** A DataSet implementation for 3 dimensional z(x,y) data sets + * where the data is arranged in a sequence of tables. Each table will have + * a set of monotonically increasing x tags and y tags. The x tags for all + * the tables, when taken together in the order that the table are in, will + * be monotonically increasing over the whole data set. The y tags are constant + * over the y scans of each table, but may change, either in number or value, + * from table to table. + * + * @author Edward West + */ +public interface TableDataSet extends DataSet { + + /** Returns the Units object representing the unit type of the y values for + * this data set. + * @return the x units + */ + Units getZUnits(); + + /** Returns the Z value for the given indices into the x and y tags as a + * Datum. + * @param i index of the x tag for the requested value. + * @param j index of the y tag for the requested value. + * @return the value at index location (i, j) as a Datum + */ + Datum getDatum(int i, int j); + + /** Returns the Z value for the given indices into the x and y tags as a + * double with the given units. + * @param j index of the x tag for the requested value. + * @param i index of the y tag for the requested value. + * @param units the units the returned value should be coverted to. + * @return the value at index location (i, j) as a double. + */ + double getDouble(int i, int j, Units units); + + DatumVector getScan(int i); + + double[] getDoubleScan(int i, Units units); + + /** Returns the Z value for the given indices into the x and y tags as a + * int with the given units. + * @param i index of the x tag for the requested value. + * @param j index of the y tag for the requested value. + * @param units the units the returned value should be coverted to. + * @return the value at index location (i, j) as a int. + */ + int getInt(int i, int j, Units units); + + /** Returns the yTags for this data set as a DatumVector + * @return the yTags for this data set as a DatumVector + */ + DatumVector getYTags(int table); + + /** Returns the value of the y tag at the given index j as a + * Datum. + * @param j the index of the requested y tag + * @return the value of the y tag at the given index j as a + * Datum. + */ + Datum getYTagDatum(int table, int j); + + /** Returns the value of the y tag at the given index j as a + * double in the given units. YTags must be + * monotonically increasing with j. + * @return the value of the y tag at the given index j as a + * double. + * @param units the units of the returned value + * @param j the index of the requested y tag + */ + double getYTagDouble(int table, int j, Units units); + + /** Returns the value of the y tag at the given index j as an + * int in the given units. YTags must be + * monotonically increasing with j. + * @return the value of the y tag at the given index j as an + * int. + * @param units the units of the returned value + * @param j the index of the requested y tag + */ + int getYTagInt(int table, int j, Units units); + + /** Returns the number of y tags in the specified table for this data set. + * YTags must be monotonically increasing with j. + * @param table index of the table + * @return the number of x tags in this data set. + */ + int getYLength(int table); + + /** Returns the first x tag index of the specified table. + * @param table the index of the table. + * @return the first x tag index of the specified table + */ + int tableStart(int table); + + /** Returns the index after the last x tag index of the specified table + * @param table the index of the table + * @return the index after the last x tag index of the specified table + */ + int tableEnd(int table); + + /** Returns the number of tables in this data set + * @return the number of tables in this data set + */ + int tableCount(); + + /** Returns the table number that the specified index is in. + * @param i x tag index + * @return the table number that the specified index is in + */ + int tableOfIndex(int i); + + /** Returns a slice view of this data set for a specific x value + */ + VectorDataSet getXSlice(int i); + + /** Returns a slice view of this data set for a specific y value + */ + VectorDataSet getYSlice(int j, int table); + + /** + * Return the property value attached to the table. This should + * simply return DataSet.getProperty() if the table has no special + * value for the table. + * @param table + * @param name + * @return + */ + Object getProperty( int table, String name ); +} diff --git a/dasCore/src/main/java/org/das2/dataset/TableDataSetBuilder.java b/dasCore/src/main/java/org/das2/dataset/TableDataSetBuilder.java new file mode 100755 index 000000000..5cb67c9e3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/TableDataSetBuilder.java @@ -0,0 +1,431 @@ +/* File: TableDataSetBuilder.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on December 10, 2003, 4:54 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import java.util.*; + +/** Build an X-Y data grid from input vectors. + * Here's an example of using this class. + *
    + * {@code
    + *		TableDataSet
    + * }
    + *
    + * @author Edward West + */ +public class TableDataSetBuilder { + + private static final double[] EMPTY = new double[0]; + + private GapListDouble xTags = new GapListDouble(); + + private List zValues = new ArrayList(); + + private List planeIDs = new ArrayList(); + + private Units xUnits = Units.dimensionless; + private Units yUnits = Units.dimensionless; + + private Map zUnitsMap = new HashMap(); + + private SortedSet yTagSet = new TreeSet(new DoubleArrayComparator()); + + private Map properties = new HashMap(); + private List tableProperties= new ArrayList<>(); + + /** Creates a new instance of TableDataSetBuilder + * This version assigns a 0-length string to the name of the first plane, and + * assumes that the zUnits are dimensionless. + */ + public TableDataSetBuilder( Units xUnits, Units yUnits) { + setXUnits(xUnits); + setYUnits(yUnits); + + zUnitsMap.put("", Units.dimensionless); + planeIDs.add(""); + } + + /** Creates a new instance of TableDataSetBuilder + * This version assigns a 0-length string to the name of the first plane. + */ + public TableDataSetBuilder( Units xUnits, Units yUnits, Units zUnits ) { + setXUnits(xUnits); + setYUnits(yUnits); + + zUnitsMap.put("", zUnits); + planeIDs.add(""); + } + + /** Create a new instance of TableDataSetBuilder + * + * @param xUnits The X Axis Units + * @param yUnits The Y Axis Units + * @param zUnits The Z Axis Units + * @param sPlane0Name A name for the 0th plane of the dataset + */ + public TableDataSetBuilder( Units xUnits, Units yUnits, Units zUnits, String sPlane0Name ) + { + setXUnits(xUnits); + setYUnits(yUnits); + zUnitsMap = new HashMap(); + zUnitsMap.put(sPlane0Name, zUnits); + planeIDs.add(sPlane0Name); + } + + public void setProperty(String name, Object value) { + properties.put(name, value); + } + + public void setProperty( int table, String name, Object value ) { + if ( tableProperties.size() plane in the dataset. + * + * @param units + */ + public void setZUnits(Units units) { + + setZUnits(units, ""); + } + + public void setZUnits(Units units, String planeID) { + if (units == null) { + throw new NullPointerException(); + } + zUnitsMap.put(planeID, units); + } + + public String toString() { + int index = 0; + return "TableDataSetBuilder ["+xTags.size()+" xtags, "+getTableCount(zValues)+"tables]"; + } + + public TableDataSet toTableDataSet() { + int count = getTableCount(zValues); + int[] tableOffsets = getTableOffsets(zValues, count); + double[][] collapsedYTags = collapseYTags(zValues, count); + double[][][] collapsedZValues = collapseZValues(zValues, planeIDs, zUnitsMap); + Units[] zUnitsArray = getUnitsArray(planeIDs, zUnitsMap); + return new DefaultTableDataSet(xTags.toArray(), xUnits, + collapsedYTags, yUnits, + collapsedZValues, zUnitsArray, + (String[])planeIDs.toArray(new String[planeIDs.size()]), tableOffsets, + properties, tableProperties ); + } + + public int getXLength() { + return xTags.size(); + } + + public double getXTag(int i) { + return xTags.get(i); + } + + private static double[] insert(double[] array, double value, int index) { + double[] result = new double[array.length + 1]; + System.arraycopy(array, 0, result, 0, index); + result[index] = value; + System.arraycopy(array, index, result, index + 1, array.length - index); + return result; + } + + private static double[][] insert(double[][] array, double[] values, int index) { + double[][] result = new double[array.length + 1][]; + System.arraycopy(array, 0, result, 0, index); + result[index] = values; + System.arraycopy(array, index, result, index + 1, array.length - index); + return result; + } + + private static String toString(double[] array) { + return toString(array, 0, array.length); + } + + private static String toString(double[] array, int startIndex, int endIndex) { + if (array.length == 0) return "[]"; + StringBuffer buffer = new StringBuffer("["); + for (int i = startIndex; i < endIndex-1; i++) { + buffer.append(array[i]).append(", "); + } + buffer.append(array[endIndex - 1]).append(']'); + return buffer.toString(); + } + + private static int getTableCount(List list) { + int count = 0; + double[] previous = null; + for (Iterator i = list.iterator(); i.hasNext();) { + MultiYScan scan = (MultiYScan)i.next(); + if (!Arrays.equals(previous, scan.getYTags())) { + previous = scan.getYTags(); + count++; + } + } + return count; + } + + private static int[] getTableOffsets(List list, int count) { + double[] previous = null; + int index = 0; + int offset = 0; + int[] tableOffsets = new int[count]; + for (Iterator i = list.iterator(); i.hasNext();) { + MultiYScan scan = (MultiYScan)i.next(); + if (!Arrays.equals(previous, scan.getYTags())) { + tableOffsets[index] = offset; + previous = scan.getYTags(); + index++; + } + offset++; + } + return tableOffsets; + } + + private static double[][] collapseYTags(List list, int count) { + double[] previous = null; + int index = 0; + double[][] result = new double[count][]; + for (Iterator i = list.iterator(); i.hasNext();) { + MultiYScan scan = (MultiYScan)i.next(); + if (!Arrays.equals(previous, scan.getYTags())) { + result[index] = scan.getYTags(); + previous = scan.getYTags(); + index++; + } + } + return result; + } + + private static double[][][] collapseZValues(List list, List planeIDs, Map unitsMap) { + double[][][] zValues = new double[planeIDs.size()][list.size()][]; + int index = 0; + for (Iterator i = list.iterator(); i.hasNext();) { + MultiYScan scan = (MultiYScan)i.next(); + for (int plane = 0; plane < planeIDs.size(); plane++) { + double[] z = scan.get((String)planeIDs.get(plane)); + if (z == null) { + z = new double[scan.getYTags().length]; + Units units = (Units)unitsMap.get(planeIDs.get(plane)); + Arrays.fill(z, units.getFillDouble()); + } + zValues[plane][index] = z; + } + index++; + } + return zValues; + } + + private static Units[] getUnitsArray(List planeIDs, Map unitsMap) { + Units[] units = new Units[planeIDs.size()]; + for (int i = 0; i < units.length; i++) { + units[i] = (Units)unitsMap.get(planeIDs.get(i)); + } + return units; + } + + private class DoubleArrayComparator implements Comparator { + + public int compare(Object o1, Object o2) { + double[] d1 = (double[])o1; + double[] d2 = (double[])o2; + if (d1.length != d2.length) { + return d1.length - d2.length; + } + for (int i = 0; i < d1.length; i++) { + double delta = d1[i] - d2[i]; + if (delta < 0.0) { + return -1; + } + else if (delta > 0.0) { + return 1; + } + } + return 0; + } + + } + + private class MultiYScan { + private HashMap map = new HashMap(); + double[] yTags; + public void put(String name, double[] scan) { + map.put(name, scan); + } + public double[] get(String name) { + return (double[])map.get(name); + } + public double[] getYTags() { + return yTags; + } + public void setYTags(double[] yTags) { + this.yTags = yTags; + } + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/TableDataSetConsumer.java b/dasCore/src/main/java/org/das2/dataset/TableDataSetConsumer.java new file mode 100644 index 000000000..443ce1609 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/TableDataSetConsumer.java @@ -0,0 +1,37 @@ +/* File: TableDataSetConsumer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +/** + * + * @author jbf + */ + +import org.das2.graph.DasAxis; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetConsumer; + +public interface TableDataSetConsumer extends DataSetConsumer { + public DasAxis getZAxis(); +} diff --git a/dasCore/src/main/java/org/das2/dataset/TableDataSetDecorator.java b/dasCore/src/main/java/org/das2/dataset/TableDataSetDecorator.java new file mode 100644 index 000000000..98ed2891e --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/TableDataSetDecorator.java @@ -0,0 +1,17 @@ +/* + * TableDataSetDecorator.java + * + * Created on December 15, 2005, 8:55 AM + * + * + */ + +package org.das2.dataset; + +/** + * + * @author Jeremy + */ +public class TableDataSetDecorator { + +} diff --git a/dasCore/src/main/java/org/das2/dataset/TableDataSetGridder.java b/dasCore/src/main/java/org/das2/dataset/TableDataSetGridder.java new file mode 100644 index 000000000..94385b0ed --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/TableDataSetGridder.java @@ -0,0 +1,107 @@ +/* + * TableDataSetGridder.java + * + * Created on July 15, 2005, 4:56 PM + * + * + */ + +package org.das2.dataset; + +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.datum.UnitsUtil; +import org.das2.util.DasMath; + +/** + * calculate TableDataSets with tables that are gridded in linear or log space. + * @author Jeremy + */ +public class TableDataSetGridder { + + private static Datum yTagGcd( TableDataSet tds, int itable, Datum error ) { + Units units= tds.getYUnits(); + double[] ytag= tds.getYTags(itable).toDoubleArray(units); + double gcd= DasMath.gcd( ytag, error.doubleValue( units.getOffsetUnits() ) ); + return units.getOffsetUnits().createDatum( gcd ); + } + + private static Datum yTagGcdLog( TableDataSet tds, int itable, Datum error ) { + Units units= tds.getYUnits(); + double[] ytag= new double[ tds.getYLength(itable) -1 ] ; + + if ( ! UnitsUtil.isRatiometric(error.getUnits()) ) throw new IllegalArgumentException("error units must be ratiometric"); + for ( int i=0; i1 ) throw new IllegalArgumentException("only simple tables for now"); + + int itable=0; + Datum xTagGcd= xTagGcd( tds, itable, xerror ); + Datum yTagGcd= yTagGcdLog( tds, itable, yerror ); + + Units xunits= tds.getXUnits(); + Units yunits= tds.getYUnits(); + + double tagWidth= DataSetUtil.guessXTagWidth(tds).doubleValue(xunits.getOffsetUnits()); + double dx= xTagGcd.doubleValue(xunits.getOffsetUnits()); + double xbase= tds.getXTagDouble( tds.tableStart(itable), xunits ) -tagWidth/2 - dx / 2.; + + int nx= (int)(( tds.getXTagDouble(tds.tableEnd(itable)-1, xunits ) + tagWidth/2 - xbase ) / dx + 1 ); + + int[] imap= new int[nx]; + + + for ( int i=tds.tableStart(itable); i(-(insertion point) - 1). (See Arrays.binarySearch) + */ + public static int yTagBinarySearch( TableDataSet ds, int table, Datum datum, int low, int high ) { + Units units= datum.getUnits(); + double key= datum.doubleValue(units); + while (low <= high) { + int mid = (low + high) >> 1; + double midVal = ds.getYTagDouble(table,mid,units); + int cmp; + if (midVal < key) { + cmp = -1; // Neither val is NaN, thisVal is smaller + } else if (midVal > key) { + cmp = 1; // Neither val is NaN, thisVal is larger + } else { + long midBits = Double.doubleToLongBits(midVal); + long keyBits = Double.doubleToLongBits(key); + cmp = (midBits == keyBits ? 0 : // Values are equal + (midBits < keyBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN) + 1)); // (0.0, -0.0) or (NaN, !NaN) + } + + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid - 1; + else + return mid; // key found + } + return -(low + 1); // key not found. + } + + /** Searches the ytags of the specified table of the specified data set for + * the specified datum using the binary search algorithm. + * + * @param ds the data set to search + * @param table the table to search + * @param datum the key to search for + * @return index of the search datum if it exists as an ytag in the + * data set; otherwise the insertion point. The insertion point is + * is defined as -1.0 if the datum is less that the first ytag + * in the data set, ds.getYLength(table) if the datum + * is larger than the last ytag, or + * (i + (datum - y0) / (y1 - y0) ) + * where y0 is the largest ytag smaller than datum, + * y1 is the smallest ytag larger than datum, and + * i is the index of y0. + */ + public static double rowFindex( TableDataSet ds, int table, Datum datum ) { + int result = yTagBinarySearch( ds, table, datum, 0, ds.getXLength()-1); + Units u = datum.getUnits(); + + if (result >= -1) { + return result; + } + + result = ~result; + + if (result == ds.getYLength(table)) { + return result; + } + else { + int lowerIndex = result-1; + double lower = ds.getYTagDouble(table, lowerIndex, u); + double upper = ds.getYTagDouble(table, result, u); + double key = datum.doubleValue(u); + return lowerIndex + (key - lower) / (upper - lower); + } + } + + public static Datum closestDatum( TableDataSet table, Datum x, Datum y ) { + int i= DataSetUtil.closestColumn( table, x ); + int j= closestRow( table, table.tableOfIndex(i), y ); + return table.getDatum(i,j); + } + + public static int tableIndexAt( TableDataSet table, int i ) { + int itable=0; + while ( table.tableEnd(itable)<=i ) itable++; + return itable; + } + + public static Datum guessYTagWidth( TableDataSet table ) { + return guessYTagWidth( table, 0 ); + } + + /** + * guess the y tag cadence by returning the difference of the first two tags. + * If the tags appear to be log spaced, then a ratiometric unit (e.g. percentIncrease) + * is returned. monotonically decreasing is handled, in which case a positive tag cadence + * is returned. + * @param table + * @param the table index. + * @return the norminal cadence of the tags. + */ + public static Datum guessYTagWidth( TableDataSet table, int itable ) { + // cheat and check for logarithmic scale. If logarithmic, then return YTagWidth as percent. + double y0= table.getYTagDouble( itable, 0, table.getYUnits()); + double y1= table.getYTagDouble( itable, 1, table.getYUnits()); + int n= table.getYLength(itable)-1; + double yn= table.getYTagDouble( itable, n, table.getYUnits() ); + double cycles= (yn-y0) / ( (y1-y0 ) * n ); + if ( y1 10. ) { + return Units.log10Ratio.createDatum( DasMath.log10(y1/y0) ); + } else { + return table.getYUnits().createDatum(y1-y0); + } + } + public static double tableMax( TableDataSet tds, Units units ) { + double result= Double.NEGATIVE_INFINITY; + + for ( int itable=0; itable result ) { + result= tds.getDouble(i,j,units); + } + } + } + } + return result; + } + + public static void checkForNaN( TableDataSet tds ) { + for ( int i=0; i"); + pout.println("Stream creation date: "+TimeUtil.now().toString()+""); + pout.print(""); + + if ( tds.getXUnits() instanceof LocationUnits ) { + base= xmin; + offsetUnits= ((LocationUnits)base.getUnits()).getOffsetUnits(); + if ( offsetUnits==Units.microseconds ) { + offsetUnits= Units.seconds; + } + } + + pout.print("[01]\n"); + pout.print(""); + + String yTagsString= ""+tds.getYTagDatum(0,0); + for ( int j=1; j"); + pout.print(""); + + NumberFormat xnf= new DecimalFormat("00000.000"); + NumberFormat ynf= new DecimalFormat("0.00E00"); + + double dx= xmax.subtract(xmin).doubleValue(offsetUnits); + for (int i=0; i=0 && x0 && ( dir * dd > 0 ) ) { + return i-1; + } else { + return i; + } + } + + /** + * return the first row after the datum. Handles mono decreasing. + * @return the row which is greater than or equal to the datum + */ + public static int getNextRow( TableDataSet ds, int itable, Datum datum ) { + int i= closestRow( ds, itable, datum ); + Units units= ds.getYUnits(); + double dir= ds.getYTagDouble(itable, 1, units ) - ds.getYTagDouble(itable, 0, units ); + double dd= ds.getYTagDouble(itable,i,units) - datum.doubleValue(units); + if ( iitable ) { + tableStartList.add( new Integer(i) ); + tableEndList.add( new Integer(i) ); + } + } + tableEndList.add( new Integer(itableMap.length) ); + + tableCount= tableEndList.size(); + tableStart= new int[tableCount]; + tableEnd= new int[tableCount]; + for ( int i=0; i + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.Datum; + +/** Interface definition for datasets comprised of a y value + * for each x tag such that y(x). + * + * @author eew + */ +public interface VectorDataSet extends DataSet { + + /** Returns the Y value for the given index into the x tags as a + * Datum. + * @param i index of the x tag for the requested value. + * @return the value at index location i as a Datum + */ + Datum getDatum(int i); + + /** Returns the Y value for the given index into the x tags as a + * double with the given units. + * @param i index of the x tag for the requested value. + * @param units the units the returned value should be coverted to. + * @return the value at index location i as a double. + */ + double getDouble(int i, Units units); + + /** Returns the Y value for the given index into the x tags as a + * int with the given units. + * @param i index of the x tag for the requested value. + * @param units the units the returned value should be coverted to. + * @return the value at index location i as a int. + */ + int getInt(int i, Units units); +} diff --git a/dasCore/src/main/java/org/das2/dataset/VectorDataSetBuilder.java b/dasCore/src/main/java/org/das2/dataset/VectorDataSetBuilder.java new file mode 100755 index 000000000..4c3978ebb --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/VectorDataSetBuilder.java @@ -0,0 +1,270 @@ +/* File: VectorDataSetBuilder.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on December 23, 2003, 8:58 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.Datum; + +import java.util.*; + +/** + * + * @author Edward West + */ +public class VectorDataSetBuilder { + + private GapListDouble xTags = new GapListDouble(); + private List yValues = new ArrayList(); + + private List planeIDs = new ArrayList(); + { + planeIDs.add(""); + } + + private Units xUnits = Units.dimensionless; + + private Map yUnitsMap = new HashMap(); + { + yUnitsMap.put("", Units.dimensionless); + } + + private Map properties = new HashMap(); + + /** Creates a new instance of VectorDataSetBuilder */ + public VectorDataSetBuilder( Units xUnits, Units yUnits ) { + this.xUnits= xUnits; + setYUnits(yUnits); + } + + public void setProperty(String name, Object value) { + properties.put(name, value); + } + + public Object getProperty(String name) { + return properties.get(name); + } + + public void addProperties(Map map) { + properties.putAll(map); + } + + public void addPlane(String name, Units yUnits ) { + if (!planeIDs.contains(name)) { + planeIDs.add(name); + yUnitsMap.put(name, yUnits ); + } + } + + public void insertY(double x, double y) { + insertY(x, y, ""); + } + + public void insertY(double x, double y, String planeID) { + if ( !planeIDs.contains(planeID) ) { + throw new IllegalArgumentException( "invalid planeID: "+planeID+", have "+planeIDs ); + } + if ( Double.isInfinite(x) || Double.isNaN(x) ) { + throw new IllegalArgumentException( "x is not finite" ); + } + int insertionIndex = xTags.indexOf(x); + if (planeID == null) { + planeID = ""; + } + if (insertionIndex < 0) { + insertionIndex = ~insertionIndex; + xTags.add(x); + MultiY scan = new MultiY(); + scan.put(planeID, y); + yValues.add(insertionIndex, scan); + } else { + MultiY scan = (MultiY)yValues.get(insertionIndex); + scan.put(planeID, y); + } + } + + /** + * Insert method favored when there is a default and one additional plane, + * because it's less prone to error when the + * one forgets the planeId. (And it's slightly more efficient because + * the index search need only be done once.) + */ + public void insertY( double x, double y, String planeId1, double planeValue1 ) { + if ( Double.isInfinite(x) || Double.isNaN(x) ) { + throw new IllegalArgumentException( "x is not finite" ); + } + int insertionIndex = xTags.indexOf(x); + if (insertionIndex < 0) { + insertionIndex = ~insertionIndex; + xTags.add(x); + MultiY scan = new MultiY(); + scan.put( "", y ); + scan.put( planeId1, planeValue1 ); + yValues.add(insertionIndex, scan); + } else { + //throw new IllegalArgumentException("already got value at this index"); + MultiY scan = (MultiY)yValues.get(insertionIndex); + scan.put( "", y ); + scan.put( planeId1, planeValue1 ); + } + } + + /** + * insert a datum for the default plane + */ + public void insertY(Datum x, Datum y) { + insertY(x, y, ""); + } + + /** + * insert a datum for the plane. + */ + public void insertY(Datum x, Datum y, String planeID) { + if (!planeIDs.contains(planeID)) { + throw new IllegalArgumentException("invalid planeID: "+planeID+", have "+planeIDs); + } + double xd = x.doubleValue(xUnits); + double yd = y.doubleValue((Units)yUnitsMap.get(planeID)); + insertY(xd, yd, planeID); + } + + public void append(VectorDataSet vds) { + String[] planeIds= DataSetUtil.getAllPlaneIds(vds); + for ( int iplane=0; iplane0 && xx[result]>x ) result--; + if ( result"); + pout.println("Stream creation date: "+TimeUtil.now().toString()+""); + pout.print(""); + + if ( vds.getXUnits() instanceof LocationUnits ) { + base= xmin; + offsetUnits= ((LocationUnits)base.getUnits()).getOffsetUnits(); + if ( offsetUnits==Units.microseconds ) { + offsetUnits= Units.seconds; + } + } + + + pout.print("[01]\n"); + pout.print(""); + + List planeIDs; + if ( vds.getProperty("plane-list")!=null ) { + planeIDs= (List)vds.getProperty("plane-list"); + } else { + planeIDs= new ArrayList(); + planeIDs.add(""); + } + + for ( int i=0; i"); + } + pout.print(""); + + NumberFormat xnf= new DecimalFormat("00000.000"); + NumberFormat ynf= new DecimalFormat("0.00E00"); + + double dx= xmax.subtract(xmin).doubleValue(offsetUnits); + for (int i=0; i=0 && xname represents + * @param name String name of the property requested + * @return the value of the property that name represents + * + */ + public Object getProperty(String name) { + return source.getProperty(name); + } + + public java.util.Map getProperties() { + return source.getProperties(); + } + + public int getXLength() { + return source.getXLength(); + } + + public Datum getXTagDatum(int i) { + return source.getXTagDatum(i); + } + + public double getXTagDouble(int i, Units units) { + return source.getXTagDouble(i,units); + } + + public int getXTagInt(int i, Units units) { + return source.getXTagInt(i,units); + } + + /** Returns the Units object representing the unit type of the x tags + * for this data set. + * @return the x units + * + */ + public Units getXUnits() { + return source.getXUnits(); + } + + /** Returns the Units object representing the unit type of the y tags + * or y values for this data set. + * @return the y units + * + */ + public Units getYUnits() { + return source.getYUnits(); + } + +} + diff --git a/dasCore/src/main/java/org/das2/dataset/WeightsTableDataSet.java b/dasCore/src/main/java/org/das2/dataset/WeightsTableDataSet.java new file mode 100644 index 000000000..faffe7e8e --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/WeightsTableDataSet.java @@ -0,0 +1,154 @@ +/* + * WeightsTableDataSet.java + * + * Created on November 29, 2005, 3:42 PM + * + * + */ + +package org.das2.dataset; + +import org.das2.datum.Units; +import java.util.HashMap; + +/** + * WeightsTableDataSet wraps a TableDataSet and returns 0.0 if the data point is + * not valid, and non-zero (generally one) otherwise. + * + * This is intended to provide a consistent way to get the weights without having + * to handle the case where the weights plane doesn't exist. + * + * @author Jeremy + */ +public class WeightsTableDataSet implements TableDataSet { + TableDataSet source; + Units sourceUnits; + double fill; + + public static TableDataSet create( TableDataSet source ) { + if ( source.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS)!=null ) { + return (TableDataSet)source.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); + } else { + return new WeightsTableDataSet( source ); + } + } + + private WeightsTableDataSet( TableDataSet source ) { + this.source= source; + this.sourceUnits= source.getZUnits(); + this.fill= source.getZUnits().getFillDouble(); + } + + public org.das2.datum.Datum getDatum(int i, int j) { + return Units.dimensionless.createDatum( getDouble( i, j, Units.dimensionless ) ); + } + + public double getDouble(int i, int j, org.das2.datum.Units units) { + return ( sourceUnits.isFill(source.getDouble( i, j, sourceUnits )) ) ? 0.0 : 1.0; + } + + public double[] getDoubleScan(int i, org.das2.datum.Units units) { + throw new IllegalStateException("not implemented"); + } + + public int getInt(int i, int j, org.das2.datum.Units units) { + return ( source.getDouble( i, j, sourceUnits ) != fill ) ? 1 : 0; + } + + public DataSet getPlanarView(String planeID) { + return this; + } + + public String[] getPlaneIds() { + return new String[] { "" }; + } + + public java.util.Map getProperties() { + return new HashMap(); + } + + public Object getProperty(String name) { + return null; + } + + public org.das2.datum.DatumVector getScan(int i) { + throw new IllegalStateException("not implemented"); + } + + public int getXLength() { + return source.getXLength(); + } + + public VectorDataSet getXSlice(int i) { + throw new IllegalStateException("not implemented"); + } + + public org.das2.datum.Datum getXTagDatum(int i) { + return source.getXTagDatum(i); + } + + public double getXTagDouble(int i, org.das2.datum.Units units) { + return source.getXTagDouble( i, units ); + } + + public int getXTagInt(int i, org.das2.datum.Units units) { + return source.getXTagInt(i, units); + } + + public org.das2.datum.Units getXUnits() { + return source.getXUnits(); + } + + public int getYLength(int table) { + return source.getYLength(table); + } + + public VectorDataSet getYSlice(int j, int table) { + throw new IllegalStateException("not implemented"); + } + + public org.das2.datum.Datum getYTagDatum(int table, int j) { + return source.getYTagDatum(table, j ); + } + + public double getYTagDouble(int table, int j, org.das2.datum.Units units) { + return source.getYTagDouble(table, j, units); + } + + public int getYTagInt(int table, int j, org.das2.datum.Units units) { + return source.getYTagInt(table, j, units); + } + + public org.das2.datum.DatumVector getYTags(int table) { + return source.getYTags(table); + } + + public org.das2.datum.Units getYUnits() { + return source.getYUnits(); + } + + public org.das2.datum.Units getZUnits() { + return Units.dimensionless; + } + + public int tableCount() { + return source.tableCount(); + } + + public int tableEnd(int table) { + return source.tableEnd(table); + } + + public int tableOfIndex(int i) { + return source.tableOfIndex(i); + } + + public int tableStart(int table) { + return source.tableStart(table); + } + + public Object getProperty(int table, String name) { + return source.getProperty(table,name); + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/WritableTableDataSet.java b/dasCore/src/main/java/org/das2/dataset/WritableTableDataSet.java new file mode 100644 index 000000000..03d4f18bc --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/WritableTableDataSet.java @@ -0,0 +1,232 @@ +/* + * WritableTableDataSet.java + * + * Created on September 2, 2004, 11:58 AM + */ + +package org.das2.dataset; + +import org.das2.datum.Datum; +import org.das2.datum.DatumVector; +import org.das2.datum.Units; +import java.util.*; +import java.util.Map; + +/** + * + * @author Jeremy + */ +public class WritableTableDataSet implements TableDataSet { + + double[] z; + double[] x; + double[] y; + final int nx; + final int ny; + Units xunits; + Units yunits; + Units zunits; + Map properties; + + public static WritableTableDataSet newSimple( int nx, Units xunits, int ny, Units yunits, Units zunits ) { + double [] z= new double[nx*ny]; + double [] x= new double[nx]; + double [] y= new double[ny]; + return new WritableTableDataSet( x, xunits, y, yunits, z, zunits, new HashMap() ); + } + + public static WritableTableDataSet newEmpty( TableDataSet tds ) { + if ( tds.tableCount() > 1 ) throw new IllegalArgumentException("only supported for simple tables"); + int nx= tds.tableEnd(0); + int ny= tds.getYLength(0); + WritableTableDataSet result= newSimple( nx, tds.getXUnits(), ny, tds.getYUnits(), tds.getZUnits() ); + for ( int i=0; i 1 ) throw new IllegalArgumentException("only supported for simple tables"); + int nx= tds.tableEnd(0); + int ny= tds.getYLength(0); + WritableTableDataSet result= newEmpty( tds ); + for ( int i=0; i +

    Provides classes and interfaces for combining Datums into structured DataSets, and operators + for working with DataSets. The DataSet interface is the base for all DataSets, which all + contain a set of monotonically-increasing xtags. DataSets also contain a set of arbitary + properties, which are String->Object mappings. These are used to store metadata such as + axis labels. DataSets can have auxiliary "planes" attached to them. This + mechanism was first introduced as a means to keep track of the weights + after averaging, but we also use them for peaks-and-averages plots and + orbits.

    +

    DataSetDescriptors are used to provide access to datasets that are parametric over a long interval + (generally time), + such as Voyager 1 power spectrum. Clients request data from a DataSetDescriptor for a given time + interval and resolution. The base class DataSetDescriptor is abstract and implements DataSet + caching. +

    +

    Rebinners are DataSet operators that rebin data to a precisely-controlled set of X and Y tags. Various + methods for rebinning data such as bin averaging and nearest neighbor sampling are provided.

    +

    Lastly, objects for caching datasets are provided.

    + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/dataset/parser/VectorDataSetParser.java b/dasCore/src/main/java/org/das2/dataset/parser/VectorDataSetParser.java new file mode 100644 index 000000000..3d8351690 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/parser/VectorDataSetParser.java @@ -0,0 +1,210 @@ +/* + * VectorDataSetParser.java + * + * Created on December 4, 2004, 12:54 PM + */ + +package org.das2.dataset.parser; + +import org.das2.datum.Units; +import java.io.*; +import java.util.regex.*; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.VectorDataSetBuilder; + +/** + * Class for reading ascii tables into a VectorDataSet. This parses a + * file by looking at each line to see if it matches one of + * two Patterns: one for properties and one for records. If a record matched, + * then the record is matched and fields pulled out, parsed and insered a + * VectorDataSetBuilder. If a property is matched, then the builder property + * is set. Two Patterns are provided NAME_COLON_VALUE_PATTERN and + * NAME_EQUAL_VALUE_PATTERN for convenience. The record pattern is currently + * the number of fields identified with whitespace in between. Note the X + * tags are just the record numbers. + * + * @author Jeremy + */ +public class VectorDataSetParser { + + Pattern propertyPattern; + String commentRegex; + Pattern recordPattern; + String[] fieldNames; + + final static String numberPart= "[\\d\\.eE\\+\\-]+"; + final static String decimalRegex= numberPart; + int skipLines; + int recordCountLimit; + int fieldCount; + + public final static Pattern NAME_COLON_VALUE_PATTERN= Pattern.compile("\\s*(.+?)\\s*\\:\\s*(.+)\\s*"); + public final static Pattern NAME_EQUAL_VALUE_PATTERN= Pattern.compile("\\s*(.+?)\\s*\\=\\s*(.+)\\s*"); + + private VectorDataSetParser( String[] fieldNames ) { + this.fieldCount= fieldNames.length; + this.fieldNames= fieldNames; + StringBuffer regexBuf= new StringBuffer(); + regexBuf.append("\\s*"); + for ( int i=0; i max ) { + imax=j; + max= recCount[j]; + } + } + + return imax; + } + + /** + * creates a parser with @param fieldCount fields, named "field0,...,fieldN" + */ + public static VectorDataSetParser newParser( int fieldCount ) { + String[] fieldNames= new String[ fieldCount ]; + for ( int i=0; i +Package containing parsers for creating data sets. + diff --git a/dasCore/src/main/java/org/das2/dataset/scratchPad.txt b/dasCore/src/main/java/org/das2/dataset/scratchPad.txt new file mode 100755 index 000000000..098d8268f --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/scratchPad.txt @@ -0,0 +1,70 @@ +jbf> TableDataSetConsumer should be renamed ZAxisDataSetComsumer or something like that + DataSet.hasZAxis()? + +jbf> Suggested Properties for DataSets: + spacecraft Spacecraft enumeration object + xRange DataRange object suggestion + yRange DataRange object suggestion + defaultRenderer Renderer Object + xOffset offsets to be applied to yscan + mouseModules list of mouseModules that should be installed + +jbf> suggested logic for per-plane properties for das2Stream client-side DataSets: + the data of the primary plane is merged with the stream and packet properties. + name clashes to be resolved by using plane overrides packet overrides stream. + +jbf> suggest introduce getPropertyNames() method and policy that rebinners + propogate the properties listed. Alternatively, this could be expressed as + an optional property. + +eew> Agreed proeprties for DataSets + xTagWidth Datum the extent of relevance of the x tags + yTagWidth Datum " " " of the y tags + introduce Units.percent, which is useful for specifying log width. + rebinner Rebinner Object + cacheTag Description of the start, end and resolution satisfied by this + data set. + plane-list java.util.List + +eew> Agreed plane identifiers + peaks - peaks + weights - rebinning weights (always dimensionless) + uncertainty - errors plane + + +Vector + get(i)->Datum + getDouble(i,Units)->double + getUnits()->Units + +Table + get(i,j)->Datum + getDouble(i,j,Units)->double + length(index)->int + getUnits()->Units + +HyperTable // arbitrary rank + rank()->int + get(i,j,k,l)->Datum + getDouble(i,j,k,l,Units)->double + length(index)->int + getUnits()->Units + slice(index,idim)->HyperTable + collapse(offset,length,idim)->HyperTable + +TableDataSet + tableCount()->int + getData(i)->Table + getDepend(index)->String[] + getTags(String,i)->Vector + getPlane(name)->DataSet + +VectorDataSet + getData->Vector + getDepend()->String[] + getTags(String)->Vector + getPlane(name)->DataSet + +HyperTableDataSet + slice or collapse to reduce to TableDataSet + diff --git a/dasCore/src/main/java/org/das2/dataset/test/BigVectorDataSet.java b/dasCore/src/main/java/org/das2/dataset/test/BigVectorDataSet.java new file mode 100644 index 000000000..f67ff0081 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/test/BigVectorDataSet.java @@ -0,0 +1,43 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.dataset.test; + +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.datum.Units; +import org.das2.util.monitor.ProgressMonitor; +import java.util.Random; + +/** + * + * @author jbf + */ +public class BigVectorDataSet { + + public static VectorDataSet getDataSet( int size, ProgressMonitor mon ) { + double dsize= (double)size; + + System.err.println("enter getDataSet"); + long t0 = System.currentTimeMillis(); + + Random random = new Random(0); + + VectorDataSetBuilder vbd = new VectorDataSetBuilder(Units.dimensionless, Units.dimensionless); + double y = 0; + for (int i = 0; i < size; i += 1) { + y += random.nextDouble() - 0.5; + if (i % 100 == 10) { + vbd.insertY( i / dsize, Units.dimensionless.getFillDouble()); + } else { + vbd.insertY( i / dsize, y); + } + } + vbd.setProperty(DataSet.PROPERTY_X_MONOTONIC, Boolean.TRUE); + VectorDataSet vds = vbd.toVectorDataSet(); + System.err.println("done getDataSet in " + (System.currentTimeMillis() - t0) + " ms"); + return vds; + } +} diff --git a/dasCore/src/main/java/org/das2/dataset/test/ContourMeDataSet.java b/dasCore/src/main/java/org/das2/dataset/test/ContourMeDataSet.java new file mode 100644 index 000000000..9b646f506 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/test/ContourMeDataSet.java @@ -0,0 +1,34 @@ +/* + * ContourMeDataSet.java + * + * Created on June 24, 2004, 9:23 PM + */ + +package org.das2.dataset.test; + +/** + * + * @author jbf + */ +public class ContourMeDataSet extends FunctionTableDataSet { + + /** Creates a new instance of ContourMeDataSet */ + public ContourMeDataSet() { + //xtags=101; + //ytags=101; + super(31,31); + xtags=31; + ytags=31; + fillCache(); + } + + public double getDoubleImpl(int i, int j, org.das2.datum.Units units) { + i= i - this.xtags / 2; + j= j - this.ytags / 2; + double d= (i*i+j*j); + //double a= Math.atan2(j,i); + //return d + xtags/40*Math.cos( a * 20 ); + return d; + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/test/DistTableDataSet.java b/dasCore/src/main/java/org/das2/dataset/test/DistTableDataSet.java new file mode 100644 index 000000000..5a509e519 --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/test/DistTableDataSet.java @@ -0,0 +1,54 @@ +/* + * DistTableDataSet.java + * + * Created on May 21, 2004, 2:58 PM + */ + +package org.das2.dataset.test; + +/** + * + * @author Jeremy + */ +public class DistTableDataSet extends FunctionTableDataSet { +/* on_error,2 ;Return to caller if an error occurs +x=findgen(n) ;Make a row +x = (x < (n-x)) ^ 2 ;column squares +if n_elements(m) le 0 then m = n + +a = FLTARR(n,m,/NOZERO) ;Make array + +for i=0L, m/2 do begin ;Row loop + y = sqrt(x + i^2) ;Euclidian distance + a[0,i] = y ;Insert the row + if i ne 0 then a[0, m-i] = y ;Symmetrical + endfor +return,a +end */ + + /** Creates a new instance of DistTableDataSet */ + public DistTableDataSet( int size ) { + super( size,size ); + fillCache(); + } + + public double getDoubleImpl(int i, int j, org.das2.datum.Units units) { + int m= xtags/2; + int n= ytags/2; + if ( i + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +package org.das2.dataset.test; + +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import java.util.*; +import org.das2.dataset.DataSet; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.XSliceDataSet; +import org.das2.dataset.YSliceDataSet; + +/** + * + * @author jbf + */ +public abstract class FunctionTableDataSet implements TableDataSet { + + Units zUnits= Units.dimensionless; + Units yUnits= Units.dimensionless; + Units xUnits= Units.dimensionless; + protected int ytags; + protected int xtags; + + double[] data; + + HashMap properties; + + public abstract double getDoubleImpl(int i, int j, Units units); + + public FunctionTableDataSet( int nx, int ny ) { + xtags= nx; + ytags= ny; + data= new double[nx*ny]; + for ( int i=0; i0 ) throw new IllegalArgumentException("table doesn't exist: "+table); + return yUnits.convertDoubleTo(units, (double)j); + } + + public int getYTagInt(int table, int j, Units units) { + return (int)getYTagDouble(table, j, units); + } + + public Units getYUnits() { + return yUnits; + } + + public Units getZUnits() { + return zUnits; + } + + public int tableCount() { + return 1; + } + + public int tableEnd(int table) { + return xtags; + } + + public int tableOfIndex(int i) { + return 0; + } + + public int tableStart(int table) { + return 0; + } + + public DatumVector getYTags(int table) { + double[] tags = new double[getYLength(table)]; + for (int j = 0; j < tags.length; j++) { + tags[j] = getYTagDouble(table, j, yUnits); + } + return DatumVector.newDatumVector(tags, yUnits); + } + + public String toString() { + return TableUtil.toString(this); + } +} diff --git a/dasCore/src/main/java/org/das2/dataset/test/MendelbrotDataLoader.java b/dasCore/src/main/java/org/das2/dataset/test/MendelbrotDataLoader.java new file mode 100644 index 000000000..e0c6aba3e --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/test/MendelbrotDataLoader.java @@ -0,0 +1,208 @@ +/* + * MendelbrotDataSetDescriptor.java + * + * Created on May 11, 2004, 10:26 AM + */ + +package org.das2.dataset.test; + +import org.das2.DasException; +import org.das2.dataset.DataSet; +import org.das2.datum.Units; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; +import org.das2.graph.DataLoader; +import org.das2.graph.Renderer; +import org.das2.system.DasLogger; +import org.das2.system.RequestProcessor; +import org.das2.util.monitor.ProgressMonitor; +import java.util.logging.Logger; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.WritableTableDataSet; + + +/** + * + * @author Jeremy + */ +public class MendelbrotDataLoader extends DataLoader { + /* we store the yaxis to kludge in the yresolution, range */ + + int limit=200; + int overSampleFactor= 1; + + Request currentRequest; + Request completedRequest; + + Logger logger= DasLogger.getLogger(DasLogger.GRAPHICS_LOG, "MendelBrotDataLoader" ); + + /** Creates a new instance of MendelbrotDataSetDescriptor */ + public MendelbrotDataLoader( Renderer r ) { + super(r); + } + + private final float punktfarbe(double xwert, double ywert) {// color value from 0.0 to 1.0 by iterations + double r = 0.0, i = 0.0, m = 0.0; + int j = 0; + final int MAX=limit; + + while ((j < MAX) && (m < 4.0)) { + j++; + m = r * r - i * i; + i = 2.0 * r * i + ywert; + r = m + xwert; + } + if ( j==MAX ) j=0; + return (float)j; + } + + public void update( ) { + if ( isActive() ) { + DasPlot p= getRenderer().getParent(); + + if ( p==null ) return; + + DasAxis xAxis= p.getXAxis(); + DasAxis yAxis= p.getYAxis(); + + if ( xAxis.valueIsAdjusting() || yAxis.valueIsAdjusting() ) return; + + final RebinDescriptor xRebinDescriptor = new RebinDescriptor( + xAxis.getDataMinimum(), xAxis.getDataMaximum(), + xAxis.getColumn().getWidth(), + xAxis.isLog()); + + final RebinDescriptor yRebinDescriptor = new RebinDescriptor( + yAxis.getDataMinimum(), yAxis.getDataMaximum(), + yAxis.getRow().getHeight(), + yAxis.isLog()); + + if ( currentRequest!=null ) { + if ( ! ( xAxis.getMemento().equals( currentRequest.xmem ) || yAxis.getMemento().equals( currentRequest.ymem ) ) ) { + logger.fine( "cancel old request" ); + currentRequest.monitor.cancel(); + } else { + logger.fine( "ignore repeat request" ); + return; // ignore the repeated request + } + } + + final String taskDescription= "mendelbrot x:"+xAxis.getMemento()+" y:"+ yAxis.getMemento(); + + if ( completedRequest!=null ) { + if ( ( xAxis.getMemento().equals( completedRequest.xmem ) && yAxis.getMemento().equals( completedRequest.ymem ) ) ) { + logger.fine( "ignore satisfied request "+taskDescription ); + return; + } + } + + currentRequest= new DataLoader.Request( getMonitor(taskDescription), xAxis.getMemento(), yAxis.getMemento() ); + + Runnable run= new Runnable() { + public void run( ) { + try { + logger.fine( "calculate dataset for "+taskDescription ); + DataSet result= getDataSet( xRebinDescriptor, yRebinDescriptor, getMonitor(taskDescription) , taskDescription ); + System.err.println( result.getProperty("TaskDescription") ); + getRenderer().setDataSet( result ); + completedRequest= currentRequest; + logger.fine( "completed "+taskDescription ); + currentRequest= null; + } catch ( DasException e ) { + getRenderer().setException( e ); + } + + } + }; + RequestProcessor.invokeAfter( run, this.getRenderer() ); + } + } + + private DataSet getDataSet( RebinDescriptor ddx, RebinDescriptor ddy, ProgressMonitor monitor, String desc) throws DasException { + + double xstart, xend, xresolution; + xstart= ddx.binCenter(0, Units.dimensionless ); + xend= ddx.binCenter( ddx.numberOfBins()-1, Units.dimensionless ); + xresolution= ddx.binWidth() / overSampleFactor; + + double ystart, yend, yresolution; + ystart= ddy.binCenter(0, Units.dimensionless ); + yend= ddy.binCenter( ddy.numberOfBins()-1, Units.dimensionless ); + yresolution= ddy.binWidth() / overSampleFactor; + + int ny= (int)(1.5+((yend-ystart)/yresolution)); + int nx= (int)(1.5+((xend-xstart)/xresolution)); + + WritableTableDataSet result= WritableTableDataSet.newSimple( nx, Units.dimensionless, ny, Units.dimensionless, Units.dimensionless ); + + double[][] z= new double[nx][ny]; + + monitor.setTaskSize(ny); + monitor.started(); + for ( int iy=0; iy + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +package org.das2.dataset.test; + +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.Units; +import org.das2.datum.Datum; + +/** + * + * @author jbf + */ +public class OrbitVectorDataSet implements VectorDataSet { + + static double[][] data; + private double[][] idata; // pixel space + + private static OrbitVectorDataSet xview= new OrbitVectorDataSet(0); + private static OrbitVectorDataSet yview= new OrbitVectorDataSet(1); + + Units xunits; + Units yunits; + Units tunits; + int view; + + private OrbitVectorDataSet(int view) { + data= new double[3][40]; + for ( int i=0; iymin ) builder.insertY(x, eval(x)); + } + if ( eval(x1)>ymin ) builder.insertY(x1, eval(x1)); + return builder.toVectorDataSet(); + } + + private double eval(double x) { + double y = 0; + for (int ic = c.length - 1; ic >= 0; ic--) { + y = y = c[ic] + x * y; + } + return y; + } + + public Units getXUnits() { + return xUnits; + } + + @SuppressWarnings("deprecation") + public static void main(String[] args) { + + double[] c = {90.0, 3.0, -1.0}; + PolynomialDataSetDescriptor dsd = new PolynomialDataSetDescriptor(c, Units.dimensionless, Units.dimensionless, Datum.create(1.0)); + + DasAxis xAxis = new DasAxis(Datum.create(-10.0), Datum.create(10.0), DasAxis.BOTTOM); + DasAxis yAxis = new DasAxis(Datum.create(0.0), Datum.create(100.0), DasAxis.LEFT); + DasPlot plot = new DasPlot(xAxis, yAxis); + plot.addRenderer(new SymbolLineRenderer(dsd)); + DasCanvas canvas = new DasCanvas(400, 400); + canvas.add(plot, new DasRow(canvas, 0.1, 0.9), new DasColumn(canvas, 0.1, 0.9)); + + JFrame frame = new JFrame("Polynomial"); + frame.setContentPane(canvas); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.pack(); + frame.setVisible(true); + } + +} diff --git a/dasCore/src/main/java/org/das2/dataset/test/RipplesDataSet.java b/dasCore/src/main/java/org/das2/dataset/test/RipplesDataSet.java new file mode 100755 index 000000000..6ec279b5a --- /dev/null +++ b/dasCore/src/main/java/org/das2/dataset/test/RipplesDataSet.java @@ -0,0 +1,81 @@ +/* File: RipplesDataSet.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on November 18, 2003, 12:52 PM by __FULLNAME__ <__EMAIL__> + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +package org.das2.dataset.test; + +import org.das2.dataset.TableDataSet; +import org.das2.datum.Units; + +/** + * + * @author jbf + */ +public final class RipplesDataSet extends FunctionTableDataSet implements TableDataSet { + + double x1,y1,p1; + double x2,y2,p2; + + public RipplesDataSet( ) { + this( 2, 3, 1, 13, 15, 2, 30, 30 ); + } + + /** + * creates a dataset that is the sum of two rippley functions that look appealling + * and are useful for testing. + * @param x1 the x coordinate of the first ripple source + * @param y1 the y coordinate of the first ripple source + * @param p1 the radius of the first ripple + * @param x2 the x coordinate of the first ripple source + * @param y2 the y coordinate of the first ripple source + * @param p2 the radius of the first ripple + * @param xlength the number of columns in the dataset. + * @param ylength the number of rows in the dataset. + */ + public RipplesDataSet( double x1, double y1, double p1, double x2, double y2, double p2, int xlength, int ylength ) { + super(xlength,ylength); + this.x1= x1; + this.y1= y1; + this.p1= p1; + this.x2= x2; + this.y2= y2; + this.p2= p2; + fillCache(); + } + + public double getDoubleImpl(int i, int j, Units units) { + double x= getXTagDouble(i,xUnits); + double y= getYTagDouble(0,j,yUnits); + if (12. + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +package org.das2.dataset.test; + +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.DasException; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.DefaultTableDataSet; +import org.das2.util.monitor.ProgressMonitor; + +/** + * + * @author jbf + */ +public class RipplesDataSetDescriptor extends DataSetDescriptor { + + double x1,y1,p1; + double x2,y2,p2; + int nx,ny; + + public RipplesDataSetDescriptor( ) { + this( 14, 17, 10, 20, 60, 15 , 100, 100 ); + } + + /** + * creates a DataSetDescriptor (note the range and resolution is ignored--an unneccessary use + * since Render now has setDataSet method) that is the sum of two ripples. + * @param x1 the x coordinate of the first ripple source + * @param y1 the y coordinate of the first ripple source + * @param p1 the radius of the first ripple + * @param x2 the x coordinate of the first ripple source + * @param y2 the y coordinate of the first ripple source + * @param p2 the radius of the first ripple + * @param nx the number of columns in the dataset. + * @param ny the number of rows in the dataset. + */ + public RipplesDataSetDescriptor( double x1, double y1, double p1, double x2, double y2, double p2 , int nx, int ny ) { + this.x1= x1; + this.y1= y1; + this.p1= p1; + this.x2= x2; + this.y2= y2; + this.p2= p2; + this.nx= nx; + this.ny= ny; + } + + public Units getXUnits() { + return Units.dimensionless; + } + + public org.das2.datum.Units getYUnits() { + return Units.dimensionless; + } + + public org.das2.datum.Units getZUnits() { + return Units.dimensionless; + } + + public DataSet getDataSetImpl(Datum start, Datum end, Datum resolution, ProgressMonitor monitor) throws DasException { + + double[] x= new double[nx]; + double[] y= new double[ny]; + double[][] z= new double[nx][ny]; + + monitor.setTaskSize(x.length); + monitor.started(); + + for (int i=0; i + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +package org.das2.dataset.test; + +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.DasException; +import org.das2.util.monitor.ProgressMonitor; +import java.util.*; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.DefaultVectorDataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.DataSet; + +/** + * + * @author jbf + */ +public class SineWaveDataSetDescriptor extends DataSetDescriptor { + + Datum amplitude; + Datum period; + Datum phase; + + /** + * Creates a new instance of SineWaveDataSetDescriptor with arbitrary phase. + * @param amplitude the amplitude of the signal. + * @param period is in the offset units of phase. + * + */ + public SineWaveDataSetDescriptor( Datum amplitude, Datum period ) { + this( amplitude, period, null ); + } + + /** + * Creates a new instance of SineWaveDataSetDescriptor + * @param amplitude the amplitude of the signal. + * @param period is in the offset units of phase. + * @param phase datum in the same units as period. null indicates that the phase is arbitrary and will change based on the data request. + * + */ + public SineWaveDataSetDescriptor( Datum amplitude, Datum period, Datum phase ) { + super(null); + if ( 0. == period.doubleValue(period.getUnits() ) ) { + throw new IllegalArgumentException( "period is zero" ); + } + + this.amplitude= amplitude; + this.period= period; + this.phase= phase; + } + + public DataSet getDataSetImpl(Datum start, Datum end, Datum resolution, ProgressMonitor monitor) throws DasException { + if ( resolution==null ) resolution= end.subtract( start ).divide(1000); + int nstep= 2 + (int)(end.subtract(start).doubleValue(resolution.getUnits()) / resolution.doubleValue(resolution.getUnits())); + int stepSize= 1; /* not sure what this is useful for jbf */ + nstep= nstep / stepSize; + + if ( phase==null ) phase= start; + + double[] yvalues= new double[nstep]; + double[] xtags= new double[nstep]; + Units xunits= phase.getUnits(); + Units offsetUnits= period.getUnits(); + Units yunits= amplitude.getUnits(); + + for ( int i=0; i +Classes for creating test data sets. + diff --git a/dasCore/src/main/java/org/das2/datum/Basis.java b/dasCore/src/main/java/org/das2/datum/Basis.java new file mode 100644 index 000000000..602714c72 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/Basis.java @@ -0,0 +1,87 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.datum; + +import java.util.IdentityHashMap; + +/** + * Model a basis vector that defines a dimension. For example, Units.us2000 has the + * getOffsetUnits() -> Units.microseconds and getBasis() -> "since 2000-01-01T00:00". + * + * @author jbf + */ +public class Basis { + + public static final Basis fahrenheit= new Basis( "fahrenheit", "fahrenheit", Basis.physicalZero, 255.370, Units.celciusDegrees ); + public static final Basis kelvin= new Basis( "kelvin", "kelvin", Basis.physicalZero, 0, Units.celciusDegrees ); + public static final Basis centigrade= new Basis( "centigrade", "centigrade", Basis.physicalZero, 273.15, Units.celciusDegrees ); + + public static final Basis since2000= new Basis( "since2000", "since 2000-01-01T00:00Z", null, 0, null ); + public static final Basis since2010= new Basis( "since2010", "since 2010-01-01T00:00Z", since2000, 315619200., Units.seconds ); + public static final Basis since1980= new Basis( "since1980", "since 1980-01-01T00:00Z", since2000, -631152000., Units.seconds ); + public static final Basis since1970= new Basis( "since1970", "since 1970-01-01T00:00Z", since2000, -938044800., Units.seconds ); + public static final Basis since1958= new Basis( "since1958", "since 1958-01-01T00:00Z", since2000, -1325376000., Units.seconds ); + public static final Basis modifiedJulian= new Basis( "modifiedJulian", "since 1858-11-17T00:00Z", since2000, 4453401600., Units.seconds ); + public static final Basis since0000= new Basis( "since0000", "since 01-Jan-0000T00:00Z", since2000, 63113904000., Units.seconds ); + + /** + * special basis representing physical zero for all combinations of physical units. + */ + public static final Basis physicalZero= new Basis( "", "physical zero", null, 0, null ); + + private IdentityHashMap bases; + + private String id; + private String description; + private Basis parent; + + public Basis( String id, String description, Basis parent, double d, Units offsetUnits ) { + this.id= id; + this.description= description; + this.parent= parent; + if ( parent!=null ) { + parent.bases.put( this, offsetUnits.createDatum(d) ); + } else { + bases= new IdentityHashMap(); + } + } + + /** + * return the location of this basis in given basis, in the given units. + * @param basis + * @param u + * @return + */ + double getOffset( Basis basis, Units u ) { + if ( parent==null ) { + return bases.get(basis).doubleValue(u); + } else { + double d0= parent.bases.get(this).doubleValue(u); + double d1= parent.bases.get(basis).doubleValue(u); + return d0 - d1; + } + } + + /** + * specify offset to another basis. Register to + * @param toBasis + * @param d + * @param u + */ + public void registerConverter( Basis toBasis, double d, Units u ) { + bases.put( toBasis, u.createDatum(d) ); + } + + + public static void main( String[] args ) { + Basis since2010= new Basis( "since2010", "since 2010-01-01T00:00Z", Basis.since2000, 315619200000000.0, Units.microseconds ); + Basis since2011= new Basis( "since2011", "since 2011-01-01T00:00Z", Basis.since2000, 347155200000000.0, Units.microseconds ); + System.err.println( since2011.getOffset(since2010, Units.days )); + + System.err.println( centigrade.getOffset( fahrenheit, Units.fahrenheitDegrees ) ); + + } +} diff --git a/dasCoreDatum/src/org/das2/datum/BestFormatter.txt b/dasCore/src/main/java/org/das2/datum/BestFormatter.txt similarity index 100% rename from dasCoreDatum/src/org/das2/datum/BestFormatter.txt rename to dasCore/src/main/java/org/das2/datum/BestFormatter.txt diff --git a/dasCore/src/main/java/org/das2/datum/CalendarTime.java b/dasCore/src/main/java/org/das2/datum/CalendarTime.java new file mode 100644 index 000000000..d720f843e --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/CalendarTime.java @@ -0,0 +1,1016 @@ +package org.das2.datum; + +import java.text.ParseException; +import java.util.Calendar; +import java.util.StringTokenizer; +import java.util.TimeZone; + +/** Represents a point in time, over thousands of years to nano-second resolution. + * The Gegorian calendar is extended in both directions, which makes little sense + * for dates prior to it's adoption. + * + * @author ljg, eew, jbf, cwp + */ +public class CalendarTime implements Comparable{ + + /** The time point's year number. + * Note: that year 1 BC is represented year 0 in this field. + */ + protected int m_nYear; + + /** The time point's month of year, normalized range is 1 to 12 */ + protected int m_nMonth; + + /** The time point's day of month, normalized range is 1 up to 31 + * depending on the month rValue. + */ + protected int m_nDom; + + // Cash the day of year calculation after a normalize. + protected int m_nDoy; + + /** The time point's hour of day, normalized range is 0 to 23 */ + protected int m_nHour; + + /** The time point's minute of hour, normalized range is 0 to 59 */ + protected int m_nMinute; + + /** The time point's second of minute, normalized range is 0 to 59. + * Note that leap seconds are not handled by this class, though it + * wouldn't be hard to do so. + */ + protected int m_nSecond; + + /** The time point's nanosecond of second, normalized range is 0 to 999,999,999 */ + protected long m_nNanoSecond; + + + //////////////////////////////////////////////////////////////////////////////////// + /** Empty constructor */ + + public CalendarTime(){ + m_nYear = 1; + m_nMonth = 1; + m_nDom = 1; + m_nDoy = 1; + m_nHour = 0; + m_nMinute = 0; + m_nSecond = 0; + m_nNanoSecond = 0; + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Static method to create a calender time set to now */ + static public CalendarTime now(){ + + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + + + CalendarTime ct = new CalendarTime(); + ct.m_nYear = cal.get(Calendar.YEAR); + ct.m_nMonth = cal.get(Calendar.MONTH) + 1; + ct.m_nDom = cal.get(Calendar.DAY_OF_MONTH); + ct.m_nDoy = cal.get(Calendar.DAY_OF_YEAR); + ct.m_nHour = cal.get(Calendar.HOUR_OF_DAY); + ct.m_nMinute = cal.get(Calendar.MINUTE); + ct.m_nSecond = cal.get(Calendar.SECOND); + ct.m_nNanoSecond = cal.get(Calendar.MILLISECOND) * 1000000L; + + return ct; + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Tuple constructor + * @param lFields an array of integer fields. Values are assumed to be in largest + * time span to smallest time span. The array may be null. Up to 7 items will + * be used from the array in the order: year, month, day, hour, min, sec, + * nanosecond. + */ + public CalendarTime(int... lFields){ + m_nYear = 1; + m_nMonth = 1; + m_nDom = 1; + m_nDoy = 1; + m_nHour = 0; + m_nMinute = 0; + m_nSecond = 0; + m_nNanoSecond = 0; + + if(lFields == null) return; + if(lFields.length > 0) m_nYear = lFields[0]; + if(lFields.length > 1) m_nMonth = lFields[1]; + if(lFields.length > 2) m_nDom = lFields[2]; + if(lFields.length > 3) m_nHour = lFields[3]; + if(lFields.length > 4) m_nMinute = lFields[4]; + if(lFields.length > 5) m_nSecond = lFields[5]; + if(lFields.length > 6) m_nNanoSecond = lFields[6]; + + normalize(); + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Copy constructor */ + public CalendarTime(CalendarTime other){ + m_nYear = other.m_nYear; + m_nMonth = other.m_nMonth; + m_nDom = other.m_nDom; + m_nDoy = other.m_nDoy; + m_nHour = other.m_nHour; + m_nMinute = other.m_nMinute; + m_nSecond = other.m_nSecond; + m_nNanoSecond = other.m_nNanoSecond; + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Constructing a calendar time from a time string. + * Taken from Larry Granroth's Das1 lib. + */ + public CalendarTime(String s) throws ParseException{ + + final int DATE = 0; + final int YEAR = 1; + final int MONTH = 2; + final int DAY = 3; + final int HOUR = 4; + final int MINUTE = 5; + final int SECOND = 6; + + final String DELIMITERS = " \t/-:,_;\r\n"; + final String PDSDELIMITERS = " \t/-T:,_;\r\n"; + + final String[] months = { + "january", "febuary", "march", "april", "may", "june", + "july", "august", "september", "october", "november", "december" + }; + final String[] mons = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + // Starting values for this object, does not default to current year. + m_nYear = 0; + m_nMonth = 0; + m_nDom = 0; + m_nDoy = 0; + m_nHour = 0; + m_nMinute = 0; + m_nSecond = 0; + m_nNanoSecond = 0; + + String[] lToks = new String[10]; + + int[] format = new int[7]; + + int ptr; + + int hold; + + int tokIndex; + + /* handle ISO8601 time format */ + String delimiters = DELIMITERS; + int iC; + if((iC = s.indexOf((int) 'Z')) != -1){ + s = s.substring(0, iC); + } + int end_of_date = s.indexOf((int) 'T'); + if(end_of_date != -1){ + iC = end_of_date - 1; + if(Character.isDigit(s.charAt(iC))){ + delimiters = PDSDELIMITERS; + } + else{ + end_of_date = -1; + } + } + + /* if not PDS then count out 3 non-space delimiters */ + int nTokens = 0; + if(end_of_date == -1){ + nTokens = 0; + int nLen = s.length(); + for(int i = 0; i < nLen; i++){ + if((iC = (delimiters.substring(2)).indexOf(s.charAt(i))) != -1){ + nTokens++; + } + if(nTokens == 3){ + end_of_date = i; + break; + } + } + } + + /* tokenize the time string */ + StringTokenizer st = new StringTokenizer(s, delimiters); + + if(!st.hasMoreTokens()) + throw new java.text.ParseException("No tokens in '" + s + "'", 0); + + for(nTokens = 0; nTokens < 10 && st.hasMoreTokens(); nTokens++) + lToks[nTokens] = st.nextToken(); + + boolean[] lWant = new boolean[]{false, false, false, false, false, false, false}; + lWant[DATE] = lWant[YEAR] = lWant[MONTH] = lWant[DAY] = true; + hold = 0; + + tokIndex = -1; + + // The big token parser loop, each iteration handles one token from the input string + for(int i = 0; i < nTokens; i++){ + tokIndex = s.indexOf(lToks[i], tokIndex + 1); + if((end_of_date != -1) && lWant[DATE] && tokIndex > end_of_date){ + lWant[DATE] = false; + lWant[HOUR] = lWant[MINUTE] = lWant[SECOND] = true; + } + + int nTokLen = lToks[i].length(); + double rValue; + + // skip 3-digit day-of-year values in parenthesis + if ((nTokLen == 5) && lToks[i].startsWith("(") && lToks[i].endsWith(")")) { + try{ + rValue = Double.parseDouble(lToks[i].substring(1, nTokLen-2)); + } + catch(NumberFormatException e){ + throw new ParseException("Error in token '"+lToks[i]+"'", 1); + } + if ((rValue > 0) && (rValue < 367)) continue; + } + + try{ + rValue = Double.parseDouble(lToks[i]); + } + catch(NumberFormatException e){ + if(nTokLen < 3 || !lWant[DATE]){ + throw new ParseException("Error at token '"+lToks[i]+"' in '"+s+"'", 0); + } + for(int j = 0; j < 12; j++){ + if(lToks[i].equalsIgnoreCase(months[j]) || + lToks[i].equalsIgnoreCase(mons[j])) { + m_nMonth = j + 1; + lWant[MONTH] = false; + if(hold > 0){ + if(m_nDom > 0) + throw new ParseException("Ambiguous dates in token '" + lToks[i] + + "' in '" + s + "'", 0); + m_nDom = hold; + hold = 0; + lWant[DAY] = false; + } + break; + } + } + if(lWant[MONTH]) + throw new ParseException("Error at token '"+lToks[i]+"' in '"+s+"'", 0); + continue; + } + + if(Math.IEEEremainder(rValue, 1.0) != 0.0){ + if(lWant[SECOND]){ + //Round normally to nearest nanosecond + long nTmp = Math.round( rValue * 1.0e+9); + m_nSecond = (int) ((long)nTmp / 1000000000L); + m_nNanoSecond = (long)nTmp % 1000000000L; + break; + } + else{ + throw new ParseException("Error at token '"+lToks[i]+"' in '"+s+"'", 0); + } + } + + int number = (int) rValue; + if(number < 0){ + throw new ParseException("Error at token '"+lToks[i]+"' in '"+s+"'", 0); + } + + if(lWant[DATE]){ + + if(number == 0){ + throw new ParseException("m,d, or y can't be 0 in '" + s + "'", 0); + } + + if(number >= 10000000 && lWant[YEAR]){ // %Y%m%d + m_nYear = number / 10000; + lWant[YEAR] = false; + m_nMonth = number / 100 % 100; + lWant[MONTH] = false; + m_nDom = number % 100; + m_nDoy = 0; + lWant[DAY] = false; + } + else if(number >= 1000000 && lWant[YEAR]){ //%Y%j + m_nYear = number / 1000; + lWant[YEAR] = false; + m_nDoy = number % 1000; + m_nMonth = 0; + lWant[MONTH] = false; + lWant[DAY] = false; + + } + else if(number > 31){ + + if(lWant[YEAR]){ + m_nYear = number; + if(m_nYear < 1000){ + m_nYear += 1900; + } + lWant[YEAR] = false; + } + else if(lWant[MONTH]){ + lWant[MONTH] = false; + m_nMonth = 0; + m_nDoy = number; + lWant[DAY] = false; + } + else{ + throw new ParseException("Error at token '"+lToks[i]+"' in '"+s+"'", 0); + } + + } + else if(number > 12){ + + if(lWant[DAY]){ + if(hold > 0){ + m_nMonth = hold; + lWant[MONTH] = false; + } + if(nTokLen == 3){ + if(m_nMonth > 0){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nDoy = number; + m_nDom = 0; + lWant[MONTH] = false; + } + else{ + m_nDom = number; + } + lWant[DAY] = false; + } + else{ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + + } + else if(!lWant[MONTH]){ + + if(m_nMonth > 0){ + m_nDom = number; + m_nDoy = 0; + } + else{ + m_nDoy = number; + m_nDom = 0; + } + lWant[DAY] = false; + + } + else if(!lWant[DAY]){ + + if(m_nDoy > 0){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nMonth = number; + lWant[MONTH] = false; + + } + else if(!lWant[YEAR]){ + + if(nTokLen == 3){ + if(m_nMonth > 0){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nDoy = number; + m_nDom = 0; + lWant[DAY] = false; + } + else{ + if(m_nDoy > 0){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nMonth = number; + if(hold > 0){ + m_nDom = hold; + lWant[DAY] = false; + } + } + lWant[MONTH] = false; + + } + else if(hold > 0){ + + m_nMonth = hold; + hold = 0; + lWant[MONTH] = false; + m_nDom = number; + lWant[DAY] = false; + + } + else{ + hold = number; + } + + if(!(lWant[YEAR] || lWant[MONTH] || lWant[DAY])){ + lWant[DATE] = false; + lWant[HOUR] = lWant[MINUTE] = lWant[SECOND] = true; + } + + } + else if(lWant[HOUR]){ + + if(nTokLen == 4){ + hold = number / 100; + // TODO: handle times like Jan-1-2001T24:00 --> Jan-2-2001T00:00, for ease of modifying times + if(hold > 23){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nHour = hold; + hold = number % 100; + if(hold > 59){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nMinute = hold; + lWant[MINUTE] = false; + } + else{ + if(number > 23){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nHour = number; + } + lWant[HOUR] = false; + + } + else if(lWant[MINUTE]){ + // TODO: handle times like 0:90 --> 1:30, for ease of modifying times + if(number > 59){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nMinute = number; + lWant[MINUTE] = false; + + } + else if(lWant[SECOND]){ + + if(number > 61){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nSecond = number; + lWant[SECOND] = false; + + } + else{ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + + } + // End of token parsing loop + + if(m_nMonth > 12){ + throw new ParseException("Month is greater than 12 in '" + s + "'", 0); + } + if(m_nMonth > 0 && m_nDom <= 0){ + m_nDom = 1; + } + + int iLeap = ((m_nYear % 4) != 0 ? 0 : ((m_nYear % 100) > 0 ? 1 : ((m_nYear % 400) > 0 ? 0 : 1))); + + if((m_nMonth > 0) && (m_nDom > 0) && (m_nDoy == 0)){ + if(m_nDom > TimeUtil.daysInMonth[iLeap][m_nMonth]){ + throw new java.text.ParseException("day of month too high in '" + s + "'", 0); + } + m_nDoy = TimeUtil.dayOffset[iLeap][m_nMonth] + m_nDom; + } + else if((m_nDoy > 0) && (m_nMonth == 0) && (m_nDom == 0)){ + if(m_nDoy > (365 + iLeap)){ + throw new java.text.ParseException("day of year too high in '" + s + "'", 0); + } + int i = 2; + while(i < 14 && m_nDoy > TimeUtil.dayOffset[iLeap][i]) i++; + i--; + m_nMonth = i; + m_nDom = m_nDoy - TimeUtil.dayOffset[iLeap][i]; + } + else{ + if(m_nMonth == 0){ + m_nMonth = 1; + } + m_nDom = 1; + } + + // Okay, hit nomalize, looking at the code above, we know that the seconds + // field can be way over value, others may be too. + normalize(); + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Construct from a datum. + * splits the time location datum into y,m,d,etc components. Note that + * seconds is a double, and micros will be 0. + * + * @param datum with time location units + */ + public CalendarTime(Datum datum){ + double microseconds = TimeUtil.getMicroSecondsSinceMidnight(datum); + Datum sansMicros = datum.subtract(microseconds, Units.microseconds); + + int jd = TimeUtil.getJulianDay(sansMicros); + if(jd < 0) + throw new IllegalArgumentException("julian day is negative."); + + int[] lDate = TimeUtil.julianToGregorian(jd); + m_nYear = lDate[0]; + m_nMonth = lDate[1]; + m_nDom = lDate[2]; + m_nNanoSecond = Math.round( microseconds * 1000); + normalize(); + } + + //////////////////////////////////////////////////////////////////////////////////// + @Override + public String toString(){ + return String.format("%04d-%02d-%02dT%02d:%02d%02d.%09d", m_nYear, m_nMonth, + m_nDom, m_nHour, m_nMinute, m_nSecond, m_nNanoSecond); + } + + public boolean isLeapYear(){ return TimeUtil.isLeapYear(m_nYear); } + + @Override + public int compareTo(CalendarTime o){ + if(m_nYear != o.m_nYear) return m_nYear - o.m_nYear; + if(m_nMonth != o.m_nMonth) return m_nMonth - o.m_nMonth; + if(m_nDom != o.m_nDom) return m_nDom - o.m_nDom; + if(m_nHour != o.m_nHour) return m_nHour - o.m_nHour; + if(m_nMinute != o.m_nMinute) return m_nMinute - o.m_nMinute; + if(m_nSecond != o.m_nSecond) return m_nSecond - o.m_nSecond; + if(m_nNanoSecond < o.m_nNanoSecond) return -1; + if(m_nNanoSecond > o.m_nNanoSecond) return 1; + + return 0; + } + + @Override + public boolean equals(Object o){ + if(! (o instanceof CalendarTime) ) return false; + + CalendarTime ctO = (CalendarTime)o; + + if(m_nYear != ctO.m_nYear) return false; + if(m_nMonth != ctO.m_nMonth) return false; + if(m_nDom != ctO.m_nDom) return false; + if(m_nHour != ctO.m_nHour) return false; + if(m_nMinute != ctO.m_nMinute) return false; + if(m_nSecond != ctO.m_nSecond) return false; + if(m_nNanoSecond != ctO.m_nNanoSecond) return false; + + return true; + } + + @Override + public int hashCode(){ + int hash = 7; + hash = 71 * hash + this.m_nYear; + hash = 71 * hash + this.m_nMonth; + hash = 71 * hash + this.m_nDom; + hash = 71 * hash + this.m_nHour; + hash = 71 * hash + this.m_nMinute; + hash = 71 * hash + this.m_nSecond; + hash = 71 * hash + (int) (this.m_nNanoSecond ^ (this.m_nNanoSecond >>> 32)); + return hash; + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Resolution flags to use when requesting time point as a string */ + public static enum Resolution { + YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISEC, MICROSEC, NANOSEC + }; + + //////////////////////////////////////////////////////////////////////////////////// + /** Produce an ISO 8601 Date-Time string with up-to nanosecond resolution. + * The primary ISO format uses YYYY-MM-DD style dates. + * + * @param res The resolution of the returned string. + * @return + */ + public String toISO8601(Resolution res){ + switch(res){ + case YEAR: + return String.format("%04d", m_nYear); + case MONTH: + return String.format("%04d-%02d", m_nYear, m_nMonth); + case DAY: + return String.format("%04d-%02d-%02d", m_nYear, m_nMonth, m_nDom); + case HOUR: + return String.format("%04d-%02d-%02dT%02d", m_nYear, m_nMonth, m_nDom, m_nHour); + case MINUTE: + return String.format("%04d-%02d-%02dT%02d:%02d", m_nYear, m_nMonth, m_nDom, m_nHour, m_nMinute); + case SECOND: + return String.format("%04d-%02d-%02dT%02d:%02d:%02d", m_nYear, m_nMonth, m_nDom, m_nHour, + m_nMinute, m_nSecond); + case MILLISEC: + // Let string.format handle rounding for me. + return String.format("%04d-%02d-%02dT%02d:%02d:%02d.%03.0f", m_nYear, m_nMonth, m_nDom, + m_nHour, m_nMinute, m_nSecond, m_nNanoSecond / 1000000.0); + case MICROSEC: + // Let string.format handle rounding for me. + return String.format("%04d-%02d-%02dT%02d:%02d:%02d.%06.0f", m_nYear, m_nMonth, m_nDom, + m_nHour, m_nMinute, m_nSecond, m_nNanoSecond / 1000.0); + case NANOSEC: + // Let string.format handle rounding for me. + return String.format("%04d-%02d-%02dT%02d:%02d:%02d.%09d", m_nYear, m_nMonth, m_nDom, + m_nHour, m_nMinute, m_nSecond, m_nNanoSecond); + } + return null; // added to make the compilier happy. + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Produce an alternate ISO 8601 Date-Time string with up-to nanosecond resolution. + * The alternate format ISO format uses ordinal day-of-year style dates, i.e. + * YYYY-DDD. + * + * @param res The resolution of the returned string. + * @return + */ + public String toAltISO8601(Resolution res){ + switch(res){ + case YEAR: + return String.format("%04d", m_nYear); + case MONTH: + throw new IllegalArgumentException("Alternate ISO time point format doesn't " + + "contain a month number."); + case DAY: + return String.format("%04d-%03d", m_nYear, m_nDoy); + case HOUR: + return String.format("%04d-%03dT%02d", m_nYear, m_nDoy, m_nHour); + case MINUTE: + return String.format("%04d-%03dT%02d:%02d", m_nYear, m_nDoy, m_nHour, m_nMinute); + case SECOND: + return String.format("%04d-%03dT%02d:%02d:%02d", m_nYear, m_nDoy, m_nHour, + m_nMinute, m_nSecond); + case MILLISEC: + // Let string.format handle rounding for me. + return String.format("%04d-%03dT%02d:%02d:%02d.%03.0f", m_nYear, m_nDoy, m_nHour, + m_nMinute, m_nSecond, m_nNanoSecond / 1000000.0); + case MICROSEC: + // Let string.format handle rounding for me. + return String.format("%04d-%03dT%02d:%02d:%02d.%06.0f", m_nYear, m_nDoy, m_nHour, + m_nMinute, m_nSecond, m_nNanoSecond / 1000.0); + case NANOSEC: + // Let string.format handle rounding for me. + return String.format("%04d-%03dT%02d:%02d:%02d.%09d", m_nYear, m_nDoy, m_nHour, + m_nMinute, m_nSecond, m_nNanoSecond); + } + return null; // added to make the compilier happy. + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Normalize date and time components for the Gregorian calendar ignoring leap seconds + * + * From Larry Granroth's original Das1 libs. + */ + private void normalize(){ + + // month is required input -- first adjust month + if( m_nMonth > 12 || m_nMonth < 1){ + // temporarily make month zero-based + m_nMonth--; + m_nYear += m_nMonth / 12; + m_nMonth %= 12; + if(m_nMonth < 0){ + m_nMonth += 12; + m_nYear--; + } + m_nMonth++; + } + + // index for leap year + int iLeap = TimeUtil.isLeapYear(m_nYear)?1:0; + + // day of year is output only -- calculate it + m_nDoy = TimeUtil.dayOffset[iLeap][m_nMonth] + m_nDom; + + // now adjust other items . . . + + // New addition, handle nanoseconds + if(m_nNanoSecond >= 1000000000 || m_nNanoSecond < 0){ + m_nSecond += m_nNanoSecond / 1000000000; + m_nNanoSecond = m_nNanoSecond % 1000000000; + if(m_nNanoSecond < 0){ + m_nNanoSecond += 1000000000; + m_nSecond--; + } + } + + // again, we're ignoring leap seconds + if( m_nSecond >= 60 || m_nSecond < 0){ + m_nMinute += m_nSecond / 60; + m_nSecond = m_nSecond % 60; + if(m_nSecond < 0){ + m_nSecond += 60; + m_nMinute--; + } + } + + if(m_nMinute >= 60 || m_nMinute < 0){ + m_nHour += m_nMinute / 60; + m_nMinute %= 60; + if(m_nMinute < 0){ + m_nMinute += 60; + m_nHour--; + } + } + + if(m_nHour >= 24 ||m_nHour < 0){ + m_nDoy += m_nHour / 24; + m_nHour %= 24; + if(m_nHour < 0){ + m_nHour += 24; + m_nDoy--; + } + } + + /* final adjustments for year and day of year */ + int ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + if(m_nDoy > ndays || m_nDoy < 1){ + while(m_nDoy > ndays){ + m_nYear++; + m_nDoy -= ndays; + ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + } + while(m_nDoy < 1){ + m_nYear--; + ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + m_nDoy += ndays; + } + } + + /* and finally convert day of year back to month and day */ + iLeap = TimeUtil.isLeapYear(m_nYear)?1:0; + while(m_nDoy <= TimeUtil.dayOffset[iLeap][m_nMonth]){ + m_nMonth--; + } + while(m_nDoy > TimeUtil.dayOffset[iLeap][m_nMonth + 1]){ + m_nMonth++; + } + m_nDom = m_nDoy - TimeUtil.dayOffset[iLeap][m_nMonth]; + } + + //////////////////////////////////////////////////////////////////////////////////// + + /** Set the year field. Use set() if you have multiple fields to set. */ + public void setYear(int nYear){ + m_nYear = nYear; + normalize(); + } + /** Set the month field. Use set() if you have multiple fields to set. */ + public void setMonth(int nMonth){ + m_nMonth = nMonth; + normalize(); + } + /** Set the day of month field. Use set() if you have multiple fields to set. */ + public void setDay(int nDay){ + m_nDom = nDay; + normalize(); + } + + /** Set the day of year, and recompute the month and day of month. + * @param nDoy the new day of year. + */ + public void setDayOfYear(int nDoy){ + m_nDoy = nDoy; + + /* final adjustments for year and day of year */ + int ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + if(m_nDoy > ndays || m_nDoy < 1){ + while(m_nDoy > ndays){ + m_nYear++; + m_nDoy -= ndays; + ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + } + while(m_nDoy < 1){ + m_nYear--; + ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + m_nDoy += ndays; + } + } + + /* and finally convert day of year back to month and day */ + int iLeap = TimeUtil.isLeapYear(m_nYear)?1:0; + while(m_nDoy <= TimeUtil.dayOffset[iLeap][m_nMonth]){ + m_nMonth--; + } + while(m_nDoy > TimeUtil.dayOffset[iLeap][m_nMonth + 1]){ + m_nMonth++; + } + m_nDom = m_nDoy - TimeUtil.dayOffset[iLeap][m_nMonth]; + } + + /** Set the hour field. Use set() if you have multiple fields to set. */ + public void setHour(int nHour){ + m_nHour = nHour; + normalize(); + } + /** Set the minute field. Use set() if you have multiple fields to set. */ + public void setMinute(int nMinute){ + m_nMinute = nMinute; + normalize(); + } + /** Set the second field. Use set() if you have multiple fields to set. */ + public void setSecond(int nSecond){ + m_nSecond = nSecond; + normalize(); + } + /** Set the nanosecond field. Use set() if you have multiple fields to set. */ + public void setNanoSecond(long nNano){ + m_nNanoSecond = nNano; + normalize(); + } + + /** Set (upto) all fields of a calendar time + * + * @param lFields An array of 1 to 7 items whose values will be assigned to the + * year, month, day, hour, minute, second and nanosecond respectively. Also + * integers can be specified one at a time in var-args fashion + */ + public void set(int... lFields){ + + if(lFields.length < 1) return; + + // Cool case fall through (good idea Ed!) + switch(lFields.length){ + default: m_nNanoSecond = lFields[6]; + case 6: m_nSecond = lFields[5]; + case 5: m_nMinute = lFields[4]; + case 4: m_nHour = lFields[3]; + case 3: m_nDom = lFields[2]; + case 2: m_nMonth = lFields[1]; + case 1: m_nYear = lFields[0]; + } + + normalize(); + } + + ///////////////////////////////////////////////////////////////////////////////////// + + public int year(){return m_nYear;} + public int month(){return m_nMonth;} + public int day(){return m_nDom;} + public int dayOfYear(){ return m_nDoy; } + public int hour(){return m_nHour;} + public int minute(){return m_nMinute;} + public int second(){return m_nSecond;} + public int nanosecond(){return (int)m_nNanoSecond;} + + public int[] get(){ + return new int[]{m_nYear, m_nMonth, m_nDom, m_nHour, m_nMinute, m_nSecond, + (int)m_nNanoSecond}; + } + + + //////////////////////////////////////////////////////////////////////////////////// + + /** Used to step add to a calendar time by 1 or more integer units. */ + public enum Step { + YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, NANOSEC, HALF_YEAR, QUARTER, + MILLISEC, MICROSEC; + + public static Step HigerStep(Step step){ + switch(step){ + case DAY: return MONTH; + case HOUR: return DAY; + case MINUTE: return HOUR; + case SECOND: return MINUTE; + case MILLISEC: return SECOND; + case MICROSEC: return MILLISEC; + case NANOSEC: return MICROSEC; + default: return YEAR; + } + } + + public static Step LowerStep(Step step){ + switch(step){ + case YEAR: return MONTH; + case MONTH: return DAY; + case DAY: return HOUR; + case HOUR: return MINUTE; + case MINUTE: return SECOND; + case SECOND: return MILLISEC; + case MILLISEC: return MICROSEC; + default: return NANOSEC; + } + } + } + + /** A convenience method for handling multiple steps at once. + * + * @param steps An array of upto 7 items, each one will step succeedingly smaller + * time fields if present. So the array items are taken to be: + * [year, month, day, hour, minute, second, nanosecond]. + * + * @return A new calendar time stepped as specified. + */ + public CalendarTime step(int lSteps[]){ + CalendarTime ct = new CalendarTime(this); + + Step fields[] = {Step.YEAR, Step.MONTH, Step.DAY, Step.HOUR, Step.MINUTE, + Step.SECOND, Step.NANOSEC}; + for(int i = 0; i < 7; i++){ + if((lSteps.length > i)&&(lSteps[i] != 0)) + ct = ct.step(fields[i], lSteps[i]); + } + + return ct; + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Introduced as a way to increase the efficiency of the time axis tick calculation. + * Step to the next higher ordinal. If the calendar time is already at the ordinal, + * then step by one unit. + * + * @param field The time field to change. Integers for this value are defined in + * TimeUtil + * @param steps number of positive or negative steps to take + * @return + */ + public CalendarTime step(Step field, int steps) { + + CalendarTime ct = new CalendarTime(this); + + if(steps == 0) return ct; + + // First change the relavent field + switch(field){ + case NANOSEC: ct.m_nNanoSecond += steps; break; + case MICROSEC: ct.m_nNanoSecond += 1000*steps; break; + case MILLISEC: ct.m_nNanoSecond += 1000000*steps; break; + case SECOND: ct.m_nSecond += steps; break; + case MINUTE: ct.m_nMinute += steps; break; + case HOUR: ct.m_nHour += steps; break; + case DAY: ct.m_nDom += steps; break; + case MONTH: ct.m_nMonth += steps; break; + case QUARTER: ct.m_nMonth += steps*3; break; + case HALF_YEAR: ct.m_nMonth += steps*6; break; + case YEAR: ct.m_nYear += steps*1; break; + default: + throw new IllegalArgumentException("Unknown time field designator: "+field); + } + + + // Handle zeroing out lower level fields (case fall throught can be handy) + switch(field){ + case YEAR: + ct.m_nMonth = 1; + case HALF_YEAR: + //Map months to a 0-1 half year scale + double dHalfYears = (ct.m_nMonth - 1)/6.0; + ct.m_nMonth = (((int)dHalfYears) * 6) + 1; //Truncates towards zero + case QUARTER: + //Map months to a 0-3 quarterly scale + double dQuarters = (ct.m_nMonth - 1)/3.0; + ct.m_nMonth = (((int)dQuarters) * 3) + 1; //Truncates towards zero + case MONTH: + ct.m_nDom = 1; + case DAY: + ct.m_nHour = 0; + case HOUR: + ct.m_nMinute = 0; + case MINUTE: + ct.m_nSecond = 0; + case MILLISEC: + ct.m_nNanoSecond = (ct.m_nNanoSecond / 1000000) * 1000000; + case MICROSEC: + ct.m_nNanoSecond = (ct.m_nNanoSecond / 1000) * 1000; + } + + ct.normalize(); + return ct; + } + + /** Special handler for changing the nanoseconds, as this field is a long */ + public CalendarTime stepNano(long steps) { + CalendarTime ct = new CalendarTime(this); + ct.m_nNanoSecond += steps; + ct.normalize(); + return ct; + } + + + /////////////////////////////////////////////////////////////////////////////////// + /** Get a time datum in us2000 units. + * + * @return A datum whose value is the number of milliseconds since midnight + * 2000-01-01, ignoring leap seconds. + */ + public Datum toDatum(){ + int jd = 367 * m_nYear - 7 * (m_nYear + (m_nMonth + 9) / 12) / 4 + - 3 * ((m_nYear + (m_nMonth - 9) / 7) / 100 + 1) / 4 + + 275 * m_nMonth / 9 + m_nDom + 1721029; + + double us2000 = (jd - 2451545) * 86400e6; // TODO: leap seconds + + return Datum.create(m_nHour*3600.0e6 + m_nMinute*60e6 + m_nSecond*1e6 + + m_nNanoSecond/1000 + us2000, Units.us2000); + } + + +} diff --git a/dasCore/src/main/java/org/das2/datum/Datum.java b/dasCore/src/main/java/org/das2/datum/Datum.java new file mode 100644 index 000000000..4cdb87669 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/Datum.java @@ -0,0 +1,610 @@ +/* File: Datum.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import org.das2.datum.format.DatumFormatter; + + +/** + *

    A Datum is a number in the context of a Unit, for example "15 microseconds.". + * A Datum object has methods for formatting itself as a String, representing + * itsself as a double in the context of another Unit, and mathematical + * operators that allow simple calculations to be made at the physical quantities. + * Also a Datum's precision can be limited to improve formatting.

    + *

    + * @author jbf + */ +public class Datum implements Comparable { + + private Units units; + private Number value; + private double resolution; + private DatumFormatter formatter; + + /** + * class backing Datums with a double. + */ + public static class Double extends Datum { + + Double( Number value, Units units ) { + super( value, units, 0. ); + } + + Double( double value, Units units ) { + super( new java.lang.Double(value), units, 0. ); + } + Double( double value ) { + super( new java.lang.Double(value), Units.dimensionless, 0. ); + } + Double( double value, Units units, double resolution ) { + super( new java.lang.Double(value), units, units.getDatumFormatterFactory().defaultFormatter(), resolution ); + } + + } + + private Datum(Number value, Units units, double resolution ) { + this( value, units, units.getDatumFormatterFactory().defaultFormatter(), resolution ); + } + + private Datum(Number value, Units units, DatumFormatter formatter, double resolution ) { + if ( value==null ) throw new IllegalArgumentException("value is null"); + this.value = value; + this.units = units; + this.resolution= resolution; + this.formatter = formatter; + } + + /** + * returns the datum's double value. This protected method allows subclasses and classes + * within the package to peek at the double value. + * + * @return the double value of the datum in the context of its units. + */ + protected double doubleValue() { + return this.getValue().doubleValue(); + } + + /** + * returns a double representing the datum in the context of units. + * + * @param units the Units in which the double should be returned + * @return a double in the context of the provided units. + */ + public double doubleValue(Units units) { + if ( units!=getUnits() ) { + return getUnits().getConverter(units).convert(this.getValue()).doubleValue(); + } else { + return this.getValue().doubleValue(); + } + } + + /** + * returns the double value without the unit, as long as the Units indicate this is a ratio measurement, and there is a meaningful 0. + * For example "5 Kg" -> 5, but "2012-02-16T00:00" would throw an IllegalArgumentException. Note this was introduced because often we just need + * to check to see if a value is zero. + * @return + */ + public double value() { + if ( UnitsUtil.isRatioMeasurement(units) ) { + return this.doubleValue( this.getUnits() ); + } else { + throw new IllegalArgumentException("datum is not ratio measurement: "+this ); + } + } + + /** + * return the absolute value (magnitude) of this Datum. If this + * datum is fill then the result is fill. + * @return + * @throws IllegalArgumentException if the datum is not a ratio measurement (like a timetag). + */ + public Datum abs() { + if ( UnitsUtil.isRatioMeasurement(units) ) { + if ( this.getUnits().isFill(value) ) { + return this; + } else if ( this.value.doubleValue()>=0 ) { + return this; + } else { + return this.getUnits().createDatum( Math.abs(value.doubleValue()) ); + } + } else { + throw new IllegalArgumentException("datum is not ratio measurement: "+this ); + } + } + + /** + * returns the resolution (or precision) of the datum. This is metadata for the datum, used + * primarily to limit the number of decimal places displayed in a string representation, + * but operators like add and multiply will propogate errors through the calculation. + * + * @param units the Units in which the double resolution should be returned. Note + * the units must be convertable to this.getUnits().getOffsetUnits(). + * @return the double resolution of the datum in the context of units. + */ + public double getResolution( Units units ) { + Units offsetUnits= getUnits().getOffsetUnits(); + if ( units!=offsetUnits ) { + return offsetUnits.getConverter(units).convert(this.resolution); + } else { + return this.resolution; + } + } + + /** + * returns the datum's int value. This protected method allows subclasses and classes + * within the package to peek at the value as an integer. (The intent was that a + * Datum might be backed by an integer instead of a double, so that numerical + * round-off issues can be avoided.) + * + * @return the integer value of the datum in the context of its units. + */ + protected int intValue() { + return this.getValue().intValue(); + } + + /** + * returns a int representing the datum in the context of units. + * + * @param units the Units in which the int should be returned + * @return a double in the context of the provided units. + */ + public int intValue(Units units) { + if ( units!=getUnits() ) { + return getUnits().getConverter(units).convert(this.getValue()).intValue(); + } else { + return this.getValue().intValue(); + } + } + + /** + * returns the datum's units. For example, UT times might have the units + * Units.us2000. + * + * @return the datum's units. + */ + public Units getUnits() { + return this.units; + } + + /** + * returns the Number representing the datum's location in the space indentified by its units. + * This protected method allows subclasses and classes + * within the package to peek at the value. (The intent was that a + * Datum might be backed by an integer, float, or double, depending on the application.) + * @return a Number in the context of the provided units. + */ + protected Number getValue() { + return this.value; + } + + /** + * convenience method for checking to see if a datum is a fill datum. + * @return true if the value is fill as defined by the Datum's units. + */ + public boolean isFill() { + return getUnits().isFill(getValue()); + } + + /** + * returns a Datum whose value is the sum of this and datum, in this.getUnits(). + * @return a Datum that is the sum of the two values in this Datum's units. + * @param datum Datum to add, that is convertable to this.getUnits(). + */ + public Datum add( Datum datum ) { + Datum result= add( datum.getValue(), datum.getUnits() ); + result.resolution= Math.sqrt( datum.resolution * datum.resolution + this.resolution * this.resolution ); + return result; + } + + /** + * returns a Datum whose value is the sum of this and value in + * the context of units, in this.getUnits(). + * @param value a Number to add in the context of units. + * @param units units defining the context of value. There should be a converter from + * units to this Datum's units. + * @return value Datum that is the sum of the two values in this Datum's units. + */ + public Datum add( Number value, Units units ) { return getUnits().add( getValue(), value, units ); } + + /** + * returns a Datum whose value is the sum of this and value in + * the context of units, in this.getUnits(). + * @param d a Number to add in the context of units. + * @param units units defining the context of value. There should be a converter from + * units to this Datum's units. + * @return value Datum that is the sum of the two values in this Datum's units. + */ + public Datum add( double d, Units units ) { return add( new java.lang.Double(d), units ); } + + /** + * returns a Datum whose value is the difference of this and value. + * The returned Datum will have units according to the type of units subtracted. + * For example, "1979-01-02T00:00" - "1979-01-01T00:00" = "24 hours" (this datum's unit's offset units), + * while "1979-01-02T00:00" - "1 hour" = "1979-01-01T23:00" (this datum's units.) + * + * Note also the resolution of the result is calculated. + * + * @return a Datum that is the sum of the two values in this Datum's units. + * @param datum Datum to add, that is convertable to this.getUnits() or offset units. + */ + public Datum subtract( Datum datum ) { + Datum result= subtract( datum.getValue(), datum.getUnits() ); + result.resolution= Math.sqrt( datum.resolution * datum.resolution + this.resolution * this.resolution ); + return result; + } + + /** + * returns a Datum whose value is the difference of this and value in + * the context of units. + * The returned Datum will have units according to the type of units subtracted. + * For example, "1979-01-02T00:00" - "1979-01-01T00:00" = "24 hours" (this datum's unit's offset units), + * while "1979-01-02T00:00" - "1 hour" = "1979-01-01T23:00" (this datum's units.) + * + * @param a a Number to add in the context of units. + * @param units units defining the context of value. There should be a converter from + * units to this Datum's units or offset units. + * @return value Datum that is the difference of the two values in this Datum's units. + */ + public Datum subtract( Number a, Units units ) { + Datum result= getUnits().subtract( getValue(), a, units ); + return result; + } + + /** + * returns a Datum whose value is the difference of this and value in + * the context of units. + * The returned Datum will have units according to the type of units subtracted. + * For example, "1979-01-02T00:00" - "1979-01-01T00:00" = "24 hours" (this datum's unit's offset units), + * while "1979-01-02T00:00" - "1 hour" = "1979-01-01T23:00" (this datum's units.) + * + * @param d a Number to add in the context of units. + * @param units units defining the context of value. There should be a converter from + * units to this Datum's units or offset units. + * @return value Datum that is the difference of the two values in this Datum's units. + */ + public Datum subtract( double d, Units units ) { return subtract( new java.lang.Double(d), units ); } + + private static double relativeErrorMult( double x, double dx, double y, double dy ) { + return Math.sqrt( dx/x * dx/x + dy/y * dy/y ); + } + + /** + * divide this by the datum a. Currently, only division is only supported:

    +     *   between convertable units, resulting in a Units.dimensionless quantity, or
    +     *   by a Units.dimensionless quantity, and a datum with this datum's units is returned.
    + * This may change, as a generic SI units class is planned. + * + * @param a the datum divisor. + * @return the quotient. + */ + public Datum divide( Datum a ) { + Datum result= divide( a.getValue(), a.getUnits() ); + result.resolution= Math.abs( result.doubleValue() ) * relativeErrorMult( doubleValue(), resolution, a.doubleValue(), a.resolution ); + return result; + } + + /** + * divide this by the Number provided in the context of units. Currently, only division is only supported:
    +     *   between convertable units, resulting in a Units.dimensionless quantity, or
    +     *   by a Units.dimensionless quantity, and a datum with this datum's units is returned.
    + * This may change, as a generic SI units class is planned. + * @param a the magnitude of the divisor. + * @param units the units of the divisor. + * @return the quotient. + */ + public Datum divide( Number a, Units units ) { return getUnits().divide( getValue(), a, units ); } + + /** + * divide this by the dimensionless double. + * @param d the magnitude of the divisor. + * @return the quotient. + */ + public Datum divide( double d ) { return divide( new java.lang.Double(d), Units.dimensionless ); } + + /** + * multiply this by the datum a. Currently, only multiplication is only supported:
    +     *   by a dimensionless datum, or when this is dimensionless.
    +     * This may change, as a generic SI units class is planned.
    +     *
    +     * This should also throw an IllegalArgumentException if the units are LocationUnits (e.g. UT time), but doesn't.  This may
    +     * change.
    +     *   
    +     * @param a the datum to multiply
    +     * @return the product.
    +     */
    +    public Datum multiply( Datum a ) { 
    +        Datum result= multiply( a.getValue(), a.getUnits() );         
    +        result.resolution= result.doubleValue() * relativeErrorMult( doubleValue(), resolution, a.doubleValue(), a.resolution );
    +        return result;
    +    }
    +    
    +    /**
    +     * multiply this by the Number provided in the context of units.  Currently, only multiplication is only supported:
    +     *   by a dimensionless datum, or when this is dimensionless.
    +     * This may change, as a generic SI units class is planned.
    +     *
    +     * This should also throw an IllegalArgumentException if the units are LocationUnits (e.g. UT time), but doesn't.  This may
    +     * change.
    +     *
    +     * @param a the magnitude of the multiplier.
    +     * @param units the units of the multiplier.
    +     * @return the product.
    +     */
    +    public Datum multiply( Number a, Units units ) { return getUnits().multiply( getValue(), a, units ); }
    +    
    +    /**
    +     * multiply by a dimensionless number.
    +     *
    +     * This should also throw an IllegalArgumentException if the units are LocationUnits (e.g. UT time), but doesn't.  This may
    +     * change.
    +     *
    +     * @param d the multiplier.
    +     * @return the product.
    +     */
    +    public Datum multiply( double d ) {  return multiply( new java.lang.Double(d), Units.dimensionless ); }
    +    
    +    /**
    +     * creates an equivalent datum using a different unit.  For example,
    +     *  x= Datum.create( 5, Units.seconds );
    +     *  System.err.println( x.convertTo( Units.seconds ) );
    +     * 
    +     * @param units the new Datum's units
    +     * @throws java.lang.IllegalArgumentException if the datum cannot be converted to the given units.
    +     * @return a datum with the new units, that is equal to the original datum.
    +     */
    +    public Datum convertTo( Units units ) throws IllegalArgumentException {
    +        UnitsConverter muc= this.units.getConverter(units);
    +        Datum result= units.createDatum( muc.convert( this.getValue() ) );
    +        if ( this.resolution!=0. ) {
    +            muc= this.units.getOffsetUnits().getConverter(units.getOffsetUnits());
    +            result.resolution= muc.convert(this.resolution);
    +        }
    +        return result;
    +    }
    +    
    +    /**
    +     * returns a hashcode that is a function of the value and the units.
    +     * @return a hashcode for the datum
    +     */
    +    public int hashCode() {
    +        long bits = (long) getValue().hashCode();
    +        int doubleHash= (int)(bits ^ (bits >>> 32));
    +        int unitsHash= units.hashCode();
    +        return doubleHash ^ unitsHash;
    +    }
    +    
    +    /**
    +     * returns true if the two datums are equal.  That is, their double values are equal when converted to the same units.
    +     * @param a the Object to compare to.
    +     * @throws java.lang.IllegalArgumentException if the Object is not a datum or the units are not convertable.
    +     * @return true if the datums are equal.
    +     */
    +    public boolean equals( Object a ) throws IllegalArgumentException {
    +        return ( a!=null && (a instanceof Datum) && this.equals( (Datum)a ) );
    +    }
    +    
    +    /**
    +     * returns true if the two datums are equal.  That is, their double values are equal when converted to the same units.
    +     * @param a the datum to compare
    +     * @throws java.lang.IllegalArgumentException if the units are not convertable.
    +     * @return true if the datums are equal.
    +     */
    +    public boolean equals( Datum a ) throws IllegalArgumentException {
    +        return ( a!=null && this.getUnits().isConvertableTo( a.getUnits() ) && this.compareTo(a)==0 );
    +    }
    +    
    +    /**
    +     * returns true if this is less than a.
    +     * @param a a datum convertable to this Datum's units.
    +     * @throws java.lang.IllegalArgumentException if the two don't have convertable units.
    +     * @return true if this is less than a.
    +     */
    +    public boolean lt( Datum a ) throws IllegalArgumentException {
    +        return (this.compareTo(a)<0);
    +    }
    +    
    +    /**
    +     * returns true if this is greater than a.
    +     * @param a a datum convertable to this Datum's units.
    +     * @throws java.lang.IllegalArgumentException if the two don't have convertable units.
    +     * @return true if this is greater than a.
    +     */
    +    public boolean gt( Datum a ) throws IllegalArgumentException {
    +        return (this.compareTo(a)>0);
    +    }
    +    
    +    /**
    +     * returns true if this is less than or equal to a.
    +     * @param a a datum convertable to this Datum's units.
    +     * @throws java.lang.IllegalArgumentException if the two don't have convertable units.
    +     * @return true if this is less than or equal to a.
    +     */
    +    public boolean le( Datum a ) throws IllegalArgumentException {
    +        return (this.compareTo(a)<=0);
    +    }
    +    
    +    /**
    +     * returns true if this is greater than or equal to a.
    +     * @param a a datum convertable to this Datum's units.
    +     * @throws java.lang.IllegalArgumentException if the two don't have convertable units.
    +     * @return true if this is greater than or equal to a.
    +     */
    +    public boolean ge( Datum a ) throws IllegalArgumentException {
    +        return (this.compareTo(a)>=0);
    +    }
    +    
    +    /**
    +     * compare this to another datum.
    +     * @return an int <0 if this comes before Datum a in this Datum's units space,
    +     * 0 if they are equal, and >0 otherwise.
    +     * @param a the Datum to compare this datum to.
    +     * @throws IllegalArgumentException if a is not convertable to this Datum's
    +     * units.
    +     */
    +    public int compareTo( Datum a ) throws IllegalArgumentException {
    +        if ( this.units != a.units ) {
    +            a= a.convertTo(this.units);
    +        }
    +        
    +        double d= this.getValue().doubleValue() - a.getValue().doubleValue();
    +        
    +        if (d==0.) {
    +            return 0;
    +        } else if ( d<0. ) {
    +            return -1;
    +        } else {
    +            return 1;
    +        }
    +    }
    +    
    +    /**
    +     * returns true if the value is non NaN.
    +     * @return true if the value is non NaN.
    +     * @deprecated Use isFinite instead, or getValue.
    +     */
    +    public boolean isValid() {
    +        return (value.doubleValue()!=java.lang.Double.NaN);
    +    }
    +    
    +    /**
    +     * returns true if the value is finite, that is not INFINITY or NaN.
    +     * @return true if the value is finite, that is not INFINITY or NaN.
    +     */
    +    public boolean isFinite() {
    +        return ( value.doubleValue()!=java.lang.Double.POSITIVE_INFINITY )
    +        && ( value.doubleValue()!=java.lang.Double.NEGATIVE_INFINITY )
    +        && ( value.doubleValue()!=java.lang.Double.NaN );
    +    }
    +    
    +    /**
    +     * returns a human readable String representation of the Datum, which should also be parseable with
    +     * Units.parse()
    +     * @return a human readable String representation of the Datum, which should also be parseable with
    +     * Units.parse()
    +     */
    +    public String toString() {
    +        if (formatter==null) {
    +            return units.getDatumFormatterFactory().defaultFormatter().format(this);
    +        } else {
    +            return formatter.format(this);
    +        }
    +    }
    +    
    +    /**
    +     * convenient method for creating a dimensionless Datum with the given value.
    +     * @param value the magnitude of the datum.
    +     * @return a dimensionless Datum with the given value.
    +     */
    +    public static Datum create(double value) {
    +        return Units.dimensionless.createDatum(value);
    +    }
    +    
    +    /**
    +     * creates a datum with the given units and value, for example,
    +     * Datum.create( 54, Units.milliseconds )
    +     * @param value the magnitude of the datum.
    +     * @param units the units of the datum.
    +     * @return a Datum with the given units and value.
    +     */
    +    public static Datum create( double value, Units units ) {
    +        return units.createDatum( value );
    +    }
    +    
    +    /**
    +     * Returns a Datum with a specific DatumFormatter attached to
    +     * it.  This was was used to limit resolution before limited resolution
    +     * Datums were introduced.
    +     *
    +     * @param value the magnitude of the datum.
    +     * @param units the units of the datum.
    +     * @param formatter the DatumFormatter that should be used to format this datum, which will be
    +     *   returned by getFormatter().
    +     * @return a Datum with the given units and value, that should return the given formatter when asked.  
    +     */
    +    public static Datum create( double value, Units units, DatumFormatter formatter ) {
    +        Datum result= create( value, units);
    +        result.formatter= formatter;
    +        return result;
    +    }
    +    
    +    /**
    +     * Returns a Datum with the given value and limited to the given resolution.
    +     * When formatted, the formatter should use this resolution to limit the 
    +     * precision displayed.
    +     * @param value the magnitude of the datum, or value to be interpreted in the context of units.
    +     * @param units the units of the datum.
    +     * @param resolution the limit to which the datum's precision is known.
    +     * @return a Datum with the given units and value.
    +     */
    +    public static Datum create( double value, Units units, double resolution ) {
    +        Datum result= units.createDatum( value, resolution );
    +        result.formatter= units.getDatumFormatterFactory().defaultFormatter();
    +        return result;
    +    }
    +    
    +    /**
    +     * Returns a Datum with the given value and limited to the given resolution.
    +     * When formatted, the formatter should use this resolution to limit the 
    +     * precision displayed.
    +     * @param value the magnitude of the datum, or value to be interpreted in the context of units.
    +     * @param units the units of the datum.
    +     * @param resolution the limit to which the datum's precision is known.
    +     * @param formatter the DatumFormatter that should be used to format this datum, which will be
    +     *   returned by getFormatter().
    +     * @return a Datum with the given units and value.
    +     */
    +    public static Datum create( double value, Units units, double resolution, DatumFormatter formatter ) {
    +        Datum result= units.createDatum( value, resolution );
    +        result.formatter= formatter;
    +        return result;
    +    }
    +    
    +    /**
    +     * creates a dimensionless datum backed by an int.
    +     * @return a dimensionless Datum with the given value.
    +     * @param value the magnitude of the dimensionless datum.
    +     */
    +    public static Datum create( int value ) {
    +        return Units.dimensionless.createDatum( value );
    +    }
    +    
    +    /**
    +     * creates a datum backed by an int with the given units.
    +     * @return a Datum with the given units and value.
    +     * @param value the magnitude of the datum
    +     * @param units the units of the datum
    +     */
    +    public static Datum create( int value, Units units ) {
    +        return units.createDatum( value );
    +    }
    +    
    +    /**
    +     * returns a formatter suitable for formatting this datum as a string.
    +     * @return a formatter to be used to format this Datum into a String.
    +     */
    +    public DatumFormatter getFormatter() {
    +        return this.formatter;
    +    }
    +    
    +}
    diff --git a/dasCore/src/main/java/org/das2/datum/DatumRange.java b/dasCore/src/main/java/org/das2/datum/DatumRange.java
    new file mode 100644
    index 000000000..6e57b9b9f
    --- /dev/null
    +++ b/dasCore/src/main/java/org/das2/datum/DatumRange.java
    @@ -0,0 +1,293 @@
    +package org.das2.datum;
    +
    +/**
    + * A DatumRange is provided as a means to carry an ordered pair of Datums
    + * representing an interval.  This sort of data structure comes up often in
    + * processing, and it's useful to define once and get the operators
    + * implemented correctly.  Consider using this object whenever you see
    + * pairs of Datums in interfaces and codes (e.g. tbegin,tend), they are probably
    + * a DatumRange!
    + */
    +
    +public class DatumRange implements Comparable {
    +    
    +    Datum s1;
    +    Datum s2;
    +    
    +    /**
    +     * Creates valid DatumRange from two Datums.
    +     * @param s1 the start or smaller value of the range.
    +     * @param s2 the stop or bigger value of the range.
    +     */
    +    public DatumRange(Datum s1, Datum s2) {
    +        if ( s2.lt(s1) ) {
    +            throw new IllegalArgumentException( "s2 [0,1).include(2)->[0,2)  (note this is exclusive of 2 since it's the end).
    +     * [0,1).include(-1)->[-1,1).
    +     * [0,1).include(0.5)->[0,1]  (and returns the same DatumRange object)
    +     * 
    + * Also, including a fill Datum returns the same DatumRange as well. + */ + public DatumRange include(Datum d) { + if ( d.isFill() ) return this; + if ( this.contains(d) || this.max().equals(d) ) return this; + Datum min= ( this.min().le(d) ? this.min() : d ); + Datum max= ( this.max().ge(d) ? this.max() : d ); + return new DatumRange( min, max ); + } + + /** + * return the units of the DatumRange. + */ + public Units getUnits() { + return this.s1.getUnits(); + } + + /** + * creates a new DatumRange object with the range specified in the space + * identified by units. Note that min must be <= max. + */ + public static DatumRange newDatumRange(double min, double max, Units units) { + return new DatumRange( Datum.create(min,units), Datum.create(max,units) ); + } + +} + diff --git a/dasCore/src/main/java/org/das2/datum/DatumRangeUtil.java b/dasCore/src/main/java/org/das2/datum/DatumRangeUtil.java new file mode 100644 index 000000000..d139180e4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/DatumRangeUtil.java @@ -0,0 +1,1115 @@ +/* + * DatumRangeUtil.java + * + * Created on September 16, 2004, 2:35 PM + */ + +package org.das2.datum; + +import org.das2.util.DasMath; +import org.das2.system.DasLogger; +import java.text.*; +import java.util.*; +import java.util.logging.*; +import java.util.regex.*; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.TimeDatumFormatter; + +/** + * + * @author Jeremy + */ +public class DatumRangeUtil { + + private static final int DATEFORMAT_USA= 1; + private static final int DATEFORMAT_EUROPE= 0; + private static final int DATEFORMAT_YYYY_DDD= 2; + + private static final boolean DEBUG=false; + + + // this pattern is always a year + private static boolean isYear( String string ) { + return string.length()==4 && Pattern.matches("\\d{4}",string); + } + + // this pattern is always a day of year + private static boolean isDayOfYear( String string ) { + return string.length()==3 && Pattern.matches("\\d{3}",string); + } + + private static int monthNumber( String string ) throws ParseException { + if ( Pattern.matches("\\d+", string) ) { + return parseInt(string); + } else { + int month= monthNameNumber(string); + if ( month==-1 ) throw new ParseException("hoping for month at, got "+string, 0); + return month; + } + } + + private static int monthNameNumber( String string ) { + if ( string.length() < 3 ) return -1; + String[] monthNames= new String[] { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; + string= string.substring(0,3).toLowerCase(); + int r=-1; + for ( int i=0; i 100 ) { + return year; + } else { + if ( year < 70 ) { + return 2000+year; + } else { + return 1900+year; + } + } + } + + public static class DateDescriptor { + String date; + String year; + String month; + String day; + String delim; + int dateformat; + } + + private int stregex( String string, String regex ) { + Matcher matcher= Pattern.compile(regex).matcher(string); + if ( matcher.find() ) { + return matcher.start(); + } else { + return -1; + } + } + + private static void caldat( int julday, DateDescriptor dateDescriptor ) { + int jalpha, j1, j2, j3, j4, j5; + + jalpha = (int)(((double)(julday - 1867216) - 0.25)/36524.25); + j1 = julday + 1 + jalpha - jalpha/4; + j2 = j1 + 1524; + j3 = 6680 + (int)(((j2-2439870)-122.1)/365.25); + j4 = 365*j3 + j3/4; + j5 = (int)((j2-j4)/30.6001); + + int day = j2 - j4 - (int)(30.6001*j5); + int month = j5-1; + month = ((month - 1) % 12) + 1; + int year = j3 - 4715; + year = year - (month > 2 ? 1 : 0); + year = year - (year <= 0 ? 1 : 0); + + dateDescriptor.day= ""+day; + dateDescriptor.month= ""+month; + dateDescriptor.year= ""+year; + + } + + private static int julday( int month, int day, int year ) { + int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - + 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + + 275 * month / 9 + day + 1721029; + return jd; + } + + private static void printGroups( Matcher matcher ) { + for ( int i=0; i<=matcher.groupCount(); i++ ) { + System.out.println(" "+i+": "+matcher.group(i) ); + } + System.out.println(" " ); + } + + private static int parseInt( String s ) throws ParseException { + try { + return Integer.parseInt(s); + } catch ( NumberFormatException e ) { + throw new ParseException( "failed attempt to parse int in "+s, 0 ); + } + } + + /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + //;; + //;; papco_parse_timerange, string -> timeRange + //;; + //;; parses a timerange from a string. Valid strings include: + // ;; "2001" + // ;; "2001-2004" + // ;; "2003-004" + // ;; "12/31/2001" + // ;; "Jan 2001" + // ;; "Jan-Feb 2004" + // ;; "2004-004 - 2003-007" + // ;; "JAN to MAR 2004" + // ;; "2004/feb-2004/mar" + // ;; "2004/004-008 + // ;; "1979-03-01T20:58:45.000Z span 17.5 s" + // ;; keeps track of format(e.g. %Y-%j) for debugging, and perhaps to reserialize + // ;; + // ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // if an element is trivially identifiable, as in "mar", then it is required + // that the corresponding range element be of the same format or not specified. + */ + + static class TimeRangeParser { + String token; + String delim=""; + + String string; + int ipos; + + final int YEAR=0; + final int MONTH=1; + final int DAY=2; + final int HOUR=3; + final int MINUTE=4; + final int SECOND=5; + final int NANO=6; + + final int STATE_OPEN=89; + final int STATE_TS1TIME=90; + final int STATE_TS2TIME=91; + + int state= STATE_OPEN; + + String delimRegEx= "\\s|-|/|\\.|:|to|through|span|T|Z|\u2013|,"; + Pattern delimPattern= Pattern.compile( delimRegEx ); + int[] ts1= new int[] { -1, -1, -1, -1, -1, -1, -1 }; + int[] ts2= new int[] { -1, -1, -1, -1, -1, -1, -1 }; + int[] ts= null; + + boolean beforeTo; + + private Pattern yyyymmddPattern= Pattern.compile("((\\d{4})(\\d{2})(\\d{2}))( |to|t|-|$)"); + + /* groups= group numbers: { year, month, day, delim } (0 is all) */ + private boolean tryPattern( Pattern regex, String string, int[] groups, DateDescriptor dateDescriptor ) throws ParseException { + Matcher matcher= regex.matcher( string.toLowerCase() ); + if ( matcher.find() && matcher.start()==0 ) { + // printGroups(matcher); + int posDate= matcher.start(); + int length= matcher.end()-matcher.start(); + dateDescriptor.delim= matcher.group(groups[3]); + dateDescriptor.date= string.substring( matcher.start(), matcher.end()-dateDescriptor.delim.length() ); + String month; + String day; + String year; + dateDescriptor.day= matcher.group(groups[2]); + dateDescriptor.month= matcher.group(groups[1]); + dateDescriptor.year= matcher.group(groups[0]); + return true; + } else { + return false; + } + } + + public boolean isTime( String string, int[] timearr ) throws ParseException { + Matcher m; + Pattern hhmmssmmPattern= Pattern.compile( "(\\d+):(\\d\\d+):(\\d\\d+).(\\d+) )" ); + Pattern hhmmssPattern= Pattern.compile( "(\\d+):(\\d\\d+):(\\d\\d+)" ); + Pattern hhmmPattern= Pattern.compile( "(\\d+):(\\d\\d+)" ); + Pattern hhPattern= Pattern.compile( "(\\d+):" ); + + if ( (m=hhmmssmmPattern.matcher(string)).matches() ) { + timearr[HOUR]= Integer.parseInt( m.group(1) ); + timearr[MINUTE]= Integer.parseInt( m.group(2) ); + timearr[SECOND]= Integer.parseInt( m.group(3) ); + timearr[NANO]= (int)( Integer.parseInt( m.group(4) ) * ( 100000 / DasMath.exp10( m.group(4).length() ) )); + throw new RuntimeException("working on this"); + } else if (( m=hhmmssPattern.matcher(string)).matches() ) { + } else if (( m=hhmmPattern.matcher(string)).matches() ) { + } else if (( m=hhPattern.matcher(string)).matches() ) { + } + return false; + } + + public boolean isDate( String string, DateDescriptor dateDescriptor ) throws ParseException { + // this is introduced because mm/dd/yy is so ambiguous, the parser + // has trouble with these dates. Check for these as a group. + + if ( string.length()<6 ) return false; + + int[] groups; + String dateDelimRegex= "( |to|t|-)"; + String yearRegex= "(\\d{2}(\\d{2})?)"; // t lower case because tryPattern folds case + + if ( tryPattern( yyyymmddPattern, string, new int[] { 2,3,4,5 }, dateDescriptor ) ) { + dateDescriptor.dateformat= DATEFORMAT_USA; + return true; + } + + String delim; + + String delims="(/|\\.|-| )"; + Matcher matcher= Pattern.compile(delims).matcher(string); + + if ( matcher.find() ) { + int posDelim= matcher.start(); + delim= string.substring(matcher.start(),matcher.end()); + } else { + return false; + } + + if ( delim.equals(".") ) { + delim="\\."; + } + + String monthNameRegex= "(jan[a-z]*|feb[a-z]*|mar[a-z]*|apr[a-z]*|may|june?|july?|aug[a-z]*|sep[a-z]*|oct[a-z]*|nov[a-z]*|dec[a-z]*)"; + String monthRegex= "((\\d?\\d)|"+monthNameRegex+")"; + String dayRegex= "(\\d?\\d)"; + + String euroDateRegex; + + if ( delim.equals("\\.") ) { + euroDateRegex= "(" + dayRegex + delim + monthRegex + delim + yearRegex + dateDelimRegex + ")"; + groups= new int [] { 6, 3, 2, 8 }; + } else { + euroDateRegex= "(" + dayRegex + delim + monthNameRegex + delim + yearRegex + dateDelimRegex + ")"; + groups= new int [] { 4, 3, 2, 6 }; + } + if ( tryPattern( Pattern.compile( euroDateRegex ), string, groups, dateDescriptor ) ) { + dateDescriptor.dateformat= DATEFORMAT_EUROPE; + return true; + } + + String usaDateRegex= monthRegex + delim + dayRegex + delim + yearRegex + dateDelimRegex ; + if ( tryPattern( Pattern.compile( usaDateRegex ), string, new int[] { 5,1,4,7 }, dateDescriptor ) ) { + dateDescriptor.dateformat= DATEFORMAT_USA; + return true; + } + + // only works for four-digit years + String lastDateRegex= "(\\d{4})" + delim + monthRegex + delim + dayRegex + dateDelimRegex; + if ( tryPattern( Pattern.compile( lastDateRegex ), string, new int[] { 1,2,5,6 }, dateDescriptor ) ) { + dateDescriptor.dateformat= DATEFORMAT_USA; + return true; + } + + String doyRegex= "(\\d{3})"; + String dateRegex= doyRegex+"(-|/)" + yearRegex + dateDelimRegex; + + if ( tryPattern( Pattern.compile( dateRegex ), string, new int[] { 3,1,1,5 }, dateDescriptor ) ) { + int doy= parseInt(dateDescriptor.day); + if ( doy>366 ) return false; + int year= parseInt(dateDescriptor.year); + caldat( julday( 12, 31, year-1 ) + doy, dateDescriptor ); + dateDescriptor.dateformat= DATEFORMAT_YYYY_DDD; + + return true; + } + + dateRegex= yearRegex +"(-|/)" + doyRegex + dateDelimRegex; + if ( tryPattern( Pattern.compile( dateRegex ), string, new int[] { 1,4,4,5 }, dateDescriptor ) ) { + int doy= parseInt(dateDescriptor.day); + if ( doy>366 ) return false; + int year= parseInt(dateDescriptor.year); + caldat( julday( 12, 31, year-1 ) + doy, dateDescriptor ); + dateDescriptor.dateformat= DATEFORMAT_YYYY_DDD; + + return true; + } + return false; + } + + + private void nextToken( ) { + Matcher matcher= delimPattern.matcher( string.substring(ipos) ); + if ( matcher.find() ) { + int r= matcher.start(); + int length= matcher.end()-matcher.start(); + token= string.substring( ipos, ipos+r ); + delim= string.substring( ipos+r, ipos+r+length ); + ipos= ipos + r + length; + } else { + token= string.substring(ipos); + delim= ""; + ipos= string.length(); + } + } + + private void setBeforeTo( boolean v ) { + beforeTo= v; + if ( beforeTo ) { + ts= ts1; + } else { + ts= ts2; + } + } + + /* identify and make the "to" delimiter unambiguous */ + public String normalizeTo( String s ) throws ParseException { + + int minusCount= 0; + for ( int i=0; i2 ) { + result= ss[0]; + for ( int i=1; i 0 ) { + ArrayList unload; + String formatUn; + int idx=0; + + if ( beforeToUnresolved.size() < afterToUnresolved.size() ) { + if ( beforeToUnresolved.size()>0 ) { + for ( int i=0; i0 ) { + for ( int i=0; i=0; i-- ) { + while ( ts[lsd]!=-1 && lsd>0 ) lsd--; + if ( ts[lsd]!=-1 ) { + throw new ParseException( "can't resolve these tokens in \""+stringIn+"\": "+unload+ " ("+format+")", 0 ); + } + ts[lsd]= parseInt((String)unload.get(i)); + String[] s= format.split(formatUn+(i+1)); + format= s[0]+formatCodes[lsd]+s[1]; + } + + } // unresolved entities + + { + StringBuffer stringBuffer= new StringBuffer("ts1: "); + for ( int i=0; i<7; i++ ) stringBuffer.append(""+ts1[i]+" "); + logger.fine( stringBuffer.toString() ); + stringBuffer= new StringBuffer("ts2: "); + for ( int i=0; i<7; i++ ) stringBuffer.append(""+ts2[i]+" "); + logger.fine( stringBuffer.toString() ); + logger.fine( format ); + } + + /* contextual fill. Copy over digits that were specified in one time but + * not the other. + */ + for ( int i=YEAR; i<=DAY; i++ ) { + if ( ts2[i] == -1 && ts1[i] != -1 ) ts2[i]= ts1[i]; + if ( ts1[i] == -1 && ts2[i] != -1 ) ts1[i]= ts2[i]; + } + + int i= NANO; + int[] implicit_timearr= new int[] { -1, 1, 1, 0, 0, 0, 0 }; + int ts1lsd= -1; + int ts2lsd= -1; + while (i>=0) { + if ( ts2[i] != -1 && ts2lsd == -1 ) ts2lsd=i; + if ( ts2lsd == -1 ) ts2[i]= implicit_timearr[i]; + if ( ts2[i] == -1 && ts2lsd != -1 ) { + throw new ParseException("not specified in stop time: "+digitIdentifiers[i]+" in "+stringIn+" ("+format+")",ipos); + } + if ( ts1[i] != -1 && ts1lsd == -1 ) ts1lsd=i; + if ( ts1lsd == -1 ) ts1[i]= implicit_timearr[i]; + if ( ts1[i] == -1 && ts1lsd != -1 ) { + throw new ParseException("not specified in start time:"+digitIdentifiers[i]+" in "+stringIn+" ("+format+")",ipos); + } + i= i-1; + } + + + if ( ts1lsd != ts2lsd && ( ts1lsd3; idigit-- ) { + if ( arr[idigit]>0 ) break; + } + stopRes= Math.max( stopRes, idigit ); + } + + int[] arr= TimeUtil.toTimeArray(time); + if ( stopRes>3 ) { + timeString+= ( arr[4] < 10 ? "0" : "" ) + arr[4]; + if ( stopRes>4 ) { + int second= arr[5]; + timeString+=":"+ ( second < 10 ? "0" : "" ) + second; + if ( stopRes>5 ) { + int millis= arr[6]; + DecimalFormat nf= new DecimalFormat("000"); + timeString+="."+nf.format(millis); + if ( stopRes>6 ) { + int micros= arr[7]; + timeString+=nf.format(micros); + } + } + } + } + + return timeString; + } + + public static String formatTimeRange( DatumRange self ) { + + String[] monthStr= new String[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + double seconds= self.width().doubleValue(Units.seconds); + + CalendarTime ts1= new CalendarTime(self.min()); + CalendarTime ts2= new CalendarTime(self.max()); + + // ts1= [ year1, month1, dom1, hour1, minute1, second1, nanos1 ] + // ts2= [ year2, month2, dom2, hour2, minute2, second2, nanos2 ] + + boolean isMidnight1= TimeUtil.getSecondsSinceMidnight( self.min() ) == 0.; + boolean isMidnight2= TimeUtil.getSecondsSinceMidnight( self.max() ) == 0.; + + boolean isMonthBoundry1= isMidnight1 && ts1.m_nDom == 1; + boolean isMonthBoundry2= isMidnight2 && ts2.m_nDom == 1; + + boolean isYearBoundry1= isMonthBoundry1 && ts1.m_nMonth == 1; + boolean isYearBoundry2= isMonthBoundry2 && ts2.m_nMonth == 1; + + //String toDelim= " \u2013 "; + String toDelim= " through "; + if ( isYearBoundry1 && isYearBoundry2 ) { // no need to indicate month + if ( ts2.m_nYear-ts1.m_nYear == 1 ) { + return "" + ts1.m_nYear; + } else { + return "" + ts1.m_nYear + toDelim + (ts2.m_nYear-1); + } + } else if ( isMonthBoundry1 && isMonthBoundry2 ) { // no need to indicate day of month + if ( ts2.m_nMonth == 1 ) { + ts2.m_nMonth=13; + ts2.m_nYear--; + } + if ( ts2.m_nYear == ts1.m_nYear ) { + if ( ts2.m_nMonth-ts1.m_nMonth == 1 ) { + return monthStr[ts1.m_nMonth-1] + " " + ts1.m_nYear; + } else { + return monthStr[ts1.m_nMonth-1]+ toDelim + monthStr[ts2.m_nMonth-1-1] + " " + ts1.m_nYear; + } + } else { + return monthStr[ts1.m_nMonth-1] + " " + ts1.m_nYear + toDelim + + monthStr[ts2.m_nMonth-1-1] + " " + ts2.m_nYear; + } + } + + if ( isMidnight1 && isMidnight2 ) { // no need to indicate HH:MM + if ( TimeUtil.getJulianDay( self.max() ) - TimeUtil.getJulianDay( self.min() ) == 1 ) { + return TimeDatumFormatter.DAYS.format( self.min() ); + } else { + Datum endtime= self.max().subtract( Datum.create( 1, Units.days ) ); + return TimeDatumFormatter.DAYS.format( self.min() ) + toDelim + + TimeDatumFormatter.DAYS.format( endtime ); + } + + } else { + DatumFormatter timeOfDayFormatter; + + if ( seconds<1. ) timeOfDayFormatter= TimeDatumFormatter.MILLISECONDS; + else if ( seconds<60. ) timeOfDayFormatter= TimeDatumFormatter.MILLISECONDS; + else if ( seconds<3600. ) timeOfDayFormatter= TimeDatumFormatter.SECONDS; + else timeOfDayFormatter= TimeDatumFormatter.MINUTES; + + int maxDay= TimeUtil.getJulianDay(self.max()); + if ( TimeUtil.getSecondsSinceMidnight(self.max())==0 ) maxDay--; // want to have 24:00, not 00:00 + if ( maxDay== TimeUtil.getJulianDay(self.min()) ) { + return TimeDatumFormatter.DAYS.format(self.min()) + + " " + efficientTime( self.min(), self.max(), self ) + + " to " + efficientTime( self.max(), self.min(), self ); + } else { + String t1str= efficientTime( self.min(), self.max(), self ); + String t2str= efficientTime( self.max(), self.min(), self ); + return TimeDatumFormatter.DAYS.format( self.min() ) + " " + t1str + + " to " + TimeDatumFormatter.DAYS.format( self.max() ) + " " + t2str; + } + } + } + + /** + * return a list of DatumRanges that together cover the space identified + * by bounds. The list should contain one DatumRange that is equal to + * element, which should define the phase and period of the list elements. + * For example, + *
     DatumRange bounds= DatumRangeUtil.parseTimeRangeValid( '2006' );
    +     * DatumRange first= DatumRangeUtil.parseTimeRangeValid( 'Jan 2006' );
    +     * List list= generateList( bounds, first );
    + * Note the procedure calls element.previous until bound.min() is contained, + * then calls bound.max until bound.max() is contained. + * + * @param bounds range to be covered. + * @param element range defining the width and phase of each list DatumRange. + * + */ + public static List generateList( DatumRange bounds, DatumRange element ) { + + ArrayList result= new ArrayList(); + DatumRange dr= element; + while ( dr.max().gt(bounds.min()) ) { + result.add(0,dr); + dr= dr.previous(); + } + dr= element.next(); + while( dr.min().lt(bounds.max() ) ) { + result.add(dr); + dr= dr.next(); + } + return result; + } + + + public static DatumRange newDimensionless(double lower, double upper) { + return new DatumRange( Datum.create(lower), Datum.create(upper) ); + } + + public static DatumRange parseDatumRange( String str, Units units ) throws ParseException { + if ( units instanceof TimeLocationUnits ) { + return parseTimeRange( str ); + } else { + // consider Patterns -- dash not handled because of negative sign. + String[] ss= str.split("to"); + if ( ss.length==1 ) { + ss= str.split("\u2013"); + } + if ( ss.length != 2 ) { + if ( ss.length==3 ) { + ss[0]= "-"+ss[1]; + ss[1]= ss[2]; + } else { + throw new IllegalArgumentException("failed to parse: "+str); + } + } + + // TODO: handle "124.0 to 140.0 kHz" when units= Units.hertz + Datum d2; + try { + d2= DatumUtil.parse(ss[1]); + if ( d2.getUnits()==Units.dimensionless ) d2= units.parse( ss[1] ); + } catch ( ParseException e ) { + d2= units.parse( ss[1] ); + } + Datum d1= d2.getUnits().parse( ss[0] ); + + if ( d1.getUnits().isConvertableTo(units) ) { + return new DatumRange( d1.convertTo(units), d2.convertTo(units) ); + } else { + throw new ParseException( "Can't convert parsed unit ("+d1.getUnits()+") to "+units, 0 ); + } + } + } + + + /** Temporary Hack until apps are moved over to the new dasQCore --cwp + * + * This provides unambiguous rules for parsing all types datum ranges strictly + * from strings, with no out of band information. This was introduced to + * support das2stream parsing. + * + * Examples include: "2013 to 2015 UTC" "3 to 4 kg" "2015-05-05T00:00/2015-06-02T00:00" + * @param str the string representing a time. + * @return the DatumRange interpreted. + * @throws java.text.ParseException + */ + public static DatumRange parseDatumRange(String str) throws ParseException { + str= str.trim(); + if ( str.endsWith("UTC" ) ) { + return parseTimeRange(str.substring(0,str.length()-3)); + // Version in DasQCore can look for times without UTC appended, this version + // can't + } else { + // consider Patterns -- dash not handled because of negative sign. + // 0to4 apples -> 0 to 4 units=apples + // 0 to 35 sector -> 0 to 35 units=sector note "to" in sector. + String[] ss= str.split("to",2); + if ( ss.length==1 ) { + ss= str.split("\u2013"); + } + if ( ss.length==1 ) { + return parseTimeRange(ss[0]); + } else if ( ss.length != 2 ) { + throw new ParseException("failed to parse: "+str,0); + } + + Datum d2; + Datum d1; + try { + d2= DatumUtil.parse(ss[1]); + d1= d2.getUnits().parse( ss[0] ); + return new DatumRange( d1, d2 ); + } catch ( ParseException ex ) { + try { + return parseTimeRange(str); + } catch ( ParseException ex2 ) { + throw ex; + } + } + } + } + + public static DatumRange parseDatumRange( String str, DatumRange orig ) throws ParseException { + return parseDatumRange( str, orig.getUnits() ); + } + + /** + * returns DatumRange relative to this, where 0. is the minimum, and 1. is the maximum. + * For example rescale(1,2) is scanNext, rescale(0.5,1.5) is zoomOut. + * @param dr a DatumRange with nonzero width. + * @param min the new min normalized with respect to this range. 0. is this range's min, 1 is this range's max, 0 is + * min-width. + * @param max the new max with normalized wrt this range. 0. is this range's min, 1 is this range's max, 0 is + * min-width. + * @return new DatumRange. + */ + public static DatumRange rescale( DatumRange dr, double min, double max ) { + Datum w= dr.width(); + if ( !w.isFinite() ) { + throw new RuntimeException("width is not finite"); + } + if ( w.doubleValue( w.getUnits() )==0. ) { + // condition that might cause an infinate loop! For now let's check for this and throw RuntimeException. + throw new RuntimeException("width is zero!"); + } + return new DatumRange( dr.min().add( w.multiply(min) ), dr.min().add( w.multiply(max) ) ); + } + + /** + * returns DatumRange relative to this, where 0. is the minimum, and 1. is the maximum, but the + * scaling is done in the log space. + * For example, rescaleLog( [0.1,1.0], -1, 2 )-> [ 0.01, 10.0 ] + * @param dr a DatumRange with nonzero width. + * @param min the new min normalized with respect to this range. 0. is this range's min, 1 is this range's max, 0 is + * min-width. + * @param max the new max with normalized wrt this range. 0. is this range's min, 1 is this range's max, 0 is + * min-width. + * @return new DatumRange. + */ + public static DatumRange rescaleLog( DatumRange dr, double min, double max ) { + Units u= dr.getUnits(); + double s1= DasMath.log10( dr.min().doubleValue(u) ); + double s2= DasMath.log10( dr.max().doubleValue(u) ); + double w= s2 - s1; + if ( w==0. ) { + // condition that might cause an infinate loop! For now let's check for this and throw RuntimeException. + throw new RuntimeException("width is zero!"); + } + s2= DasMath.exp10( s1 + max * w ); // danger + s1= DasMath.exp10( s1 + min * w ); + return new DatumRange( s1, s2, u ); + } + + /** + * returns the position within dr, where 0. is the dr.min(), and 1. is dr.max() + * @param dr a datum range with non-zero width. + * @param d a datum to normalize with respect to the range. + * @return a double indicating the normalized datum. + */ + public static double normalize( DatumRange dr, Datum d ) { + return d.subtract(dr.min()).divide(dr.width()).doubleValue(Units.dimensionless); + } + + /** + * returns the position within dr, where 0. is the dr.min(), and 1. is dr.max() + * @param dr a datum range with non-zero width. + * @param d a datum to normalize with respect to the range. + * @return a double indicating the normalized datum. + */ + public static double normalizeLog( DatumRange dr, Datum d ) { + Units u= dr.getUnits(); + double d0= Math.log( dr.min().doubleValue( u ) ); + double d1= Math.log( dr.max().doubleValue( u ) ); + double dd= Math.log( d.doubleValue( u ) ); + return (dd-d0) / ( d1-d0 ); + } + + /** + * Like DatumRange.intesects, but returns a zero-width range when the two do + * not intersect. When they do not intersect, the min or max of the first range + * is returned, depending on whether or not the second range is above or below + * the first range. Often this allows for simpler code. + * @see DatumRange.intersection. + */ + public static DatumRange sloppyIntersection( DatumRange range, DatumRange include ) { + Units units= range.getUnits(); + double s11= range.min().doubleValue(units); + double s12= include.min().doubleValue(units); + double s21= range.max().doubleValue(units); + if ( range.intersects(include) ) { + double s1= Math.max( s11, s12 ); + double s22= include.max().doubleValue(units); + double s2= Math.min( s21, s22 ); + return new DatumRange( s1, s2, units ); + } else { + if ( s11 + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import org.das2.system.DasLogger; +import org.das2.util.DasMath; +import java.text.*; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.DatumFormatterFactory; +import org.das2.datum.format.DefaultDatumFormatterFactory; +import org.das2.datum.format.EnumerationDatumFormatterFactory; +import org.das2.datum.format.ExponentialDatumFormatter; +import org.das2.datum.format.TimeDatumFormatter; + +/** + * + * @author Edward West + */ +public final class DatumUtil { + + /** Creates a new instance of DatumUtil */ + private DatumUtil() { + } + + public static DatumFormatter bestFormatter( DatumVector datums ) { + double[] array; + Units units; + + if ( datums.getUnits() instanceof EnumerationUnits ) { + return EnumerationDatumFormatterFactory.getInstance().defaultFormatter(); + } + + if ( datums.getUnits() instanceof TimeLocationUnits ) { + Datum t1= datums.get(0); + int nticks= datums.getLength(); + Datum t2= datums.get(nticks-1); + return DatumUtil.bestTimeFormatter(t1,t2,nticks-1); + } + + if ( datums.getUnits() instanceof LocationUnits ) { + array= new double[ datums.getLength() ]; + units= ((LocationUnits)datums.get(0).getUnits()).getOffsetUnits(); + array[0]= 0.; + for ( int i=1; i(gcd*0.1) ) { // don't look at fuzzy zero + int ee= (int)Math.floor(0.05+DasMath.log10(Math.abs(d))); + if ( ee(discernable*0.1) ) { // don't look at fuzzy zero + int ee= (int)Math.floor(0.05+DasMath.log10(Math.abs(d))); + if ( ee 60 ) { + return DefaultDatumFormatterFactory.getInstance().defaultFormatter(); + } else if ( smallestExp < -3 || smallestExp > 3 ) { + return new ExponentialDatumFormatter( smallestExp - (-1*fracDigits) +1 , smallestExp ); + } else { + int nFraction= -1 * (int)Math.floor(0.05+DasMath.log10(discernable)); + nFraction= nFraction<0 ? 0 : nFraction; + String formatString = zeros(nFraction); + return factory.newFormatter(formatString); + } + } + catch (java.text.ParseException pe) { + Logger logger = DasLogger.getLogger(); + //Should not happen under normal circumstances, so bail. + RuntimeException re = new RuntimeException(pe); + logger.log(Level.SEVERE, pe.getMessage(), re); + throw re; + } + } + + private static String exp(int power) { + StringBuffer buffer = new StringBuffer(power+4); + for (int i = 0; i < power - 1; i++) { + buffer.append('#'); + } + buffer.append("0.#E0"); + return buffer.toString(); + } + + private static final String zeros100= "0.00000000000000000000" + + "0000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000"; + public static String zeros(int count) { + if ( count < 0 ) return "0"; + else if ( count <= 100 ) return zeros100.substring(0,count+2); + else { + StringBuffer buff = new StringBuffer(count+2).append("0."); + for (int index = 0; index < count; index++) { + buff.append('0'); + } + return buff.toString(); + } + } + + public static DatumFormatter bestTimeFormatter(Datum minimum, Datum maximum, int nsteps) { + double secondsPerStep = maximum.subtract(minimum).doubleValue(Units.seconds) / ( nsteps ); + double daysPerStep= secondsPerStep/86400; + if (secondsPerStep < 1.) { + return TimeDatumFormatter.MILLISECONDS; + } + else if (secondsPerStep < 60.) { + return TimeDatumFormatter.SECONDS; + } + else if (secondsPerStep < 3600.) { + return TimeDatumFormatter.MINUTES; + } + else if (secondsPerStep < 86400. ) { + return TimeDatumFormatter.HOURS; + } + else if ( secondsPerStep < 28*86400.0 ) { + return TimeDatumFormatter.DAYS; + } + else if ( secondsPerStep < 31557600.0 ) { + return TimeDatumFormatter.MONTHS; + } + else { + return TimeDatumFormatter.YEARS; + } + } + + /** + * attempt to parse the string as a datum. Note that if the + * units aren't specified, then of course the Datum will be + * assumed to be dimensionless. + * @throws ParseException when the double can't be parsed or the units aren't recognized. + */ + public static Datum parse(java.lang.String s) throws ParseException { + String[] ss= s.trim().split("\\s"); + Units units; + double value; + if ( ss.length==1 ) { + units= Units.dimensionless; + } else { + try { + units= Units.lookupUnits(ss[1]); + } catch ( IllegalArgumentException e ) { + throw new ParseException( e.getMessage(), 0 ); + } + } + return Datum.create( Double.parseDouble(ss[0]), units ); + } + + public static Datum parseValid(java.lang.String s) { + try { + return parse( s ); + } catch ( ParseException e ) { + throw new RuntimeException(e); + } + } + + public static Datum createValid( String s ) { + return Datum.create( Double.parseDouble(s), Units.dimensionless ); + } + + public static double[] doubleValues( Datum[] datums, Units units ) { + double[] result= new double[datums.length]; + for (int j=0; j 20) + score = 20/nn; + else + score = nn; + + if (score > bestScore) { + bestScore = score; + bestDatum = dd; + } + } + return bestDatum; + } + + +} diff --git a/dasCoreDatum/src/org/das2/datum/DatumVector.java b/dasCore/src/main/java/org/das2/datum/DatumVector.java similarity index 100% rename from dasCoreDatum/src/org/das2/datum/DatumVector.java rename to dasCore/src/main/java/org/das2/datum/DatumVector.java diff --git a/dasCore/src/main/java/org/das2/datum/EnumerationUnits.java b/dasCore/src/main/java/org/das2/datum/EnumerationUnits.java new file mode 100755 index 000000000..3449b2158 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/EnumerationUnits.java @@ -0,0 +1,228 @@ +/* File: EnumerationUnits.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.datum; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.das2.datum.format.DatumFormatterFactory; +import org.das2.datum.format.EnumerationDatumFormatterFactory; + +/** + * Units class for mapping arbitary objects to Datums. Nothing about the contract + * for a Datum requires that they correspond to physical quanities, and we can + * assign a mapping from numbers to objects using this class. This allows + * information such as "Cluster 1" or "Spin Flip" to be encoded. + * + * This is used to model ordinal or nominal data, as described in + * http://en.wikipedia.org/wiki/Level_of_measurement + * + * @author Jeremy + */ +public class EnumerationUnits extends Units { + + private HashMap ordinals; // maps from ordinal to Datum.Integer + private int highestOrdinal; // highest ordinal for each Units type + private HashMap objects; // maps from object to Datum.Integer + private HashMap invObjects; // maps from Datum.Integer to object + public static HashMap unitsInstances; + + public EnumerationUnits(String id) { + this(id, ""); + } + + public EnumerationUnits(String id, String description) { + super(id, description); + highestOrdinal = 0; + ordinals = new HashMap(); + objects = new HashMap(); + invObjects = new HashMap(); + } + + public static Datum createDatumAndUnits(Object object) { + return create(object).createDatum(object); + } + + /** + * creates the datum, explicitly setting the ordinal. Use with caution. + * @param ival + * @param sval + * @throws IllegalArgumentException if this ordinal is already taken by a different value. + */ + public Datum createDatum(int ival, Object object) { + if (objects.containsKey(object)) { + return objects.get(object); + } else { + if (highestOrdinal < ival) { + highestOrdinal = ival; + } + Integer ordinal = new Integer(ival); + Datum result = new Datum.Double(ordinal, this); + if ( ordinals.containsKey(ordinal) ) { + Datum d= ordinals.get(ordinal); + if ( ! invObjects.get( d ).equals(object) ) { + throw new IllegalArgumentException("value already exists for this ordinal!"); + } + } + ordinals.put(ordinal, result); + invObjects.put(result, object); + objects.put(object, result); + return result; + } + + } + + public DatumVector createDatumVector(Object[] objects) { + double[] doubles = new double[objects.length]; + for (int i = 0; i < objects.length; i++) { + doubles[i] = createDatum(objects[i]).doubleValue(this); + } + return DatumVector.newDatumVector(doubles, this); + } + + public synchronized Datum createDatum(Object object) { + if (objects.containsKey(object)) { + return objects.get(object); + } else { + highestOrdinal++; + Integer ordinal = new Integer(highestOrdinal); + Datum result = new Datum.Double(ordinal, this); + ordinals.put(ordinal, result); + invObjects.put(result, object); + objects.put(object, result); + return result; + } + } + + /** + * provides access to map of all values. + * @return + */ + public Map getValues() { + return Collections.unmodifiableMap(ordinals); + } + + public Datum createDatum(int value) { + Integer key = new Integer(value); + if (ordinals.containsKey(key)) { + return ordinals.get(key); + } else { + throw new IllegalArgumentException("No Datum exists for this ordinal: " + value); + } + } + + public Datum createDatum(long value) { + return createDatum((int) value); + } + + public Datum createDatum(Number value) { + return createDatum(value.intValue()); + } + + public Object getObject(Datum datum) { + if (invObjects.containsKey(datum)) { + return invObjects.get(datum); + } else { + throw new IllegalArgumentException("This Datum doesn't map back to an object! This shouldn't happen!"); + } + } + + public static synchronized EnumerationUnits create(Object o) { + if (unitsInstances == null) + unitsInstances = new HashMap(); + Class c = o.getClass(); + if (unitsInstances.containsKey(c)) { + return unitsInstances.get(c); + } else { + Units u= null; + try { + u= Units.getByName(c.toString() + "Unit"); + } catch ( IllegalArgumentException ex ) { + EnumerationUnits result = new EnumerationUnits(c.toString() + "Unit"); + unitsInstances.put(c, result); + return result; + } + if ( u instanceof EnumerationUnits ) { + return (EnumerationUnits)u; + } else { + throw new IllegalArgumentException("unit already exists: "+u); + } + } + } + + public Datum createDatum(double d) { + return createDatum((int) d); + } + + public Datum createDatum(double d, double resolution) { + return createDatum((int) d); + } + + public DatumFormatterFactory getDatumFormatterFactory() { + return EnumerationDatumFormatterFactory.getInstance(); + } + + public Datum subtract(Number a, Number b, Units bUnits) { + throw new IllegalArgumentException("subtract on EnumerationUnit"); + } + + public Datum add(Number a, Number b, Units bUnits) { + throw new IllegalArgumentException("add on EnumerationUnit"); + } + + public Datum divide(Number a, Number b, Units bUnits) { + throw new IllegalArgumentException("divide on EnumerationUnit"); + } + + public Datum multiply(Number a, Number b, Units bUnits) { + throw new IllegalArgumentException("multiply on EnumerationUnit"); + } + + public Datum parse(String s) throws java.text.ParseException { + Datum result = null; + for (Iterator i = objects.keySet().iterator(); i.hasNext();) { + Object key = i.next(); + Object value = objects.get(key); + if (key.toString().equals(s)) { // if the look the same, they are the same + if (result == null) { + result = (Datum) objects.get(key); + } else { + throw new IllegalStateException("Multiple Objects' string representations match"); + } + } + } + if (result == null) { + throw new java.text.ParseException("no objects match \"" + s + "\"", 0); + } + return result; + } + + public int getHighestOrdinal() { + return this.highestOrdinal; + } + + public String toString() { + return this.getId() + "(ordinal)"; + } +} diff --git a/dasCoreDatum/src/org/das2/datum/InconvertibleUnitsException.java b/dasCore/src/main/java/org/das2/datum/InconvertibleUnitsException.java similarity index 100% rename from dasCoreDatum/src/org/das2/datum/InconvertibleUnitsException.java rename to dasCore/src/main/java/org/das2/datum/InconvertibleUnitsException.java diff --git a/dasCoreDatum/src/org/das2/datum/LocationUnits.java b/dasCore/src/main/java/org/das2/datum/LocationUnits.java similarity index 100% rename from dasCoreDatum/src/org/das2/datum/LocationUnits.java rename to dasCore/src/main/java/org/das2/datum/LocationUnits.java diff --git a/dasCore/src/main/java/org/das2/datum/MonthDatumRange.java b/dasCore/src/main/java/org/das2/datum/MonthDatumRange.java new file mode 100644 index 000000000..e544eca32 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/MonthDatumRange.java @@ -0,0 +1,82 @@ +/* + * MonthDatumRange.java + * + * Created on November 15, 2004, 4:28 PM + */ + +package org.das2.datum; + +/** + * + * @author Jeremy + */ +public class MonthDatumRange extends DatumRange { + + int width; + int widthDigit; + int[] start; + int[] end; + + public MonthDatumRange( int[] start, int[] end ) { + super( TimeUtil.toDatum( start ), + TimeUtil.toDatum( end ) ); + widthDigit= -1; + int[] widthArr= new int[7]; + boolean haveNonZeroDigit= false; + for ( int i=0; i<7; i++ ) { + widthArr[i]= end[i]-start[i]; + } + while( widthArr[1]<0 ) { + widthArr[1]+= 12; + widthArr[0]--; + } + for ( int i=0; i<7; i++ ) { + if ( widthArr[i]!=0 ) { + if ( widthDigit!=-1 ) { + throw new IllegalArgumentException("MonthDatumRange must only vary in month or year, not both"); + } else { + widthDigit=i; + width= widthArr[widthDigit]; + } + } + } + this.start= start; + this.end= end; + } + + public DatumRange next() { + int[] end1= new int[7]; + for ( int i=0; i<7; i++ ) { + end1[i]= this.end[i]; + } + end1[widthDigit]= end1[widthDigit]+this.width; + switch ( widthDigit ) { + case 1: while( end1[1]>12 ) { + end1[1]-= 12; + end1[0]++; + } + case 0: break; + default: throw new IllegalArgumentException("not implemented"); + } + return new MonthDatumRange( this.end, end1 ); + } + + public DatumRange previous() { + int[] start1= new int[7]; + for ( int i=0; i<7; i++ ) { + start1[i]= this.start[i]; + } + start1[widthDigit]= start1[widthDigit]-this.width; + switch ( widthDigit ) { + case 1: while( start1[1]<1 ) { + start1[1]+= 12; + start1[0]--; + } + case 0: break; + default: throw new IllegalArgumentException("not implemented"); + } + + return new MonthDatumRange( start1, this.start ); + } + +} diff --git a/dasCore/src/main/java/org/das2/datum/NumberUnits.java b/dasCore/src/main/java/org/das2/datum/NumberUnits.java new file mode 100755 index 000000000..c9f06e1e1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/NumberUnits.java @@ -0,0 +1,276 @@ +/* File: Units.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import org.das2.util.DasMath; +import java.math.*; +import java.text.ParseException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.das2.datum.format.DefaultDatumFormatterFactory; +import org.das2.datum.format.DatumFormatterFactory; + +/** + * + * @author jbf + */ +public class NumberUnits extends Units { + + public NumberUnits(String id) { + this(id,""); + } + + public NumberUnits(String id, String description) { + super(id,description); + } + + public Datum createDatum( double value ) { + return new Datum.Double( value, this, 0. ); + } + + public Datum createDatum( double value, double resolution ) { + return new Datum.Double( value, this, resolution ); + } + + public Datum createDatum( int value ) { + return new Datum.Double( value, this ); + } + + public Datum createDatum( long value ) { + return new Datum.Double( value, this ); + } + + public Datum createDatum( Number value ) { + return new Datum.Double( value, this ); + } + + + public DatumFormatterFactory getDatumFormatterFactory() { + return DefaultDatumFormatterFactory.getInstance(); + } + + /* + * @returns double[2], [0] is number, [1] is the resolution + */ + private double[] parseDecimal( String s ) { + s= s.trim(); + BigDecimal bd= new BigDecimal(s); + + if ( bd.scale()>0 ) { + double resolution= DasMath.exp10( -1*bd.scale() ); + return new double[] { Double.parseDouble(s), resolution }; + } else { + int ie= s.indexOf( 'E' ); + if ( ie==-1 ) ie= s.indexOf('e'); + String mant; + if ( ie==-1 ) { + int id= s.indexOf('.'); + double[] dd= new double[2]; + dd[0]= Double.parseDouble(s); + if ( id==-1 ) { + dd[1]= 1.; + } else { + int scale= s.length()-id-1; + dd[1]= DasMath.exp10(-1*scale); + } + return dd; + } else { + mant= s.substring(0,ie); + double[] dd= parseDecimal( mant ); + double exp= DasMath.exp10( Double.parseDouble( s.substring(ie+1) ) ); + dd[0]= dd[0] * exp; + dd[1]= dd[1] * exp; + return dd; + } + } + } + + // note + and - are left out because of ambiguity with sign. + private static Pattern expressionPattern= Pattern.compile( "(.+)([\\*/])(.+)" ); + + private Datum parseExpression( String s ) throws ParseException { + Matcher m= expressionPattern.matcher(s); + if ( !m.matches() ) throw new IllegalArgumentException("not an expression"); + String operator= m.group(2); + Datum operand1; + try { + operand1= Units.dimensionless.parse( m.group(1) ); + } catch ( IllegalArgumentException e ) { + operand1= this.parse( m.group(1) ); + } + Datum operand2; + try { + operand2= Units.dimensionless.parse( m.group(3) ); + } catch ( IllegalArgumentException e ) { + operand2= this.parse( m.group(3) ); + } + Datum result; + if ( operator.equals("*") ) { + result= operand1.multiply(operand2); + } else if ( operator.equals("/") ) { + result= operand1.divide(operand2); + } else { + throw new IllegalArgumentException("Bad operator: "+operator+" of expression "+s); + } + return result; + } + + /* + * parse the string in the context of this. If units are not + * specified, then assume units are this. Otherwise, parse the + * unit and attempt to convert to this before creating the unit. + */ + public Datum parse(String s) throws ParseException { + expressionPattern= Pattern.compile( "(.+)([\\*/])(.+)" ); + if ( expressionPattern.matcher(s).matches() ) { + Datum result= parseExpression( s ); + if ( result.getUnits()==Units.dimensionless ) { + result= this.createDatum( result.doubleValue() ); + } else { + // throw exception if it's not convertable + result= result.convertTo(this); + } + return result; + } else { + try { + s= s.trim(); + if ( s.endsWith(this.getId()) ) { + s= s.substring(0,s.length()-this.getId().length()); + } + String[] ss= s.split("\\s+"); + double[] dd= parseDecimal(ss[0]); + if ( ss.length==1 ) { + return Datum.create( dd[0], this, dd[1] ); + } else { + String unitsString= ss[1]; + for ( int i=2; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import org.das2.datum.format.DatumFormatterFactory; +import org.das2.datum.format.TimeDatumFormatterFactory; + +/** + * + * @author jbf + */ +public class TimeLocationUnits extends LocationUnits { + + /* TimeLocationUnits class is introduced because it is often necessary to + * easily identify a time quantity, for instance when deciding whether to + * use a timeAxis or not. (TimeAxis is no longer a class, but we use a + * special tickV for time units.) + */ + + public TimeLocationUnits( String id, String description, Units offsetUnits, Basis basis ) { + super(id,description,offsetUnits,basis); + } + + public DatumFormatterFactory getDatumFormatterFactory() { + return TimeDatumFormatterFactory.getInstance(); + } + + public Datum parse(String s) throws java.text.ParseException { + CalendarTime ct = new CalendarTime(s); + return ct.toDatum(); + } + + public String getTimeZone() { + return "UT"; + } + +} diff --git a/dasCore/src/main/java/org/das2/datum/TimeUtil.java b/dasCore/src/main/java/org/das2/datum/TimeUtil.java new file mode 100755 index 000000000..df962f885 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/TimeUtil.java @@ -0,0 +1,400 @@ +/* File: TimeUtil.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 22, 2003, 11:00 AM by Jeremy Faden + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import java.text.ParseException; +import java.util.Map; +import java.util.HashMap; +import org.das2.datum.format.TimeDatumFormatter; + +/** + * Various time utilities + * @author jbf + */ +public final class TimeUtil { + + private TimeUtil() { + } + + // One of the few times package private is useful + final static int[][] daysInMonth = { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0} + }; + + // One of the few times package private is useful + final static int[][] dayOffset = { + {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} + }; + + public static int daysInMonth(int month, int year) { + return daysInMonth[isLeapYear(year)?1:0][month]; + } + + public static int julday( int month, int day, int year ) { + int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - + 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + + 275 * month / 9 + day + 1721029; + return jd; + } + + public static int dayOfYear( int month, int day, int year ) { + return day + dayOffset[isLeapYear(year)?1:0][month]; + } + + // intruduced to aid in debugging + public static class TimeDigit{ + CalendarTime.Step ordinal; // YEAR, MONTH, etc. + String label; + int divisions; // approximate + private static Map digits = + new HashMap(); + @Override + public String toString(){ + return label; + } + private TimeDigit(CalendarTime.Step ordinal, String label, int divisions){ + this.ordinal = ordinal; + this.label = label; + this.divisions = divisions; + digits.put(ordinal, this); + } + public CalendarTime.Step getOrdinal(){ + return ordinal; + } + public int divisions(){ + return divisions; + } + public static TimeDigit fromOrdinal(CalendarTime.Step ordinal){ + return digits.get(ordinal); + } + } + + public static final TimeDigit TD_YEAR = new TimeDigit( CalendarTime.Step.YEAR, "YEAR", 12 ); + public static final TimeDigit TD_MONTH = new TimeDigit( CalendarTime.Step.MONTH, "MONTH", 30 ); + public static final TimeDigit TD_DAY = new TimeDigit( CalendarTime.Step.DAY, "DAY", 24 ); + public static final TimeDigit TD_HOUR = new TimeDigit( CalendarTime.Step.HOUR, "HOUR", 60 ); + public static final TimeDigit TD_MINUTE = new TimeDigit( CalendarTime.Step.MINUTE, "MINUTE", 60 ); + public static final TimeDigit TD_SECOND = new TimeDigit( CalendarTime.Step.SECOND, "SECOND", 1000 ); + public static final TimeDigit TD_MILLI= new TimeDigit( CalendarTime.Step.MILLISEC, "MILLISECONDS", 1000 ); + public static final TimeDigit TD_MICRO = new TimeDigit( CalendarTime.Step.MICROSEC, "MICROSECONDS", 1000 ); + public static final TimeDigit TD_NANO = new TimeDigit( CalendarTime.Step.NANOSEC, "NANOSECONDS", 1000 ); + + public static double getSecondsSinceMidnight(Datum datum) { + double xx= datum.doubleValue(Units.t2000); + if (xx<0) { + xx= xx % 86400; + if (xx==0) { + return 0; + } else { + return 86400+xx; + } + } else { + return xx % 86400; + } + } + + public static double getMicroSecondsSinceMidnight(Datum datum) { + double xx= datum.doubleValue( Units.us2000 ); + if (xx<0) { + xx= xx % 86400e6; + if (xx==0) { + return 0; + } else { + return 86400e6+xx; + } + } else { + return xx % 86400e6; + } + } + + /** + * return the the integer number of days that have elapsed since roughly Monday, January 1, 4713 BC. Julian Day + * is defined as starting at noon UT, here is is defined starting at midnight. + * @param datum + * @return + */ + public static int getJulianDay( Datum datum ) { + double xx= datum.doubleValue(Units.mj1958); + return (int)Math.floor( xx ) + 2436205; + } + + /** + *Break the Julian day apart into month, day year. This is based on + *http://en.wikipedia.org/wiki/Julian_day (GNU Public License), and + *was introduced when toTimeStruct failed when the year was 1886. + *@param julian the (integer) number of days that have elapsed since the initial epoch at noon Universal Time (UT) Monday, January 1, 4713 BC + *@return a CalendarTime with the month, day and year fields set. + */ + public static int[] julianToGregorian( int julian ) { + + int[] lRet = {0,0,0}; + + int j = julian + 32044; + int g = j / 146097; + int dg = j % 146097; + int c = (dg / 36524 + 1) * 3 / 4; + int dc = dg - c * 36524; + int b = dc / 1461; + int db = dc % 1461; + int a = (db / 365 + 1) * 3 / 4; + int da = db - a * 365; + int y = g * 400 + c * 100 + b * 4 + a; + int m = (da * 5 + 308) / 153 - 2; + int d = da - (m + 4) * 153 / 5 + 122; + int Y = y - 4800 + (m + 2) / 12; + int M = (m + 2) % 12 + 1; + int D = d + 1; + + lRet[0] = Y; + lRet[1] = M; + lRet[2] = D; + return lRet; + } + + /** Here millis are the number of milliseconds after the second, and micros are + * the number of micro seconds after the millisecond not the second. + * + * returns int[] { year, month, day, hour, minute, second, millis, micros } + */ + public static int[] toTimeArray( Datum time ) { + + CalendarTime ts= new CalendarTime( time ); + int millis = (int) (ts.m_nNanoSecond / 1000000); + int micros = (int) ((ts.m_nNanoSecond % 1000000)/ 1000); + + return new int[] { ts.m_nYear, ts.m_nMonth, ts.m_nDom, ts.m_nHour, ts.m_nMinute, ts.m_nSecond, + millis, micros }; + } + + public static Datum toDatum( int[] timeArray ) { + int year = timeArray[0]; + int month = timeArray[1]; + int day = timeArray[2]; + if ( timeArray[1]<1 ) { + throw new IllegalArgumentException(""); + } + int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - + 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + + 275 * month / 9 + day + 1721029; + int hour = (int)timeArray[3]; + int minute = (int)timeArray[4]; + double seconds = timeArray[5] + hour*(float)3600.0 + minute*(float)60.0 + timeArray[6]/1e9; + double us2000= UnitsConverter.getConverter(Units.mj1958,Units.us2000).convert(( jd - 2436205 ) + seconds / 86400. ); + return Datum.create( us2000, Units.us2000 ); + } + + /** Is year a leap year. + * Warning: Not tested for years prior to 1. + */ + public static boolean isLeapYear( int year ) { + if(year % 4 != 0) return false; + if(year % 400 == 0) return true; + if(year % 100 == 0) return false; + else return true; + } + + + public static Datum next( TimeDigit td, int count, Datum datum ) { + if ( td==TD_NANO ) throw new IllegalArgumentException("not supported nanos"); + CalendarTime ct = new CalendarTime(datum).step(td.getOrdinal(), count); + Datum result= ct.toDatum(); + return result; + } + + public static Datum next(CalendarTime.Step step, Datum datum ) { + CalendarTime ct = new CalendarTime(datum).step(step, 1); + return ct.toDatum(); + } + + + /** step down the previous ordinal. If the datum is already at an ordinal + * boundry, then step down by one ordinal. + * @param step + * @param datum + * @return + */ + public static Datum prev(CalendarTime.Step step, Datum datum ) { + CalendarTime ct= new CalendarTime(datum).step(step, -1); + return ct.toDatum(); + } + + public static Datum now() { + double us2000= ( System.currentTimeMillis() - 946684800e3 ) * 1000; + return Units.us2000.createDatum(us2000); + } + + /** + * @param year the year + * @param month the month + * @param day the day of month, unless month==0, then day is day of year. + * @param hour additional hours + * @param minute additional minutes + * @param second additional seconds + * @param units the Units in which to return the result. + * @return a double in the context of units. + */ + public static double convert(int year, int month, int day, int hour, int minute, + double second, TimeLocationUnits units) { + // if month==0, then day is doy (day of year). + int jd; + if ( month>0 ) { + jd = julday(month,day,year); + } else { + // if month==0 then day is doy + int month1= 1; + int day1= 1; + jd = julday(month1,day1,year); + jd+= ( day - 1 ); + } + + second+= hour*3600.0 + minute*60.0; + + double us2000 = (jd-2451545)*86400000000. + second * 1000000; + + if ( units==Units.us2000 ) { + return us2000; + } else { + return Units.us2000.convertDoubleTo(units, us2000); + } + } + + private final static String[] mons= { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + /** + * returns 1..12 for the English month name + * + * @throws ParseException if the name isn't recognized + */ + public static int monthNumber( String s ) throws ParseException { + s= s.substring(0,3); + for ( int i=0; i<12; i++ ) { + if ( s.equalsIgnoreCase( mons[i] ) ) return i+1; + } + throw new ParseException("Unable to parse month", 0 ); + } + + /** + * returns "Jan", "Feb", ... for month number (1..12). + * @param mon integer from 1 to 12. + * @return three character English month name. + */ + public static String monthNameAbbrev( int mon ) { + if ( mon<1 || mon>12 ) throw new IllegalArgumentException("invalid month number: "+mon); + return mons[mon-1]; + } + + /** Creates a datum from a string + * @param s + * @throws ParseException + * @return + */ + public static Datum create(String s) throws java.text.ParseException { + CalendarTime ts= new CalendarTime(s); + return ts.toDatum(); + } + + /** creates a Datum from a string which is known to contain + * a valid time format. Throws a RuntimeException if the + * string is not valid. + * @param validString + * @return + */ + public static Datum createValid(String validString ) { + try { + return create( validString ); + } catch ( java.text.ParseException ex ) { + throw new RuntimeException( ex ); + } + } + + public static boolean isValidTime( String string ) { + try { + create( string ); + return true; + } catch ( java.text.ParseException ex ) { + return false; + } + } + + public static Datum prevMidnight(Datum datum) { + //return datum.subtract(getMicroSecondsSinceMidnight(datum), Units.microseconds); + return datum.subtract(getSecondsSinceMidnight(datum), Units.seconds); + } + + /** + * returns the next midnight, or this datum if we are already on midnight. + * @param datum + * @return + */ + public static Datum nextMidnight( Datum datum ) { + CalendarTime ct = new CalendarTime(datum).step(CalendarTime.Step.DAY, 1); + return ct.toDatum(); + } + /** + * creates a Datum representing the time given in integer years, months, ..., seconds, nanoseconds. The year + * must be at least 1960, and must be a four-digit year. A double in Units.us2000 is used to represent the + * Datum, so resolution will drop as the year drops away from 2000. + * + * @param year four digit year >= 1960. + * @param month integer month, 1..12. + * @param day integer day of month. + * @param hour additional hours + * @param minute additional minutes + * @param second additional seconds + * @param nano additional nanoseconds + * @return a Datum with units Units.us2000. + */ + public static Datum createTimeDatum( int year, int month, int day, int hour, int minute, int second, int nano ) { + //if ( year<1960 ) throw new IllegalArgumentException("year must not be < 1960, and no 2 digit years (year="+year+")"); + if ( year<100 ) throw new IllegalArgumentException("year must not be < 100, and no 2 digit years (year="+year+")"); + int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - + 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + + 275 * month / 9 + day + 1721029; + double microseconds = second*1e6 + hour*3600e6 + minute*60e6 + nano/1e3; + double us2000= UnitsConverter.getConverter(Units.mj1958,Units.us2000).convert(( jd - 2436205 )) + microseconds; + return Datum.create( us2000, Units.us2000 ); + } + + public static void main(String[] args) throws Exception { + System.out.println( TimeUtil.now() ); + System.out.println( Datum.create( TimeUtil.convert(2000,1,2, 0, 0, 0, Units.us2000 ), Units.us2000 )); + Datum x=create( "2000-1-1 0:00:33.45" ); + System.out.println( x ); + + CalendarTime ts= new CalendarTime(x); + System.out.println( ts.toDatum() ); + + TimeDatumFormatter tf = TimeDatumFormatter.DEFAULT; + + for ( int i=0; i<44; i++ ) { + System.out.println(tf.format(x)+"\t"+(long)x.doubleValue(Units.us2000)); + x= TimeUtil.prev(CalendarTime.Step.SECOND,x); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/datum/Units.java b/dasCore/src/main/java/org/das2/datum/Units.java new file mode 100755 index 000000000..87b2a196f --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/Units.java @@ -0,0 +1,821 @@ +/* File: Units.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.das2.datum.format.DatumFormatterFactory; + +/** + * Class for indicating physical units, and other random units. + * @author jbf + */ +public abstract class Units { + + private static final Logger logger= Logger.getLogger("datum.units"); + + private static Map unitsMap = new HashMap(); + + public static final Units dimensionless= new NumberUnits("","dimensionless quantities"); + + public static final Units radians= new NumberUnits("radian"); + public static final Units degrees= new NumberUnits("degrees"); + public static final Units deg= new NumberUnits("deg"); + static { + degrees.registerConverter(radians, new UnitsConverter.ScaleOffset(Math.PI/180.0,0.0) ); + degrees.registerConverter(deg, UnitsConverter.IDENTITY); + } + + /** + * + */ + public static final Units rgbColor= new NumberUnits("rgbColor","256*256*red+256*green+blue"); + + /** + * this is left in in case legacy code needs to see the conversion from dB to dimensionless offset. + */ + private static final class dBConverter extends UnitsConverter { + @Override + public double convert(double value) { + return 10 * Math.log10(value); + } + @Override + public UnitsConverter getInverse() { + if (inverse == null) { + inverse = new UnitsConverter() { + @Override + public double convert(double value) { + return Math.pow(10.0, value / 10.0); + } + @Override + public UnitsConverter getInverse() { + return dBConverter.this; + } + }; + } + return inverse; + } + } + + public static final Units celciusDegrees= new NumberUnits("celcius degrees"); // disambiguate from "deg C" which is the temperature scale + public static final Units fahrenheitDegrees= new NumberUnits("fahrenheit degrees"); // disambiguate from "deg F" which is the temperature scale + + public static final Units hours= new NumberUnits("hr"); + public static final Units minutes= new NumberUnits("min"); + public static final Units seconds= new NumberUnits("s"); + public static final Units seconds2= new NumberUnits("sec"); + //public static final Units seconds3= new NumberUnits("seconds"); // note s was not convertible to seconds. + public static final Units milliseconds= new NumberUnits("ms","milliseconds"); + public static final Units milliseconds2= new NumberUnits("msec"); + public static final Units microseconds= new NumberUnits("microseconds"); + public static final Units microseconds2= new NumberUnits("\u00B5s"); + + public static final Units nanoseconds= new NumberUnits("nanoseconds"); + public static final Units ns= new NumberUnits("ns","nanoseconds"); + public static final Units picoseconds= new NumberUnits("picoseconds"); + public static final Units days= new NumberUnits("days"); + static { + seconds.registerConverter(milliseconds, UnitsConverter.MILLI); + seconds.registerConverter(microseconds, UnitsConverter.MICRO); + seconds.registerConverter(nanoseconds,UnitsConverter.NANO); + seconds.registerConverter(ns,UnitsConverter.NANO); + nanoseconds.registerConverter( ns, UnitsConverter.IDENTITY ); + seconds.registerConverter(picoseconds,UnitsConverter.PICO); + seconds.registerConverter(seconds2,UnitsConverter.IDENTITY); + microseconds.registerConverter(nanoseconds, UnitsConverter.MILLI); // to support time formatting, often from us2000 to microseconds offset. + microseconds.registerConverter(microseconds2, UnitsConverter.IDENTITY); + milliseconds.registerConverter(milliseconds2, UnitsConverter.IDENTITY); + hours.registerConverter(seconds, new UnitsConverter.ScaleOffset( 3600.,0.0)); + minutes.registerConverter(seconds, new UnitsConverter.ScaleOffset( 60.,0.0)); + days.registerConverter(seconds, new UnitsConverter.ScaleOffset(8.64e4, 0.0)); + } + + public static final Units bytesPerSecond= new NumberUnits("bytes/s"); + public static final Units kiloBytesPerSecond= new NumberUnits("KBytes/s"); + public static final Units bytes= new NumberUnits( "bytes" ); + public static final Units kiloBytes= new NumberUnits( "KBytes" ); + static { + bytesPerSecond.registerConverter( kiloBytesPerSecond, UnitsConverter.KILO ); + bytes.registerConverter( kiloBytes, UnitsConverter.KILO ); + } + + public static final Units hertz= new NumberUnits("Hz"); + public static final Units kiloHertz = new NumberUnits("kHz"); // I verified that this should be lower case k. I wonder why... + public static final Units megaHertz = new NumberUnits("MHz"); + public static final Units gigaHertz = new NumberUnits("GHz"); + static { + hertz.registerConverter(kiloHertz, UnitsConverter.KILO); + hertz.registerConverter(megaHertz, UnitsConverter.MEGA); + hertz.registerConverter(gigaHertz, UnitsConverter.GIGA); + } + + public static final Units eV= new NumberUnits("eV"); + public static final Units ev= new NumberUnits("ev"); // Mike at LANL had run into these... + public static final Units keV= new NumberUnits("keV"); + public static final Units MeV= new NumberUnits("MeV"); + static { + eV.registerConverter(Units.ev, UnitsConverter.IDENTITY); + eV.registerConverter(Units.keV, UnitsConverter.KILO); + eV.registerConverter(Units.MeV, UnitsConverter.MEGA); + } + + /** + * 1 / cm3 + */ + public static final Units pcm3= new NumberUnits("cm!a-3!n"); + + public static final Units kelvin= new NumberUnits("K"); + public static final Units cmps= new NumberUnits("cm/s"); + + public static final Units cm_2s_1keV_1= new NumberUnits( "cm!U-2!N s!U-1!N keV!U-1!N" ); + public static final Units cm_2s_1MeV_1= new NumberUnits( "cm!U-2!N s!U-1!N MeV!U-1!N" ); + static { + cm_2s_1keV_1.registerConverter( Units.cm_2s_1MeV_1, UnitsConverter.KILO ); + } + /** + * Volts 2 m-2 Hz-1 + */ + public static final Units v2pm2Hz= new NumberUnits("V!a2!nm!a-2!nHz!a-1"); + static { + unitsMap.put("V**2 m**-2 Hz**-1", v2pm2Hz); + } + public static final Units V_per_m = new NumberUnits("V m**-1"); + static { + unitsMap.put("V/m", V_per_m); + } + + /** + * Watts / m2 + */ + public static final Units wpm2= new NumberUnits("W/m!a-2!n"); + public static final Units W_per_m2_Hz = new NumberUnits("W m**-2 Hz**-1"); + + + public static final Units meters = new NumberUnits("m"); + public static final Units millimeters = new NumberUnits("mm"); + public static final Units centimeters = new NumberUnits("cm"); + public static final Units kiloMeters = new NumberUnits("km"); + public static final Units inches = new NumberUnits("inch"); + public static final Units typographicPoints = new NumberUnits("points"); + static { + meters.registerConverter(kiloMeters, UnitsConverter.KILO); + meters.registerConverter(centimeters, UnitsConverter.CENTI ); + meters.registerConverter(millimeters, UnitsConverter.MILLI ); + inches.registerConverter( meters, new UnitsConverter.ScaleOffset(0.0254,0.0) ); + inches.registerConverter( typographicPoints, new UnitsConverter.ScaleOffset(72,0.0) ); + } + + /**** begin of LocationUnits. These must be defined after the physical units to support Basis. ****/ + + public static final Units centigrade= new LocationUnits( "centigrade", "centigrade", Units.celciusDegrees, Basis.centigrade ); + public static final Units fahrenheitScale= new LocationUnits("deg F", "deg F", Units.fahrenheitDegrees, Basis.fahrenheit ); + + static { + centigrade.registerConverter(fahrenheitScale, new UnitsConverter.ScaleOffset(1.8, 32)); + celciusDegrees.registerConverter(fahrenheitDegrees, new UnitsConverter.ScaleOffset(1.8,0) ); + } + + /** + * Microseconds since midnight Jan 1, 2000, excluding those within a leap second. Differences across leap + * second boundaries do not represent the number of microseconds elapsed. + */ + public static final TimeLocationUnits us2000= new TimeLocationUnits("us2000", "Microseconds since midnight Jan 1, 2000.", + Units.microseconds, Basis.since2000); + + /** + * Microseconds since midnight Jan 1, 1980, excluding those within a leap second. + */ + public static final TimeLocationUnits us1980= new TimeLocationUnits("us1980", "Microseconds since midnight Jan 1, 1980.", + Units.microseconds, Basis.since1980 ); + + /** + * Seconds since midnight Jan 1, 2010, excluding leap seconds. + */ + public static final TimeLocationUnits t2010= new TimeLocationUnits("t2010","Seconds since midnight Jan 1, 2010.", + Units.seconds, Basis.since2010 ); + + /** + * Seconds since midnight Jan 1, 2000, excluding leap seconds. + */ + public static final TimeLocationUnits t2000= new TimeLocationUnits("t2000","Seconds since midnight Jan 1, 2000.", + Units.seconds, Basis.since2000 ); + + /** + * seconds since midnight Jan 1, 1970, excluding leap seconds. + */ + public static final TimeLocationUnits t1970= new TimeLocationUnits("t1970","Seconds since midnight Jan 1, 1970", + Units.seconds, Basis.since1970 ); + + /** + * milliseconds since midnight Jan 1, 1970, excluding leap seconds. + */ + public static final TimeLocationUnits ms1970= new TimeLocationUnits("ms1970","Milliseconds since midnight Jan 1, 1970", + Units.milliseconds, Basis.since1970 ); + + /** + * roughly days since noon on some day in 1958, Julian - 2436204.5 to be more precise. + */ + public static final TimeLocationUnits mj1958= new TimeLocationUnits("mj1958","Julian - 2436204.5", + Units.days, Basis.since1958 ); + + /** + * The Modified Julian Day (MJD) is the number of days (with decimal fraction of the day) that have elapsed since midnight at the beginning of Wednesday November 17, 1858. + * Julian - 2400000.5 + */ + public static final TimeLocationUnits mjd= new TimeLocationUnits("mjd", "days since midnight November 17, 1858.", + Units.days , Basis.modifiedJulian ); + + static { + ((Units)t2000).registerConverter(us2000, UnitsConverter.MICRO); + ((Units)us1980).registerConverter(us2000, new UnitsConverter.ScaleOffset(1.0, -631152000000000L ) ); + ((Units)t2000).registerConverter(t1970, new UnitsConverter.ScaleOffset(1.0, 9.466848e8)); + ((Units)t1970).registerConverter(ms1970, UnitsConverter.MILLI ); + ((Units)t2000).registerConverter(t2010, new UnitsConverter.ScaleOffset(1.0, -3.1561920e+8 )); + ((Units)t2000).registerConverter(mj1958, new UnitsConverter.ScaleOffset(1.0/8.64e4, 15340 )); + ((Units)t2000).registerConverter(mjd, new UnitsConverter.ScaleOffset(1.0/8.64e4, 51544 )); + } + + /**** ratiometric units ***********/ + + public static final Units percent= new NumberUnits("%",""); + + /** + * Define a set of units to describe ratiometric (logarithmic) spacing. Note that Units.percent + * is no longer the defacto ratiometric spacing, and Units.percentIncrease takes its place. + * Note the log10Ratio is the preferred method for expressing spacing, but all are convertible + * See logERatio, log10Ratio and google for "fold change." + */ + + /* percentIncrease is defined as ( b-a )*100. / a. So { 1,2,4,8 } has a spacing of 100 % diff. */ + public static final Units dB = new NumberUnits("dB","decibels"); + public static final Units ampRatio= new NumberUnits("ampratio","amplitude ratio"); + public static final Units percentIncrease= new NumberUnits("% diff","Special dimensionless number, useful for expressing on logarithmic scale. 100% indicates a doubling"); + public static final Units log10Ratio= new NumberUnits("log10Ratio", "Special dimensionless number, useful for expressing distances on a log10 scale" ); + public static final Units logERatio= new NumberUnits("logERatio", "Special dimensionless number, useful for expressing distances on a logE scale" ); + private static class PercentRatioConverter extends UnitsConverter { + @Override + public double convert(double value) { + return ( Math.exp(value) - 1.0 ) * 100; + } + @Override + public UnitsConverter getInverse() { + if (inverse == null) { + inverse = new UnitsConverter() { + @Override + public double convert(double value) { + return Math.log( value / 100 + 1. ); + } + @Override + public UnitsConverter getInverse() { + return PercentRatioConverter.this; + } + }; + } + return inverse; + } + } + + /** + * see http://en.wikipedia.org/wiki/Decibel + */ + private static class AmpRatioConverter extends UnitsConverter { + @Override + public double convert(double value) { + return ( Math.pow(10,value/20.) ); + } + @Override + public UnitsConverter getInverse() { + if (inverse == null) { + inverse = new UnitsConverter() { + @Override + public double convert(double value) { + return 20 * Math.log10( value ); + } + @Override + public UnitsConverter getInverse() { + return AmpRatioConverter.this; + } + }; + } + return inverse; + } + } + + static { + log10Ratio.registerConverter( logERatio, new UnitsConverter.ScaleOffset( Math.log(10), 0. ) ); + logERatio.registerConverter( percentIncrease, new PercentRatioConverter() ); + dB.registerConverter( log10Ratio, new UnitsConverter.ScaleOffset( 10, 0 ) ); + dB.registerConverter( ampRatio, new AmpRatioConverter() ); + } + + /* static { + unitsMap.put("mj1958", Units.mj1958); + unitsMap.put("t1970", Units.t1970); + unitsMap.put("t2000", Units.t2000); + unitsMap.put("us2000", Units.us2000); + unitsMap.put("seconds", Units.seconds); + unitsMap.put("s", Units.seconds); + unitsMap.put("days", Units.days); + unitsMap.put("microseconds", Units.microseconds); + unitsMap.put("", Units.dimensionless); + unitsMap.put("dB", Units.dB); + + unitsMap.put("Hz", Units.hertz); + unitsMap.put("kHz", Units.kiloHertz); + unitsMap.put("MHz", Units.megaHertz); + }*/ + + private String id; + private String description; + private final Map conversionMap = new ConcurrentHashMap(); + + protected Units( String id ) { + this( id, "" ); + }; + + protected Units( String id, String description ) { + this.id= id; + this.description= description; + unitsMap.put( id, this ); + }; + + /** + * get the id uniquely identifying the units. Note the id may contain + * special tokens, like "since" for time locations. + * @return the id. + */ + public String getId() { + return this.id; + } + + /** + * register a converter between the units. Note these converters can be + * changed together to derive conversions. (A to B, B to C defines A to C.) + * @param toUnits the target units + * @param converter the converter that goes from this unit to target units. + */ + public void registerConverter(Units toUnits, UnitsConverter converter) { + conversionMap.put(toUnits, converter); + UnitsConverter inverse = (UnitsConverter)toUnits.conversionMap.get(this); + if (inverse == null || inverse.getInverse() != converter) { + toUnits.registerConverter(this, converter.getInverse()); + } + } + + /** + * return the units to which this unit is convertible. + * @return the units to which this unit is convertible. + */ + public Units[] getConvertableUnits() { + Set result= new HashSet(); + LinkedList queue = new LinkedList(); + queue.add(this); + while (!queue.isEmpty()) { + Units current = (Units)queue.removeFirst(); + for (Map.Entry entry : current.conversionMap.entrySet()) { + Units next = (Units)entry.getKey(); + if (!result.contains(next)) { + queue.add(next); + result.add(next); + } + } + } + return (Units[])result.toArray( new Units[result.size()] ); + } + + /** + * return true if the unit can be converted to toUnits. + * @deprecated use isConvertibleTo (which does not contain spelling error) + * @param toUnits Units object. + * @return true if the unit can be converted to toUnits. + */ + public boolean isConvertableTo( Units toUnits ) { + UnitsConverter result= getConverterInternal(this, toUnits); + return result!=null; + } + + /** + * return true if the unit can be converted to toUnits. + * @param toUnits Units object. + * @return true if the unit can be converted to toUnits. + */ + public boolean isConvertibleTo( Units toUnits ) { + UnitsConverter result= getConverterInternal(this, toUnits); + return result!=null; + } + + /** + * lookup the UnitsConverter object that takes numbers from fromUnits to toUnits. + * This will chain together UnitsConverters registered via units.registerConverter. + * @param fromUnits units instance that is the source units. + * @param toUnits units instance that is the target units. + * @return UnitsConverter object + * @throws InconvertibleUnitsException when the conversion is not possible. + */ + public static UnitsConverter getConverter( final Units fromUnits, final Units toUnits ) { + logger.log(Level.FINER, "getConverter( {0} to {1} )", new Object[]{fromUnits, toUnits}); //TODO: THIS IS CALLED WITH EVERY REPAINT!!! + UnitsConverter result= getConverterInternal(fromUnits, toUnits); + if ( result==null ) { + throw new InconvertibleUnitsException( fromUnits, toUnits ); + } + return result; + } + + /** + * lookup the UnitsConverter object that takes numbers from fromUnits to toUnits. + * This will chain together UnitsConverters registered via units.registerConverter. + * @param fromUnits + * @param toUnits + * @return UnitsConverter object + * @throws InconvertibleUnitsException when the conversion is not possible. + */ + private static UnitsConverter getConverterInternal( final Units fromUnits, final Units toUnits ) { + logger.log(Level.FINE, "fromUnits={0} {1} toUnits={2} {3}", new Object[]{fromUnits,fromUnits.hashCode(), toUnits,toUnits.hashCode()}); + if (fromUnits == toUnits) { + return UnitsConverter.IDENTITY; + } + + UnitsConverter o = fromUnits.conversionMap.get(toUnits); + if ( o != null) { + return o; + } + + Map visited = new HashMap(); + visited.put(fromUnits, null); + LinkedList queue = new LinkedList(); + queue.add(fromUnits); + while (!queue.isEmpty()) { + Units current = (Units)queue.removeFirst(); + for ( Map.Entry entry : current.conversionMap.entrySet() ) { + Units next = (Units)entry.getKey(); + if (!visited.containsKey(next)) { + visited.put(next, current); + queue.add(next); + if (next == toUnits) { + logger.log(Level.FINE, "build conversion from {0} to {1}", new Object[]{fromUnits, toUnits}); + return buildConversion(fromUnits, toUnits, visited); + } + } + } + } + return null; + } + + private static UnitsConverter buildConversion(Units fromUnits, Units toUnits, Map parentMap) { + ArrayList list = new ArrayList(); + Units current = toUnits; + while (current != null) { + list.add(current); + current = (Units)parentMap.get(current); + } + UnitsConverter converter = UnitsConverter.IDENTITY; + for (int i = list.size() - 1; i > 0; i--) { + Units a = (Units)list.get(i); + Units b = (Units)list.get(i - 1); + UnitsConverter c = (UnitsConverter)a.conversionMap.get(b); + converter = converter.append(c); + } + fromUnits.registerConverter(toUnits, converter); + return converter; + } + + /** + * Get the converter that goes from this Unit to toUnits. E.g. + * Units.meters.getConverter(Units.centimeters) yields a converter that + * multiplies by 100. + * @param toUnits + * @return a converter from this unit to toUnits. + * @throws IllegalArgumentException if conversion between units is not possible + */ + public UnitsConverter getConverter( Units toUnits ) { + return getConverter( this, toUnits ); + } + + /** + * convert the double in this units' space to toUnits' space. + * @param toUnits the units. + * @param value the value in toUnits. + * @return the double in the new units system. + */ + public double convertDoubleTo( Units toUnits, double value ) { + if ( this==toUnits ) { + return value; + } else { + return getConverter(this,toUnits).convert(value); + } + } + + @Override + public String toString() { + return id; + } + + /** + * return the units from the Basis for the unit, such as "seconds" in + * "seconds since midnight, Jan 1, 1970" + * @return this units offsets. + */ + public Units getOffsetUnits() { + return this; + } + + /** + * return the Basis which defines the meaning of zero and the direction of positive values, such as + * "since midnight, Jan 1, 1970" + * @return the Basis object, which simply identifies a basis. + */ + public Basis getBasis() { + return Basis.physicalZero; + } + + public abstract Datum createDatum( double value ); + public abstract Datum createDatum( int value ); + public abstract Datum createDatum( long value ); + public abstract Datum createDatum( Number value ); + + public abstract Datum createDatum( double value, double resolution ); + + private final static double FILL_DOUBLE= -1e31; + private final static float FILL_FLOAT= -1e31f; + private final static int FILL_INT= Integer.MAX_VALUE; + private final static long FILL_LONG= Long.MAX_VALUE; + + public double getFillDouble() { return FILL_DOUBLE; } + public float getFillFloat() { return FILL_FLOAT; } + public int getFillInt() { return FILL_INT; } + public long getFillLong() { return FILL_LONG; } + public Datum getFillDatum() { return this.createDatum(FILL_DOUBLE); } + + public boolean isFill( double value ) { return valueFILL_DOUBLE/10 ; + } + + public abstract DatumFormatterFactory getDatumFormatterFactory(); + + public abstract Datum parse(String s) throws ParseException; + public String format( Datum datum ) { + return getDatumFormatterFactory().defaultFormatter().format(datum); + } + public String grannyFormat( Datum datum ) { + return getDatumFormatterFactory().defaultFormatter().grannyFormat(datum); + } + + public abstract Datum add( Number a, Number b, Units bUnits ); + public abstract Datum subtract( Number a, Number b, Units bUnits ); + public abstract Datum multiply( Number a, Number b, Units bUnits ); + public abstract Datum divide( Number a, Number b, Units bUnits ); + + /** + * return all the known units. + * @return list of all the known units. + */ + public static List getAllUnits() { + return new ArrayList(unitsMap.keySet()); + } + + /** + * returns a Units object with the given string representation that is stored in the unitsMap. + * + * @param s units identifier + * @return units object + * @throws IllegalArgumentException if the unit is not recognized. + */ + public static Units getByName(String s) { + Units units = (Units)unitsMap.get(s); + if (units == null) { + throw new IllegalArgumentException("Unrecognized units: "+s); + } else return units; + } + + /** + * return canonical das2 unit for colloquial time. + * @param s string containing time unit like s, sec, millisec, etc. + * @return + */ + public static Units lookupTimeLengthUnit(String s) throws ParseException { + s= s.toLowerCase().trim(); + if ( s.startsWith("sec") || s.equals("s") ) { + return Units.seconds; + } else if ( s.startsWith("ms") || s.startsWith("millisec") || s.startsWith("milliseconds") ) { + return Units.milliseconds; + } else if ( s.equals("hr") || s.startsWith("hour") ) { + return Units.hours; + } else if ( s.equals("mn") || s.startsWith("min") ) { + return Units.minutes; + } else if ( s.startsWith("us") || s.startsWith("\u00B5s" ) || s.startsWith("micros")) { + return Units.microseconds; + } else if ( s.startsWith("ns") || s.startsWith("nanos" ) ) { + return Units.nanoseconds; + } else if ( s.startsWith("d") ) { //TODO: yikes... + return Units.days; + } else { + throw new ParseException("failed to identify unit: "+s,0); + } + } + + /** + * lookupUnits canonical units object, or allocate one. If one is + * allocated, then parse for "<unit> since <datum>" and add conversion to + * "microseconds since 2000-001T00:00." Note leap seconds are ignored! + * @param base the base time, for example 2000-001T00:00. + * @param offsetUnits the offset units for example microseconds. Positive values of the units will be since the base time. + * @return the unit. + */ + public static synchronized Units lookupTimeUnits( Datum base, Units offsetUnits ) { + Units result; + String canonicalName = "" + offsetUnits + " since "+ base; + try { + result= Units.getByName(canonicalName); + return result; + } catch ( IllegalArgumentException ex ) { + Basis basis= new Basis( "since "+ base, "since "+ base, Basis.since2000, base.doubleValue(Units.us2000), Units.us2000.getOffsetUnits() ); + result= new TimeLocationUnits( canonicalName, canonicalName, offsetUnits, basis ); + result.registerConverter( Units.us2000, + new UnitsConverter.ScaleOffset( + offsetUnits.convertDoubleTo(Units.microseconds, 1.0), + base.doubleValue(Units.us2000) ) ); + return result; + } + } + + /** + * lookupUnits canonical units object, or allocate one. If one is + * allocated, then parse for "<unit> since <datum>" and add conversion to + * "microseconds since 2000-001T00:00" (us2000). Note leap seconds are ignored + * in the returned units, so each day is 86400 seconds long, and differences in + * times should not include leap seconds. Note this contains a few kludges + * as this for datasets encountered by Autoplot. + * @param units string like "microseconds since 2000-001T00:00" which will be the id. + * @return a units object that implements. + * @throws java.text.ParseException if the time cannot be parsed, etc. + */ + public static synchronized Units lookupTimeUnits( String units ) throws ParseException { + + Units result; + + //see if it's already registered. + try { + result= Units.getByName(units); + return result; + } catch ( IllegalArgumentException ex ) { + //do nothing until later + } + + if(units.trim().equalsIgnoreCase("UTC")) return us2000; + + String[] ss= units.split("since"); + Units offsetUnits= lookupTimeLengthUnit(ss[0]); + Datum datum; + + if ( ss[1].equals(" 1-1-1 00:00:00" ) ) { // make this into something that won't crash. + //datum= Units.mj1958.createDatum(-714779); + ss[1]= "1901-01-01 00:00:00"; // /media/mini/data.backup/examples/netcdf/sst.ltm.1961-1990.nc + } + if ( ss[1].contains("1970-01-01 00:00:00.0 0:00") ) { + ss[1]= "1970-01-01 00:00:00"; + } + if ( ss[1].endsWith(" UTC") ) { // http://www.ngdc.noaa.gov/stp/satellite/dmsp/f16/ssj/2011/01/f16_20110101_ssj.h5?TIME + ss[1]= ss[1].substring(0,ss[1].length()-4); + } + datum= TimeUtil.create(ss[1]); + return lookupTimeUnits( datum, offsetUnits ); + } + + /** + * lookupUnits canonical units object, or allocate one. + * Examples include: + * "nT" where it's already allocated, + * "apples" where it allocates a new one, and + * "seconds since 2011-12-21T00:00" where it uses lookupTimeUnits. + * @param sunits string identifier. + * @return canonical units object. + */ + public static synchronized Units lookupUnits(String sunits) { + Units result; + sunits= sunits.trim(); + try { + result= Units.getByName(sunits); + + } catch ( IllegalArgumentException ex ) { + if ( sunits.contains(" since ") || sunits.equalsIgnoreCase("UTC") ) { + try { + result = lookupTimeUnits(sunits); + } catch (ParseException ex1) { + result= new NumberUnits( sunits ); + } + } else if ( sunits.equals("sec") ) { // begin, giant table of kludges + result= Units.seconds; + } else if ( sunits.equals("msec") ) { // CDF + result= Units.milliseconds; + } else if ( sunits.contains("(All Qs)")) { //themis files have this annotation on the units. Register a converter. TODO: solve this in a nice way. The problem is I wouldn't want to assume nT(s) doesn't mean nT * sec. + result= new NumberUnits( sunits ); + Units targetUnits= lookupUnits( sunits.replace("(All Qs)","").trim() ); + result.registerConverter( targetUnits, UnitsConverter.IDENTITY ); + } else { + Pattern multPattern= Pattern.compile("([.0-9]+)\\s*([a-zA-Z]+)"); + Matcher m= multPattern.matcher(sunits); + if ( m.matches() ) { // kludge for ge_k0_mgf which has "0.1nT" for units. We register a converter when we see these. Note this is going to need more attention + try { + Units convTo; + convTo = lookupUnits(m.group(2)); + if ( convTo!=null ) { + double fact= Double.parseDouble(m.group(1)); + result= new NumberUnits( sunits ); + result.registerConverter( convTo, new UnitsConverter.ScaleOffset(fact,0.0) ); + } else { + result= lookupUnits(sunits); + } + } catch ( NumberFormatException ex2 ) { + result= lookupUnits(sunits); + } + } else { + result= new NumberUnits( sunits ); + } + } + } + + // look to see if there is a standard unit for this and register a converter if so. E.g. [ms]<-->ms + String stdunits= sunits; + if ( stdunits.startsWith("[") && stdunits.endsWith("]") ) { // we can't just pop these off. Hudson has case where this causes problems. We need to make units in vap files canonical as well. + stdunits= stdunits.substring(1,stdunits.length()-1); + } + if ( stdunits.startsWith("(") && stdunits.endsWith(")") ) { // often units get [] or () put around them. Pop these off. + stdunits= stdunits.substring(1,stdunits.length()-1); + } + if ( !stdunits.equals(sunits) ) { + Units stdUnit= lookupUnits(stdunits); // we need to register "foo" when "[foo]" so that order doesn't matter. + if ( !stdUnit.isConvertibleTo(result) ) { + logger.log(Level.FINE, "registering identity converter {0} -> {1}", new Object[]{stdUnit, result}); + stdUnit.registerConverter( result, UnitsConverter.IDENTITY ); + stdUnit.getConverter(result); + } + } + return result; + } + + public static void main( String[] args ) throws java.text.ParseException { + //Datum ratio = Datum.create(100); + Datum ratio = Units.ampRatio.createDatum(100); + Datum db = ratio.convertTo(dB); + System.out.println("ratio: " + ratio); + System.out.println("dB: " + db); + + Datum Hz = Datum.create(1000000.0, hertz); + Datum kHz = Hz.convertTo(kiloHertz); + Datum MHz = kHz.convertTo(megaHertz); + System.out.println("Hz: " + Hz); + System.out.println("kHz: " + kHz); + System.out.println("MHz: " + MHz); + + System.err.println( Units.ms1970.createDatum(1000) ); + } +} diff --git a/dasCore/src/main/java/org/das2/datum/UnitsConverter.java b/dasCore/src/main/java/org/das2/datum/UnitsConverter.java new file mode 100644 index 000000000..cf44b15ba --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/UnitsConverter.java @@ -0,0 +1,227 @@ +/* File: UnitsConverter.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +/** + * + * @author jbf + */ +public abstract class UnitsConverter { + + public static final UnitsConverter IDENTITY = new UnitsConverter() { + public UnitsConverter getInverse() { + return this; + } + public double convert(double value) { + return value; + } + public String toString() { + return "IDENTITY UnitsConverter"; + } + }; + + public static final UnitsConverter TERA = new ScaleOffset(1e-12, 0.0); + public static final UnitsConverter GIGA = new ScaleOffset(1e-9, 0.0); + public static final UnitsConverter MEGA = new ScaleOffset(1e-6, 0.0); + public static final UnitsConverter KILO = new ScaleOffset(1e-3, 0.0); + public static final UnitsConverter MILLI = new ScaleOffset(1e3, 0.0); + public static final UnitsConverter CENTI = new ScaleOffset(1e2, 0.0); + public static final UnitsConverter MICRO = new ScaleOffset(1e6, 0.0); + public static final UnitsConverter NANO = new ScaleOffset(1e9, 0.0); + public static final UnitsConverter PICO = new ScaleOffset(1e12, 0.0); + + protected UnitsConverter inverse; + + protected UnitsConverter() { + } + + protected UnitsConverter(UnitsConverter inverse) { + this.inverse = inverse; + } + + public abstract UnitsConverter getInverse(); + + public abstract double convert(double value); + + public Number convert( Number number ) { + double value = number.doubleValue(); + value = convert(value); + if (number instanceof Integer) { + return new Integer((int)value); + } + else if (number instanceof Long) { + return new Long((long)value); + } + else { + return new Double(value); + } + } + + public UnitsConverter append(UnitsConverter that) { + return new Appended(this, that); + } + + public static class ScaleOffset extends UnitsConverter { + private final double offset; + private final double scale; + private final int hashCode; + + /** + * Creates a new UnitsConverter.ScaleOffset. This + * converter multiplies by scale and adds offset, so + * offset is in the target Units. For example, + * deg C to deg F would be + *
    new UnitsConverter.ScaleOffset( 9./5, 32 )
    . + * + */ + public ScaleOffset(double scale, double offset) { + this(scale, offset, null); + } + + private ScaleOffset(double scale, double offset, UnitsConverter inverse) { + super(inverse); + this.scale = scale; + this.offset = offset; + hashCode = computeHashCode(); + } + + private int computeHashCode() { + long scaleBits = Double.doubleToLongBits(scale); + long offsetBits = Double.doubleToLongBits(offset); + long code = (11 * 13 * 13) + (13 * scaleBits) + offsetBits; + int a = (int)(code >> 32); + int b = (int)(0xFFFFFFFFL & code); + return a + b; + } + + public UnitsConverter getInverse() { + if (((UnitsConverter)this).inverse == null) { + ((UnitsConverter)this).inverse = new ScaleOffset(1.0 / scale, -(offset / scale), this); + } + return ((UnitsConverter)this).inverse; + } + + public double convert( double value ) { + return scale * value + offset; + } + + public UnitsConverter append(UnitsConverter that) { + if (this.equals(IDENTITY)) { + return that; + } + else if (that.equals(IDENTITY)) { + return this; + } + else if (that instanceof ScaleOffset) { + ScaleOffset so = (ScaleOffset)that; + double aScale = this.scale * so.scale; + double aOffset = this.offset * so.scale + so.offset; + return new ScaleOffset(aScale, aOffset); + } + else { + return super.append(that); + } + } + + public boolean equals(Object o) { + if (!(o instanceof ScaleOffset)) { + return false; + } + ScaleOffset that = (ScaleOffset)o; + return this.scale == that.scale && this.offset == that.offset; + } + + public String toString() { + return getClass().getName() + "[scale=" + scale + ",offset=" + offset + "]"; + } + + public int hashCode() { + return hashCode; + } + + } + + public static class Appended extends UnitsConverter{ + + UnitsConverter[] converters; + + public Appended(UnitsConverter uc1, UnitsConverter uc2) { + UnitsConverter[] a1 = ucToArray(uc1); + UnitsConverter[] a2 = ucToArray(uc2); + converters = new UnitsConverter[a1.length + a2.length]; + for (int i = 0; i < a1.length; i++) { + converters[i] = a1[i]; + } + for (int i = 0; i < a2.length; i++) { + converters[i + a1.length] = a2[i]; + } + } + + private Appended(UnitsConverter[] array, UnitsConverter inverse) { + super(inverse); + converters = array; + } + + public double convert(double value) { + for (int i = 0; i < converters.length; i++) { + value = converters[i].convert(value); + } + return value; + } + + public Number convert(Number value) { + for (int i = 0; i < converters.length; i++) { + value = converters[i].convert(value); + } + return value; + } + + public UnitsConverter getInverse() { + if (inverse == null) { + int length = converters.length; + UnitsConverter[] inverseArray = new UnitsConverter[length]; + for (int i = 0; i < length; i++) { + inverseArray[i] = converters[length - i - 1].getInverse(); + } + inverse = new Appended(inverseArray, this); + } + return inverse; + } + + private static UnitsConverter[] ucToArray(UnitsConverter uc) { + if (uc instanceof Appended) { + return ((Appended)uc).converters; + } + else { + return new UnitsConverter[] {uc}; + } + } + + } + + public static UnitsConverter getConverter(Units fromUnits, Units toUnits) { + return Units.getConverter(fromUnits,toUnits); + } + +} diff --git a/dasCore/src/main/java/org/das2/datum/UnitsUtil.java b/dasCore/src/main/java/org/das2/datum/UnitsUtil.java new file mode 100644 index 000000000..69abf1d73 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/UnitsUtil.java @@ -0,0 +1,153 @@ +/* + * UnitsUtil.java + * + * Created on December 1, 2004, 10:25 PM + */ + +package org.das2.datum; + +/** + * + * @author Jeremy + */ +public class UnitsUtil { + + /** + * returns true if the unit is used to measure distance in a logarithmic + * space, such as decibels or percent increase. Note Units.dimensionless + * are not considered ratiometric. (Of course, all ratiometic + * units are dimensionless...) + */ + public static final boolean isRatiometric( Units unit ) { + return unit!=Units.dimensionless && unit.isConvertableTo(Units.logERatio); + } + + /** + * returns true if the unit describes a location in time, as in us2000. + */ + public static final boolean isTimeLocation( Units unit ) { + return unit.isConvertableTo(Units.us2000); + } + + /** + * returns true if the unit is a ratio measurement, meaning there is a physical zero + * and you can make meaningful ratios between arbitary numbers. All operations + * like add, multiply and divide are allowed. (What about negative numbers? We + * need a statistician!) + * Examples include "5 km" or "0.2/cc" and "15 counts" + * See http://en.wikipedia.org/wiki/Level_of_measurement + * @param unit + * @return + */ + public static final boolean isRatioMeasurement( Units unit ) { + return !(unit instanceof EnumerationUnits) && unit.getOffsetUnits()==unit; + } + + /** + * returns true if the unit is a interval measurement, meaning the choice of + * zero is arbitrary. Subtraction and comparison are allowed, but addition, + * multiplication and division are invalid operators. + * Examples include "2008-04-09T14:27:00Z" and 15 deg W Longitude. + * See http://en.wikipedia.org/wiki/Level_of_measurement + * @param unit + * @return + */ + public static final boolean isIntervalMeasurement( Units unit ) { + return !(unit instanceof EnumerationUnits) && unit.getOffsetUnits()!=unit; + } + /** + * returns true if the unit is nominal, meaning that Datums with this unit + * can only be equal or not equal. Currently all nominal data is stored + * as ordinal data, so this always returns false. + * Examples include "Iowa City", and "Voyager 1". + * See http://en.wikipedia.org/wiki/Level_of_measurement + * @param unit + * @return true if the unit is nominal. + */ + public static final boolean isNominalMeasurement( Units unit ) { + return false; + } + + /** + * returns true if the unit is ordinal, meaning that Datums with this unit + * can only be equal or not equal, or compared. subtract, add, multiply, + * divide are invalid. + * Examples include energy bin labels and quality measures. + * See http://en.wikipedia.org/wiki/Level_of_measurement + * @param unit + * @return true if the unit is ordinal. + */ + public static final boolean isOrdinalMeasurement( Units unit ) { + return unit instanceof EnumerationUnits; + } + + /** + * returns the unit whose product with the parameter unit is + * unity. + */ + public static Units getInverseUnit( Units unit ) { + if ( unit==Units.seconds ) { + return Units.hertz; + } else if ( unit==Units.hertz ) { + return Units.seconds; + } else if ( unit==Units.dimensionless ) { + return Units.dimensionless; + } else if ( unit==Units.milliseconds ) { + return Units.kiloHertz; + } else if ( unit==Units.microseconds ) { + return Units.megaHertz; + } else { + throw new IllegalArgumentException( "units not supported: "+unit ); + } + } + + /** + * Special division operation that either does the Datum division if + * possible, or returns the division of the magitude parts of the + * Datums plus the unit names "A/B", suitable for human consumption. + */ + public static String divideToString( Datum aDatum, Datum bDatum ) { + try { + Datum result= divide( aDatum, bDatum ); + return String.valueOf(result); + } catch ( IllegalArgumentException e ) { + Units aUnits= aDatum.getUnits(); + Units bUnits= bDatum.getUnits(); + double a= aDatum.doubleValue(aUnits); + double b= bDatum.doubleValue(bUnits); + return ""+(a/b)+" "+aUnits+" / " +bUnits; + } + } + + /** + * attempt to perform the division of two Datums by looking for + * convertable units or dimensionless. + */ + public static Datum divide( Datum aDatum, Datum bDatum ) { + Units bUnits= bDatum.getUnits(); + Units aUnits= aDatum.getUnits(); + + Units bInvUnits; + try{ + bInvUnits= getInverseUnit(bUnits); + } catch ( IllegalArgumentException e ) { + bInvUnits= null; + } + + double a= aDatum.doubleValue(aUnits); + double b= bDatum.doubleValue(bUnits); + + if ( bUnits==Units.dimensionless ) { + return aUnits.createDatum( a/b ); + } else if ( aUnits==Units.dimensionless ) { + return bInvUnits.createDatum(a/b); + } else { + if ( !bUnits.isConvertableTo(aUnits) ) { + throw new IllegalArgumentException("unable to calculate, b units not convertable to a"); + } else { + UnitsConverter uc= bUnits.getConverter(aUnits); + return Units.dimensionless.createDatum( a / uc.convert(b) ); + } + } + } +} diff --git a/dasCore/src/main/java/org/das2/datum/format/DatumFormatter.java b/dasCore/src/main/java/org/das2/datum/format/DatumFormatter.java new file mode 100644 index 000000000..9b0047534 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/format/DatumFormatter.java @@ -0,0 +1,99 @@ +/* File: DatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 24, 2003, 4:45 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumVector; +import org.das2.datum.Units; + +/** Formats Datum objects for printing and parses strings to Datum objects. + * + * @author Edward West + */ +public abstract class DatumFormatter { + //TODO: consider the following api: + /* + * String format( Datum datum ) returns fully-qualified datum e.g. "12 days" + * String format( Datum datum, Units units ) returns formatted in the context of units (e.g.for axis) + * + * we've considered this and it needs to be implemented. + */ + + /** Available for use by subclasses */ + protected DatumFormatter() {} + + /* + * format the Datum so that it is understood out-of-context. For + * example, "4.5 seconds" + */ + public abstract String format( Datum datum ); + + /* + * format the Datum in the context of a given unit. For example, + * "4.5". It is acceptable to return the fully-qualified Datum, which + * is also the default class behavior. This will give somewhat undesirable + * results on axes. + */ + public String format( Datum datum, Units units ) { + return format( datum ); + } + + + + /** Returns the datum formatted as a String with special formatting + * characters. As with format, this should be out-of-context and should + * be tagged with the Units. + * + * The default implementation just returns the result of + * {@link #format(org.das2.datum.Datum)} + */ + public String grannyFormat(Datum datum) { + return format(datum); + } + + + /** formats the Datum in the context of the units. + */ + public String grannyFormat( Datum datum, Units units ) { + return format( datum, units ); + } + + /** + * format the set of Datums using a consistent and optimized format. + * First introduced to support DasAxis, where tighter coupling between + * the two is required to efficiently provide context. + * @param datums + * @param context visible range, context should be provided. + * @return + */ + public String[] axisFormat( DatumVector datums, DatumRange context ) { + String [] result= new String[datums.getLength()]; + for ( int i=0; i + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.datum.format; + +import org.das2.util.NumberFormatUtil; +import org.das2.util.DasMath; + +import java.text.*; +import java.util.Locale; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumVector; +import org.das2.datum.Units; + +/** Formats Datum objects for printing and parses strings to Datum objects. + * + * @author Edward West + */ +public class DefaultDatumFormatter extends DatumFormatter { + + private String formatString; + private NumberFormat format; + + /** Available for use by subclasses */ + protected DefaultDatumFormatter() { + } + + /** Creates a new instance of DatumFormatter */ + public DefaultDatumFormatter(String formatString) throws ParseException { + if (formatString.equals("")) { + this.formatString = ""; + format = null; + } else { + this.formatString = formatString; + format = NumberFormatUtil.getDecimalFormat(formatString); + } + } + + public String format(Datum datum) { + return format(datum, datum.getUnits()) + " " + datum.getUnits(); + } + + public String format(Datum datum, Units units) { + double d = datum.doubleValue(units); + if (Double.isInfinite(d) || Double.isNaN(d)) { + return "" + d; + } + String result; + if (format == null) { + double resolution = datum.getResolution(units.getOffsetUnits()); + result = formatLimitedResolution(d, resolution); + } else { + result = format.format(datum.doubleValue(units)); + } + return result; + } + + @Override + public String grannyFormat(Datum datum, Units units) { + String formt = format(datum, units); + if (formt.indexOf("E") != -1) { + int iE = formt.indexOf("E"); + StringBuffer granny = new StringBuffer(formt.length() + 4); + String mant = formt.substring(0, iE); + if (Double.parseDouble(mant) != 1.0) { + granny.append(mant).append("\u00d7"); + } + granny.append("10").append("!A").append(formt.substring(iE + 1)).append("!N"); + formt = granny.toString(); + } + return formt; + } + + @Override + public String[] axisFormat(DatumVector datums, DatumRange context ) { + Units units= context.getUnits(); + String[] result = new String[datums.getLength()]; + for (int i = 0; i < result.length; i++) { + result[i] = format(datums.get(i), units); + } + boolean hasMant = false; + for (int i = 0; i < result.length; i++) { + String res1 = result[i]; + if (res1.indexOf("E") != -1) { + int iE = res1.indexOf("E"); + String mant = res1.substring(0, iE); + if (Double.parseDouble(mant) != 1.0) { + hasMant = true; + } + } + } + for (int i = 0; i < result.length; i++) { + String res1 = result[i]; + if (res1.indexOf("E") != -1) { + int iE = res1.indexOf("E"); + StringBuffer granny = new StringBuffer(res1.length() + 4); + String mant = res1.substring(0, iE); + + if (hasMant) { + granny.append(mant).append("\u00d7"); + } + + granny.append("10").append("!A").append(res1.substring(iE + 1)).append("!N"); + result[i] = granny.toString(); + } + + } + return result; + } + + public String grannyFormat(Datum datum) { + return grannyFormat(datum, datum.getUnits()) + " " + datum.getUnits(); + } + + public String toString() { + return formatString; + } + + private String formatLimitedResolution(double d, double resolution) { + String result; + if (resolution == 0. && Double.toString(d).length() > 7) { + // make the default resolution be 0.01%. + resolution = d / 10000; + } + if (resolution > 0) { + // 28 --> scale = -1 + // 2.8 --> scale = 0 + int scale = (int) Math.ceil(-1 * DasMath.log10(resolution) - 0.00001); + int exp; + if (d != 0.) { + exp = (int) DasMath.log10(Math.abs(d)); + } else { + exp = 0; + } + if (scale >= 0) { + DecimalFormat f; + if (exp <= -5 || exp >= 5) { + f = NumberFormatUtil.getDecimalFormat("0E0"); + f.setMinimumFractionDigits(scale + exp - 1); + f.setMaximumFractionDigits(scale + exp - 1); + } else { + f = NumberFormatUtil.getDecimalFormat("0"); + f.setMinimumFractionDigits(scale); + f.setMaximumFractionDigits(scale); + } + result = f.format(d); + } else { + double round = DasMath.exp10(-1 * scale); + d = Math.round(d / round) * round; + DecimalFormat f; + if (exp <= -5 || exp >= 5) { + f = NumberFormatUtil.getDecimalFormat("0E0"); + f.setMinimumFractionDigits(scale + exp + 1); + f.setMaximumFractionDigits(scale + exp + 1); + } else { + f = NumberFormatUtil.getDecimalFormat("0"); + } + result = f.format(d); + } + } else { + result = Double.toString(d); + } + return result; + } +} diff --git a/dasCore/src/main/java/org/das2/datum/format/DefaultDatumFormatterFactory.java b/dasCore/src/main/java/org/das2/datum/format/DefaultDatumFormatterFactory.java new file mode 100644 index 000000000..4566068a1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/format/DefaultDatumFormatterFactory.java @@ -0,0 +1,61 @@ +/* File: DatumFormatterFactory.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 25, 2003, 3:35 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +/** + * + * @author Edward West + */ +public final class DefaultDatumFormatterFactory extends DatumFormatterFactory { + + private static DatumFormatterFactory factory; + + /** provided for use by subclasses */ + protected DefaultDatumFormatterFactory() { + } + + public DatumFormatter newFormatter(String format) throws java.text.ParseException { + return new DefaultDatumFormatter(format); + } + + /** Get an instance of this factory. */ + public static DatumFormatterFactory getInstance() { + //This isn't thread safe, but who cares. Instances are small and + //functionally identical. + if (factory == null) { + factory = new DefaultDatumFormatterFactory(); + } + return factory; + } + + public DatumFormatter defaultFormatter() { + try { + return newFormatter(""); + } + catch (java.text.ParseException pe) { + throw new RuntimeException(pe); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/datum/format/EnumerationDatumFormatter.java b/dasCore/src/main/java/org/das2/datum/format/EnumerationDatumFormatter.java new file mode 100644 index 000000000..d9cb23c6c --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/format/EnumerationDatumFormatter.java @@ -0,0 +1,47 @@ +/* File: EnumerationDatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 29, 2003, 4:34 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +import org.das2.datum.Datum; +import org.das2.datum.EnumerationUnits; + +/** + * + * @author Edward West + */ +public class EnumerationDatumFormatter extends DatumFormatter { + + /** Creates a new instance of EnumerationDatumFormatter */ + public EnumerationDatumFormatter() { + } + + public String toString() { + return getClass().getName(); + } + + public String format(Datum datum) { + return ((EnumerationUnits)datum.getUnits()).getObject(datum).toString(); + } + +} diff --git a/dasCore/src/main/java/org/das2/datum/format/EnumerationDatumFormatterFactory.java b/dasCore/src/main/java/org/das2/datum/format/EnumerationDatumFormatterFactory.java new file mode 100644 index 000000000..5d3c88370 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/format/EnumerationDatumFormatterFactory.java @@ -0,0 +1,51 @@ +/* File: EnumerationDatumFormatterFactory.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 29, 2003, 4:34 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +/** + * + * @author Edward West + */ +public class EnumerationDatumFormatterFactory extends DatumFormatterFactory { + + private final static EnumerationDatumFormatterFactory INSTANCE + = new EnumerationDatumFormatterFactory(); + + /** Creates a new instance of EnumerationDatumFormatterFactory */ + private EnumerationDatumFormatterFactory() { + } + + public DatumFormatter defaultFormatter() { + return new EnumerationDatumFormatter(); + } + + public DatumFormatter newFormatter(String format) throws java.text.ParseException { + return defaultFormatter(); + } + + public static EnumerationDatumFormatterFactory getInstance() { + return INSTANCE; + } + +} diff --git a/dasCore/src/main/java/org/das2/datum/format/ExponentialDatumFormatter.java b/dasCore/src/main/java/org/das2/datum/format/ExponentialDatumFormatter.java new file mode 100644 index 000000000..6ed5ea25d --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/format/ExponentialDatumFormatter.java @@ -0,0 +1,107 @@ +/* File: DefaultDatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 1, 2003, 4:45 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +import org.das2.util.NumberFormatUtil; +import org.das2.util.DasMath; + +import java.text.*; +import org.das2.datum.Datum; +import org.das2.datum.Units; + +/** Formats Datums forcing a given exponent and number of decimal places. + * This is useful for axes where each of the labels should have the same + * exponent. Zero is treated specially, just "0" is returned. This helps + * one to quickly identify zero on the axis. + * + * @author Jeremy Faden + */ +public class ExponentialDatumFormatter extends DatumFormatter { + + private DecimalFormat format; + + int digits; + int exponent; + NumberFormat mantFormat; + String mantFormatString; + + /* print with digits in the mantissa, use exponent for the exponent */ + /* mEe */ + public ExponentialDatumFormatter(int digits, int exponent) { + this.digits= digits; + this.exponent= exponent; + StringBuffer buff = new StringBuffer(digits+2).append("0"); + if ( digits>1 ) buff.append('.'); + for (int i = 1; i< digits; i++) { + buff.append('0'); + } + mantFormatString= buff.toString(); + this.mantFormat= NumberFormatUtil.getDecimalFormat( buff.toString() ); + } + + public String format(Datum datum) { + return format( datum, datum.getUnits() ) + " " + datum.getUnits(); + } + + public String format( Datum datum, Units units ) { + double x= datum.doubleValue(datum.getUnits()); + if ( x == 0. ) return "0."; + double exp= DasMath.exp10(exponent); + double mant= x/exp; + double tenToN= DasMath.exp10(digits); + mant= Math.round( mant * tenToN ) / tenToN; + return mantFormat.format(mant)+"E"+exponent; + } + + public String grannyFormat( Datum datum, Units units ) { + String format= format(datum,units); + if ( format.indexOf("E")!=-1 ) { + int iE= format.indexOf("E"); + StringBuffer granny = new StringBuffer(format.length() + 4); + String mant= format.substring(0,iE); + granny.append(mant).append("\u00d7"); + granny.append("10").append("!A").append(format.substring(iE+1)).append("!N"); + format = granny.toString(); + } + return format; + } + + public String grannyFormat( Datum datum ) { + String format= format(datum,datum.getUnits()); + if ( format.indexOf("E")!=-1 ) { + int iE= format.indexOf("E"); + StringBuffer granny = new StringBuffer(format.length() + 4); + String mant= format.substring(0,iE); + granny.append(mant).append("\u00d7"); + granny.append("10").append("!A").append(format.substring(iE+1)).append("!N"); + format = granny.toString(); + } + return format + " " +datum.getUnits(); + } + + public String toString() { + return mantFormatString + "E"+exponent; + } + +} diff --git a/dasCore/src/main/java/org/das2/datum/format/LatinPrefixDatumFormatter.java b/dasCore/src/main/java/org/das2/datum/format/LatinPrefixDatumFormatter.java new file mode 100644 index 000000000..60c643c0a --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/format/LatinPrefixDatumFormatter.java @@ -0,0 +1,98 @@ +/* File: DefaultDatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 1, 2003, 4:45 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +import org.das2.util.NumberFormatUtil; +import org.das2.util.DasMath; + +import java.text.*; +import java.util.Locale; +import org.das2.datum.Datum; +import org.das2.datum.Units; + +/** Formats Datums using K and M, etc labels and a specified precision. + * + * @author Jeremy Faden + */ +public class LatinPrefixDatumFormatter extends DatumFormatter { + + private DecimalFormat format; + + int digits; + int exponent; + + /* print with digits in the mantissa */ + /* m.mmK */ + public LatinPrefixDatumFormatter( int digits ) { + this.digits= digits; + } + + public String format(Datum datum) { + return format( datum, datum.getUnits() ) + " " + datum.getUnits(); + } + + public String format( Datum datum, Units units ) { + double x= datum.doubleValue(units); + if ( x == 0. ) return "0."; + int exponent= (int) DasMath.log10(1.000001*Math.abs(x)) / 3 * 3; + + String expString; + switch (exponent) { + case -18: expString="a"; break; + case -15: expString="f"; break; + case -12: expString="p"; break; + case -9: expString="n";break; + case -6: expString="\u03BC";break; // micro + case -3: expString="m";break; + case 0: expString="";break; + case 3: expString="k";break; + case 6: expString="M";break; + case 9: expString="G";break; + case 12: expString="T"; break; + default: expString=""; exponent=0; break; + } + + int sign= x < 0 ? -1 : 1; + + double exp= DasMath.exp10(exponent); + double mant= x / exp; + + int mantFracDigits= digits - (int)DasMath.log10(mant); + + StringBuffer buff = new StringBuffer(digits+2).append("0"); + if ( digits>1 ) buff.append('.'); + for (int i = 0; i< mantFracDigits; i++) { + buff.append('0'); + } + String mantFormatString= buff.toString(); + NumberFormat mantFormat= NumberFormatUtil.getDecimalFormat(buff.toString()); + + return mantFormat.format(mant) + expString; + } + + public String toString() { + return "EngineeringFormatter("+digits+" sig fig)"; + } + +} diff --git a/dasCore/src/main/java/org/das2/datum/format/TimeDatumFormatter.java b/dasCore/src/main/java/org/das2/datum/format/TimeDatumFormatter.java new file mode 100644 index 000000000..6d55255c2 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/format/TimeDatumFormatter.java @@ -0,0 +1,350 @@ +/* File: TimeDatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 25, 2003, 1:47 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +import java.text.*; +import java.util.regex.*; +import org.das2.datum.CalendarTime; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.TimeUtil; + +/** + * + * @author Edward West + */ +public class TimeDatumFormatter extends DatumFormatter { + + /** Private constants for referencing an array of timestamp fields */ + private static final int YEAR_FIELD_INDEX = 0; + private static final int MONTH_FIELD_INDEX = 1; + private static final int DAY_FIELD_INDEX = 2; + private static final int DOY_FIELD_INDEX = 3; + private static final int HOUR_FIELD_INDEX = 4; + private static final int MINUTE_FIELD_INDEX = 5; + private static final int SECONDS_FIELD_INDEX = 6; + private static final int TIMESTAMP_FIELD_COUNT = 7; + + /** + * yyyy-MM-dd'T'HH:mm:ss.SSS'Z + */ + public static final TimeDatumFormatter DEFAULT; + /** + * yyyy-MM-dd + */ + public static final TimeDatumFormatter DAYS; + /** + * yyyy + */ + public static final TimeDatumFormatter YEARS; + /** + * yyyy-MM + */ + public static final TimeDatumFormatter MONTHS; + /** + * yyyy-MM-dd HH:'00' + */ + public static final TimeDatumFormatter HOURS; + /** + * HH:mm + */ + public static final TimeDatumFormatter MINUTES; + /** + * HH:mm:ss + */ + public static final TimeDatumFormatter SECONDS; + /** + * HH:mm:ss.SSS + */ + public static final TimeDatumFormatter MILLISECONDS; + /** + * HH:mm:ss.SSSSSS + */ + public static final TimeDatumFormatter MICROSECONDS; + /** + * HH:mm:ss.SSSSSSSSS + */ + public static final TimeDatumFormatter NANOSECONDS; + + //Initialize final constants + static { + try { + DEFAULT = new TimeDatumFormatter("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + YEARS= new TimeDatumFormatter("yyyy"); + MONTHS= new TimeDatumFormatter("yyyy-MM"); + DAYS = new TimeDatumFormatter("yyyy-MM-dd"); + HOURS = new TimeDatumFormatter("yyyy-MM-dd HH:'00'"); + MINUTES = new TimeDatumFormatter("HH:mm"); + SECONDS = new TimeDatumFormatter("HH:mm:ss"); + MILLISECONDS = new TimeDatumFormatter("HH:mm:ss.SSS"); + MICROSECONDS = new TimeDatumFormatter("HH:mm:ss.SSSSSS"); + NANOSECONDS = new TimeDatumFormatter("HH:mm:ss.SSSSSSSSS"); + } catch (ParseException pe) { + throw new RuntimeException(pe); + } + } + + private String formatString; + + private MessageFormat format; + + private int[] scaleSeconds; + + /** Creates a new instance of TimeDatumFormatter */ + public TimeDatumFormatter(String formatString) throws ParseException { + this.formatString = formatString; + if ( formatString.contains( "%" ) ) { + format = new MessageFormat(parseTimeFormatStringPercent(formatString)); + } else { + format = new MessageFormat(parseTimeFormatString(formatString)); + } + } + + /** + * returns a TimeDatumFormatter suitable for the specified scale and context. + * Context may be null to indicate that the formatted string will be interpreted + * outside of any context. + * @param scale the length we wish to represent, such as TimeUtil.HOUR + * @param context the context for the formatter, or null if the formatted string + * will be interpreted outside of any context. + * @throws IllegalArgumentException if the scale is TimeUtil.NANOS or is not found in TimeUtil. + */ + public static TimeDatumFormatter formatterForScale(CalendarTime.Step scale, DatumRange context ) { + try { + if ( context!=null ) { + switch ( scale ) { + case YEAR: return YEARS; + case MONTH: return MONTHS; + case DAY: return DAYS; + case HOUR: return MINUTES; + case MINUTE: return MINUTES; + case SECOND: return SECONDS; + case MILLISEC: return MILLISECONDS; + case MICROSEC: return MICROSECONDS; + case NANOSEC: return NANOSECONDS; + default: throw new IllegalArgumentException("unsupported scale: "+scale); + } + } else { + switch ( scale ) { + case YEAR: return YEARS; + case MONTH: return MONTHS; + case DAY: return DAYS; + case HOUR: return HOURS; + case MINUTE: return new TimeDatumFormatter("yyyy-MM-dd HH:mm"); + case SECOND: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss"); + case MILLISEC: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss.SSS"); + case MICROSEC: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss.SSSSSS"); + case NANOSEC: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"); + default: throw new IllegalArgumentException("unsupported scale: "+scale); + } + } + } catch ( ParseException e ) { + throw new RuntimeException(e); + } + } + + public String toString() { + return formatString; + } + + public String format(Datum datum) { + if ( datum.isFill() ) return "fill"; + CalendarTime ct = new CalendarTime(datum); + Number[] array = timeStructToArray(ct); + return format.format(array); + } + + protected Format getFormat() { + return format; + } + + protected String parseTimeFormatString(String input) throws ParseException { + final String formatPattern = "(([yMDdHmsS])\\2*)"; + final String delimiterPattern = "([-/:.,_ \t]+)"; + final String literalPattern = "('(?:[^']|'')*')"; + Pattern token = Pattern.compile( + formatPattern + "|" + delimiterPattern + "|" + literalPattern + ); + int from = 0; + StringBuffer formatString = new StringBuffer(); + Matcher matcher = token.matcher(input); + while (matcher.find(from)) { + int start = matcher.start(); + if (start > from) { + char[] dots = new char[start + 1]; + java.util.Arrays.fill(dots, from, start, '.'); + dots[from] = '^'; + dots[start] = '^'; + StringBuffer errorString = new StringBuffer("Unrecognized sub-pattern\n"); + errorString.append(input).append("\n"); + errorString.append(dots); + throw new ParseException(errorString.toString(), from); + } + String format = matcher.group(1); + String delimiter = matcher.group(3); + String literal = matcher.group(4); + if (format != null) { + switch (format.charAt(0)) { + case 'y': { + appendSubFormat(formatString, YEAR_FIELD_INDEX, format.length()); + } break; + case 'M': { + appendSubFormat(formatString, MONTH_FIELD_INDEX, format.length()); + } break; + case 'D': { + appendSubFormat(formatString, DOY_FIELD_INDEX, format.length()); + } break; + case 'd': { + appendSubFormat(formatString, DAY_FIELD_INDEX, format.length()); + } break; + case 'H': { + appendSubFormat(formatString, HOUR_FIELD_INDEX, format.length()); + } break; + case 'm': { + appendSubFormat(formatString, MINUTE_FIELD_INDEX, format.length()); + } break; + case 's': { + appendSubFormat(formatString, SECONDS_FIELD_INDEX, format.length()); + }break; + case 'S': { + int digitCount = format.length(); + int fieldIndex = addScaleFactor(digitCount); + appendSubFormat(formatString, fieldIndex, digitCount); + } + break; + default: break; + } + } else if (delimiter != null) { + formatString.append(delimiter); + } else if (literal != null) { + literal = literal.substring(1, literal.length() - 1); + literal = literal.replaceAll("''", "'"); + formatString.append(literal); + } + from = matcher.end(); + } + return formatString.toString(); + } + + /** + * create the message format, based on %Y, %m, %d format specification. + * @param input + * @return + * @throws java.text.ParseException + */ + protected String parseTimeFormatStringPercent(String format) throws ParseException { + StringBuffer formatString= new StringBuffer(); + String[] ss= format.split("%"); + formatString.append(ss[0]); + int offset= ss[0].length(); + + for ( int i=1; i + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +/** + * + * @author Edward West + */ +public class TimeDatumFormatterFactory extends DatumFormatterFactory { + + private static TimeDatumFormatterFactory factory; + + /** Creates a new instance of TimeDatumFormatterFactory */ + protected TimeDatumFormatterFactory() {} + + public DatumFormatter defaultFormatter() { + return TimeDatumFormatter.DEFAULT; + } + + public DatumFormatter newFormatter(String format) throws java.text.ParseException { + return new TimeDatumFormatter(format); + } + + /** Get an instance of this factory. */ + public static TimeDatumFormatterFactory getInstance() { + //This isn't thread safe, but who cares. Instances are small and + //functionally identical. + if (factory == null) { + factory = new TimeDatumFormatterFactory(); + } + return factory; + } + +} diff --git a/dasCoreDatum/src/org/das2/datum/format/package.html b/dasCore/src/main/java/org/das2/datum/format/package.html similarity index 100% rename from dasCoreDatum/src/org/das2/datum/format/package.html rename to dasCore/src/main/java/org/das2/datum/format/package.html diff --git a/dasCore/src/main/java/org/das2/datum/package.html b/dasCore/src/main/java/org/das2/datum/package.html new file mode 100644 index 000000000..295af20da --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/package.html @@ -0,0 +1,18 @@ + +

    Provides classes representing physical quanties. The Units class +defines an enumeration of physical units such as Units.hertz and Units.microseconds. It +also contains a convention for identifying locations in time with units like +Units.t1970, the number of microseconds elapsed since midnight, January 1, 1970. +The Units class also contains a registry of Units and the conversions between them. +

    +

    A Datum is a number in the context of a Unit, for example "15 microseconds.". + A Datum object has methods for formatting itself as a String, representing + itsself as a double in the context of another Unit, and mathematical +operators that allow simple calculations to be made at the physical quantities. +Also a Datum's precision can be limited to improve formatting. +

    +

    A DatumRange is a pair of Datums that identify a range in the physical space +of a Unit. An example of a formatted DatumRange is "January, 2005" or "0 to 50Hz."

    +

    Also there are utility classes for parsing and formatting times.

    + +

    \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/datum/swing/DatumJFormatterFactory.java b/dasCore/src/main/java/org/das2/datum/swing/DatumJFormatterFactory.java new file mode 100644 index 000000000..35fdd878a --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/swing/DatumJFormatterFactory.java @@ -0,0 +1,69 @@ +/* + * DatumJFormatter.java + * + * Created on June 12, 2007, 9:11 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.datum.swing; + +import java.text.ParseException; +import javax.swing.JFormattedTextField; +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.DefaultDatumFormatterFactory; + +/** + * + * @author eew + */ +public class DatumJFormatterFactory extends JFormattedTextField.AbstractFormatterFactory { + + DatumFormatter format; + SwingFormatter wrapper; + Units units; + Units implicitUnits = Units.dimensionless; + + public DatumJFormatterFactory() { + this(DefaultDatumFormatterFactory.getInstance().defaultFormatter()); + } + + /** Creates a new instance of DatumJFormatter */ + public DatumJFormatterFactory(DatumFormatter format) { + this.format = format; + this.wrapper = new SwingFormatter(); + } + + public void explicitUnits(Units u) { + units = u; + } + + public JFormattedTextField.AbstractFormatter getFormatter(JFormattedTextField tf) { + return wrapper; + } + + private class SwingFormatter extends JFormattedTextField.AbstractFormatter { + public Object stringToValue(String text) throws ParseException { + Units u = units != null ? units : implicitUnits; + return u.parse(text); + } + + public String valueToString(Object value) throws ParseException { + Datum datum = (Datum)value; + if (datum == null) { + throw new ParseException("Null values not allowed.", -1); + } + implicitUnits = datum.getUnits(); + if (units != null) { + return format.format(datum, units); + } + else { + return format.format(datum); + } + } + } + +} diff --git a/dasCore/src/main/java/org/das2/datum/swing/SwingDatumFormatter.java b/dasCore/src/main/java/org/das2/datum/swing/SwingDatumFormatter.java new file mode 100644 index 000000000..4f1d4c217 --- /dev/null +++ b/dasCore/src/main/java/org/das2/datum/swing/SwingDatumFormatter.java @@ -0,0 +1,61 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.datum.swing; + +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.DefaultDatumFormatterFactory; +import java.text.ParseException; +import javax.swing.JFormattedTextField.AbstractFormatter; +import javax.swing.JFormattedTextField.AbstractFormatterFactory; +import javax.swing.text.DefaultFormatterFactory; + +/** + * + * @author eew + */ +public class SwingDatumFormatter extends AbstractFormatter { + + private Units units; + + private DatumFormatter formatter; + + public SwingDatumFormatter() { + formatter = DefaultDatumFormatterFactory.getInstance().defaultFormatter(); + } + + @Override + public Object stringToValue(String text) throws ParseException { + if (units == null) { + units = Units.dimensionless; + } + return units.parse(text); + } + + @Override + public String valueToString(Object value) throws ParseException { + Datum d = (Datum)value; + if (d == null) { + return ""; + } + units = d.getUnits(); + return formatter.format(d, units); + } + + public Units getUnits() { + return units; + } + + public void setUnits(Units units) { + this.units = units; + } + + public static final AbstractFormatterFactory newFactory() { + return new DefaultFormatterFactory(new SwingDatumFormatter()); + } + +} diff --git a/dasCore/src/main/java/org/das2/event/AngleSelectionDragRenderer.java b/dasCore/src/main/java/org/das2/event/AngleSelectionDragRenderer.java new file mode 100644 index 000000000..ff1edb801 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/AngleSelectionDragRenderer.java @@ -0,0 +1,41 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.event; + +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; + +/** + * + * @author jbf + */ +public class AngleSelectionDragRenderer implements DragRenderer{ + + public Rectangle[] renderDrag(Graphics g, Point p1, Point p2) { + g.drawLine( p1.x, p1.y, p2.x, p2.y ); + Rectangle r= new Rectangle(p1); + r.add(p2); + return new Rectangle[] { r }; + } + + public void clear(Graphics g) { + + } + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + return new MouseBoxEvent( source, p1, p2, isModified ); + } + + public boolean isPointSelection() { + return false; + } + + public boolean isUpdatingDragSelection() { + return true; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/AngleSlicerMouseModule.java b/dasCore/src/main/java/org/das2/event/AngleSlicerMouseModule.java new file mode 100644 index 000000000..72fb3827d --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/AngleSlicerMouseModule.java @@ -0,0 +1,83 @@ +/* File: HorizontalSlicerMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.DataSetConsumer; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; +import org.das2.graph.Renderer; +/** + * + * @author jbf + */ +public class AngleSlicerMouseModule extends MouseModule { + + private DasAxis xaxis; + private DasAxis yaxis; + + private TableDataSetConsumer dataSetConsumer; + + private DataPointSelectionEvent de; + + private javax.swing.event.EventListenerList listenerList = null; + + public AngleSlicerMouseModule( DasPlot parent, TableDataSetConsumer dataSetConsumer, DasAxis xaxis, DasAxis yaxis) { + this( parent, (DataSetConsumer)dataSetConsumer, xaxis, yaxis ); + } + + protected AngleSlicerMouseModule(DasPlot parent, DataSetConsumer dataSetConsumer, DasAxis xaxis, DasAxis yaxis) { + super( parent, new AngleSelectionDragRenderer(), "Angle Slice" ); + + if (!(dataSetConsumer instanceof TableDataSetConsumer)) { + throw new IllegalArgumentException("dataSetConsumer must be an XTaggedYScanDataSetConsumer"); + } + this.dataSetConsumer= ( TableDataSetConsumer)dataSetConsumer; + this.xaxis= xaxis; + this.yaxis= yaxis; + this.de= new DataPointSelectionEvent(this,null,null); + + } + + public static AngleSlicerMouseModule create(DasPlot parent) { + DasAxis xaxis= parent.getXAxis(); + DasAxis yaxis= parent.getYAxis(); + return new AngleSlicerMouseModule(parent,parent,xaxis,yaxis); + } + + public static AngleSlicerMouseModule create(Renderer renderer) + { + DasPlot parent= renderer.getParent(); + DasAxis xaxis= parent.getXAxis(); + DasAxis yaxis= parent.getYAxis(); + return new AngleSlicerMouseModule(parent,renderer,xaxis,yaxis); + } + + @Override + public void mouseRangeSelected(MouseDragEvent e0) { + MouseBoxEvent e= (MouseBoxEvent)e0; + + } + +} diff --git a/dasCore/src/main/java/org/das2/event/AnnotatorMouseModule.java b/dasCore/src/main/java/org/das2/event/AnnotatorMouseModule.java new file mode 100644 index 000000000..f9a04be3a --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/AnnotatorMouseModule.java @@ -0,0 +1,54 @@ +/* + * AnnotatorMouseModule.java + * + * Created on March 25, 2005, 1:07 PM + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasColumn; +import org.das2.graph.DasAnnotation; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasRow; + +/** + * + * @author Jeremy + */ +public class AnnotatorMouseModule extends MouseModule { + + DasCanvas canvas; + + /** Creates a new instance of AnnotatorMouseModule */ + public AnnotatorMouseModule( DasCanvasComponent parent ) { + super( parent, new BoxRenderer(parent), "Annotate" ); + this.canvas= (DasCanvas)parent.getParent(); + } + + public DasCanvas getCanvas() { + return canvas; + } + + public void mouseRangeSelected(MouseDragEvent e) { + super.mouseRangeSelected(e); + + System.out.println(e); + MouseBoxEvent me= (MouseBoxEvent) e; + + double n; + n= canvas.getHeight(); + DasRow row= new DasRow( canvas, me.getYMinimum()/n, me.getYMaximum()/n ); + + n= canvas.getWidth(); + DasColumn col= new DasColumn( canvas, me.getXMinimum()/n, me.getXMaximum()/n ); + + DasAnnotation anno= new DasAnnotation("right click"); + + canvas.add( anno, row, col ); + canvas.revalidate(); + } + + + +} diff --git a/dasCore/src/main/java/org/das2/event/ArrowDragRenderer.java b/dasCore/src/main/java/org/das2/event/ArrowDragRenderer.java new file mode 100644 index 000000000..22ad28252 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/ArrowDragRenderer.java @@ -0,0 +1,51 @@ +/* + * ArrowDragRenderer.java + * + * Created on February 13, 2007, 3:50 PM + * + * + */ + +package org.das2.event; + +import org.das2.graph.Arrow; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; + +/** + * + * @author Jeremy + */ +public class ArrowDragRenderer implements DragRenderer { + public Rectangle[] renderDrag(Graphics g, Point p1, Point p2) { + g.setClip( null ); + Arrow.paintArrow( (Graphics2D)g, p2, p1, 12 , Arrow.HeadStyle.DRAFTING ); + Rectangle result= new Rectangle(p1); + result.add(p2); + result.x-= 6; + result.y-= 6; + result.width+= 12; + result.height+= 12; + + return new Rectangle[] { result }; + } + + public void clear(Graphics g) { + + } + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + return null; + } + + public boolean isPointSelection() { + return true; + } + + public boolean isUpdatingDragSelection() { + return true; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/BoxGesturesRenderer.java b/dasCore/src/main/java/org/das2/event/BoxGesturesRenderer.java new file mode 100644 index 000000000..4bef8abd8 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/BoxGesturesRenderer.java @@ -0,0 +1,82 @@ +/* File: BoxRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; + +import java.awt.*; + +/** + * + * @author eew + */ +public class BoxGesturesRenderer extends BoxRenderer { + + GesturesRenderer gr; + + public BoxGesturesRenderer(DasCanvasComponent parent) { + super(parent); + gr= new GesturesRenderer(parent); + } + + public void clear(Graphics g) { + parent.paintImmediately(dirtyBounds); + } + + public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { + Graphics2D g= (Graphics2D) g1; + + if ( gr.isGesture( p1, p2 ) ) { + Rectangle[] rr= gr.renderDrag( g, p1, p2 ); + dirtyBounds= rr[0]; + } else { + Rectangle r = new Rectangle(p1); + r.add(p2); + + Color color0= g.getColor(); + g.setColor(new Color(255,255,255,100)); + g.setStroke(new BasicStroke( 3.0f, + BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND )); + + g.drawRect(r.x, r.y, r.width, r.height); + + g.setStroke(new BasicStroke()); + g.setColor(color0); + + g.drawRect(r.x, r.y, r.width, r.height); + + dirtyBounds.setLocation(r.x-2,r.y-3); + dirtyBounds.add(r.x+r.width+2,r.y+r.height+3); + } + return new Rectangle[] { dirtyBounds }; + } + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + if ( gr.isGesture(p1,p2) ) { + return gr.getMouseDragEvent( source, p1, p2, isModified ); + } else { + return super.getMouseDragEvent( source, p1, p2, isModified ); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/event/BoxRangeSelectorMouseModule.java b/dasCore/src/main/java/org/das2/event/BoxRangeSelectorMouseModule.java new file mode 100755 index 000000000..5c9584830 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/BoxRangeSelectorMouseModule.java @@ -0,0 +1,124 @@ +/* File: BoxRangeSelectorMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.dataset.DataSetConsumer; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasPlot; +import javax.swing.event.EventListenerList; + +/** + * //@deprecated use BoxSelectorMouseModule + * @author jbf + */ +public class BoxRangeSelectorMouseModule extends MouseModule { + + protected DasAxis xAxis; + protected DasAxis yAxis; + private DataSetConsumer consumer; + + /** Utility field used by event firing mechanism. */ + private EventListenerList listenerList = new javax.swing.event.EventListenerList(); + + /** + * @param parent the DasCanvasComponent this module will be associated with + * @param consumer is the source context of the data set selection + * @param xAxis the horizontal axis to use when transforming from pixel to data space + * @param yAxis the vertical axis to use when transforming from pixel to data space + */ + public BoxRangeSelectorMouseModule(DasCanvasComponent parent, DataSetConsumer consumer, DasAxis xAxis, DasAxis yAxis) { + super( parent, new BoxGesturesRenderer(parent), "Box Selection" ); + if (!xAxis.isHorizontal()) { + throw new IllegalArgumentException("X Axis orientation is not horizontal"); + } + if (yAxis.isHorizontal()) { + throw new IllegalArgumentException("Y Axis orientation is not vertical"); + } + this.xAxis= xAxis; + this.yAxis= yAxis; + this.consumer = consumer; + } + + public static BoxRangeSelectorMouseModule create(DasPlot parent) { + BoxRangeSelectorMouseModule result= + new BoxRangeSelectorMouseModule(parent, null, parent.getXAxis(),parent.getYAxis()); + return result; + } + + @Override + public void mouseRangeSelected(MouseDragEvent e0) { + if (e0 instanceof MouseBoxEvent) { + MouseBoxEvent e= (MouseBoxEvent)e0; + + Datum xMin = xAxis.invTransform(e.getXMinimum()); + Datum xMax = xAxis.invTransform(e.getXMaximum()); + Datum yMin, yMax; + if (yAxis.isFlipped()) { + yMin = yAxis.invTransform(e.getYMinimum()); + yMax = yAxis.invTransform(e.getYMaximum()); + } + else { + yMin = yAxis.invTransform(e.getYMaximum()); + yMax = yAxis.invTransform(e.getYMinimum()); + } + BoxSelectionEvent evt = new BoxSelectionEvent(this, new DatumRange( xMin, xMax ), new DatumRange( yMin, yMax) ); + if (consumer != null) { + evt.setDataSet(consumer.getConsumedDataSet()); + } + fireBoxSelected(evt); + } + } + + /** Registers DataRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public void addBoxSelectionListener(BoxSelectionListener listener) { + listenerList.add(BoxSelectionListener.class, listener); + } + + /** Removes DataRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public void removeBoxSelectionListener(BoxSelectionListener listener) { + listenerList.remove(BoxSelectionListener.class, listener); + } + + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + protected void fireBoxSelected(BoxSelectionEvent event) { + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i] == BoxSelectionListener.class) { + ((BoxSelectionListener)listeners[i+1]).BoxSelected(event); + } + } + } + +} diff --git a/dasCore/src/main/java/org/das2/event/BoxRenderer.java b/dasCore/src/main/java/org/das2/event/BoxRenderer.java new file mode 100644 index 000000000..cc9cc3f9d --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/BoxRenderer.java @@ -0,0 +1,104 @@ +/* File: BoxRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; + +import java.awt.*; + +/** + * Draws a box + * @author eew + */ +public class BoxRenderer implements DragRenderer { + + Rectangle dirtyBounds; + DasCanvasComponent parent; + + boolean updating; + + /** use this value for P1 instead of the one sent by the client. This + * allows the box to used to render tweak of corner. + */ + Point overrideP1; + + public BoxRenderer(DasCanvasComponent parent, boolean updating ) { + this.parent= parent; + dirtyBounds= new Rectangle(); + this.updating= updating; + } + + public BoxRenderer( DasCanvasComponent parent ) { + this( parent, false ); + } + + public void clear(Graphics g) { + parent.paintImmediately(dirtyBounds); + } + + public Rectangle[] renderDrag( Graphics g1, Point p1, Point p2 ) { + Graphics2D g= (Graphics2D) g1; + + if ( overrideP1!=null ) p1= overrideP1; + + Rectangle r = new Rectangle(p1); + r.add(p2); + + Color color0= g.getColor(); + g.setColor(new Color(255,255,255,100)); + g.setStroke(new BasicStroke( 3.0f, + BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND )); + + g.drawRect(r.x, r.y, r.width, r.height); + + g.setStroke(new BasicStroke()); + g.setColor(color0); + + g.drawRect(r.x, r.y, r.width, r.height); + + dirtyBounds.setLocation(r.x-2,r.y-3); + dirtyBounds.add(r.x+r.width+2,r.y+r.height+3); + return new Rectangle[] { dirtyBounds }; + } + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + if ( overrideP1!=null ) p1= overrideP1; + return new MouseBoxEvent(source,p1,p2,isModified); + } + + public boolean isPointSelection() { + return false; + } + + public boolean isUpdatingDragSelection() { + return updating; + } + + /** + * set this to override + * @param p1 + */ + public void setDragStart( Point p1 ) { + this.overrideP1= p1; + } +} diff --git a/dasCore/src/main/java/org/das2/event/BoxSelectionEvent.java b/dasCore/src/main/java/org/das2/event/BoxSelectionEvent.java new file mode 100755 index 000000000..f2ed22a2e --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/BoxSelectionEvent.java @@ -0,0 +1,155 @@ +/* File: BoxSelectionEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.dataset.DataSet; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import java.util.HashMap; + +/** + * This is the range anolog to the DataPointSelectionEvent. The DPSE is a point, + * and this is a box. + * + * Note that it's acceptible to have null xrange and yrange, so that the same + * code can support a variety of applications. It's left to the programmer to + * see that these are used consistently. + * + * @author jbf + */ +public class BoxSelectionEvent extends DasEvent { + + private DatumRange xrange; + private DatumRange yrange; + private Datum finishx, finishy; + private Datum startx, starty; + private DataSet ds; + private HashMap planes; + + /** + * @deprecated use BoxSelectionEvent( Object, DatumRange, DatumRange ); + */ + public BoxSelectionEvent(Object source, org.das2.datum.Datum xMin, org.das2.datum.Datum xMax, org.das2.datum.Datum yMin, org.das2.datum.Datum yMax) { + this( source, xMin.le(xMax) ? new DatumRange( xMin, xMax ) : new DatumRange( xMax, xMin ), + yMin.le(yMax) ? new DatumRange( yMin, yMax ) : new DatumRange( yMax, yMin ) ); + } + + public BoxSelectionEvent( Object source, DatumRange xrange, DatumRange yrange ) { + this( source, xrange, yrange, null ); + } + + public BoxSelectionEvent( Object source, DatumRange xrange, DatumRange yrange, HashMap planes ) { + super( source ); + this.xrange= xrange; + this.yrange= yrange; + this.planes= planes; + } + + public void setFinish( Datum x, Datum y ) { + this.finishx = x; + this.finishy = y; + } + + public Datum getFinishX() { + return this.finishx; + } + + public Datum getFinishY() { + return this.finishy; + } + + public void setStart( Datum x, Datum y ) { + this.startx = x; + this.starty = y; + } + + public Datum getStartX() { + return this.startx; + } + + public Datum getStartY() { + return this.starty; + } + + /** + * @deprecated use getXRange().min(); + */ + public org.das2.datum.Datum getXMinimum() { + if ( xrange!=null ) return xrange.min(); else return null; + } + + /** + * @deprecated use getXRange().max(); + */ + public org.das2.datum.Datum getXMaximum() { + if ( xrange!=null ) return xrange.max(); else return null; + } + + /** + * @deprecated use getYRange().min(); + */ + public org.das2.datum.Datum getYMinimum() { + if ( yrange!=null ) return yrange.min(); else return null; + } + + /** + * @deprecated use getYRange().max(); + */ + public org.das2.datum.Datum getYMaximum() { + if ( yrange!=null ) return yrange.max(); else return null; + } + + public DatumRange getXRange() { + return xrange; + } + + public DatumRange getYRange() { + return yrange; + } + + public Object getPlane( String plane ) { + return planes==null ? null : planes.get(plane); + } + + public String[] getPlaneIds() { + if ( planes==null ) { + return new String[0]; + } else { + return (String[])planes.keySet().toArray( new String[ planes.keySet().size() ] ); + } + } + + public void setDataSet(DataSet ds) { + this.ds = ds; + } + + public DataSet getDataSet() { + return ds; + } + + public String toString() { + return "[BoxSelectionEvent x: "+xrange+", y: "+yrange+"]"; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/BoxSelectionListener.java b/dasCore/src/main/java/org/das2/event/BoxSelectionListener.java new file mode 100644 index 000000000..309a89772 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/BoxSelectionListener.java @@ -0,0 +1,32 @@ +/* File: BoxSelectionListener.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author jbf + */ +public abstract interface BoxSelectionListener extends java.util.EventListener { + public void BoxSelected(BoxSelectionEvent e); +} diff --git a/dasCore/src/main/java/org/das2/event/BoxSelectorMouseModule.java b/dasCore/src/main/java/org/das2/event/BoxSelectorMouseModule.java new file mode 100644 index 000000000..f18e545f3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/BoxSelectorMouseModule.java @@ -0,0 +1,300 @@ +/* + * DataPointSelectorMouseModule.java + * + * Created on November 3, 2005, 2:53 PM + * + * + */ +package org.das2.event; + +import org.das2.dataset.DataSetConsumer; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumRangeUtil; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasPlot; +import java.awt.Cursor; +import java.awt.Point; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.HashMap; + +/** + * General purpose mouse module for getting data point selections. The client + * provides the DragRenderer, generally a vertical line, horizontal line or a + * crosshair. + * + * Three properties control when BoxSelectionEvents are to be fired: + * dragEvents as the mouse is dragged, + * keyEvents when a key is pressed. (The key is the "keyChar" plane of the event) + * releaseEvents when the mouse is released. (false by default) + * + * @see BoxRenderer + * @author Jeremy + */ +public class BoxSelectorMouseModule extends MouseModule { + + DasAxis xaxis, yaxis; + DataSetConsumer dataSetConsumer; + javax.swing.event.EventListenerList listenerList = null; + MouseDragEvent lastMouseEvent; + /** when true, box selections are remembered, and tweaks to corners are allowed. */ + boolean tweakable = false; + BoxSelectionEvent lastSelectionEvent = null; + + public static BoxSelectorMouseModule create( DasPlot parent, String label ) { + return new BoxSelectorMouseModule( parent, parent.getXAxis(), parent.getYAxis(), null, new BoxRenderer(parent), label ); + } + + public BoxSelectorMouseModule(DasCanvasComponent parent, DasAxis xAxis, DasAxis yAxis, + DataSetConsumer consumer, + DragRenderer dragRenderer, String label) { + super(parent, dragRenderer, label); + this.xaxis = xAxis; + this.yaxis = yAxis; + this.dataSetConsumer = consumer; + } + + /** + * allow the last selection to be tweaked. It's the client's responsibility + * to draw the current selection. + * @param b + */ + public void setTweakable(boolean b) { + this.tweakable = b; + } + + private Datum[] checkTweak(Point p) { + double nx = DatumRangeUtil.normalize(lastSelectionEvent.getXRange(), xaxis.invTransform(p.getX())); + double ny = DatumRangeUtil.normalize(lastSelectionEvent.getYRange(), yaxis.invTransform(p.getY())); + + System.err.println("" + nx + " " + ny); + Datum otherx = null; + Datum othery = null; + + if (nx >= 0.0 && nx < 0.1) { + otherx = lastSelectionEvent.getXRange().max(); + } else if (nx > 0.9 && nx < 1.0) { + otherx = lastSelectionEvent.getXRange().min(); + } + + if (ny >= 0.0 && ny < 0.1) { + othery = lastSelectionEvent.getYRange().max(); + } else if (ny > 0.9 && ny < 1.0) { + othery = lastSelectionEvent.getYRange().min(); + } + + Datum[] otherCorner = new Datum[2]; + + otherCorner[0] = otherx; + otherCorner[1] = othery; + + return otherCorner; + + } + + @Override + public void mousePressed(MouseEvent e) { + + if (tweakable && lastSelectionEvent != null) { + Point p = new Point(e.getPoint()); + p.translate(e.getComponent().getX(), e.getComponent().getY()); + + Datum[] otherCorner= checkTweak( p ); + if (otherCorner[0] != null && otherCorner[1] != null) { // we're tweaking + double p1x = xaxis.transform(otherCorner[0]); + double p2x = yaxis.transform(otherCorner[1]); + Point p1 = new Point((int) p1x, (int) p2x); + ((BoxRenderer) dragRenderer).setDragStart(p1); + } else { + ((BoxRenderer) dragRenderer).setDragStart(null); + } + } + } + + @Override + public void mouseMoved(MouseEvent e) { + Cursor c= null; + if (tweakable && lastSelectionEvent != null) { + Point p = new Point(e.getPoint()); + p.translate(e.getComponent().getX(), e.getComponent().getY()); + + Datum[] otherCorner= checkTweak( p ); + if (otherCorner[0] != null && otherCorner[1] != null) { // we're tweaking + c= new Cursor( Cursor.MOVE_CURSOR ); + } + } + parent.getCanvas().setCursor(c); + } + + private BoxSelectionEvent getBoxSelectionEvent(MouseDragEvent mde) { + + MouseBoxEvent e = (MouseBoxEvent) mde; + + DatumRange xrange = null; + DatumRange yrange = null; + + Datum x=null, y=null; + Datum sx= null, sy=null; + + if (xaxis != null) { + Datum min = xaxis.invTransform(e.getXMinimum()); + Datum max = xaxis.invTransform(e.getXMaximum()); + if (min.gt(max)) { + Datum t = min; + min = max; + max = t; + } + xrange = new DatumRange(min, max); + x= xaxis.invTransform(e.getPoint().x); + sx= xaxis.invTransform(e.getPressPoint().x); + } + + if (yaxis != null) { + Datum min = yaxis.invTransform(e.getYMinimum()); + Datum max = yaxis.invTransform(e.getYMaximum()); + if (min.gt(max)) { + Datum t = min; + min = max; + max = t; + } + yrange = new DatumRange(min, max); + y= yaxis.invTransform( e.getPoint().y ); + sy= yaxis.invTransform( e.getPressPoint().y ); + } + + BoxSelectionEvent evt = new BoxSelectionEvent(this, xrange, yrange); + evt.setStart( sx,sy ); + evt.setFinish( x,y ); + + + this.lastSelectionEvent = evt; + return evt; + } + + public void mouseRangeSelected(MouseDragEvent e) { + lastMouseEvent = e; + if (keyEvents) { + parent.requestFocus(); + } + if (dragEvents) { + fireBoxSelectionListenerBoxSelected(getBoxSelectionEvent(e)); + } + } + + public void keyPressed(KeyEvent e) { + int keyCode = e.getKeyCode(); + + if (lastMouseEvent != null) { + BoxSelectionEvent dpse = getBoxSelectionEvent(lastMouseEvent); + HashMap planes = new HashMap(); + planes.put("keyChar", String.valueOf(e.getKeyChar())); + dpse = new BoxSelectionEvent(this, dpse.getXRange(), dpse.getYRange(), planes); + fireBoxSelectionListenerBoxSelected(dpse); + } + } + + /** Registers BoxSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addBoxSelectionListener(org.das2.event.BoxSelectionListener listener) { + if (listenerList == null) { + listenerList = new javax.swing.event.EventListenerList(); + } + listenerList.add(org.das2.event.BoxSelectionListener.class, listener); + } + + /** Removes BoxSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeBoxSelectionListener(org.das2.event.BoxSelectionListener listener) { + listenerList.remove(org.das2.event.BoxSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + protected void fireBoxSelectionListenerBoxSelected(BoxSelectionEvent event) { + if (listenerList == null) { + return; + } + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == org.das2.event.BoxSelectionListener.class) { + ((org.das2.event.BoxSelectionListener) listeners[i + 1]).BoxSelected(event); + } + } + } + /** + * Holds value of property dragEvents. + */ + private boolean dragEvents = false; + + /** + * Getter for property dragEvents. + * @return Value of property dragEvents. + */ + public boolean isDragEvents() { + return this.dragEvents; + } + + /** + * Setter for property dragEvents. + * @param dragEvents New value of property dragEvents. + */ + public void setDragEvents(boolean dragEvents) { + this.dragEvents = dragEvents; + } + /** + * Holds value of property keyEvents. + */ + private boolean keyEvents = false; + + /** + * Getter for property keyEvents. + * @return Value of property keyEvents. + */ + public boolean isKeyEvents() { + + return this.keyEvents; + } + + /** + * Setter for property keyEvents. + * @param keyEvents New value of property keyEvents. + */ + public void setKeyEvents(boolean keyEvents) { + this.keyEvents = keyEvents; + } + + public void mouseReleased(java.awt.event.MouseEvent e) { + super.mouseReleased(e); + if (releaseEvents) { + fireBoxSelectionListenerBoxSelected(getBoxSelectionEvent(lastMouseEvent)); + } + } + /** + * Holds value of property releaseEvents. + */ + private boolean releaseEvents = true; + + /** + * Getter for property releaseEvents. + * @return Value of property releaseEvents. + */ + public boolean isReleaseEvents() { + + return this.releaseEvents; + } + + /** + * Setter for property releaseEvents. + * @param releaseEvents New value of property releaseEvents. + */ + public void setReleaseEvents(boolean releaseEvents) { + + this.releaseEvents = releaseEvents; + } +} diff --git a/dasCore/src/main/java/org/das2/event/BoxZoomDialog.form b/dasCore/src/main/java/org/das2/event/BoxZoomDialog.form new file mode 100644 index 000000000..7f2e3f840 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/BoxZoomDialog.form @@ -0,0 +1,165 @@ + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dasCore/src/main/java/org/das2/event/BoxZoomDialog.java b/dasCore/src/main/java/org/das2/event/BoxZoomDialog.java new file mode 100644 index 000000000..635067620 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/BoxZoomDialog.java @@ -0,0 +1,229 @@ +/* + * BoxZoomDialog.java + * + * Created on May 16, 2007, 7:36 AM + */ + +package org.das2.event; + +import javax.swing.JOptionPane; + +/** + * + * @author jbf + */ +public class BoxZoomDialog extends javax.swing.JPanel { + + BoxZoomMouseModule module; + + /** Creates new form BoxZoomDialog */ + public BoxZoomDialog( BoxZoomMouseModule module ) { + initComponents(); + this.module= module; + zoomYButton.setAction( module.getZoomYAction() ); + zoomXButton.setAction( module.getZoomXAction() ); + zoomBoxButton.setAction( module.getZoomBoxAction() ); + } + + String getXRange( ) { + return xRangeTextField.getText(); + } + + String getYRange( ) { + return yRangeTextField.getText(); + } + + public boolean isAutoBoxZoom() { + return autoCheckBox.isSelected(); + } + + public boolean isConstrainProportions() { + return constrainProportionsCheckBox.isSelected(); + } + + public boolean isDisablePopup() { + return disablePopupCheckBox.isSelected(); + } + + public void setDisablePopup( boolean v ) { + disablePopupCheckBox.setSelected(v); + } + + public void setConstrainProportions( boolean v ) { + constrainProportionsCheckBox.setSelected(v); + } + + public void setAutoBoxZoom( boolean v ) { + autoCheckBox.setSelected(v); + } + + public void setXRange( String s ) { + this.xRangeTextField.setText(s); + } + + public void setYRange( String s ) { + this.yRangeTextField.setText(s); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + jLabel1 = new javax.swing.JLabel(); + xRangeTextField = new javax.swing.JTextField(); + jLabel2 = new javax.swing.JLabel(); + yRangeTextField = new javax.swing.JTextField(); + zoomYButton = new javax.swing.JButton(); + zoomBoxButton = new javax.swing.JButton(); + zoomXButton = new javax.swing.JButton(); + autoCheckBox = new javax.swing.JCheckBox(); + disablePopupCheckBox = new javax.swing.JCheckBox(); + constrainProportionsCheckBox = new javax.swing.JCheckBox(); + + jLabel1.setText("X:"); + + jLabel2.setText("Y:"); + + zoomYButton.setText("z
    o
    o
    m
    Y"); + zoomYButton.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + zoomBoxButton.setText("Zoom
    Box"); + + zoomXButton.setText("zoom X"); + + autoCheckBox.setText("auto box zoom"); + autoCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + autoCheckBox.setMargin(new java.awt.Insets(0, 0, 0, 0)); + autoCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + autoCheckBoxActionPerformed(evt); + } + }); + + disablePopupCheckBox.setText("disable popup"); + disablePopupCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + disablePopupCheckBox.setMargin(new java.awt.Insets(0, 0, 0, 0)); + disablePopupCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + disablePopupCheckBoxActionPerformed(evt); + } + }); + + constrainProportionsCheckBox.setText("constrain proportions"); + constrainProportionsCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + constrainProportionsCheckBox.setMargin(new java.awt.Insets(0, 0, 0, 0)); + constrainProportionsCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + constrainProportionsCheckBoxActionPerformed(evt); + } + }); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(jLabel1) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(xRangeTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 155, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(layout.createSequentialGroup() + .add(jLabel2) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(yRangeTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(layout.createSequentialGroup() + .add(10, 10, 10) + .add(zoomYButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 21, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(zoomXButton) + .add(zoomBoxButton, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 109, Short.MAX_VALUE))) + .add(constrainProportionsCheckBox) + .add(autoCheckBox) + .add(layout.createSequentialGroup() + .add(17, 17, 17) + .add(disablePopupCheckBox))))) + .addContainerGap()) + ); + + layout.linkSize(new java.awt.Component[] {xRangeTextField, yRangeTextField}, org.jdesktop.layout.GroupLayout.HORIZONTAL); + + layout.linkSize(new java.awt.Component[] {zoomBoxButton, zoomXButton}, org.jdesktop.layout.GroupLayout.HORIZONTAL); + + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(jLabel1) + .add(xRangeTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(jLabel2) + .add(yRangeTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(zoomBoxButton) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(zoomXButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 31, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(zoomYButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 80, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(constrainProportionsCheckBox) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(autoCheckBox) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(disablePopupCheckBox) + .addContainerGap()) + ); + }//
    //GEN-END:initComponents + + private void disablePopupCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_disablePopupCheckBoxActionPerformed + if ( disablePopupCheckBox.isSelected() ) { + int result= JOptionPane.showConfirmDialog(this, + "Hitting OK will disable this popup and will perform the zoom. " + + "Popup may be re-enabled via the plot's property editor.", + "hiding box zoom popup", + JOptionPane.OK_CANCEL_OPTION ); + if ( result==JOptionPane.OK_OPTION ) { + module.setAutoUpdate(true); + module.zoomBox(); + this.module.dialog.setVisible(false); + } else { + disablePopupCheckBox.setSelected(false); + } + } + module.guiChanged(); + }//GEN-LAST:event_disablePopupCheckBoxActionPerformed + + private void constrainProportionsCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_constrainProportionsCheckBoxActionPerformed + module.guiChanged(); + }//GEN-LAST:event_constrainProportionsCheckBoxActionPerformed + + private void autoCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_autoCheckBoxActionPerformed + disablePopupCheckBox.setEnabled( autoCheckBox.isSelected() ); + if ( !autoCheckBox.isSelected() ) disablePopupCheckBox.setSelected(false); + module.guiChanged(); + }//GEN-LAST:event_autoCheckBoxActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox autoCheckBox; + private javax.swing.JCheckBox constrainProportionsCheckBox; + private javax.swing.JCheckBox disablePopupCheckBox; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JTextField xRangeTextField; + private javax.swing.JTextField yRangeTextField; + private javax.swing.JButton zoomBoxButton; + private javax.swing.JButton zoomXButton; + private javax.swing.JButton zoomYButton; + // End of variables declaration//GEN-END:variables + +} diff --git a/dasCore/src/main/java/org/das2/event/BoxZoomMouseModule.java b/dasCore/src/main/java/org/das2/event/BoxZoomMouseModule.java new file mode 100644 index 000000000..1666b184d --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/BoxZoomMouseModule.java @@ -0,0 +1,209 @@ +/* + * BoxZoomMouseModule.java + * + * Created on May 20, 2005, 12:21 PM + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.GraphUtil; +import org.das2.graph.DasAxis; +import org.das2.dataset.DataSetConsumer; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.DatumRangeUtil; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; + +/** + * + * @author Jeremy + */ +public class BoxZoomMouseModule extends BoxRangeSelectorMouseModule { + + DatumRange xrange, yrange; + JDialog dialog; + JLabel xrangeLabel, yrangeLabel; + JCheckBox autoUpdateCB, constrainProportionsCB; + BoxZoomDialog bzdialog; + + boolean autoUpdate= true; + boolean constrainProportions= false; + + /** Creates a new instance of BoxZoomMouseModule */ + public BoxZoomMouseModule( DasCanvasComponent parent, DataSetConsumer consumer, DasAxis xAxis, DasAxis yAxis ) { + super( parent, consumer, xAxis, yAxis ); + setLabel("Box Zoom"); + } + + JDialog getDialog() { + if ( dialog==null ) { + dialog= new JDialog( (Frame)null ); + dialog.setLocationRelativeTo( parent ); + + Container content= dialog.getContentPane(); + bzdialog= new BoxZoomDialog( this ); + content.add( bzdialog ); + + dialog.pack(); + } + + bzdialog.setAutoBoxZoom( autoUpdate ); + bzdialog.setDisablePopup( popupDisabled ); + bzdialog.setConstrainProportions( constrainProportions ); + + if ( !popupDisabled || !autoUpdate ) dialog.setVisible(true); + + return dialog; + } + + protected void guiChanged() { + autoUpdate= bzdialog.isAutoBoxZoom( ); + popupDisabled= bzdialog.isDisablePopup( ); + constrainProportions = bzdialog.isConstrainProportions( ); + } + + Action getZoomYAction() { + return new AbstractAction("
    z
    o
    o
    m
    Y
    ") { + public void actionPerformed( ActionEvent e ) { + if ( yrange!=null ) yAxis.setDatumRange(yrange); + } + }; + } + + Action getZoomXAction() { + return new AbstractAction("zoom X") { + public void actionPerformed( ActionEvent e ) { + if ( xrange!=null ) xAxis.setDatumRange(xrange); + } + }; + } + + Action getZoomBoxAction() { + return new AbstractAction("
    Zoom
    Box
    ") { + public void actionPerformed( ActionEvent e ) { + zoomBox(); + } + }; + } + + protected void zoomBox() { + if ( yrange!=null ) yAxis.setDatumRange(yrange); + if ( xrange!=null ) xAxis.setDatumRange(xrange); + } + + @Override + public void mouseRangeSelected(MouseDragEvent e0) { + if ( e0 instanceof MouseBoxEvent ) { + MouseBoxEvent e= (MouseBoxEvent)e0; + + xrange= GraphUtil.invTransformRange( xAxis, e.getXMinimum(), e.getXMaximum() ); + yrange= GraphUtil.invTransformRange( yAxis, e.getYMinimum(), e.getYMaximum() ); + + if ( constrainProportions ) { + double aspect= yAxis.getHeight() / (double)xAxis.getWidth(); + DatumRange mx= new DatumRange( e.getXMinimum(), e.getXMaximum(), Units.dimensionless ); + DatumRange my= new DatumRange( e.getYMinimum(), e.getYMaximum(), Units.dimensionless ); + double mouseAspect= my.width().divide(mx.width()).doubleValue(Units.dimensionless); + if ( mouseAspect > aspect ) { + double f= mouseAspect / aspect; + mx= DatumRangeUtil.rescale(my, 0.5-f/2, 0.5+f/2 ); + } else { + double f= aspect / mouseAspect; + my= DatumRangeUtil.rescale(my, 0.5-f/2, 0.5+f/2 ); + } + xrange= GraphUtil.invTransformRange( xAxis, mx.min().doubleValue(Units.dimensionless), + mx.max().doubleValue(Units.dimensionless) ); + yrange= GraphUtil.invTransformRange( yAxis, my.max().doubleValue(Units.dimensionless), + my.min().doubleValue(Units.dimensionless) ); + } else { + xrange= GraphUtil.invTransformRange( xAxis, e.getXMinimum(), e.getXMaximum() ); + yrange= GraphUtil.invTransformRange( yAxis, e.getYMaximum(), e.getYMinimum() ); + } + + if ( ! autoUpdate ) { + getDialog(); + bzdialog.setXRange( xrange.toString() ); + bzdialog.setYRange(yrange.toString()); + } else { + zoomBox(); + } + + } else if ( e0.isGesture() ) { + if ( e0.getGesture()==Gesture.ZOOMOUT ) { + xAxis.setDataRangeZoomOut(); + yAxis.setDataRangeZoomOut(); + } else if ( e0.getGesture()==Gesture.BACK ) { + xAxis.setDataRangePrev(); + yAxis.setDataRangePrev(); + } else if ( e0.getGesture()==Gesture.FORWARD ) { + xAxis.setDataRangeForward(); + yAxis.setDataRangeForward(); + } + } + + } + + /** + * Getter for property autoUpdate. + * @return Value of property autoUpdate. + */ + public boolean isAutoUpdate() { + return this.autoUpdate; + } + + /** + * Setter for property autoUpdate. + * @param autoUpdate New value of property autoUpdate. + */ + public void setAutoUpdate(boolean autoUpdate) { + if ( bzdialog!=null ) bzdialog.setAutoBoxZoom( autoUpdate ); + this.autoUpdate = autoUpdate; + } + + /** + * Getter for property constrainProportions. + * @return Value of property constrainProportions. + */ + public boolean isConstrainProportions() { + return this.constrainProportions; + } + + /** + * Setter for property constrainProportions. + * @param constrainProportions New value of property constrainProportions. + */ + public void setConstrainProportions(boolean constrainProportions) { + if ( bzdialog!=null ) bzdialog.setConstrainProportions(constrainProportions); + this.constrainProportions = constrainProportions; + } + + /** + * Holds value of property popupDisabled. + */ + private boolean popupDisabled; + + /** + * Getter for property popupDisabled. + * @return Value of property popupDisabled. + */ + public boolean isPopupDisabled() { + return this.popupDisabled; + } + + /** + * Setter for property popupDisabled. + * @param popupDisabled New value of property popupDisabled. + */ + public void setPopupDisabled(boolean popupDisabled) { + if ( bzdialog!=null ) bzdialog.setDisablePopup(popupDisabled); + this.popupDisabled = popupDisabled; + } + + + + + +} diff --git a/dasCore/src/main/java/org/das2/event/ColorBarRepaletteMouseModule.java b/dasCore/src/main/java/org/das2/event/ColorBarRepaletteMouseModule.java new file mode 100644 index 000000000..15bc4b104 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/ColorBarRepaletteMouseModule.java @@ -0,0 +1,114 @@ +/* File: VerticalRangeSelectorMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumRangeUtil; +import org.das2.graph.DasColorBar; +import org.das2.graph.DasRow; +import org.das2.graph.Renderer; +import java.awt.event.MouseEvent; +import javax.swing.event.EventListenerList; + +/** + * + * @author jbf + */ +public class ColorBarRepaletteMouseModule extends MouseModule { + + DasColorBar colorBar; + Renderer parent; + DatumRange range0; + boolean animated0; + + /** Utility field used by event firing mechanism. */ + private EventListenerList listenerList = null; + + public String getLabel() { return "Repalette"; }; + + public ColorBarRepaletteMouseModule( Renderer parent, DasColorBar colorBar ) { + if (colorBar.isHorizontal()) { + throw new IllegalArgumentException("Axis orientation is not vertical"); + } + this.parent= parent; + // this.dragRenderer= (DragRenderer)HorizontalRangeRenderer.renderer; + this.dragRenderer= new HorizontalSliceSelectionRenderer(parent.getParent()); + this.colorBar= colorBar; + } + + private void setColorBar( int y ) { + DatumRange dr; + DasRow row= colorBar.getRow(); + double alpha= ( row.getDMaximum() - y ) / (1.*row.getHeight()); + dr= DatumRangeUtil.rescale(range0, 0, alpha ); + colorBar.setDatumRange(dr); + parent.update(); + } + + public void mouseReleased( MouseEvent e ) { + colorBar.setAnimated(animated0); + } + + public void mousePointSelected(MousePointSelectionEvent e) { + setColorBar( e.y ); + } + + /** Registers DataRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new EventListenerList(); + } + listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Removes DataRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { + ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); + } + } + } + + public void mousePressed(java.awt.event.MouseEvent e) { + super.mousePressed(e); + animated0= colorBar.isAnimated(); + colorBar.setAnimated(false); + range0= colorBar.getDatumRange(); + } + +} diff --git a/dasCore/src/main/java/org/das2/event/CommentDataPointSelectionEvent.java b/dasCore/src/main/java/org/das2/event/CommentDataPointSelectionEvent.java new file mode 100755 index 000000000..8a627e150 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/CommentDataPointSelectionEvent.java @@ -0,0 +1,36 @@ +/* + * DataTupleSelectionEvent.java + * + * Created on October 13, 2003, 11:27 AM + */ + +package org.das2.event; + +import org.das2.datum.Datum; +import org.das2.event.DataPointSelectionEvent; + +/** + * @deprecated use DataPointSelectionEvent planes hashmap "comment" key + * @author jbf + */ +public class CommentDataPointSelectionEvent extends DataPointSelectionEvent { + + String comment; + + public static CommentDataPointSelectionEvent create( DataPointSelectionEvent e, String comment ) { + CommentDataPointSelectionEvent ce = new CommentDataPointSelectionEvent( e.getSource(), e.getX(), e.getY(), comment ); + ce.setDataSet(e.getDataSet()); + return ce; + } + + /** Creates a new instance of DataTupleSelectionEvent */ + private CommentDataPointSelectionEvent( Object source, Datum x, Datum y, String comment ) { + super( source, x, y ); + this.comment= comment; + } + + public String getComment() { + return comment; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/CrossHairMouseModule.java b/dasCore/src/main/java/org/das2/event/CrossHairMouseModule.java new file mode 100755 index 000000000..52817ac3a --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/CrossHairMouseModule.java @@ -0,0 +1,146 @@ +/* File: CrossHairMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; +import org.das2.dataset.DataSet; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; +import org.das2.graph.Renderer; + + +/** + * + * @author Owner + */ +public class CrossHairMouseModule extends MouseModule { + + public static abstract class InfoItem { + public abstract void label(org.das2.datum.Datum x, org.das2.datum.Datum y, StringBuilder out); + public org.das2.datum.Datum[] snap(org.das2.datum.Datum x, org.das2.datum.Datum y) { + return null; + } + } + + private DasAxis xaxis; + private DasAxis yaxis; + private DasPlot plot; + + protected DataPointSelectionEvent de; + + org.das2.dataset.DataSetConsumer dataSetConsumer; + + /** Utility field used by event firing mechanism. */ + private javax.swing.event.EventListenerList listenerList = null; + + public CrossHairMouseModule(DasPlot parent, DasAxis xaxis, DasAxis yaxis) { + this( parent, parent, xaxis, yaxis ); + } + + public CrossHairMouseModule( DasPlot parent, org.das2.dataset.DataSetConsumer dataSetConsumer, DasAxis xAxis, DasAxis yAxis ) { + super(parent,new CrossHairRenderer(parent,dataSetConsumer,xAxis,yAxis),"Crosshair Digitizer"); + this.plot= parent; + this.dataSetConsumer= dataSetConsumer; + this.xaxis= xAxis; + this.yaxis= yAxis; + this.de= new DataPointSelectionEvent(this,null,null); + } + + public static CrossHairMouseModule create( DasPlot parent ) { + DasAxis xaxis= null; + DasAxis yaxis= null; + return new CrossHairMouseModule(parent,null,xaxis,yaxis); + } + + public void addInfoItem(InfoItem i) { + DragRenderer dr = getDragRenderer(); + if (dr instanceof CrossHairRenderer) { + ((CrossHairRenderer)dr).addInfoItem(i); + } + } + + public void removeInfoItem(InfoItem i) { + DragRenderer dr = getDragRenderer(); + if (dr instanceof CrossHairRenderer) { + ((CrossHairRenderer)dr).removeInfoItem(i); + } + } + + private DataSet getContextDataSet() { + DataSet ds; + if ( dataSetConsumer!=null ) { + ds = dataSetConsumer.getConsumedDataSet(); + } else { + Renderer[] rends= ((DasPlot)this.parent).getRenderers(); + if ( rends.length>0 ) { + ds= rends[0].getConsumedDataSet(); + } else { + ds= null; + } + } + return ds; + } + protected DataPointSelectionEvent getDataPointSelectionEvent(MousePointSelectionEvent e) { + de.setDataSet( getContextDataSet() ); + DasAxis xa, ya; + xa= ( this.xaxis==null ) ? plot.getXAxis() : xaxis; + ya= ( this.yaxis==null ) ? plot.getYAxis() : yaxis; + de.set(xa.invTransform(e.getX()),ya.invTransform(e.getY())); + return de; + } + + public void mousePointSelected(MousePointSelectionEvent e) { + fireDataPointSelectionListenerDataPointSelected(getDataPointSelectionEvent(e)); + } + + /** Registers DataPointSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } + listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** Removes DataPointSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + listenerList.remove(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + protected void fireDataPointSelectionListenerDataPointSelected(DataPointSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataPointSelectionListener.class) { + ((org.das2.event.DataPointSelectionListener)listeners[i+1]).dataPointSelected(event); + } + } + } + +} diff --git a/dasCore/src/main/java/org/das2/event/CrossHairRenderer.java b/dasCore/src/main/java/org/das2/event/CrossHairRenderer.java new file mode 100755 index 000000000..421a15c6a --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/CrossHairRenderer.java @@ -0,0 +1,460 @@ +/* File: CrossHairRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.event; + +import org.das2.components.propertyeditor.Editable; +import org.das2.dataset.DataSetConsumer; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.format.DefaultDatumFormatterFactory; +import org.das2.datum.format.DatumFormatter; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; + +import org.das2.datum.Datum; + +import org.das2.graph.Renderer; +import java.awt.*; +import java.awt.geom.Point2D; +import java.text.*; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author eew + */ +public class CrossHairRenderer extends LabelDragRenderer implements DragRenderer, Editable { + + protected int xInitial; + protected int yInitial; + protected DasAxis XAxis; + protected DasAxis YAxis; + protected DasPlot parent; + private int ix = 0; // store the current position within the dataset object + private int iy = 0; + private int context; + private DatumFormatter nfx; + private DatumFormatter nfy; + private DatumFormatter nfz; + private FontMetrics fm; + private int dxMax = -999999; + private Rectangle hDirtyBounds; + private Rectangle vDirtyBounds; + private Point crossHairLocation = null; + private DataSetConsumer dataSetConsumer; + + private List infoItems = new ArrayList(); + + /** + * Holds value of property allPlanesReport. + */ + private boolean allPlanesReport; + /** + * Holds value of property debugging. + */ + private boolean debugging; + /** + * snapping = true means that the cross-hair digitizer will only + * display x and y values that are valid tags in the data set. + */ + private boolean snapping; + + public CrossHairRenderer(DasPlot parent, DataSetConsumer dataSetConsumer, DasAxis xAxis, DasAxis yAxis) { + super(parent); + this.XAxis = xAxis; + this.YAxis = yAxis; + this.parent = parent; + this.dataSetConsumer = dataSetConsumer; + hDirtyBounds = new Rectangle(); + vDirtyBounds = new Rectangle(); + } + + void addInfoItem(CrossHairMouseModule.InfoItem item) { + infoItems.add(item); + } + + void removeInfoItem(CrossHairMouseModule.InfoItem item) { + infoItems.remove(item); + } + + private DatumFormatter addResolutionToFormat(DatumFormatter nfz) throws ParseException { + String formatString = nfz.toString(); + String result; + if (formatString.indexOf('E') == -1) { + result = formatString + "00"; + } else { + String[] ss = formatString.split("E"); + if (ss[0].indexOf('.') == -1) { + result = ss[0] + ".00" + "E0"; + } else { + result = ss[0] + "00" + "E0"; + } + } + return DefaultDatumFormatterFactory.getInstance().newFormatter(result); + } + + private String getZString(TableDataSet tds, Datum x, Datum y, int[] ij) { + int i = DataSetUtil.closestColumn(tds, x); + int j = TableUtil.closestRow(tds, tds.tableOfIndex(i), y); + Datum zValue = tds.getDatum(i, j); + + if (ij != null) { + ij[0] = i; + ij[1] = j; + } + + try { + if (dataSetConsumer instanceof TableDataSetConsumer) { + nfz = ((TableDataSetConsumer) dataSetConsumer).getZAxis().getDatumFormatter(); + nfz = addResolutionToFormat(nfz); + } else { + nfz = DefaultDatumFormatterFactory.getInstance().newFormatter("0.000"); + } + } catch (java.text.ParseException pe) { + org.das2.DasProperties.getLogger().severe("failure to create formatter"); + DasAxis axis = ((TableDataSetConsumer) dataSetConsumer).getZAxis(); + axis.getUnits().getDatumFormatterFactory().defaultFormatter(); + } + + String result; + if (zValue.isFill()) { + result = "fill"; + } else { + result = nfz.grannyFormat(zValue); + } + if (allPlanesReport) { + if (debugging) { + result += "!c" + tds.toString(); + } + String[] planeIds = tds.getPlaneIds(); + for (int iplane = 0; iplane < planeIds.length; iplane++) { + if (!planeIds[iplane].equals("")) { + result = result + "!c"; + result += planeIds[iplane] + ":" + nfz.grannyFormat(((TableDataSet) tds.getPlanarView(planeIds[iplane])).getDatum(i, j)); + if (debugging) { + result += " " + ((TableDataSet) tds.getPlanarView(planeIds[iplane])).toString(); + } + } + } + if (debugging) { + result += "!ci:" + i + " j:" + j; + } + } + return result; + } + + private int closestPointVector(VectorDataSet ds, Datum x, Datum y) { + + Boolean xmono = (Boolean) ds.getProperty(DataSet.PROPERTY_X_MONOTONIC); + + DasAxis xa, ya; + xa = (this.XAxis == null) ? parent.getXAxis() : XAxis; + ya = (this.YAxis == null) ? parent.getYAxis() : YAxis; + + int start, end; + Point2D.Double me = new Point2D.Double(xa.transform(x), ya.transform(y)); + if (xmono != null && xmono.equals(Boolean.TRUE)) { + start = DataSetUtil.getPreviousColumn(ds, xa.getDataMinimum()); + end = DataSetUtil.getNextColumn(ds, xa.getDataMaximum()); + } else { + start = 0; + end = ds.getXLength(); + } + + int bestIndex = -1; + double bestXDist = Double.POSITIVE_INFINITY; + double bestDist = Double.POSITIVE_INFINITY; + int comparisons = 0; + + // prime the best dist comparison by scanning decimated dataset + for (int i = start; i < end; i += 100) { + double x1 = xa.transform(ds.getXTagDatum(i)); + double dist = Math.abs(x1 - me.getX()); + if (dist < bestXDist) { + bestXDist = dist; + } + } + + for (int i = start; i < end; i++) { + double x1 = xa.transform(ds.getXTagDatum(i)); + if (Math.abs(x1 - me.getX()) <= bestXDist) { + Point2D them = new Point2D.Double(x1, ya.transform(ds.getDatum(i))); + double dist = me.distance(them); + comparisons++; + if (dist < bestDist) { + bestIndex = i; + bestDist = dist; + bestXDist = Math.abs(x1 - me.getX()); + } + } + } + + return bestIndex; + + + } + + @Override + public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { + Graphics2D g = (Graphics2D) g1; + g.setRenderingHints((RenderingHints) org.das2.DasProperties.getRenderingHints()); + + DataSet ds; + + if ( dataSetConsumer!=null ) { + ds = dataSetConsumer.getConsumedDataSet(); + } else { + Renderer[] rends= ((DasPlot)this.parent).getRenderers(); + if ( rends.length>0 ) { + ds= rends[0].getConsumedDataSet(); + } else { + ds= null; + } + } + + Rectangle[] superDirty = null; + + Datum x = null; + Datum y = null; + + DasAxis xa, ya; + xa = (this.XAxis == null) ? parent.getXAxis() : XAxis; + ya = (this.YAxis == null) ? parent.getYAxis() : YAxis; + + if (crossHairLocation == null) { + + x = xa.invTransform(p2.x); + y = ya.invTransform(p2.y); + + nfy = y.getFormatter(); + + String xAsString; + nfx = x.getFormatter(); + xAsString = nfx.format(x); + + + String yAsString; + yAsString = nfy.format(y); + + String report; + + String nl = multiLine ? "!c" : " "; + + report = "x:" + xAsString + nl + "y:" + yAsString; + + if (ds != null) { + if (ds instanceof TableDataSet) { + TableDataSet tds = (TableDataSet) ds; + String zAsString; + if (tds != null && snapping) { + int[] ij = new int[2]; + zAsString = getZString(tds, x, y, ij); + x = tds.getXTagDatum(ij[0]); + xAsString = nfx.format(x); + y = tds.getYTagDatum(tds.tableOfIndex(ij[0]), ij[1]); + yAsString = nfy.format(y); + } else { + zAsString = getZString(tds, x, y, null); + } + report = "x:" + xAsString + nl + "y:" + yAsString + nl + "z:" + zAsString; + } else { + if (ds == null && dataSetConsumer instanceof DasPlot) { + if (((DasPlot) dataSetConsumer).getRenderers().length > 0) { + ds = ((DasPlot) dataSetConsumer).getRenderer(0).getDataSet(); + } + } + if (ds != null && snapping) { + VectorDataSet vds = (VectorDataSet) ds; + if (vds.getXLength() == 0) { + yAsString = "(empty dataset)"; + } else { + int i = closestPointVector(vds, x, y); + x = vds.getXTagDatum(i); + y = vds.getDatum(i); + xAsString = nfx.format(x); + yAsString = nfy.format(y); + if (allPlanesReport) { + String result = yAsString; + String[] planeIds = vds.getPlaneIds(); + for (int iplane = 0; iplane < planeIds.length; iplane++) { + if (!planeIds[iplane].equals("")) { + result = result + "!c"; + result += planeIds[iplane] + ":" + nfy.grannyFormat(((VectorDataSet) vds.getPlanarView(planeIds[iplane])).getDatum(i)); + if (debugging) { + result += " " + ((VectorDataSet) vds.getPlanarView(planeIds[iplane])).toString(); + } + } + } + yAsString = result; + } + } + } + report = "x:" + xAsString + nl + "y:" + yAsString; + } + } + + StringBuilder builder = new StringBuilder(report); + for (CrossHairMouseModule.InfoItem item : infoItems) { + builder.append("!C"); + item.label(x, y, builder); + } + report = builder.toString(); + + setLabel(report); + super.renderDrag(g, p1, p2); + } + + if (snapping && x != null && y != null) { + Point p3 = new Point((int) xa.transform(x), (int) ya.transform(y)); + drawCrossHair(g, p3); + /* + //p2= GraphUtil.moveTowards( p2, p3, 4 ); + g.drawLine( p2.x, p2.y, p3.x, p3.y ); + dirtyBounds.add( p3 ); + dirtyBounds.add( p2 );*/ + } else { + drawCrossHair(g, p2); + } + + + return new Rectangle[]{this.hDirtyBounds, + this.vDirtyBounds, + dirtyBounds + }; + } + + private void drawCrossHair(Graphics g0, Point p) { + + Graphics2D g = (Graphics2D) g0.create(); + g.setClip(null); + + Color color0 = Color.black; + + g.setColor(color0); + + Dimension d = parent.getCanvas().getSize(); + hDirtyBounds.setBounds(0, p.y - 1, d.width, 3); + + Stroke stroke0 = g.getStroke(); + + g.setColor(ghostColor); + g.setStroke(new BasicStroke(3.0f)); + g.drawLine(0, p.y, d.width, p.y); + g.drawLine(p.x, 0, p.x, d.height); + + g.setColor(color0); + g.setStroke(stroke0); + + g.drawLine(0, p.y, d.width, p.y); + vDirtyBounds.setBounds(p.x - 1, 0, 3, d.height); + g.drawLine(p.x, 0, p.x, d.height); + + g.dispose(); + + } + + public void clear(Graphics g) { + super.clear(g); + parent.paintImmediately(hDirtyBounds); + parent.paintImmediately(vDirtyBounds); + } + + public boolean isPointSelection() { + return true; + } + + public boolean isUpdatingDragSelection() { + return false; + } + + /** + * Getter for property allPlanesReport. + * @return Value of property allPlanesReport. + */ + public boolean isAllPlanesReport() { + return this.allPlanesReport; + } + + /** + * Setter for property allPlanesReport. + * @param allPlanesReport New value of property allPlanesReport. + */ + public void setAllPlanesReport(boolean allPlanesReport) { + this.allPlanesReport = allPlanesReport; + } + + /** + * Getter for property debugging. + * @return Value of property debugging. + */ + public boolean isDebugging() { + return this.debugging; + } + + /** + * Setter for property debugging. + * @param debugging New value of property debugging. + */ + public void setDebugging(boolean debugging) { + this.debugging = debugging; + } + + public Rectangle[] getDirtyBounds() { + return new Rectangle[]{super.dirtyBounds, this.hDirtyBounds, this.vDirtyBounds}; + } + + public boolean isSnapping() { + return snapping; + } + + public void setSnapping(boolean b) { + snapping = b; + } + /** + * Holds value of property multiLine. + */ + private boolean multiLine = false; + + /** + * Getter for property multiLine. + * @return Value of property multiLine. + */ + public boolean isMultiLine() { + + return this.multiLine; + } + + /** + * Setter for property multiLine. + * @param multiLine New value of property multiLine. + */ + public void setMultiLine(boolean multiLine) { + + this.multiLine = multiLine; + } +} diff --git a/dasCore/src/main/java/org/das2/event/CutoffMouseModule.java b/dasCore/src/main/java/org/das2/event/CutoffMouseModule.java new file mode 100644 index 000000000..c99ea6839 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/CutoffMouseModule.java @@ -0,0 +1,876 @@ +/* + * Cutoff2MouseModule.java + * + * Created on November 10, 2005, 1:41 PM + * + * + */ + +package org.das2.event; + +import java.text.ParseException; +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.dataset.AverageTableRebinner; +import org.das2.dataset.ClippedTableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetConsumer; +import org.das2.dataset.DataSetRebinner; +import org.das2.dataset.DataSetUpdateEvent; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.DefaultVectorDataSet; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.SingleVectorDataSet; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasColumn; +import org.das2.graph.DasPlot; +import org.das2.graph.DasRow; +import org.das2.graph.SymbolLineRenderer; +import org.das2.util.monitor.ProgressMonitor; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.beans.PropertyChangeEvent; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.swing.JFrame; +import org.das2.util.monitor.NullProgressMonitor; + +/** + * CutoffMouseModule contains Ondrej's code for selecting the cutoff, and allows + * operator to graphically adjust the control parameters. + * @author Jeremy + */ +public class CutoffMouseModule extends BoxSelectorMouseModule { + + DasAxis xaxis, yaxis; + DataSetConsumer dataSetConsumer; + DatumRange xrange; + DatumRange yrange; + String lastComment; + CutoffSlicer cutoffSlicer; + DasApplication application; + + private static final Logger logger= org.das2.system.DasLogger.getLogger( + org.das2.system.DasLogger.DATA_OPERATIONS_LOG ); + + public CutoffMouseModule( DasPlot parent, DataSetConsumer consumer ) { + super( parent, parent.getXAxis(), parent.getYAxis(), consumer, + new BoxRenderer(parent,true), "Cutoff" ); + application= parent.getCanvas().getApplication(); + this.dataSetConsumer= consumer; + } + + public static final String ALGO_ONDREJ = "Ondrej: "; + + /** Set the configuration of slope calculation and digitizer output using an + * algorithm selection and configuration string. At present only one algorithm is + * supported: + * + * ALGO_ONDREJ + * + * Parameter settings for the Ondrej algorithm are: + * min: (TODO explain) + * slopeMin (TODO explain) + * nave (TODO explain) + * cutoff (TODO explain) + * xres (TODO explain) + * + * Example configuration strings known to work are: + * + * For Voyager FFT'ed Waveforms: min=-4. slopeMin=0.26 nave=3 cutoff=lower xres=1s + * For Galileo Survey Spectra: min=1.78 slopeMin=0.072 nave=3 cutoff=lower xres=120s + * min=1.78 slopeMin=0.072 nave=3 cutoff=lower xres=60s + * min=1.78 slopeMin=0.072 nave=3 cutoff=lower xres=30s + * @param config + */ + public void setConfig(String algo, String config ) throws ParseException { + if ( !algo.equals(ALGO_ONDREJ) ) + throw new IllegalArgumentException("Only Ondrej's cutoff algorithim has been" + + " implemented at this time"); + + Pattern p= Pattern.compile("(\\S+)=(\\S+)"); + + Matcher m= p.matcher(config); + + while ( m.find() ) { + String name= m.group(1); + String sval= m.group(2); + if ( name.equals("min") ) { + setLevelMin(Units.dimensionless.parse(sval)); + } else if ( name.equals("slopeMin") ) { + setSlopeMin(Units.dimensionless.parse(sval)); + } else if ( name.equals("nave") ) { + setNave( Integer.parseInt(sval) ); + } else if (name.equals("cutoff") ) { + setLowCutoff( "lower".equals(sval) ); + } else if (name.equals("xres") ) { + setXResolution( Units.seconds.parse(sval) ); + } + } + } + + @Override + protected void fireBoxSelectionListenerBoxSelected(BoxSelectionEvent event) { + + DatumRange xrange0= xrange; + DatumRange yrange0= yrange; + + xrange= event.getXRange(); + yrange= event.getYRange(); + if ( event.getPlane("keyChar")!=null ) { + lastComment= (String)event.getPlane("keyChar"); + } else { + if ( xrange.width().lt( getXResolution().multiply(5) ) ) { + super.fireBoxSelectionListenerBoxSelected(event); + } + return; + } + + String keyChar= String.valueOf( event.getPlane("keyChar") ); + if ( keyChar.equals("!") ) { // note null becomes "null" + assertChannel(new NullProgressMonitor()); + } else { + try { + recalculateSoon( ); + } catch ( RuntimeException ex ) { + xrange= xrange0; + yrange= yrange0; + throw ex; + } + } + } + + /** + * return RebinDescriptor that is on discrete, repeatable boundaries. + * get us2000, divide by resolution, truncate, multiply by resolution. + */ + private RebinDescriptor getRebinDescriptor( DatumRange range ) { + double res= xResolution.doubleValue(Units.microseconds); + double min= range.min().doubleValue(Units.us2000); + min= Math.floor( min / res ); + double max= range.max().doubleValue(Units.us2000); + max= Math.ceil( max / res ); + int nbin= (int)(max-min); + + RebinDescriptor ddx= new RebinDescriptor( min*res, max*res, Units.us2000, nbin, false ); + return ddx; + } + + private void recalculateSoon( ) { + Runnable run= new Runnable() { + @Override + public void run() { + ProgressMonitor mon= application.getMonitorFactory().getMonitor( + parent, "calculating cutoffs", "calculating cutoffs" ); + recalculate( mon ); + } + }; + new Thread( run, "digitizer recalculate" ).start(); + } + + private synchronized void assertChannel( ProgressMonitor mon ) { + TableDataSet tds= (TableDataSet)dataSetConsumer.getConsumedDataSet(); + if ( tds==null ) return; + if ( xrange==null ) return; + + tds= new ClippedTableDataSet( tds, xrange, yrange ); + + if ( xResolution.value()>0. ) { + // average the data down to xResolution + DataSetRebinner rebinner= new AverageTableRebinner(); + DatumRange range= DataSetUtil.xRange( tds ); + RebinDescriptor ddx= getRebinDescriptor( range ); + try { + tds= (TableDataSet)rebinner.rebin( tds, ddx, null, null ); + } catch (IllegalArgumentException | DasException ex) { + Logger.getLogger(CutoffMouseModule.class.getName()).log(Level.SEVERE, null, ex); + } + } + + VectorDataSetBuilder builder= new VectorDataSetBuilder( tds.getXUnits(), tds.getYUnits() ); + mon.setTaskSize( tds.getXLength() ); + mon.started(); + + Datum level= tds.getYTagDatum( 0, tds.getYLength(0)/2 ); + + for ( int i=0; i0 ) { + builder.setProperty( DataSet.PROPERTY_X_TAG_WIDTH, this.xResolution ); + } + VectorDataSet vds= builder.toVectorDataSet(); + + fireDataSetUpdateListenerDataSetUpdated( new DataSetUpdateEvent( this,vds ) ); + + + } + + private synchronized void recalculate( ProgressMonitor mon) { + TableDataSet tds= (TableDataSet)dataSetConsumer.getConsumedDataSet(); + if ( tds==null ) return; + if ( xrange==null ) return; + + tds= new ClippedTableDataSet( tds, xrange, yrange ); + + // average the data down to xResolution + + if ( xResolution.value()>0. ) { + // average the data down to xResolution + DataSetRebinner rebinner= new AverageTableRebinner(); + DatumRange range= DataSetUtil.xRange( tds ); + RebinDescriptor ddx= getRebinDescriptor( range ); + try { + tds= (TableDataSet)rebinner.rebin( tds, ddx, null, null ); + } catch (IllegalArgumentException | DasException ex) { + Logger.getLogger(CutoffMouseModule.class.getName()).log(Level.SEVERE, null, ex); + } + } + + VectorDataSetBuilder builder= new VectorDataSetBuilder( tds.getXUnits(), tds.getYUnits() ); + + mon.setTaskSize( tds.getXLength() ); + mon.started(); + + for ( int i=0; i-1 ) { + builder.insertY( tds.getXTagDatum(i), tds.getYTagDatum( tds.tableOfIndex(i), icutoff ) ); + } else if ( icutoff<0 ) { + Units yunits=tds.getYUnits(); + builder.insertY( tds.getXTagDatum(i), yunits.createDatum(yunits.getFillDouble()) ); + } + } + + mon.finished(); + + if ( mon.isCancelled() ) return; + + String comment= "Ondrej:"+levelMin+":"+slopeMin+":"+nave; + if ( lastComment!=null ) { + comment= lastComment + " "+comment; + } + builder.setProperty("comment",comment); + if ( this.xResolution.value()>0 ) { + builder.setProperty( DataSet.PROPERTY_X_TAG_WIDTH, this.xResolution ); + } + VectorDataSet vds= builder.toVectorDataSet(); + + fireDataSetUpdateListenerDataSetUpdated( new DataSetUpdateEvent( this,vds ) ); + + } + + /** + * @param ds PSD vector (spectrum) + * @param slopeMin required PSD slope per frequency bin + * @param nave required bandwidth + * @param mult -1 for lower cutoff, 1 for upper cutoff (check this, I think Ondrej's + * got this backwards.) + * @param levelMin + * @param cutoffSlicer if available, render data to here for diagnostics + * @return the index of the cutoff. + */ + public static int cutoff( + VectorDataSet ds, Datum slopeMin, int nave, int mult, Datum levelMin, + CutoffSlicer cutoffSlicer + ) { + assert mult==-1 || mult==1; + + int nfr= ds.getXLength(); + if ( nfr < (nave+1) ) { + logger.fine( "DataSet doesn't contain enough elements" ); + return 0; + } + double[] cumul= new double[nfr]; + Units units= ds.getYUnits(); + double level= levelMin.doubleValue( units ); + double slope= slopeMin.doubleValue( units ); + + cumul[0]= ds.getDouble(0, units); + for ( int i=1; i0 ? ave[j+k] : ave[j]; + if ( uave <= level ) icof[j]=false; + icofBuilder.insertY( ds.getXTagDatum(j), + icof[j] ? units.dimensionless.createDatum(1) : units.dimensionless.createDatum(0) ); + } + } + + if ( cutoffSlicer!=null ) { + cutoffSlicer.slopeRenderer.setDataSet( slopeBuilder.toVectorDataSet() ); + cutoffSlicer.levelRenderer.setDataSet( levelBuilder.toVectorDataSet() ); + cutoffSlicer.icofRenderer.setDataSet( icofBuilder.toVectorDataSet() ); + } + + int icutOff=-1; + + if ( mult<0 ) { + for ( int j= nfr-1; j>=0; j-- ) { + if ( icof[j] ) { + icutOff= j; + break; + } + } + + } else { + for ( int j= 0; j0 ) { + DatumRange range= DataSetUtil.xRange( tds ); + RebinDescriptor ddx= getRebinDescriptor( range ); + + try { + tds= (TableDataSet)rebinner.rebin( tds, ddx, null, null ); + } catch ( DasException e ) { + throw new RuntimeException(e); + } + } + + int i= DataSetUtil.closestColumn( tds, event.getX() ); + + VectorDataSet contextDs= tds.getXSlice(i); + contextLevelRenderer.setDataSet( DataSetUtil.log10( contextDs ) ); + + //VectorDataSet slopeDs= VectorUtil.finiteDerivative( contextDs, nave ); + //contextSlopeRenderer.setDataSet( slopeDs ); + + tds= new ClippedTableDataSet( tds, DataSetUtil.xRange(tds), yrange ); + + this.xValue= tds.getXTagDatum(i); + topPlot.setTitle( "" + xValue + " " + yValue); + + VectorDataSet spec= DataSetUtil.log10( tds.getXSlice(i) ); + + int icutoff= cutoff( spec, slopeMin, nave, isLowCutoff() ? 1 : -1, levelMin, cutoffSlicer ); + if ( icutoff==-1 ) { + cutoff= spec.getXUnits().getFillDatum(); + } else { + cutoff= spec.getXTagDatum(icutoff); + } + + showPopup(); + } + + private void showPopup() { + if ( !frame.isVisible() ) frame.setVisible(true); + } + + } + + public DataPointSelectionListener getSlicer( DasPlot plot, TableDataSetConsumer consumer ) { + DasAxis sourceYAxis = plot.getYAxis(); + DasAxis sourceZAxis = consumer.getZAxis(); + + DatumRange range= sourceYAxis.getDatumRange(); + DasAxis xAxis = sourceYAxis.createAttachedAxis( DasAxis.HORIZONTAL ); + cutoffSlicer= new CutoffSlicer( plot, xAxis ); + return cutoffSlicer; + + } + + private transient java.util.ArrayList dataSetUpdateListenerList; + + public synchronized void addDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) + { + if (dataSetUpdateListenerList == null ) { + dataSetUpdateListenerList = new java.util.ArrayList(); + } + dataSetUpdateListenerList.add(listener); + } + + public synchronized void removeDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + if (dataSetUpdateListenerList != null ) { + dataSetUpdateListenerList.remove(listener); + } + } + + private void fireDataSetUpdateListenerDataSetUpdated(org.das2.dataset.DataSetUpdateEvent event) { + java.util.ArrayList list; + synchronized (this) { + if (dataSetUpdateListenerList == null) return; + list = (java.util.ArrayList)dataSetUpdateListenerList.clone(); + } + for(Object listener : list){ + ((org.das2.dataset.DataSetUpdateListener) listener).dataSetUpdated(event); + } + } + + /** + * Holds value of property slopeMin. + */ + private Datum slopeMin= Units.dimensionless.createDatum( 0.26 ); + + /** + * Getter for property slopeMin. + * @return Value of property slopeMin. + */ + public Datum getSlopeMin() { + return this.slopeMin; + } + + /** + * Setter for property slopeMin. + * @param slopeMin New value of property slopeMin. + */ + public void setSlopeMin(Datum slopeMin) { + Datum oldVal= this.slopeMin; + if ( !this.slopeMin.equals( slopeMin ) ) { + this.slopeMin = slopeMin; + PropertyChangeEvent e= new PropertyChangeEvent( this, "slope", oldVal, slopeMin ); + firePropertyChangeListenerPropertyChange( e ); + recalculateSoon(); + } + } + + /** + * Holds value of property levelMin. + */ + private Datum levelMin= Units.dimensionless.createDatum(-4.); + + /** + * Getter for property levelMin. + * @return Value of property levelMin. + */ + public Datum getLevelMin() { + return this.levelMin; + } + + /** + * Setter for property levelMin. + * @param levelMin New value of property levelMin. + */ + public void setLevelMin(Datum levelMin) { + Datum oldVal= this.levelMin; + if ( !this.levelMin.equals( levelMin ) ) { + this.levelMin = levelMin; + PropertyChangeEvent e= new PropertyChangeEvent( this, "level", oldVal, levelMin ); + firePropertyChangeListenerPropertyChange( e ); + + levelMin.getFormatter().format(levelMin); + + recalculateSoon(); + + } + } + + /** + * Holds value of property nave. + */ + private int nave=3; + + /** + * Getter for property nave. + * @return Value of property nave. + */ + public int getNave() { + return this.nave; + } + + /** + * Setter for property nave. + * @param nave New value of property nave. + */ + public void setNave(int nave) { + int oldVal= this.nave; + if ( this.nave!=nave ) { + this.nave = nave; + PropertyChangeEvent e= new PropertyChangeEvent( + this, "nave", new Integer(oldVal), new Integer(nave) ); + firePropertyChangeListenerPropertyChange( e ); + recalculateSoon(); + } + } + + private Datum xResolution= Units.milliseconds.createDatum(1000); + + public Datum getXResolution() { + return this.xResolution; + } + + public void setXResolution(Datum xResolution) { + Datum oldVal= this.xResolution; + if ( !this.xResolution.equals( xResolution ) ) { + this.xResolution = xResolution; + PropertyChangeEvent e= new PropertyChangeEvent( + this, "timeResolution", oldVal, this.xResolution ); + firePropertyChangeListenerPropertyChange( e ); + recalculateSoon(); + } + } + + /** + * Holds value of property lowCutoff. true indicates this is a low + * cutoff (look for peak in slope). false indicates this is a + * high cutoff (look for valley in slope). + */ + private boolean lowCutoff; + + /** + * Getter for property lowCutoff. + * @return Value of property lowCutoff. + */ + public boolean isLowCutoff() { + return this.lowCutoff; + } + + /** + * Setter for property lowCutoff. + * @param lowCutoff New value of property lowCutoff. + */ + public void setLowCutoff(boolean lowCutoff) { + Boolean oldVal= this.lowCutoff; + if ( this.lowCutoff!=lowCutoff ) { + this.lowCutoff = lowCutoff; + PropertyChangeEvent e= new PropertyChangeEvent( + this, "lowCutoff", oldVal, Boolean.valueOf(lowCutoff) ); + firePropertyChangeListenerPropertyChange( e ); + recalculateSoon(); + if ( this.cutoffSlicer != null ) this.cutoffSlicer.slopePlot.repaint(); + } + } + + /** + * Utility field used by event firing mechanism. + */ + private javax.swing.event.EventListenerList listenerList = null; + + /** + * Registers PropertyChangeListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } + listenerList.add(java.beans.PropertyChangeListener.class, listener); + } + + /** + * Removes PropertyChangeListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { + listenerList.remove(java.beans.PropertyChangeListener.class, listener); + } + + /** + * Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void firePropertyChangeListenerPropertyChange(java.beans.PropertyChangeEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i]==java.beans.PropertyChangeListener.class) { + ((java.beans.PropertyChangeListener)listeners[i+1]).propertyChange(event); + } + } + } + + + private static void testCutoff() { + // see /home/jbf/voyager/cutoff/input.txt + double[] spec= new double[] { + -12.7093, -12.8479, -13.0042, -13.1509, -13.3007, -13.4671, + -13.5536, -13.6603, -13.8000, -13.8873, -13.9908, -14.1162, + -14.2016, -14.2694, -14.2844, -14.3126, -14.3507, -14.3841, + -14.4252, -14.4779, -14.4972, -14.5226, -14.6059, -14.6517, + -14.6545, -14.2863, -13.9616, -13.6898, -13.7407, -13.8821, + -14.1541, -14.4287, -14.6663, -14.8647, -15.0540, -15.0863, + -15.1190, -15.1464, -15.1479, -15.1399, -15.1284, -15.2001, + -15.2780, -15.3611, -15.3976, -15.4230, -15.4467, -15.4879, + -15.5437, -15.6058, -15.6501, -15.6606, -15.6737, -15.6867, + -15.6955, -15.7425, -15.8222, -15.9376, -16.0174, -16.0091, + }; + double[] tags= new double[ spec.length ]; + for ( int i=0; i< tags.length; i++ ) { tags[i]= i+1; } + double slope= 0.266692; + + int nave=3; + int mult= -1; // low cutoff + double level= -14; + int icut= cutoff( + new DefaultVectorDataSet( spec, Units.hertz, spec, Units.v2pm2Hz, new HashMap() ), + Units.v2pm2Hz.createDatum(slope), nave, mult, Units.v2pm2Hz.createDatum(level), null ); + System.out.println("icut="+icut+" should be 25"); + } + +} diff --git a/dasCore/src/main/java/org/das2/event/DasEvent.java b/dasCore/src/main/java/org/das2/event/DasEvent.java new file mode 100644 index 000000000..3fa0a79bd --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DasEvent.java @@ -0,0 +1,37 @@ +/* File: DasEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author jbf + */ +public class DasEvent extends java.util.EventObject { + + /** Creates a new instance of DasEvent */ + public DasEvent(Object source) { + super(source); + } + +} diff --git a/dasCore/src/main/java/org/das2/event/DasEventMulticaster.java b/dasCore/src/main/java/org/das2/event/DasEventMulticaster.java new file mode 100644 index 000000000..869407099 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DasEventMulticaster.java @@ -0,0 +1,122 @@ +/* File: DasEventMulticaster.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import java.util.EventListener; + +/** + * + * @author eew + */ +public class DasEventMulticaster extends java.awt.AWTEventMulticaster + implements DataPointSelectionListener, DataRangeSelectionListener, + TimeRangeSelectionListener { + + /** Creates a new instance of DasEventMultiCaster */ + protected DasEventMulticaster(EventListener a, EventListener b) { + super(a, b); + } + + public void dataPointSelected(DataPointSelectionEvent e) { + ((DataPointSelectionListener)a).dataPointSelected(e); + ((DataPointSelectionListener)b).dataPointSelected(e); + } + + public void dataRangeSelected(DataRangeSelectionEvent e) { + ((DataRangeSelectionListener)a).dataRangeSelected(e); + ((DataRangeSelectionListener)b).dataRangeSelected(e); + } + + public void timeRangeSelected(TimeRangeSelectionEvent e) { + ((TimeRangeSelectionListener)a).timeRangeSelected(e); + ((TimeRangeSelectionListener)b).timeRangeSelected(e); + } + + public static DataPointSelectionListener add(DataPointSelectionListener a, DataPointSelectionListener b) { + if (a == null) return b; + if (b == null) return a; + return new DasEventMulticaster(a, b); + } + + public static DataRangeSelectionListener add(DataRangeSelectionListener a, DataRangeSelectionListener b) { + if (a == null) return b; + if (b == null) return a; + return new DasEventMulticaster(a, b); + } + + public static TimeRangeSelectionListener add(TimeRangeSelectionListener a, TimeRangeSelectionListener b) { + if (a == null) return b; + if (b == null) return a; + return new DasEventMulticaster(a, b); + } + + public static DataPointSelectionListener remove(DataPointSelectionListener a, DataPointSelectionListener b) { + if (a instanceof DasEventMulticaster) { + return (DataPointSelectionListener)((DasEventMulticaster)a).remove(b); + } + return (a == b ? null : a); + } + + public static DataRangeSelectionListener remove(DataRangeSelectionListener a, DataRangeSelectionListener b) { + if (a instanceof DasEventMulticaster) { + return (DataRangeSelectionListener)((DasEventMulticaster)a).remove(b); + } + return (a == b ? null : a); + } + + public static TimeRangeSelectionListener remove(TimeRangeSelectionListener a, TimeRangeSelectionListener b) { + if (a instanceof DasEventMulticaster) { + return (TimeRangeSelectionListener)((DasEventMulticaster)a).remove(b); + } + return (a == b ? null : a); + } + + protected EventListener remove(EventListener listener) { + if (listener == a) return b; + if (listener == b) return a; + EventListener aa; + EventListener bb; + if (a instanceof DasEventMulticaster) { + aa = ((DasEventMulticaster)a).remove(listener); + } + else { + aa = a; + } + if (b instanceof DasEventMulticaster) { + bb = ((DasEventMulticaster)b).remove(listener); + } + else { + bb = b; + } + if (bb == b && aa == a) return this; + return new DasEventMulticaster(aa, bb); + } + + public String toString() { + + return "[" + a + "," + b + "]"; + + } + +} diff --git a/dasCore/src/main/java/org/das2/event/DasMouseEvent.java b/dasCore/src/main/java/org/das2/event/DasMouseEvent.java new file mode 100644 index 000000000..2781530b1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DasMouseEvent.java @@ -0,0 +1,37 @@ +/* File: DasMouseEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author jbf + */ +public class DasMouseEvent extends DasEvent { + + /** Creates a new instance of DasMouseEvent */ + public DasMouseEvent(Object o) { + super(o); + } + +} diff --git a/dasCore/src/main/java/org/das2/event/DasMouseInputAdapter.java b/dasCore/src/main/java/org/das2/event/DasMouseInputAdapter.java new file mode 100755 index 000000000..99121d0e3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DasMouseInputAdapter.java @@ -0,0 +1,1157 @@ +/* File: DasMouseInputAdapter.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.event; + +import org.das2.graph.DasColumn; +import org.das2.graph.DasRow; +import org.das2.system.DasLogger; +import org.das2.DasApplication; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasCanvasComponent; +import org.das2.util.DasExceptionHandler; +import java.awt.*; + +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import java.awt.event.*; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.util.*; +import java.util.logging.Logger; +import org.das2.components.propertyeditor.Editable; + +/** + * DasMouseInputAdapter delegates mouse and key events to mouse modules, which + * do something with the events. Also, mouse events are promoted to MouseDragEvents + * which conveniently store information about the entire drag gesture. + * + * The base class of MouseModule has do-nothing stubs for KeyListener, MouseListener, + * MouseMotionListener, and MouseWheelListener, which can be implemented if the + * module wants to do something with these events. Also MouseDragEvents will be + * sent to the module as its DragRenderer has requested: after the mouse release, + * during the drag, or when keys are pressed. + * + * The module will first receive the low-level events before receiving the MouseDragEvents. + * + * @author jbf + */ +public class DasMouseInputAdapter extends MouseInputAdapter implements Editable, MouseWheelListener { + + private MouseModule primary = null; + private MouseModule secondary = null; + /* + * array of active modules. This will be removed, as the idea was + * that a few modules could be used together simultaneously, but this implementation + * only allows for one to be active at a time. + */ + private Vector active = null; + private boolean pinned = false; + private Vector modules; + private HashMap primaryActionButtonMap; + private HashMap secondaryActionButtonMap; + protected JPopupMenu primaryPopup; + protected JPopupMenu secondaryPopup; + private Point primaryPopupLocation; + private Point secondaryPopupLocation; + private JPanel pngFileNamePanel; + private JTextField pngFileTextField; + private JFileChooser pngFileChooser; + JCheckBoxMenuItem primarySelectedItem; + JCheckBoxMenuItem secondarySelectedItem; // must be non-null, but may contain null elements + Rectangle[] dirtyBoundsList; + Logger log = DasLogger.getLogger(DasLogger.GUI_LOG); + /** + * number of additional inserted popup menu items to the primary menu. + */ + int numInserted; + + /** + * number of additional inserted popup menu items to the secondary menu. + * Components can be added to the primary menu, but not the secondary. + */ + int numInsertedSecondary; + + protected ActionListener popupListener; + protected DasCanvasComponent parent = null; + //private Point selectionStart; // in component frame + //private Point selectionEnd; // in component frame + private Point dSelectionStart; // in DasCanvas device frame + private Point dSelectionEnd; // in DasCanvas device frame + private MousePointSelectionEvent mousePointSelection; + private int xOffset; // parent to canvas offset + private int yOffset; // parent to canvas offset + private int button = 0; // current depressed button + private MouseMode mouseMode = MouseMode.idle; + private boolean drawControlPoints = false; + private DragRenderer resizeRenderer = null; + private Point resizeStart = null; + /* + *this will be removed, and the component can add its own popup buttons. + */ + Vector hotSpots = null; + Rectangle dirtyBounds = null; + private boolean hasFocus = false; + private Point pressPosition; // in the component frame + private boolean headless; + + private static class MouseMode { + + String s; + boolean resizeTop = false; + boolean resizeBottom = false; + boolean resizeRight = false; + boolean resizeLeft = false; + Point moveStart = null; // in the DasCanvas frame + static MouseMode idle = new MouseMode("idle"); + static MouseMode resize = new MouseMode("resize"); + static MouseMode move = new MouseMode("move"); + static MouseMode moduleDrag = new MouseMode("moduleDrag"); + + MouseMode(String s) { + this.s = s; + } + + public String toString() { + return s; + } + } + + /** Creates a new instance of dasMouseInputAdapter */ + public DasMouseInputAdapter(DasCanvasComponent parent) { + + this.parent = parent; + + modules = new Vector(); + + primaryActionButtonMap = new HashMap(); + secondaryActionButtonMap = new HashMap(); + + this.headless = DasApplication.getDefaultApplication().isHeadless(); + if (!headless) { + primaryPopup= new JPopupMenu(); + numInserted = createPopup(primaryPopup); + secondaryPopup= new JPopupMenu(); + numInsertedSecondary = createPopup(secondaryPopup); + } + + active = null; + + mousePointSelection = new MousePointSelectionEvent(this, 0, 0); + + resizeRenderer = new BoxRenderer(parent); + + + dirtyBoundsList = new Rectangle[0]; + } + + public void replaceMouseModule(MouseModule oldModule, MouseModule newModule) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(oldModule); + primaryActionButtonMap.put(newModule, j); + primaryActionButtonMap.remove(oldModule); + secondaryActionButtonMap.put(newModule, secondaryActionButtonMap.get(oldModule)); + secondaryActionButtonMap.remove(oldModule); + modules.removeElement(oldModule); + modules.addElement(newModule); + } + + /** + * add a mouse module to the list of available modules. If a module with the same + * label exists already, it will be replaced. + */ + public void addMouseModule(MouseModule module) { + + if (headless) { + DasLogger.getLogger(DasLogger.GUI_LOG).fine("not adding module since headless is true"); + + } else { + MouseModule preExisting = getModuleByLabel(module.getLabel()); + if (preExisting != null) { + DasLogger.getLogger(DasLogger.GUI_LOG).fine("Replacing mouse module " + module.getLabel() + "."); + replaceMouseModule(preExisting, module); + + } else { + + modules.add(module); + + String name = module.getLabel(); + + JCheckBoxMenuItem primaryNewItem = new JCheckBoxMenuItem(name); + JCheckBoxMenuItem secondaryNewItem = new JCheckBoxMenuItem(name); + + primaryNewItem.addActionListener(popupListener); + primaryNewItem.setActionCommand("primary"); + secondaryNewItem.addActionListener(popupListener); + secondaryNewItem.setActionCommand("secondary"); + + primaryActionButtonMap.put(module, primaryNewItem); + secondaryActionButtonMap.put(module, secondaryNewItem); + + try { + // insert the check box after the separator, and at the end of the actions list. + primaryPopup.add(primaryNewItem, numInserted + 1 + primaryActionButtonMap.size() - 1); + secondaryPopup.add(secondaryNewItem, numInsertedSecondary + 1 + secondaryActionButtonMap.size() - 1); + } catch ( IllegalArgumentException ex ) { + ex.printStackTrace(); + } + + } + } + } + + public KeyAdapter getKeyAdapter() { + return new KeyAdapter() { + + public void keyPressed(KeyEvent ev) { + log.finest("keyPressed "); + if (ev.getKeyCode() == KeyEvent.VK_ESCAPE & active != null) { + active = null; + getGlassPane().setDragRenderer(null, null, null); + parent.getCanvas().paintImmediately(0, 0, parent.getCanvas().getWidth(), parent.getCanvas().getHeight()); + refresh(); + ev.consume(); + } else if (ev.getKeyCode() == KeyEvent.VK_SHIFT) { + drawControlPoints = true; + parent.repaint(); + } else if (ev.getKeyChar() == 'p') { + pinned = true; + ev.consume(); + } else { + if (active == null) { + return; + } + for (int i = 0; i < active.size(); i++) { + ((MouseModule) active.get(i)).keyPressed(ev); + } + } + } + + public void keyReleased(KeyEvent ev) { + if (ev.getKeyCode() == KeyEvent.VK_SHIFT) { + drawControlPoints = false; + parent.repaint(); + } + if (active == null) { + return; + } + for (int i = 0; i < active.size(); i++) { + ((MouseModule) active.get(i)).keyReleased(ev); + } + } + + public void keyTyped(KeyEvent ev) { + if (active == null) { + return; + } + for (int i = 0; i < active.size(); i++) { + ((MouseModule) active.get(i)).keyTyped(ev); + } + } + }; + } + + public MouseModule getPrimaryModule() { + ArrayList activ = new ArrayList(); + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(modules.get(i)); + if (j.isSelected()) { + activ.add(modules.get(i)); + } + } + return (MouseModule) activ.get(0); // at one time we allowed multiple modules at once. + } + + public MouseModule getSecondaryModule() { + ArrayList activ = new ArrayList(); + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(modules.get(i)); + if (j.isSelected()) { + activ.add(modules.get(i)); + } + } + return (MouseModule) activ.get(0); // at one time we allowed multiple modules at once. + } + + /** + * set the primary module, the module receiving left-button events, to the + * module provided. If the module is not already loaded, implicitly addMouseModule + * is called. + */ + public void setPrimaryModule(MouseModule module) { + if (headless) { + return; + } + JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(module); + if (j == null) { + addMouseModule(module); + } + for (Iterator i = primaryActionButtonMap.entrySet().iterator(); i.hasNext();) { + try { + Object ii = ((Map.Entry) i.next()).getValue(); + ((JCheckBoxMenuItem) ii).setSelected(false); + } catch (RuntimeException e) { + e.printStackTrace(); + throw e; + } + } + + j = (JCheckBoxMenuItem) primaryActionButtonMap.get(module); + if (j != null) { + j.setSelected(true); + } + primarySelectedItem = j; + primary = module; + parent.setCursor(primary.getCursor()); + } + + /** + * set the secondary module, the module receiving middle-button events, to the + * module provided. If the module is not already loaded, implicitly addMouseModule + * is called. + */ + public void setSecondaryModule(MouseModule module) { + if (headless) { + return; + } + JCheckBoxMenuItem j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(module); + if (j == null) { + addMouseModule(module); + } + for (Iterator i = secondaryActionButtonMap.entrySet().iterator(); i.hasNext();) { + try { + Object ii = ((Map.Entry) i.next()).getValue(); + ((JCheckBoxMenuItem) ii).setSelected(false); + } catch (RuntimeException e) { + e.printStackTrace(); + throw e; + } + } + + j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(module); + if (j != null) { + j.setSelected(true); + } + secondarySelectedItem = j; + secondary = module; + } + + /** + * create the popup for the component. This popup has three + * sections: + *
    1. component actions
    +     *2. mouse modules
    +     *3. canvas actions
    + * The variable numInserted is the number of actions inserted, and + * is used to calculate the position of inserted mouse modules. + */ + private int createPopup(JPopupMenu popup) { + + popupListener = createPopupMenuListener(); + + Action[] componentActions = parent.getActions(); + for (int iaction = 0; iaction < componentActions.length; iaction++) { + JMenuItem item = new JMenuItem(); + item.setAction(componentActions[iaction]); + popup.add(item); + } + int numInsert = componentActions.length; + + popup.addSeparator(); + // mouse modules go here + popup.addSeparator(); + + Action[] canvasActions = DasCanvas.getActions(); + for (int iaction = 0; iaction < canvasActions.length; iaction++) { + JMenuItem item = new JMenuItem(); + item.setAction(canvasActions[iaction]); + popup.add(item); + } + + return numInsert; + } + + private ActionListener createPopupMenuListener() { + return new ActionListener() { + + public void actionPerformed(ActionEvent e) { + DasMouseInputAdapter outer = DasMouseInputAdapter.this; // useful for debugging + String command = e.getActionCommand(); + if (command.equals("properties")) { + parent.showProperties(); + } else if (command.equals("print")) { + Printable p = ((DasCanvas) parent.getParent()).getPrintable(); + PrinterJob pj = PrinterJob.getPrinterJob(); + pj.setPrintable(p); + if (pj.printDialog()) { + try { + pj.print(); + } catch (PrinterException pe) { + Object[] message = {"Error printing", pe.getMessage()}; + JOptionPane.showMessageDialog(null, message, "ERROR", JOptionPane.ERROR_MESSAGE); + } + } + } else if (command.equals("toPng")) { + if (pngFileNamePanel == null) { + pngFileNamePanel = new JPanel(); + pngFileNamePanel.setLayout(new BoxLayout(pngFileNamePanel, BoxLayout.X_AXIS)); + pngFileTextField = new JTextField(32); + pngFileTextField.setMaximumSize(pngFileTextField.getPreferredSize()); + pngFileChooser = new JFileChooser(); + pngFileChooser.setApproveButtonText("Select File"); + pngFileChooser.setDialogTitle("Write to PNG"); + JButton b = new JButton("Browse"); + b.setActionCommand("pngBrowse"); + b.addActionListener(this); + pngFileNamePanel.add(pngFileTextField); + pngFileNamePanel.add(b); + } + pngFileTextField.setText(pngFileChooser.getCurrentDirectory().getPath()); + String[] options = {"Write to PNG", "Cancel"}; + int choice = JOptionPane.showOptionDialog(parent, + pngFileNamePanel, + "Write to PNG", + 0, + JOptionPane.QUESTION_MESSAGE, + null, + options, + "Ok"); + if (choice == 0) { + DasCanvas canvas = (DasCanvas) parent.getParent(); + try { + canvas.writeToPng(pngFileTextField.getText()); + } catch (java.io.IOException ioe) { + org.das2.util.DasExceptionHandler.handle(ioe); + } + } + } else if (command.equals("pngBrowse")) { + int choice = pngFileChooser.showDialog(parent, "Select File"); + if (choice == JFileChooser.APPROVE_OPTION) { + pngFileTextField.setText(pngFileChooser.getSelectedFile().getPath()); + } + } else if (command.equals("close")) { + } else if (command.equals("primary")) { + if (primarySelectedItem != null) { + primarySelectedItem.setSelected(false); + } + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(modules.get(i)); + if (j.isSelected()) { + primarySelectedItem = j; + break; + } + } + primarySelectedItem.setSelected(true); // for case when selection wasn't changed. + //primaryPopup.show( parent, l.x, l.y ); + } else if (command.equals("secondary")) { + if (secondarySelectedItem != null) { + secondarySelectedItem.setSelected(false); + } + Point l = secondaryPopupLocation; + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(modules.get(i)); + if (j.isSelected()) { + secondarySelectedItem = j; + break; + } + } + //secondaryPopup.show( parent, l.x, l.y ); + } else { + org.das2.util.DasDie.println("" + command); + } + } + }; + } + + /** + * call the renderDrag method of the active module's dragRenderer. This method + * returns an array of Rectangles, or null, indicating the affected regions. + * It's also permisable for a array element to be null. + */ + private void renderSelection(Graphics2D g2d) { + try { + //DasCanvas canvas = parent.getCanvas(); + //selectionStart = SwingUtilities.convertPoint(canvas, dSelectionStart, parent); + //selectionEnd = SwingUtilities.convertPoint(canvas, dSelectionEnd, parent); + + for (int i = 0; i < active.size(); i++) { + DragRenderer dr = ((MouseModule) active.get(i)).getDragRenderer(); + + //Rectangle[] dd = dr.renderDrag( getGlassPane().getGraphics(), dSelectionStart, dSelectionEnd); + //dirtyBoundsList = new Rectangle[dd.length]; + //for (i = 0; i < dd.length; i++) { + // dirtyBoundsList[i] = new Rectangle(dd[i]); + //} + getGlassPane().setDragRenderer(dr, dSelectionStart, dSelectionEnd); + } + } catch (RuntimeException e) { + DasExceptionHandler.handle(e); + } + } + + /* This attempts to redraw just the affected portions of parent. Presently it + * needs to call the parent's paintImmediately twice, because we don't know what + * the dragRenderer's dirty bounds will be. + */ + private synchronized void refresh() { + if (dirtyBoundsList.length > 0) { + Rectangle[] dd = new Rectangle[dirtyBoundsList.length]; + for (int i = 0; i < dd.length; i++) { + if (dirtyBoundsList[i] != null) { + dd[i] = new Rectangle(dirtyBoundsList[i]); + } + } + for (int i = 0; i < dd.length; i++) { + if (dd[i] != null) { + parent.getCanvas().paintImmediately(dd[i]); + } + } + for (int i = 0; i < dirtyBoundsList.length; i++) { + if (dirtyBoundsList[i] != null) { + parent.getCanvas().paintImmediately(dirtyBoundsList[i]); + } + } + } else { + if (active != null) { + parent.getCanvas().paintImmediately(0, 0, parent.getCanvas().getWidth(), parent.getCanvas().getHeight()); + } + } + if (active == null) { + dirtyBoundsList = new Rectangle[0]; + } + } + + /* + * Paint the drag renderer on top of parent. Graphics g1 should be in + * the parent's coordinate frame. + */ + public void paint(Graphics g1) { + Graphics2D g = (Graphics2D) g1.create(); + //g= (Graphics2D)getGlassPane().getGraphics(); + //g.translate(parent.getX(),parent.getY()); + + g.translate(-parent.getX(), -parent.getY()); + + if (active != null) { + renderSelection(g); + } + if (hasFocus && hoverHighlite) { + g.setColor(new Color(255, 0, 0, 10)); + g.setStroke(new BasicStroke(10)); + g.draw(parent.getBounds()); + return; + } + if (hasFocus && drawControlPoints) { + drawControlPoints(g); + } + } + + private void drawControlPoints(Graphics2D g) { + if (parent.getRow() != DasRow.NULL && parent.getColumn() != DasColumn.NULL) { + int xLeft = parent.getColumn().getDMinimum(); + int xRight = parent.getColumn().getDMaximum(); + int yTop = parent.getRow().getDMinimum(); + int yBottom = parent.getRow().getDMaximum(); + + Graphics2D gg = (Graphics2D) g.create(); + + //gg.translate(-parent.getX(),-parent.getY()); + gg.setColor(new Color(0, 0, 0, 255)); + + int ss = 9; + gg.fillRect(xLeft + 1, yTop + 1, ss - 2, ss - 2); + gg.fillRect(xRight - ss + 1, yTop + 1, ss - 2, ss - 2); + gg.fillRect(xLeft + 1, yBottom - ss + 1, ss - 2, ss - 2); + gg.fillRect(xRight - ss + 1, yBottom - ss + 1, ss - 2, ss - 2); + + gg.setColor(new Color(255, 255, 255, 100)); + gg.drawRect(xLeft, yTop, ss, ss); + gg.drawRect(xRight - ss, yTop, ss, ss); + gg.drawRect(xLeft, yBottom - ss, ss, ss); + gg.drawRect(xRight - ss, yBottom - ss, ss, ss); + + int xmid = (xLeft + xRight) / 2; + int ymid = (yTop + yBottom) / 2; + + int rr = 4; + g.setColor(new Color(255, 255, 255, 100)); + gg.fillOval(xmid - rr - 1, ymid - rr - 1, rr * 2 + 3, rr * 2 + 3); + + gg.setColor(new Color(0, 0, 0, 255)); + + gg.drawOval(xmid - rr, ymid - rr, rr * 2, rr * 2); + gg.fillOval(xmid - 1, ymid - 1, 3, 3); + + gg.dispose(); + } + } + + private MouseMode activateMouseMode(MouseEvent e) { + + boolean xLeftSide = false; + boolean xRightSide = false; + boolean xMiddle = false; + boolean yTopSide = false; + boolean yBottomSide = false; + boolean yMiddle = false; + + Point mousePoint = e.getPoint(); + mousePoint.translate(parent.getX(), parent.getY());// canvas coordinate system + + if (parent.getRow() != DasRow.NULL && parent.getColumn() != DasColumn.NULL) { + int xLeft = parent.getColumn().getDMinimum(); + int xRight = parent.getColumn().getDMaximum(); + int yTop = parent.getRow().getDMinimum(); + int yBottom = parent.getRow().getDMaximum(); + int xmid = (xLeft + xRight) / 2; + int ymid = (yTop + yBottom) / 2; + + xLeftSide = mousePoint.getX() < xLeft + 10; + xRightSide = mousePoint.getX() > xRight - 10; + xMiddle = Math.abs(mousePoint.getX() - xmid) < 4; + yTopSide = (mousePoint.getY() < yTop + 10) && (mousePoint.getY() >= yTop); + yBottomSide = mousePoint.getY() > (yBottom - 10); + yMiddle = Math.abs(mousePoint.getY() - ymid) < 4; + + } + + MouseMode result = MouseMode.idle; + Cursor cursor = new Cursor(Cursor.DEFAULT_CURSOR); + + if (!(parent instanceof DasAxis)) { + if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) == MouseEvent.SHIFT_DOWN_MASK) { + if (xLeftSide) { + if (yTopSide) { + result = MouseMode.resize; + cursor = new Cursor(Cursor.NW_RESIZE_CURSOR); + } else if (yBottomSide) { + result = MouseMode.resize; + cursor = new Cursor(Cursor.SW_RESIZE_CURSOR); + } + } else if (xRightSide) { + if (yTopSide) { + result = MouseMode.resize; + cursor = new Cursor(Cursor.NE_RESIZE_CURSOR); + } else if (yBottomSide) { + result = MouseMode.resize; + cursor = new Cursor(Cursor.SE_RESIZE_CURSOR); + } + } else if (xMiddle && yMiddle) { + result = MouseMode.move; + cursor = new Cursor(Cursor.MOVE_CURSOR); + } + } + + } + + if (result == MouseMode.resize) { + result.resizeBottom = yBottomSide; + result.resizeTop = yTopSide; + result.resizeRight = xRightSide; + result.resizeLeft = xLeftSide; + } else if (result == MouseMode.move) { + result.moveStart = e.getPoint(); + result.moveStart.translate(-parent.getX(), -parent.getY()); + } + + if (result != mouseMode) { + getGlassPane().setCursor(cursor); + } + return result; + } + + @Override + public void mouseMoved(MouseEvent e) { + log.finest("mouseMoved"); + Point l = parent.getLocation(); + xOffset = l.x; + yOffset = l.y; + + boolean drawControlPoints0 = this.drawControlPoints; + if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) == MouseEvent.SHIFT_DOWN_MASK) { + drawControlPoints = true; + } else { + drawControlPoints = false; + } + + if (drawControlPoints0 != drawControlPoints) { + parent.repaint(); + } + + MouseMode m; + if ((m = activateMouseMode(e)) != null) { + mouseMode = m; + } else { + mouseMode = MouseMode.idle; + } + } + + private void showPopup(JPopupMenu menu, MouseEvent ev) { + log.finest("showPopup"); + HashMap map = null; + if (menu == primaryPopup) { + map = primaryActionButtonMap; + } else if (menu == secondaryPopup) { + map = secondaryActionButtonMap; + } else { + throw new IllegalArgumentException("menu must be primary or secondary popup menu"); + } + for (Iterator i = modules.iterator(); i.hasNext();) { + MouseModule mm = (MouseModule) i.next(); + JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(mm); + j.setText(mm.getLabel()); + } + menu.show(ev.getComponent(), ev.getX(), ev.getY()); + } + + public void setPinned(boolean b) { + pinned = b; + } + + public boolean getPinned() { + return pinned; + } + + @Override + public void mousePressed(MouseEvent e) { + log.finer("mousePressed " + mouseMode); + if (pinned) { + active = null; + refresh(); + } + pinned = false; + Point l = parent.getLocation(); + parent.requestFocus(); + xOffset = l.x; + yOffset = l.y; + pressPosition = e.getPoint(); + + Point cp = new Point(e.getPoint()); + cp.translate(xOffset, yOffset); + if (!parent.acceptContext(cp.x, cp.y)) { + return; + } + if (mouseMode == MouseMode.resize) { + resizeStart = new Point(0, 0); + if (mouseMode.resizeRight) { + resizeStart.x = 0 + xOffset; + } else if (mouseMode.resizeLeft) { + resizeStart.x = parent.getWidth() + xOffset; + } + if (mouseMode.resizeTop) { + resizeStart.y = parent.getHeight() + yOffset; + } else if (mouseMode.resizeBottom) { + resizeStart.y = 0 + yOffset; + } + + } else if (mouseMode == MouseMode.move) { + mouseMode.moveStart = e.getPoint(); + mouseMode.moveStart.translate(xOffset, yOffset); + + } else { + if (active == null) { + button = e.getButton(); + //selectionStart = e.getPoint(); + dSelectionStart = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), parent.getCanvas()); + //selectionEnd = e.getPoint(); + dSelectionEnd = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), parent.getCanvas()); + //graphics = (Graphics2D) parent.getGraphics(); + + if (e.isControlDown() || button == MouseEvent.BUTTON3) { + if (button == MouseEvent.BUTTON1 || button == MouseEvent.BUTTON3) { + showPopup(primaryPopup, e); + } else { + showPopup(secondaryPopup, e); + } + } else { + + active = new Vector(); + + if (button == MouseEvent.BUTTON1 || button == MouseEvent.BUTTON3) { + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(modules.get(i)); + if (j.isSelected()) { + active.add(modules.get(i)); + } + } + } else { + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(modules.get(i)); + if (j.isSelected()) { + active.add(modules.get(i)); + } + } + } + + mouseMode = MouseMode.moduleDrag; + + mousePointSelection.set(e.getX() + xOffset, e.getY() + yOffset); + for (int i = 0; i < active.size(); i++) { + MouseModule j = (MouseModule) active.get(i); + j.mousePressed(e); + if (j.dragRenderer.isPointSelection()) { + mouseDragged(e); + } + } + } + } + } + } + + @Override + public void mouseDragged(MouseEvent e) { + log.finest("mouseDragged in " + mouseMode); + if (mouseMode == MouseMode.resize) { + Point p = e.getPoint(); + p.translate(parent.getX(), parent.getY()); + getGlassPane().setDragRenderer(resizeRenderer, resizeStart, p); + getGlassPane().repaint(); + + } else if (mouseMode == MouseMode.move) { + Point moveEnd = e.getPoint(); + moveEnd.translate(xOffset, yOffset); + int dx = moveEnd.x - mouseMode.moveStart.x; + int dy = moveEnd.y - mouseMode.moveStart.y; + + int xmin = parent.getColumn().getDMinimum(); + int xmax = parent.getColumn().getDMaximum(); + + int ymin = parent.getRow().getDMinimum(); + int ymax = parent.getRow().getDMaximum(); + Point p1 = new Point(xmin + dx, ymin + dy); + Point p2 = new Point(xmax + dx, ymax + dy); + + getGlassPane().setDragRenderer(resizeRenderer, p1, p2); + getGlassPane().repaint(); + //resizeRenderer.clear(graphics); + //resizeRenderer.renderDrag(graphics, p1, p2); + } else { + if (active != null) { + //clearSelection(graphics); + //selectionEnd = e.getPoint(); + dSelectionEnd = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), parent.getCanvas()); + + mousePointSelection.set((int) dSelectionEnd.getX(), (int) dSelectionEnd.getY()); + for (int i = 0; i < active.size(); i++) { + try { + MouseModule j = (MouseModule) active.get(i); + if (j.dragRenderer.isPointSelection()) { + log.finest("mousePointSelected"); + j.mousePointSelected(mousePointSelection); + } + if (j.dragRenderer.isUpdatingDragSelection()) { + // Really it should be the DMM that indicates it wants updates...whoops... + MouseDragEvent de = j.dragRenderer.getMouseDragEvent(parent, dSelectionStart, dSelectionEnd, e.isShiftDown()); + log.finest("mouseRangeSelected"); + j.mouseRangeSelected(de); + } + j.mouseDragged(e); + } catch (RuntimeException except) { + DasExceptionHandler.handle(except); + } + } + refresh(); + } + } + } + + private void performResize(MouseEvent e) { + int dxLeft = parent.getColumn().getDMinimum(); + int dxRight = parent.getColumn().getDMaximum(); + int dyTop = parent.getRow().getDMinimum(); + int dyBottom = parent.getRow().getDMaximum(); + + int dx = e.getX() + xOffset; + int dy = e.getY() + yOffset; + if (mouseMode.resizeRight) { + dxRight = dx; + } else if (mouseMode.resizeLeft) { + dxLeft = dx; + } + if (mouseMode.resizeTop) { + dyTop = dy; + } else if (mouseMode.resizeBottom) { + dyBottom = dy; + } + + parent.getColumn().setDPosition(dxLeft, dxRight); + parent.getRow().setDPosition(dyTop, dyBottom); + + xOffset += dx; + yOffset += dy; + + parent.resize(); + getGlassPane().setDragRenderer(null, null, null); + getGlassPane().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + } + + public void mouseReleased(MouseEvent e) { + log.finest("mouseReleased"); + if (mouseMode == MouseMode.resize) { + performResize(e); + getGlassPane().setDragRenderer(null, null, null); + parent.getCanvas().paintImmediately(0, 0, parent.getCanvas().getWidth(), parent.getCanvas().getHeight()); + refresh(); + } else if (mouseMode == MouseMode.move) { + performMove(e); + getGlassPane().setDragRenderer(null, null, null); + parent.getCanvas().paintImmediately(0, 0, parent.getCanvas().getWidth(), parent.getCanvas().getHeight()); + refresh(); + + } else { + if (e.getButton() == button) { + if (active != null) { + //clearSelection(graphics); + int x = e.getX(); + int y = e.getY(); + for (int i = 0; i < active.size(); i++) { + MouseModule j = (MouseModule) active.get(i); + try { + MouseDragEvent de = + j.dragRenderer.getMouseDragEvent(parent, dSelectionStart, dSelectionEnd, e.isShiftDown()); + j.mouseRangeSelected(de); + } catch (RuntimeException ex) { + DasExceptionHandler.handle(ex); + } finally { + button = 0; + try { + j.mouseReleased(e); + } catch (RuntimeException ex2) { + DasExceptionHandler.handle(ex2); + } + } + } + if (!pinned) { + active = null; + getGlassPane().setDragRenderer(null, null, null); + parent.getCanvas().paintImmediately(0, 0, parent.getCanvas().getWidth(), parent.getCanvas().getHeight()); + refresh(); + } + } + } + } + + } + + public void removeMouseModule(MouseModule module) { + // not implemented yet + } + + /** + * Getter for property mouseModules. + * @return Value of property mouseModules. + */ + public MouseModule getMouseModule(int i) { + return (MouseModule) modules.get(i); + } + + public MouseModule[] getMouseModules() { + MouseModule[] result = new MouseModule[modules.size()]; + modules.copyInto(result); + return result; + } + + /** + * @deprecated use getPrimaryModuleByLabel + * @return + */ + public String getPrimaryModuleLabel() { + MouseModule primary = getPrimaryModule(); + return primary == null ? "" : primary.getLabel(); + } + + public String getPrimaryModuleByLabel() { + MouseModule primary = getPrimaryModule(); + return primary == null ? "" : primary.getLabel(); + } + + public void setPrimaryModuleByLabel(String label) { + MouseModule mm = getModuleByLabel(label); + if (mm != null) { + setPrimaryModule(mm); + } + } + + /** + * @deprecated use getSecondaryModuleByLabel + * @return + */ + public String getSecondaryModuleLabel() { + MouseModule secondary = getPrimaryModule(); + return secondary == null ? "" : secondary.getLabel(); + } + + public String getSecondaryModuleByLabel() { + MouseModule secondary = getPrimaryModule(); + return secondary == null ? "" : secondary.getLabel(); + } + + public void setSecondaryModuleByLabel(String label) { + MouseModule mm = getModuleByLabel(label); + if (mm != null) { + setSecondaryModule(mm); + } + } + + /** + * //TODO: check this + * Setter for property mouseModules. + * @param mouseModule the new mouseModule to use. + */ + public void setMouseModule(int i, MouseModule mouseModule) { + this.modules.set(i, mouseModule); + } + + public void mouseEntered(MouseEvent e) { + hasFocus = true; + if (e.isShiftDown()) { + parent.repaint(); + } + if (primary != null) { + getGlassPane().setCursor(primary.getCursor()); + } + } + + public void mouseExited(MouseEvent e) { + hasFocus = false; + if (e.isShiftDown()) { + parent.repaint(); + } + getGlassPane().setCursor(Cursor.getDefaultCursor()); + } + + /** + * hack to provide way to get rid of "Dump Data". + * @param label string to search for. + */ + public synchronized void removeMenuItem(String label) { + if (headless) { + return; + } + MenuElement[] ele = primaryPopup.getSubElements(); + int index = -1; + for (int i = 0; i < numInserted; i++) { + if (ele[i] instanceof JMenuItem) { + if (((JMenuItem) ele[i]).getText().contains(label)) { + index = i; + break; + } + } + } + if (index != -1) { + primaryPopup.remove(index); + numInserted--; + } + + } + + public synchronized void addMenuItem(final Component b) { + if (headless) { + return; + } + if (numInserted == 0) { + primaryPopup.insert(new JPopupMenu.Separator(), 0); + } + primaryPopup.insert(b, numInserted); + numInserted++; + + } + + /** + * return a menu with font to match LAF. + * @param label + * @return + */ + public JMenu addMenu(String label) { + JMenu result = new JMenu(label); + //result.setFont(primaryPopup.getFont()); + addMenuItem(result); + return result; + } + + private DasCanvas.GlassPane getGlassPane() { + DasCanvas.GlassPane r = (DasCanvas.GlassPane) ((DasCanvas) parent.getParent()).getGlassPane(); + if (r.isVisible() == false) { + r.setVisible(true); + } + return r; + } + + public MouseModule getModuleByLabel(java.lang.String label) { + MouseModule result = null; + for (int i = 0; i < modules.size(); i++) { + if (label.equals(((MouseModule) modules.get(i)).getLabel())) { + result = (MouseModule) modules.get(i); + } + } + return result; + } + /** + * Draws a faint box around the border when the mouse enters the component, + * to help indicate what's going on. + */ + private boolean hoverHighlite = false; + + public boolean isHoverHighlite() { + return this.hoverHighlite; + } + + public void setHoverHighlite(boolean value) { + this.hoverHighlite = value; + } + + /** + * returns the position of the last mouse press. This is a hack so that + * the mouse position can be obtained to get the context of the press. + * The result point is in the parent's coordinate system. + */ + public Point getMousePressPosition() { + return this.pressPosition; + } + + private void performMove(MouseEvent e) { + Point moveEnd = e.getPoint(); + moveEnd.translate(xOffset, yOffset); + int dx = moveEnd.x - mouseMode.moveStart.x; + int dy = moveEnd.y - mouseMode.moveStart.y; + + this.xOffset += dx; + this.yOffset += dy; + + int min = parent.getColumn().getDMinimum(); + int max = parent.getColumn().getDMaximum(); + parent.getColumn().setDPosition(min + dx, max + dx); + + min = + parent.getRow().getDMinimum(); + max = + parent.getRow().getDMaximum(); + parent.getRow().setDPosition(min + dy, max + dy); + } + + public void mouseWheelMoved(MouseWheelEvent e) { + if (secondary != null) { + secondary.mouseWheelMoved(e); + } + } +} diff --git a/dasCore/src/main/java/org/das2/event/DasSelectionEvent.java b/dasCore/src/main/java/org/das2/event/DasSelectionEvent.java new file mode 100644 index 000000000..038279f91 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DasSelectionEvent.java @@ -0,0 +1,148 @@ +/* File: DasSelectionEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import java.awt.*; +import java.util.EventObject; + +/** + * + * @author eew + */ +public class DasSelectionEvent extends EventObject +{ + + /** Type-safe enumeration class for selection type contants. */ + public static class Type + { + public static final Type AREA_SELECTION = + new Type("AREA_SELECTION", false); + public static final Type POINT_SELECTION = + new Type("POINT_SELECTION", true); + public static final Type VERTICAL_SLICE_SELECTION = + new Type("VERTICAL_SLICE_SELECTION", true); + public static final Type HORIZONTAL_SLICE_SELECTION = + new Type("HORIZONTAL_SLICE_SELECTION", true); + public static final Type VERTICAL_RANGE_SELECTION = + new Type("VERTICAL_RANGE_SELECTION", false); + public static final Type HORIZONTAL_RANGE_SELECTION = + new Type("HORIZONTAL_RANGE_SELECTION", false); + public static final Type NO_SELECTION = + new Type("NO_SELECTION", false); + + private String type; + private boolean single; + + private Type(String type, boolean single) + { this.type = type; this.single = single; } + public String toString() { return type; } + public boolean isSingleSelection() { return single; } + public boolean equals(Object o) { return this==o; } + } + + protected Point dot; + protected Point mark; + protected boolean isShiftDown; + protected boolean clearSelection; + protected DasSelectionEvent.Type selectionType; + + private Point selectionEnd; + + private DasSelectionEvent.Type selectionMode = DasSelectionEvent.Type.POINT_SELECTION; + + private Point selectionStart; + + /** Creates a new instance of DasSelectionEvent + * + * @param source The source of the event. + * @param selectionType The type of selection. + * @param isShiftDown true if the shift buttons was + * down when the selection was made, false otherwise. + * @param dot The point at which the selection started. + * @param mark The point at which the selection ended, or the point + * of the selection for single selections. + */ + public DasSelectionEvent(Object source, + DasSelectionEvent.Type selectionType, + boolean isShiftDown, + Point dot, Point mark) + { + super(source); + this.selectionType = selectionType; + this.isShiftDown = isShiftDown; + this.dot = new Point(dot); + this.mark = new Point(mark); + this.clearSelection = false; + } + + public Point getDot() + { + return new Point(dot); + } + + public Point getMark() + { + return new Point(mark); + } + + public int getDotX() + { + return dot.x; + } + + public int getDotY() + { + return dot.y; + } + + public int getMarkX() + { + return mark.x; + } + + public int getMarkY() + { + return mark.y; + } + + public boolean isShiftDown() + { + return isShiftDown; + } + + public boolean shouldClearSelection() + { + return clearSelection; + } + + public void clearSelection() + { + clearSelection = true; + } + + public DasSelectionEvent.Type getSelectionType() + { + return selectionType; + } +} diff --git a/dasCore/src/main/java/org/das2/event/DasUpdateEvent.java b/dasCore/src/main/java/org/das2/event/DasUpdateEvent.java new file mode 100644 index 000000000..4c78bbbc8 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DasUpdateEvent.java @@ -0,0 +1,39 @@ +/* File: DasUpdateEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author eew + */ +public class DasUpdateEvent extends java.awt.AWTEvent { + + public static final int DAS_UPDATE_EVENT_ID = java.awt.AWTEvent.RESERVED_ID_MAX + 0xAD01; + + /** Creates a new instance of DasUpdateEvent */ + public DasUpdateEvent(org.das2.graph.DasCanvasComponent source) { + super(source, DAS_UPDATE_EVENT_ID); + } + +} diff --git a/dasCore/src/main/java/org/das2/event/DataPointSelectionEvent.java b/dasCore/src/main/java/org/das2/event/DataPointSelectionEvent.java new file mode 100644 index 000000000..795f436c8 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DataPointSelectionEvent.java @@ -0,0 +1,104 @@ +/* File: DataPointSelectionEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; +import org.das2.dataset.DataSet; +import org.das2.datum.Datum; +import java.util.Map; + +/** + * This is the general-purpose "a data point was selected" event. Note that + * auxillary data is supported, such as a keystroke that triggered the event. + * + * The X and Y Datums may be null, so that code may be reused. + * + * @author jbf + */ +public class DataPointSelectionEvent extends DasEvent { + + private Datum x; + private Datum y; + private Map planes; + + public long birthMilli; + + private DataSet ds=null; + + private Object source; + + /** Creates a new instance of DataPointSelectionEvent */ + public DataPointSelectionEvent(Object source, + Datum x, + Datum y, + Map planes ) { + super(source); + this.birthMilli= System.currentTimeMillis(); + this.x= x; + this.y= y; + this.ds= null; + this.planes= planes; + } + + public DataPointSelectionEvent(Object source, + Datum x, + Datum y ) { + this( source, x, y, null ); + } + + public org.das2.datum.Datum getX() { + return x; + } + + public org.das2.datum.Datum getY() { + return y; + } + + public Object getPlane( String plane ) { + return planes==null ? null : planes.get(plane); + } + + public String[] getPlaneIds() { + if ( planes==null ) { + return new String[0]; + } else { + return (String[])planes.keySet().toArray( new String[ planes.keySet().size() ] ); + } + } + + public void set( Datum x, Datum y) { + this.x= x; + this.y= y; + } + + public void setDataSet(org.das2.dataset.DataSet ds) { + this.ds= ds; + } + + public org.das2.dataset.DataSet getDataSet() { + return this.ds; + } + + public String toString() { + return "[DataPointSelectionEvent x:"+x+" y:"+y+"]"; + } +} diff --git a/dasCore/src/main/java/org/das2/event/DataPointSelectionListener.java b/dasCore/src/main/java/org/das2/event/DataPointSelectionListener.java new file mode 100644 index 000000000..c44e047b3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DataPointSelectionListener.java @@ -0,0 +1,32 @@ +/* File: DataPointSelectionListener.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author jbf + */ +public abstract interface DataPointSelectionListener extends java.util.EventListener { + public void dataPointSelected(DataPointSelectionEvent e); +} diff --git a/dasCore/src/main/java/org/das2/event/DataPointSelectorMouseModule.java b/dasCore/src/main/java/org/das2/event/DataPointSelectorMouseModule.java new file mode 100644 index 000000000..dbd489661 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DataPointSelectorMouseModule.java @@ -0,0 +1,204 @@ +/* + * DataPointSelectorMouseModule.java + * + * Created on November 3, 2005, 2:53 PM + * + * + */ + +package org.das2.event; +import org.das2.dataset.DataSetConsumer; +import org.das2.datum.Datum; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.HashMap; + +/** + * General purpose mouse module for getting data point selections. The client + * provides the DragRenderer, generally a vertical line, horizontal line or a + * crosshair. + * + * Three properties control when DataPointSelectionEvents are to be fired: + * dragEvents as the mouse is dragged, + * keyEvents when a key is pressed. (The key is the "keyChar" plane of the event) + * releaseEvents when the mouse is released. (false by default) + * + * @see CrossHairRenderer + * @author Jeremy + */ +public class DataPointSelectorMouseModule extends MouseModule { + DasAxis xaxis, yaxis; + DataSetConsumer dataSetConsumer; + javax.swing.event.EventListenerList listenerList = null; + MousePointSelectionEvent lastMousePoint; + + public DataPointSelectorMouseModule( DasPlot parent, + DataSetConsumer consumer, + DragRenderer dragRenderer, String label ) { + super( parent, dragRenderer, label ); + this.xaxis= parent.getXAxis(); + this.yaxis= parent.getYAxis(); + this.dataSetConsumer= consumer; + } + + private DataPointSelectionEvent getDataPointSelectionEvent(MousePointSelectionEvent e) { + Datum x= xaxis.invTransform(e.getX()); + Datum y= yaxis.invTransform(e.getY()); + DataPointSelectionEvent de= new DataPointSelectionEvent( this, x, y ); + return de; + } + + public void mousePointSelected(MousePointSelectionEvent e) { + lastMousePoint= e; + if ( keyEvents ) parent.requestFocus(); + if ( dragEvents ) fireDataPointSelectionListenerDataPointSelected(getDataPointSelectionEvent(e)); + } + + public void keyPressed(KeyEvent e) { + int keyCode= e.getKeyCode(); + + if ( lastMousePoint!=null ) { + if ( keyCode==KeyEvent.VK_LEFT || keyCode==KeyEvent.VK_RIGHT || keyCode==KeyEvent.VK_UP || keyCode==KeyEvent.VK_DOWN ) { + int x=0; + int y=0; + try { + int xOff= parent.getLocationOnScreen().x-parent.getX(); + int yOff= parent.getLocationOnScreen().y-parent.getY(); + final java.awt.Robot robot= new java.awt.Robot(); + switch ( keyCode ) { + case KeyEvent.VK_LEFT: + robot.mouseMove(lastMousePoint.getX()+xOff-1, lastMousePoint.getY()+yOff); + break; + case KeyEvent.VK_RIGHT: + robot.mouseMove(lastMousePoint.getX()+xOff+1, lastMousePoint.getY()+yOff); + break; + case KeyEvent.VK_UP: + robot.mouseMove(lastMousePoint.getX()+xOff, lastMousePoint.getY()+yOff-1); + break; + case KeyEvent.VK_DOWN: + robot.mouseMove(lastMousePoint.getX()+xOff, lastMousePoint.getY()+yOff+1); + break; + } + } catch ( java.awt.AWTException e1 ) { + org.das2.util.DasDie.println(e1.getMessage()); + } + + } else { + + DataPointSelectionEvent dpse= getDataPointSelectionEvent(lastMousePoint); + HashMap planes= new HashMap(); + planes.put( "keyChar", String.valueOf( e.getKeyChar() ) ); + dpse= new DataPointSelectionEvent( this, dpse.getX(), dpse.getY(), planes ); + fireDataPointSelectionListenerDataPointSelected( dpse ); + } + } + } + + /** Registers DataPointSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } + listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** Removes DataPointSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + listenerList.remove(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + protected void fireDataPointSelectionListenerDataPointSelected(DataPointSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataPointSelectionListener.class) { + ((org.das2.event.DataPointSelectionListener)listeners[i+1]).dataPointSelected(event); + } + } + } + + /** + * Holds value of property dragEvents. + */ + private boolean dragEvents= true; + + /** + * Getter for property dragEvents. + * @return Value of property dragEvents. + */ + public boolean isDragEvents() { + return this.dragEvents; + } + + /** + * Setter for property dragEvents. + * @param dragEvents New value of property dragEvents. + */ + public void setDragEvents(boolean dragEvents) { + this.dragEvents = dragEvents; + } + + /** + * Holds value of property keyEvents. + */ + private boolean keyEvents= true; + + /** + * Getter for property keyEvents. + * @return Value of property keyEvents. + */ + public boolean isKeyEvents() { + + return this.keyEvents; + } + + /** + * Setter for property keyEvents. + * @param keyEvents New value of property keyEvents. + */ + public void setKeyEvents(boolean keyEvents) { + + this.keyEvents = keyEvents; + } + + public void mouseReleased(java.awt.event.MouseEvent e) { + super.mouseReleased(e); + if ( releaseEvents ) { + fireDataPointSelectionListenerDataPointSelected(getDataPointSelectionEvent(lastMousePoint)); + } + } + + /** + * Holds value of property releaseEvents. + */ + private boolean releaseEvents= false; + + /** + * Getter for property releaseEvents. + * @return Value of property releaseEvents. + */ + public boolean isReleaseEvents() { + + return this.releaseEvents; + } + + /** + * Setter for property releaseEvents. + * @param releaseEvents New value of property releaseEvents. + */ + public void setReleaseEvents(boolean releaseEvents) { + + this.releaseEvents = releaseEvents; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/DataRangeSelectionEvent.java b/dasCore/src/main/java/org/das2/event/DataRangeSelectionEvent.java new file mode 100755 index 000000000..ee6292e20 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DataRangeSelectionEvent.java @@ -0,0 +1,87 @@ +/* File: DataRangeSelectionEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.dataset.DataSet; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; + +/** + * + * @author jbf + */ +public class DataRangeSelectionEvent extends DasEvent { + + private DataSet ds=null; + + Datum min; + Datum max; + + Datum reference; // this is where the selection was made at (perpendicular axis) + + public DataRangeSelectionEvent(Object source, Datum min, Datum max) { + super(source); + if (min.gt(max)) { + Datum t=min; + min=max; + max=t; + } + this.min= min; + this.max= max; + reference= null; + } + + public Datum getMinimum() { + return min; + } + + public Datum getMaximum() { + return max; + } + + public DatumRange getDatumRange() { + return new DatumRange( min, max ); + } + + public void setDataSet(DataSet ds) { + this.ds= ds; + } + + public DataSet getDataSet() { + return this.ds; + } + + public void setReference(Datum reference) { + this.reference= reference; + } + + public Datum getReference() { + return this.reference; + } + + public String toString() { + return "[DataRangeSelectionEvent min:"+min+" max:"+max+"]"; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/DataRangeSelectionListener.java b/dasCore/src/main/java/org/das2/event/DataRangeSelectionListener.java new file mode 100644 index 000000000..97547a8cb --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DataRangeSelectionListener.java @@ -0,0 +1,32 @@ +/* File: DataRangeSelectionListener.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author jbf + */ +public abstract interface DataRangeSelectionListener extends java.util.EventListener { + public void dataRangeSelected(DataRangeSelectionEvent e); +} diff --git a/dasCore/src/main/java/org/das2/event/DisplayDataMouseModule.java b/dasCore/src/main/java/org/das2/event/DisplayDataMouseModule.java new file mode 100644 index 000000000..f8cbd81fc --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DisplayDataMouseModule.java @@ -0,0 +1,272 @@ +/* + * DisplayDataMouseModule.java + * + * Created on October 23, 2007, 7:08 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ +package org.das2.event; + +import org.das2.dataset.ClippedTableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.format.DatumFormatter; +import org.das2.graph.DasPlot; +import org.das2.graph.Renderer; +import org.das2.util.DasExceptionHandler; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Font; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import org.das2.datum.UnitsUtil; + +/** + * + * @author jbf + */ +public class DisplayDataMouseModule extends MouseModule { + + final static String LABEL = "Display Data"; + DasPlot plot; + static JFrame myFrame; + static JPanel myPanel; + static JEditorPane myEdit; + + /** Creates a new instance of DisplayDataMouseModule */ + public DisplayDataMouseModule(DasPlot parent) { + super(parent, new BoxGesturesRenderer(parent), LABEL); + this.plot = parent; + } + + private void maybeCreateFrame() { + if (myFrame == null) { + myFrame = new JFrame(LABEL); + myPanel = new JPanel(); + myPanel.setPreferredSize(new Dimension(300, 300)); + myPanel.setLayout(new BorderLayout()); + myEdit = new JEditorPane(); + myEdit.setFont(Font.decode("fixed-10")); + JScrollPane scrollPane = new JScrollPane(myEdit, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + myPanel.add(scrollPane, BorderLayout.CENTER); + myFrame.getContentPane().add(myPanel); + myFrame.pack(); + } + return; + } + + private String unitsStr(Units u) { + return u == Units.dimensionless ? "" : "(" + u.toString() + ")"; + } + + @Override + public void mouseRangeSelected(MouseDragEvent e0) { + + if ( e0.isGesture() ) return; + + maybeCreateFrame(); + myFrame.setVisible(true); + + MouseBoxEvent e = (MouseBoxEvent) e0; + + DatumRange xrange; + DatumRange yrange; + + if (plot.getXAxis().isFlipped()) { + xrange = new DatumRange(plot.getXAxis().invTransform(e.getXMaximum()), plot.getXAxis().invTransform(e.getXMinimum())); + } else { + xrange = new DatumRange(plot.getXAxis().invTransform(e.getXMinimum()), plot.getXAxis().invTransform(e.getXMaximum())); + } + if (plot.getYAxis().isFlipped()) { + yrange = new DatumRange(plot.getYAxis().invTransform(e.getYMinimum()), plot.getYAxis().invTransform(e.getYMaximum())); + } else { + yrange = new DatumRange(plot.getYAxis().invTransform(e.getYMaximum()), plot.getYAxis().invTransform(e.getYMinimum())); + } + + Renderer[] rends = plot.getRenderers(); + + Document doc = myEdit.getDocument(); + + try { + + AttributeSet attrSet = null; + + doc.remove(0, doc.getLength()); // erase all + + for (int irend = 0; irend < rends.length; irend++) { + + doc.insertString(doc.getLength(), "Renderer #" + irend + "\n", attrSet); + + DataSet ds = rends[irend].getDataSet(); + + if (ds == null) { + doc.insertString(doc.getLength(), "(no dataset)\n", attrSet); + } + + // copy for this renderer. + DatumRange rx1 = xrange; + DatumRange ry1 = yrange; + + if (ds != null) { + if (!rx1.getUnits().isConvertableTo(ds.getXUnits())) { + rx1 = DataSetUtil.xRange(ds); + } + if (!ry1.getUnits().isConvertableTo(ds.getYUnits())) { + ry1 = DataSetUtil.yRange(ds); + } + } + + DataSet outds; + if (ds instanceof TableDataSet) { + TableDataSet tds = (TableDataSet) ds; + + TableDataSet toutds = new ClippedTableDataSet(tds, rx1, ry1); + + StringBuffer buf = new StringBuffer(); + + Units zunits = tds.getZUnits(); + DatumFormatter df; + df = (DatumFormatter) tds.getProperty(DataSet.PROPERTY_FORMATTER); + if (df == null) { + df = tds.getDatum(0, 0).getFormatter(); + } + buf.append("TableDataSet " + toutds.getXLength() + "x" + toutds.getYLength(0) + " " + unitsStr(zunits) + "\n"); + for (int i = 0; i < toutds.getXLength(); i++) { + for (int j = 0; j < toutds.getYLength(0); j++) { + try { + buf.append(df.format(toutds.getDatum(i, j), zunits)); + buf.append(" "); + } catch (IndexOutOfBoundsException ex) { + System.err.println("here"); + } + } + buf.append("\n"); + } + doc.insertString(doc.getLength(), buf.toString(), attrSet); + + } else if (ds instanceof VectorDataSet) { + VectorDataSet vds = (VectorDataSet) ds; + + Units units = vds.getYUnits(); + Units xunits = vds.getXUnits(); + + StringBuffer buf = new StringBuffer(); + DatumFormatter df = vds.getDatum(0).getFormatter(); + DatumFormatter xdf = vds.getXTagDatum(0).getFormatter(); + + String[] planes = vds.getPlaneIds(); + VectorDataSet[] vdss = new VectorDataSet[planes.length]; + for (int j = 0; j < planes.length; j++) { + vdss[j] = (VectorDataSet) vds.getPlanarView(planes[j]); + } + + if (planes.length > 1) { + buf.append("X" + unitsStr(xunits) + "\t"); + buf.append("Y" + unitsStr(units) + "\t"); + for (int j = 0; j < vdss.length; j++) { + if (!planes[j].equals("")) { + buf.append("" + planes[j] + "" + unitsStr(vdss[j].getYUnits()) + "\t"); + } + } + buf.append("\n"); + } + + VectorDataSetBuilder builder = new VectorDataSetBuilder(vds.getXUnits(), vds.getYUnits()); + for (int i = 0; i < vds.getXLength(); i++) { + if (xrange.contains(vds.getXTagDatum(i)) && (!yclip || yrange.contains(vds.getDatum(i)))) { + buf.append(xdf.format(vds.getXTagDatum(i), xunits) + "\t" + df.format(vds.getDatum(i), units)); + for (int j = 0; j < planes.length; j++) { + if (!planes[j].equals("")) { + Units u= vdss[j].getYUnits(); + if ( UnitsUtil.isIntervalMeasurement(u) || UnitsUtil.isRatioMeasurement(u) ) { + buf.append("\t" + df.format(vdss[j].getDatum(i), vdss[j].getYUnits())); + } else { + buf.append("\t" + vdss[j].getDatum(i) ); + } + } + } + + buf.append("\n"); + } + } + doc.insertString(doc.getLength(), buf.toString(), attrSet); + + } + + } + } catch (BadLocationException ex) { + DasExceptionHandler.handle(ex); + } + } + + public String getListLabel() { + return getLabel(); + } + + @Override + public Icon getListIcon() { + ImageIcon icon; + icon = new ImageIcon(this.getClass().getResource("/images/icons/showDataMouseModule.png")); + return icon; + } + + @Override + public String getLabel() { + return LABEL; + } + /** + * Holds value of property yclip. + */ + private boolean yclip = false; + /** + * Utility field used by bound properties. + */ + private java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); + + /** + * Adds a PropertyChangeListener to the listener list. + * @param l The listener to add. + */ + public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.addPropertyChangeListener(l); + } + + /** + * Removes a PropertyChangeListener from the listener list. + * @param l The listener to remove. + */ + public void removePropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.removePropertyChangeListener(l); + } + + /** + * Getter for property yclip. + * @return Value of property yclip. + */ + public boolean isYclip() { + return this.yclip; + } + + /** + * Setter for property yclip. + * @param yclip New value of property yclip. + */ + public void setYclip(boolean yclip) { + boolean oldYclip = this.yclip; + this.yclip = yclip; + propertyChangeSupport.firePropertyChange("yclip", new Boolean(oldYclip), new Boolean(yclip)); + } +} diff --git a/dasCore/src/main/java/org/das2/event/DragRenderer.java b/dasCore/src/main/java/org/das2/event/DragRenderer.java new file mode 100755 index 000000000..288605144 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DragRenderer.java @@ -0,0 +1,62 @@ +/* File: DragRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import java.awt.*; + +/** A DragRenderer provides the feedback to the human operator + * of what his or her mousing is doing. It applies constraints to the + * drag as well. It promotes the awt mouse events into events + * that represent the operation, implementing for example mouse + * gestures. + * + * @author eew + */ +public interface DragRenderer +{ + /* use this color when drawing ghostly backgrounds for contrast */ + public Color ghostColor= new Color(255,255,255,100); + + /* draws the drag for mousing from p1 to p2, and returns an array of + * Rectangles covering the rendering. If nothing is drawn, then an + * array of length zero should be returned, and nulls are allowed in the + * array. p1 and p2, and g are in the canvas frame of reference. + */ + public abstract Rectangle[] renderDrag(Graphics g, Point p1, Point p2); + + /* clears whatever renderDrag rendered. This is not used by the DasMouseInputAdapter, + * but must still be supported for now. + */ + public abstract void clear(Graphics g); + + /* promotes the drag begin and end into a mouseDragEvent */ + public abstract MouseDragEvent getMouseDragEvent( Object source, Point p1, Point p2, boolean isModified ); + + /* indicates that MM.mousePointSelected() should called as new mouse events come in */ + public boolean isPointSelection(); + + /* range selection events should be fired during drag */ + public boolean isUpdatingDragSelection(); + +} diff --git a/dasCore/src/main/java/org/das2/event/DumpToFileMouseModule.java b/dasCore/src/main/java/org/das2/event/DumpToFileMouseModule.java new file mode 100644 index 000000000..1c41d3d5f --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/DumpToFileMouseModule.java @@ -0,0 +1,115 @@ +/* + * DumpToFileMouseModule.java + * + * Created on December 1, 2004, 10:31 PM + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; +import org.das2.dataset.DataSetConsumer; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.dataset.VectorUtil; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.ClippedTableDataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.DatumRange; +import org.das2.util.DasExceptionHandler; +import org.das2.system.UserMessageCenter; +import java.io.*; +import java.nio.channels.*; +import javax.swing.*; + +/** + * + * @author Jeremy + */ +public class DumpToFileMouseModule extends MouseModule { + DasAxis xAxis; + DasAxis yAxis; + DataSetConsumer dsConsumer; + + public DumpToFileMouseModule(DasCanvasComponent parent, DataSetConsumer dsConsumer, DasAxis xAxis, DasAxis yAxis) { + super( parent, new BoxRenderer(parent), "Dump to File" ); + if (!xAxis.isHorizontal()) { + throw new IllegalArgumentException("X Axis orientation is not horizontal"); + } + if (yAxis.isHorizontal()) { + throw new IllegalArgumentException("Y Axis orientation is not vertical"); + } + this.xAxis= xAxis; + this.yAxis= yAxis; + this.dsConsumer= dsConsumer; + } + + public static DumpToFileMouseModule create( DasPlot parent, DataSetConsumer dsConsumer ) { + DumpToFileMouseModule result= + new DumpToFileMouseModule(parent, dsConsumer, parent.getXAxis(),parent.getYAxis()); + return result; + } + + public void mouseRangeSelected(MouseDragEvent e0) { + MouseBoxEvent e= (MouseBoxEvent)e0; + + DatumRange xrange; + DatumRange yrange; + + xrange= new DatumRange( xAxis.invTransform(e.getXMinimum()), xAxis.invTransform(e.getXMaximum()) ); + yrange= new DatumRange( yAxis.invTransform(e.getYMaximum()), yAxis.invTransform(e.getYMinimum()) ); + + DataSet ds= dsConsumer.getConsumedDataSet(); + + if ( ds==null ) { + UserMessageCenter.getDefault().notifyUser( this, "This renderer doesn't have a dataset loaded" ); + return; + } + + DataSet outds; + if ( ds instanceof TableDataSet ) { + TableDataSet tds= (TableDataSet)ds; + outds= new ClippedTableDataSet( tds, xrange, yrange ); + + } else { + VectorDataSet vds= (VectorDataSet)ds; + VectorDataSetBuilder builder= new VectorDataSetBuilder(vds.getXUnits(),vds.getYUnits()); + for ( int i=0; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import java.awt.*; + +/** + * + * @author eew + */ +public class EmptyDragRenderer implements DragRenderer +{ + public static final EmptyDragRenderer renderer = new EmptyDragRenderer(); + private EmptyDragRenderer(){} + public Rectangle[] renderDrag(Graphics g, Point p1, Point p2) { return new Rectangle[0]; } + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + return null; + } + + public void clear(Graphics g) { + } + + public boolean isPointSelection() { + return true; + } + + public boolean isUpdatingDragSelection() { + return false; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/FrequencyDragRenderer.java b/dasCore/src/main/java/org/das2/event/FrequencyDragRenderer.java new file mode 100644 index 000000000..527bc5990 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/FrequencyDragRenderer.java @@ -0,0 +1,198 @@ +/* + * FrequencyDragRenderer.java + * + * Created on September 6, 2005, 4:55 PM + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasAxis; +import org.das2.datum.Datum; +import java.awt.*; +import java.awt.event.*; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.text.*; + +/** + * + * @author Jeremy + * @author eew + */ +public class FrequencyDragRenderer extends LabelDragRenderer implements DragRenderer, KeyListener { + + DasCanvasComponent parent; + + DasAxis axis; + int ncycles; + private boolean horizontal; + double period; + + private PropertyChangeSupport pcs; + + /** Creates a new instance of HorizontalFrequencyDragRenderer */ + public FrequencyDragRenderer( DasCanvasComponent parent, DasAxis axis ) { + super( parent ); + this.parent= parent; + parent.addKeyListener(this); + this.axis= axis; + this.dirtyBounds= new Rectangle(); + ncycles=1; + } + + public Rectangle[] renderDrag(java.awt.Graphics g1, java.awt.Point p1, java.awt.Point p2) { + + Rectangle myDirtyBounds= new Rectangle(); + + //Probably only need to do this once. + horizontal = axis.isHorizontal(); + + Graphics2D g= (Graphics2D) g1; + int x1= p1.x; + int x2 = p2.x; + int y1 = p1.y; + int y2 = p2.y; + if (horizontal&&x2 6 ) + g.drawLine(x1+3, y2, x2-3, y2); + g.drawLine(x1, y2+2, x1, y2-2 ); //serifs + g.drawLine(x2, y2+2, x2, y2-2 ); + + dirtyBounds.setRect(x1-2,y2-5,4,10); + } + else { + if ( width > 6 ) + g.drawLine(x2, y1+3, x2, y2-3); + g.drawLine(x2+2, y1, x2-2, y1 ); //serifs + g.drawLine(x2+2, y2, x2-2, y2 ); + + dirtyBounds.setRect(x2-5,y1-2,10,4); + } + + double rwidth= width / (double) ncycles; + if ( width>3*ncycles ) { + double start = horizontal ? x2 + rwidth : y2 + rwidth; + int limit = horizontal ? axis.getColumn().getWidth() : axis.getRow().getHeight(); + for ( double ii= start; ii0; ii-= rwidth ) { + if(horizontal) { + g.drawLine( (int)ii, y2+2, (int)ii, y2-2 ); + dirtyBounds.add((int)ii-2,y2-5); + } + else { + g.drawLine( x2+2, (int)ii, x2, (int)ii ); + dirtyBounds.add(x2-5,(int)ii-2); + } + } + } + } + + public boolean isPointSelection() { + return false; + } + + public void clear(java.awt.Graphics g) { + parent.paintImmediately(dirtyBounds); + } + + public boolean isUpdatingDragSelection() { + return false; + } + + public MouseDragEvent getMouseDragEvent(Object source, java.awt.Point p1, java.awt.Point p2, boolean isModified) { + return null; + } + + public void keyPressed(KeyEvent e) { + int keyCode= e.getKeyCode(); + System.out.println(e); + if ( e.getKeyChar()=='1' ) { + ncycles= 1; + } else if ( e.getKeyChar()=='2' ) { + ncycles= 2; + } else if ( e.getKeyChar()=='3' ) { + ncycles= 3; + } + } + + public void keyReleased(KeyEvent e) { + } + + public void keyTyped(KeyEvent e) { + } + + private void fireChange(final double oldPeriod, final double newPeriod) { + if (pcs != null) { + EventQueue.invokeLater(new Runnable() { + public void run() { + pcs.firePropertyChange("period", new Double(oldPeriod), new Double(newPeriod)); + } + }); + } + } + + public void addPropertyChangeListener(String p, PropertyChangeListener l) { + if (pcs == null) { + pcs = new PropertyChangeSupport(this); + } + pcs.addPropertyChangeListener(p, l); + } + + public void removePropertyChangeListener(String p, PropertyChangeListener l) { + if (pcs != null) { + pcs.removePropertyChangeListener(p, l); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/event/Gesture.java b/dasCore/src/main/java/org/das2/event/Gesture.java new file mode 100755 index 000000000..999d3eac6 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/Gesture.java @@ -0,0 +1,46 @@ +/* File: Gesture.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.event; + +public class Gesture { + + public static final Gesture NONE = new Gesture("GestureNONE"); + public static final Gesture BACK = new Gesture("GestureBACK"); + public static final Gesture FORWARD = new Gesture("GestureFORWARD"); + public static final Gesture ZOOMOUT = new Gesture("GestureZOOMOUT"); + public static final Gesture UNDEFINED = new Gesture("GestureUNDEFINED"); + public static final Gesture SCANNEXT = new Gesture("GestureSCANNEXT"); + public static final Gesture SCANPREV = new Gesture("GestureSCANPREV"); + + String name; + + Gesture(String name) { + this.name= name; + } + + public String toString() { + return name; + } + +} + diff --git a/dasCore/src/main/java/org/das2/event/GesturesRenderer.java b/dasCore/src/main/java/org/das2/event/GesturesRenderer.java new file mode 100644 index 000000000..5fb423c6d --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/GesturesRenderer.java @@ -0,0 +1,138 @@ +/* File: GesturesRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; +import org.das2.graph.DasCanvasComponent; + +import java.awt.*; + +/** + * + * @author jbf + */ +public class GesturesRenderer implements DragRenderer { + + DasCanvasComponent parent; + Rectangle dirtyBounds; + + /** Creates a new instance of GesturesRenderer */ + public GesturesRenderer(DasCanvasComponent parent) { + this.parent= parent; + dirtyBounds= new Rectangle(); + } + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + Gesture g=null; + double dx= p2.x-p1.x; + double dy= -1* ( p2.y-p1.y ); + double angle= Math.atan2(dy, dx) * 180 / Math.PI; + double radius= Math.sqrt(dy*dy+dx*dx); + int width= ((Component)source).getWidth(); + int xOffset= ((Component)source).getLocation().x; + if ( radius<20 && radius>4) { + if ( (p1.x-xOffset<10) && (p1.x-xOffset)>=0 && (p2.x-xOffset)<0 ) { + g= Gesture.SCANPREV; + } else if ((p1.x-xOffset)>(width-10) && (p1.x-xOffset)=width ) { + g= Gesture.SCANNEXT; + } else if (Math.abs(angle)>160) { + g= Gesture.BACK; + } else if (-1104) { + + Color color0= g.getColor(); + + for (int i=0; i<2; i++) { + if (i==0) { + g.setColor(new Color(255,255,255,100)); + g.setStroke(new BasicStroke( 3.0f, + BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND )); + } else { + g.setColor(color0); + g.setStroke(new BasicStroke()); + } + + if (Math.abs(angle)>160) { + g.drawLine(p1.x,p1.y,p1.x-5,p1.y); + g.drawLine(p1.x-5,p1.y,p1.x-3,p1.y-2); + g.drawLine(p1.x-5,p1.y,p1.x-3,p1.y+2); + } else if (-110 + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; + +import java.awt.*; + +/** + * + * @author eew + */ +public class HorizontalDragRangeRenderer implements DragRenderer { + + private Rectangle dirtyBounds; + DasCanvasComponent parent; + boolean updating; + + public HorizontalDragRangeRenderer(DasCanvasComponent parent) { + this.parent= parent; + dirtyBounds= new Rectangle(); + updating= true; + } + + public HorizontalDragRangeRenderer(DasCanvasComponent parent, boolean updating ) { + this( parent ); + this.updating= updating; + } + + public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { + + Graphics2D g= (Graphics2D) g1; + int x2 = p2.x; + int x1= p1.x; + if (x2 6 ) + g.drawLine(x1+3, y, x2-3, y); + g.drawLine(x1, y+2, x1, y-2 ); //serifs + g.drawLine(x2, y+2, x2, y-2 ); + + g.setStroke(new BasicStroke()); + g.setColor(color0); + + if ( width > 6 ) + g.drawLine(x1+3, y, x2-3, y); + g.drawLine(x1, y+2, x1, y-2 ); //serifs + g.drawLine(x2, y+2, x2, y-2 ); + + dirtyBounds.setLocation(x1-2,y+3); + dirtyBounds.add(x2+2,y-3); + + return new Rectangle[] { dirtyBounds }; + } + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + MouseRangeSelectionEvent me= new MouseRangeSelectionEvent(source,p1.x,p2.x,isModified); + return me; + } + + public void clear(Graphics g) { + parent.paintImmediately(dirtyBounds); + } + + public boolean isPointSelection() { + return true; + } + + public boolean isUpdatingDragSelection() { + return updating; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/HorizontalDragRangeSelectorMouseModule.java b/dasCore/src/main/java/org/das2/event/HorizontalDragRangeSelectorMouseModule.java new file mode 100755 index 000000000..1e10d1a2d --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/HorizontalDragRangeSelectorMouseModule.java @@ -0,0 +1,111 @@ +/* File: HorizontalDragRangeSelectorMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.dataset.DataSetConsumer; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.datum.Datum; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; + +/** + * + * @author jbf + */ +public class HorizontalDragRangeSelectorMouseModule extends MouseModule { + + DasAxis axis; + + int start; + + /** Utility field used by event firing mechanism. */ + private javax.swing.event.EventListenerList listenerList = null; + + private org.das2.dataset.DataSetConsumer dataSetConsumer; + + public HorizontalDragRangeSelectorMouseModule(DasPlot parent, org.das2.dataset.DataSetConsumer dataSetConsumer, DasAxis axis) { + super( parent, new HorizontalDragRangeRenderer(parent), "Horizontal Drag Range" ); + if (!axis.isHorizontal()) { + throw new IllegalArgumentException("Axis orientation is not horizontal"); + } + this.dataSetConsumer= dataSetConsumer; + this.axis= axis; + + } + + public static HorizontalDragRangeSelectorMouseModule create(DasPlot parent) { + DasAxis axis= parent.getXAxis(); + HorizontalDragRangeSelectorMouseModule result= + new HorizontalDragRangeSelectorMouseModule(parent,parent,parent.getXAxis()); + return result; + } + + public void mouseRangeSelected(MouseDragEvent e0) { + MouseRangeSelectionEvent e= (MouseRangeSelectionEvent)e0; + org.das2.datum.Datum min; + org.das2.datum.Datum max; + + min= axis.invTransform(e.getMinimum()); + max= axis.invTransform(e.getMaximum()); + DataRangeSelectionEvent te= + new DataRangeSelectionEvent(parent,min,max); + fireDataRangeSelectionListenerDataRangeSelected(te); + } + + /** Registers DataRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } + listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Removes DataRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); + } + + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { + if ( dataSetConsumer instanceof TableDataSetConsumer ) { + event.setDataSet(dataSetConsumer.getConsumedDataSet()); + } + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { + ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); + } + } + } + +} diff --git a/dasCore/src/main/java/org/das2/event/HorizontalDragRenderer.java b/dasCore/src/main/java/org/das2/event/HorizontalDragRenderer.java new file mode 100644 index 000000000..69886cf05 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/HorizontalDragRenderer.java @@ -0,0 +1,36 @@ +/* File: HorizontalDragRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author Owner + */ +public class HorizontalDragRenderer { + + /** Creates a new instance of HorizontalDragRenderer */ + public HorizontalDragRenderer() { + } + +} diff --git a/dasCore/src/main/java/org/das2/event/HorizontalFrequencyDragRenderer.java b/dasCore/src/main/java/org/das2/event/HorizontalFrequencyDragRenderer.java new file mode 100755 index 000000000..27299fc64 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/HorizontalFrequencyDragRenderer.java @@ -0,0 +1,170 @@ +/* + * HorizontalFrequencyDragRenderer.java + * + * Created on February 17, 2004, 6:06 PM + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasAxis; +import org.das2.datum.Datum; +import java.awt.*; +import java.awt.event.*; +import java.text.*; + +/** + * + * @author Jeremy + */ +public class HorizontalFrequencyDragRenderer implements DragRenderer, KeyListener { + + private Rectangle dirtyBounds; + DasCanvasComponent parent; + + DasAxis axis; + int ncycles; + + /** Creates a new instance of HorizontalFrequencyDragRenderer */ + public HorizontalFrequencyDragRenderer( DasCanvasComponent parent, DasAxis axis ) { + this.parent= parent; + parent.addKeyListener(this); + this.axis= axis; + this.dirtyBounds= new Rectangle(); + ncycles=1; + } + + public void renderLabel( java.awt.Graphics g1, java.awt.Point p1, java.awt.Point p2, String report ) { + int dxMax= Integer.MIN_VALUE; + + Graphics2D g= ( Graphics2D ) g1; + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + + FontMetrics fm= parent.getGraphics().getFontMetrics(); + + Color color0= g.getColor(); + g.setColor(new Color(255,255,255,200)); + + Dimension d= parent.getSize(); + + int dx= fm.stringWidth(report)+6; + if (dxMaxd.width-3) && (p2.x-3-dx>0) ) { + xp= p2.x-3-dx; + } + + if (yp<13) { + yp= p2.y+3; + } + + Rectangle bg= new Rectangle(xp,yp,dx,dy); + g.fill(bg); + + g.setColor(new Color(20,20,20)); + g.drawString(report,xp+3,yp+fm.getAscent()); + g.setColor(color0); + + dirtyBounds.add(bg); + } + + public Rectangle[] renderDrag(java.awt.Graphics g1, java.awt.Point p1, java.awt.Point p2) { + + Graphics2D g= (Graphics2D) g1; + int x2 = p2.x; + int x1= p1.x; + if (x2 6 ) + g.drawLine(x1+3, y, x2-3, y); + g.drawLine(x1, y+2, x1, y-2 ); //serifs + g.drawLine(x2, y+2, x2, y-2 ); + */ + + g.setStroke(new BasicStroke()); + g.setColor(color0); + + internalRender(g, dirtyBounds, x1, x2, y); + + Datum periodDatum= axis.invTransform( x2 ) . subtract( axis.invTransform( x1 ) ); + double period= periodDatum.doubleValue( periodDatum.getUnits() ); + double freq= ncycles / period; + + DecimalFormat df= new DecimalFormat("0.00"); + renderLabel(g1, p1, p2, "T:"+df.format(period)+" f:"+df.format(freq) ); + + return new Rectangle[] { dirtyBounds }; + } + + private void internalRender(Graphics2D g, Rectangle dirtyBounds, int x1, int x2, int y) { + double width = x2 - x1; + if ( width > 6 ) + g.drawLine(x1+3, y, x2-3, y); + g.drawLine(x1, y+2, x1, y-2 ); //serifs + g.drawLine(x2, y+2, x2, y-2 ); + + dirtyBounds.setRect(x1-2,y-5,4,10); + + double rwidth= width / (double) ncycles; + if ( width>3*ncycles ) { + + for ( double ii= x2 + rwidth; ii0; ii-= rwidth ) { + g.drawLine( (int)ii, y+2, (int)ii, y-2 ); + dirtyBounds.add((int)ii-2,y-5); + } + } + } + + public boolean isPointSelection() { + return false; + } + + public void clear(java.awt.Graphics g) { + parent.paintImmediately(dirtyBounds); + } + + public boolean isUpdatingDragSelection() { + return false; + } + + public MouseDragEvent getMouseDragEvent(Object source, java.awt.Point p1, java.awt.Point p2, boolean isModified) { + return null; + } + + public void keyPressed(KeyEvent e) { + int keyCode= e.getKeyCode(); + System.out.println(e); + if ( e.getKeyChar()=='1' ) { + ncycles= 1; + } else if ( e.getKeyChar()=='2' ) { + ncycles= 2; + } else if ( e.getKeyChar()=='3' ) { + ncycles= 3; + } + } + + public void keyReleased(KeyEvent e) { + } + + public void keyTyped(KeyEvent e) { + } + +} diff --git a/dasCore/src/main/java/org/das2/event/HorizontalRangeGesturesRenderer.java b/dasCore/src/main/java/org/das2/event/HorizontalRangeGesturesRenderer.java new file mode 100644 index 000000000..a9cd4e82b --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/HorizontalRangeGesturesRenderer.java @@ -0,0 +1,125 @@ +/* File: HorizontalRangeGesturesRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; + +import java.awt.*; + +/** + * + * @author eew + */ +public class HorizontalRangeGesturesRenderer implements DragRenderer { + + protected int xInitial; + protected int yInitial; + + private Rectangle dirtyBounds; + DasCanvasComponent parent; + GesturesRenderer gr; + + public HorizontalRangeGesturesRenderer(DasCanvasComponent parent) { + this.parent= parent; + dirtyBounds= new Rectangle(); + gr= new GesturesRenderer(parent); + } + + public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { + + Graphics2D g= (Graphics2D) g1; + + double dx= p2.x-p1.x; + double dy= -1* ( p2.y-p1.y ); + double radius= Math.sqrt(dy*dy+dx*dx); + + if ( radius<20 ) { + gr.renderDrag( g, p1, p2 ); + dirtyBounds.setBounds(gr.getDirtyBounds()); + + } else { + + int x2 = p2.x; + int x1= p1.x; + if (x2 6 ) + g.drawLine(x1+3, y, x2-3, y); + g.drawLine(x1, y+2, x1, y-2 ); //serifs + g.drawLine(x2, y+2, x2, y-2 ); + + g.setStroke(new BasicStroke()); + g.setColor(color0); + + if ( width > 6 ) + g.drawLine(x1+3, y, x2-3, y); + g.drawLine(x1, y+2, x1, y-2 ); //serifs + g.drawLine(x2, y+2, x2, y-2 ); + + // see if drawing again fixes WebStart bug // + if ( width > 6 ) + g.drawLine(x1+3, y, x2-3, y); + g.drawLine(x1, y+2, x1, y-2 ); //serifs + g.drawLine(x2, y+2, x2, y-2 ); + + dirtyBounds.setLocation(x1-2,y+3); + dirtyBounds.add(x2+2,y-3); + } + return new Rectangle[] { dirtyBounds }; + } + + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + + double dx= p2.x-p1.x; + double dy= -1* ( p2.y-p1.y ); + double radius= Math.sqrt(dy*dy+dx*dx); + if ( radius<20 ) { + return gr.getMouseDragEvent(source,p1,p2,isModified); + } else { + return new MouseRangeSelectionEvent( source,p1.x,p2.x, isModified ); + } + + } + + public void clear(Graphics g) { + parent.paintImmediately(dirtyBounds); + } + + public boolean isPointSelection() { + return false; + } + + public boolean isUpdatingDragSelection() { + return false; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/HorizontalRangeRenderer.java b/dasCore/src/main/java/org/das2/event/HorizontalRangeRenderer.java new file mode 100644 index 000000000..81933431f --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/HorizontalRangeRenderer.java @@ -0,0 +1,95 @@ +/* File: HorizontalRangeRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; + +import java.awt.*; + +/** + * + * @author eew + */ +public class HorizontalRangeRenderer implements DragRenderer { + + protected int xInitial; + protected int yInitial; + + private Rectangle dirtyBounds; + DasCanvasComponent parent; + + public HorizontalRangeRenderer(DasCanvasComponent parent) { + this.parent= parent; + dirtyBounds= new Rectangle(); + } + + public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { + + Graphics2D g= (Graphics2D) g1; + int x2 = p2.x; + int x1= p1.x; + if (x2 6 ) + g.drawLine(x1+3, y, x2-3, y); + g.drawLine(x1, y+2, x1, y-2 ); //serifs + g.drawLine(x2, y+2, x2, y-2 ); + + g.setStroke(new BasicStroke()); + g.setColor(color0); + + if ( width > 6 ) + g.drawLine(x1+3, y, x2-3, y); + g.drawLine(x1, y+2, x1, y-2 ); //serifs + g.drawLine(x2, y+2, x2, y-2 ); + + dirtyBounds.setLocation(x1-2,y+3); + dirtyBounds.add(x2+2,y-3); + return new Rectangle[] { dirtyBounds }; + } + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + return new MouseRangeSelectionEvent(source,p1.x,p2.x,isModified); + } + + public void clear(Graphics g) { + parent.paintImmediately(dirtyBounds); + } + + public boolean isPointSelection() { + return false; + } + + public boolean isUpdatingDragSelection() { + return false; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/HorizontalRangeSelectorMouseModule.java b/dasCore/src/main/java/org/das2/event/HorizontalRangeSelectorMouseModule.java new file mode 100755 index 000000000..843be3c38 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/HorizontalRangeSelectorMouseModule.java @@ -0,0 +1,117 @@ +/* File: HorizontalRangeSelectorMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasPlot; +import javax.swing.event.EventListenerList; + + +/** + * + * @author jbf + */ +public class HorizontalRangeSelectorMouseModule extends MouseModule { + + DasAxis axis; + + /** Utility field used by event firing mechanism. */ + private EventListenerList listenerList = null; + + public HorizontalRangeSelectorMouseModule(DasCanvasComponent parent, DasAxis axis) { + super(parent,new HorizontalRangeGesturesRenderer(parent),"Zoom X"); + if (!axis.isHorizontal()) { + throw new IllegalArgumentException("Axis orientation is not horizontal"); + } + this.axis= axis; + } + + public static HorizontalRangeSelectorMouseModule create(DasPlot parent) { + DasAxis axis= parent.getXAxis(); + HorizontalRangeSelectorMouseModule result= + new HorizontalRangeSelectorMouseModule(parent,parent.getXAxis()); + return result; + } + + public void mouseRangeSelected(MouseDragEvent e0) { + if (!e0.isGesture()) { + Datum min; + Datum max; + MouseRangeSelectionEvent e= (MouseRangeSelectionEvent)e0; + min= axis.invTransform(e.getMinimum()); + max= axis.invTransform(e.getMaximum()); + DatumRange dr= new DatumRange( min, max ); + DatumRange nndr= axis.getTickV().enclosingRange(dr, true); + DataRangeSelectionEvent te= + new DataRangeSelectionEvent(e0.getSource(),nndr.min(),nndr.max()); + fireDataRangeSelectionListenerDataRangeSelected(te); + } else if ( e0.getGesture()==Gesture.BACK ) { + axis.setDataRangePrev(); + } else if ( e0.getGesture()==Gesture.ZOOMOUT ) { + axis.setDataRangeZoomOut(); + } else if ( e0.getGesture()==Gesture.FORWARD ) { + axis.setDataRangeForward(); + } else if ( e0.getGesture()==Gesture.SCANPREV) { + axis.scanPrevious(); + } else if ( e0.getGesture()==Gesture.SCANNEXT) { + axis.scanNext(); + } + } + + /** Registers DataRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new EventListenerList(); + } + listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Removes DataRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); + } + + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { + ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); + } + } + } + +} diff --git a/dasCore/src/main/java/org/das2/event/HorizontalRangeTorsionMouseModule.java b/dasCore/src/main/java/org/das2/event/HorizontalRangeTorsionMouseModule.java new file mode 100644 index 000000000..bcc177e21 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/HorizontalRangeTorsionMouseModule.java @@ -0,0 +1,109 @@ +/* + * HorizontalRangeTorsionMouseModule.java + * + * Created on February 12, 2004, 6:00 PM + */ + +package org.das2.event; + +import org.das2.graph.DasAxis; +import org.das2.datum.Units; +import java.awt.event.*; +import javax.swing.*; +/** + * + * @author Jeremy + */ +public class HorizontalRangeTorsionMouseModule extends MouseModule { + + + // this dumb idea was abandoned. + DasAxis axis; + double min; + double max; + Units units; + int x0; + int y0; + long tt; + + double inOutVelocity; + double inOutPosition; + double nextPrevVelocity; + double nextPrevPosition; + + static final double inOutFactor= 1e-6; + static final double nextPrevFactor= 100000; + + boolean mouseButtonPressed; + + /** Creates a new instance of HorizontalRangeTorsionMouseModule */ + public HorizontalRangeTorsionMouseModule( DasAxis axis ) { + //super( axis, new PointSlopeDragRenderer(axis), "AxisDriver" ); + this.axis= axis; + } + + public void mousePressed(java.awt.event.MouseEvent e) { + super.mousePressed(e); + units= axis.getUnits(); + min= axis.getDataMinimum(units); + max= axis.getDataMaximum(units); + x0= e.getX(); + y0= e.getY(); + tt= System.currentTimeMillis(); + inOutPosition=0.; + nextPrevPosition=0.; + mouseButtonPressed= true; + } + + void timerTask() { + if ( mouseButtonPressed ) { + long dt= System.currentTimeMillis() - tt; + inOutPosition+= inOutVelocity * dt; + nextPrevPosition+= nextPrevVelocity * dt; + tt+= dt; + reportPosition(); + startTimer(); + + } + } + + public void startTimer() { + int delay = 100; //milliseconds + ActionListener taskPerformer = new ActionListener() { + public void actionPerformed(ActionEvent evt) { + timerTask(); + } + }; + new Timer(delay, taskPerformer).start(); + } + + public void mouseDragged(java.awt.event.MouseEvent e) { + super.mouseDragged(e); + int dx= e.getX()-x0; + int dy= e.getY()-y0; + long dt= System.currentTimeMillis() - tt; + inOutVelocity= dy * inOutFactor; + nextPrevVelocity= dx * nextPrevFactor; + timerTask(); + } + + private void reportPosition() { + double dd= max - min; + + double offset= nextPrevPosition; + double scale= 1 + inOutPosition; + + double newMin= min + offset ; + double newMax= min + offset + dd * scale; + + //System.out.println( "min: " + newMin + "offset: " + offset + " scale: " + scale ); + //System.out.println( "" + units.createDatum(newMin) + " " + units.createDatum(newMax) ); + axis.setDataRange( units.createDatum(newMin), units.createDatum(newMax) ); + } + + public void mouseReleased(java.awt.event.MouseEvent e) { + super.mouseReleased(e); + mouseButtonPressed= false; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/HorizontalSliceSelectionRenderer.java b/dasCore/src/main/java/org/das2/event/HorizontalSliceSelectionRenderer.java new file mode 100644 index 000000000..130d78dc1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/HorizontalSliceSelectionRenderer.java @@ -0,0 +1,86 @@ +/* File: HorizontalSliceSelectionRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasColumn; + +import java.awt.*; + + +/** + * + * @author eew + */ +public class HorizontalSliceSelectionRenderer implements DragRenderer { + + DasCanvasComponent parent; + Rectangle dirtyBounds; + + /** Creates a new instance of HorizontalSliceSelectionRenderer */ + public HorizontalSliceSelectionRenderer( DasCanvasComponent parent ) { + this.parent = parent; + dirtyBounds= new Rectangle(); + } + + private void drawCrossHair(Graphics g0, Point p) { + + Graphics g= g0.create(); + + g.setColor(new Color(0,0,0)); + g.setXORMode(Color.white); + + DasColumn col= parent.getColumn(); + g.drawLine( col.getDMinimum(), (int)p.y, col.getDMaximum(), (int)p.y); + + g.dispose(); + + } + + public Rectangle[] renderDrag(Graphics g, Point p1, Point p2) { + //g.drawLine(0, p2.y, parent.getWidth(), p2.y); + DasColumn col= parent.getColumn(); + + dirtyBounds.setRect(col.getDMinimum(),p2.y,col.getDMaximum(),1); + drawCrossHair(g,p2); + return new Rectangle[] { dirtyBounds }; + } + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + return null; + } + + public void clear(Graphics g) { + parent.paintImmediately(dirtyBounds); + } + + public boolean isPointSelection() { + return true; + } + + public boolean isUpdatingDragSelection() { + return false; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/HorizontalSlicerMouseModule.java b/dasCore/src/main/java/org/das2/event/HorizontalSlicerMouseModule.java new file mode 100755 index 000000000..75bb74d2a --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/HorizontalSlicerMouseModule.java @@ -0,0 +1,119 @@ +/* File: HorizontalSlicerMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetConsumer; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; +import org.das2.graph.Renderer; +/** + * + * @author jbf + */ +public class HorizontalSlicerMouseModule extends MouseModule { + + private DasAxis xaxis; + private DasAxis yaxis; + + private TableDataSetConsumer dataSetConsumer; + + /** Creates a new instance of VerticalSlicerMouseModule */ + + private DataPointSelectionEvent de; + + /** Utility field used by event firing mechanism. */ + private javax.swing.event.EventListenerList listenerList = null; + + public HorizontalSlicerMouseModule(DasPlot parent, TableDataSetConsumer dataSetConsumer, DasAxis xaxis, DasAxis yaxis) { + this( parent, (DataSetConsumer)dataSetConsumer, xaxis, yaxis ); + } + + protected HorizontalSlicerMouseModule(DasPlot parent, DataSetConsumer dataSetConsumer, DasAxis xaxis, DasAxis yaxis) { + super( parent, new HorizontalSliceSelectionRenderer(parent), "Horizontal Slice" ); + + if (!(dataSetConsumer instanceof TableDataSetConsumer)) { + throw new IllegalArgumentException("dataSetConsumer must be an XTaggedYScanDataSetConsumer"); + } + this.dataSetConsumer= ( TableDataSetConsumer)dataSetConsumer; + this.xaxis= xaxis; + this.yaxis= yaxis; + this.de= new DataPointSelectionEvent(this,null,null); + } + + public static HorizontalSlicerMouseModule create(DasPlot parent) { + DasAxis xaxis= parent.getXAxis(); + DasAxis yaxis= parent.getYAxis(); + return new HorizontalSlicerMouseModule(parent,parent,xaxis,yaxis); + } + + public static HorizontalSlicerMouseModule create(Renderer renderer) + { + DasPlot parent= renderer.getParent(); + DasAxis xaxis= parent.getXAxis(); + DasAxis yaxis= parent.getYAxis(); + return new HorizontalSlicerMouseModule(parent,renderer,xaxis,yaxis); + } + + public void mousePointSelected(MousePointSelectionEvent e) { + org.das2.dataset.DataSet ds= dataSetConsumer.getConsumedDataSet(); + de.setDataSet(ds); + de.set(xaxis.invTransform(e.getX()),yaxis.invTransform(e.getY())); + + fireDataPointSelectionListenerDataPointSelected(de); + } + + /** Registers DataPointSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataPointSelectionListener( DataPointSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } + listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** Removes DataPointSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataPointSelectionListener( DataPointSelectionListener listener) { + listenerList.remove( DataPointSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataPointSelectionListenerDataPointSelected(DataPointSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataPointSelectionListener.class) { + ((org.das2.event.DataPointSelectionListener)listeners[i+1]).dataPointSelected(event); + } + } + } + +} diff --git a/dasCore/src/main/java/org/das2/event/LabelDragRenderer.java b/dasCore/src/main/java/org/das2/event/LabelDragRenderer.java new file mode 100644 index 000000000..be541bf5c --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/LabelDragRenderer.java @@ -0,0 +1,333 @@ +/* + * LabelDragRenderer.java + * + * Created on October 5, 2004, 1:25 PM + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.util.GrannyTextRenderer; +import org.das2.system.DasLogger; +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.logging.Logger; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JWindow; +import javax.swing.SwingUtilities; +import org.das2.graph.EventsRenderer; + +/** + * + * @author Jeremy + */ +public class LabelDragRenderer implements DragRenderer { + + String label="Label not set"; + GrannyTextRenderer gtr; + DasCanvasComponent parent; + InfoLabel infoLabel; + + int labelPositionX=1; // 1=right, -1=left + int labelPositionY=-1; // 1=below, -1=above + + /* the implementing class is responsible for setting this */ + Rectangle dirtyBounds; + + Logger logger= DasLogger.getLogger(DasLogger.GUI_LOG); + + int maxLabelWidth; + + public void clear(Graphics g) { + if ( dirtyBounds!=null ) parent.paintImmediately(dirtyBounds); + dirtyBounds= null; + } + + public LabelDragRenderer( DasCanvasComponent parent ) { + this.parent= parent; + this.dirtyBounds= new Rectangle(); + gtr= new GrannyTextRenderer(); + } + + /** + * This method is called by the DMIA on mouse release. We use this to infer the mouse release + * and hide the Window. Note this assumes isUpdatingDragSelection is false! + * TODO: DMIA should call clear so this is more explicit. + */ + public MouseDragEvent getMouseDragEvent(Object source, java.awt.Point p1, java.awt.Point p2, boolean isModified) { + maxLabelWidth= 0; + if ( tooltip ) { + if ( infoLabel!=null ) infoLabel.hide(); + } + return null; + } + + public boolean isPointSelection() { + return true; + } + + public boolean isUpdatingDragSelection() { + return false; + } + + public void setLabel( String s ) { + this.label= s; + } + + private class InfoLabel { + JWindow window; + JPanel label; + GrannyTextRenderer gtr; + + JPanel containedPanel; + JComponent glassPane; + + boolean contained= true; + + void init() { + Window root= (Window)SwingUtilities.getRoot( parent ); + window= new JWindow( root ); + label= new JPanel() { + public void paintComponent( Graphics g ) { + g.clearRect(0,0, getWidth(), getHeight() ); + gtr.draw( g, 0, (int)gtr.getAscent() ); + } + }; + label.setOpaque( true ); + label.setPreferredSize( new Dimension( 300,20 ) ); + window.getContentPane().add( label ); + window.pack(); + gtr= new GrannyTextRenderer(); + + glassPane= (JComponent)parent.getCanvas().getGlassPane(); + containedPanel= new JPanel() { + public void paintComponent( Graphics g ) { + g.clearRect(0,0, getWidth(), getHeight() ); + gtr.draw( g, 0, (int)gtr.getAscent() ); + } + }; + containedPanel.setVisible(false); + glassPane.add(containedPanel); + contained= true; + + } + + void setText( String text, Point p ) { + if ( window==null ) init(); + if ( text!=null ) { + gtr.setString( containedPanel.getFont(), text ); + Rectangle rect= gtr.getBounds(); + + int posx= p.x + labelPositionX * 3 + Math.min( labelPositionX, 0 ) * rect.width; + int posy= p.y + labelPositionY * 3 + Math.min( labelPositionY, 0 ) * rect.height; + + Rectangle bounds= gtr.getBounds(); + + Point p2= new Point( posx, posy ); + SwingUtilities.convertPointFromScreen( p2, glassPane ); + + bounds.translate( p2.x, p2.y ); + + contained= glassPane.getBounds().contains( bounds ); + + if ( contained ) { + + containedPanel.setSize( new Dimension( rect.width, rect.height ) ); + containedPanel.setLocation( p2.x, p2.y ); + window.setVisible(false); + containedPanel.setVisible(true); + containedPanel.repaint(); + + } else { + + gtr.setString(label.getFont(),text); + rect= gtr.getBounds(); + window.setSize( new Dimension( rect.width, rect.height ) ); + + posx= p.x + labelPositionX * 3 + Math.min( labelPositionX, 0 ) * rect.width; + posy= p.y + labelPositionY * 3 + Math.min( labelPositionY, 0 ) * rect.height; + + containedPanel.setVisible(false); + window.setLocation( posx, posy ); + window.setVisible(true); + window.repaint(); + } + } else { + hide(); + } + } + + void hide() { + if ( window==null ) init(); + if ( contained ) { + containedPanel.setVisible(false); + } else { + window.setVisible(false); + } + } + + } + + private Rectangle paintLabel( Graphics g1, java.awt.Point p2 ) { + + if ( label==null ) return null; + + Graphics2D g= (Graphics2D)g1; + g.setClip(null); + g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); + + Dimension d= parent.getCanvas().getSize(); + + gtr.setString( g1, label); + + int dx= (int)gtr.getWidth()+6; + + int dy= (int)gtr.getHeight(); + + if (maxLabelWidth d.width ) && (p2.x-3-dx>0) ) { + labelPositionX= -1; + } else { + labelPositionX= 1; + } + + int xp; + if ( labelPositionX==1 ) { + xp= p2.x+3; + } else { + xp= p2.x-3-dx; + } + + int yp; + if ( p2.y-3-dy < 13) { + labelPositionY= -1; + } else { + labelPositionY= 1; + } + + if ( labelPositionY==1 ) { + yp= p2.y-3-dy; + } else { + yp= p2.y+3; + } + + dirtyBounds= new Rectangle();; + + Color color0= g.getColor(); + + // draw the translucent background + g.setColor(new Color(255,255,255,200)); + dirtyBounds.setRect(xp,yp,dx,dy); + g.fill(dirtyBounds); + + // draw the label + g.setColor(new Color(20,20,20)); + gtr.draw( g, xp+3, (float)(yp+gtr.getAscent()) ); + + g.setColor(color0); + + return dirtyBounds; + } + + public Rectangle[] renderDrag(Graphics g, Point p1, Point p2) { + logger.finest("renderDrag "+p2); + Rectangle[] result; + if ( tooltip ) { + if ( infoLabel==null ) infoLabel= new InfoLabel(); + Point p= (Point)p2.clone(); + SwingUtilities.convertPointToScreen( p, parent.getCanvas() ); + infoLabel.setText( label, p ); + result= new Rectangle[0]; + } else { + if ( label==null ) { + result= new Rectangle[0]; + } else { + Rectangle r= paintLabel( g, p2 ); + result= new Rectangle[] { r }; + } + } + return result; + } + + /** + * added to more conveniently keep track of dirty bounds when subclassing. + */ + ArrayList newDirtyBounds; + + protected void resetDirtyBounds( ) { + newDirtyBounds= new ArrayList(); + } + + protected void addDirtyBounds( Rectangle[] dirty ) { + if ( dirty!=null && dirty.length>0 ) newDirtyBounds.addAll( Arrays.asList( dirty ) ); + } + + protected void addDirtyBounds( Rectangle dirty ) { + if ( dirty!=null ) newDirtyBounds.add( dirty ); + } + + protected Rectangle[] getDirtyBounds() { + try { + return (Rectangle[]) newDirtyBounds.toArray( new Rectangle[newDirtyBounds.size()] ); + } catch ( RuntimeException e ) { + throw e; + } + } + + /* public void keyPressed(KeyEvent e) { + int keyCode= e.getKeyCode(); + + if ( keyCode==KeyEvent.VK_LEFT || keyCode==KeyEvent.VK_RIGHT || keyCode==KeyEvent.VK_UP || keyCode==KeyEvent.VK_DOWN ) { + int x=0; + int y=0; + try { + int xOff= parent.getLocationOnScreen().x-parent.getX(); + int yOff= parent.getLocationOnScreen().y-parent.getY(); + final java.awt.Robot robot= new java.awt.Robot(); + switch ( keyCode ) { + case KeyEvent.VK_LEFT: + robot.mouseMove(lastMousePoint.getX()+xOff-1, lastMousePoint.getY()+yOff); + break; + case KeyEvent.VK_RIGHT: + robot.mouseMove(lastMousePoint.getX()+xOff+1, lastMousePoint.getY()+yOff); + break; + case KeyEvent.VK_UP: + robot.mouseMove(lastMousePoint.getX()+xOff, lastMousePoint.getY()+yOff-1); + break; + case KeyEvent.VK_DOWN: + robot.mouseMove(lastMousePoint.getX()+xOff, lastMousePoint.getY()+yOff+1); + break; + } + } catch ( java.awt.AWTException e1 ) { + org.das2.util.DasDie.println(e1.getMessage()); + } + + } else { + + DataPointSelectionEvent dpse= getDataPointSelectionEvent(lastMousePoint); + HashMap planes= new HashMap(); + planes.put( "keyChar", String.valueOf( e.getKeyChar() ) ); + dpse= new DataPointSelectionEvent( this, dpse.getX(), dpse.getY(), planes ); + fireDataPointSelectionListenerDataPointSelected( dpse ); + } + } + }*/ + + boolean tooltip= false; + public boolean isTooltip() { + return tooltip; + } + + public void setTooltip( boolean tooltip ) { + this.tooltip= tooltip; + if ( tooltip ) { + labelPositionX= 1; + labelPositionY= -1; + } + } +} diff --git a/dasCore/src/main/java/org/das2/event/LengthDragRenderer.java b/dasCore/src/main/java/org/das2/event/LengthDragRenderer.java new file mode 100644 index 000000000..bbdfc614f --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/LengthDragRenderer.java @@ -0,0 +1,196 @@ +/* + * PointSlopeDragRenderer.java + * + * Created on February 19, 2004, 11:31 PM + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.datum.DatumUtil; +import org.das2.datum.UnitsUtil; +import java.awt.*; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.text.*; + +/** + * + * @author Owner + */ +public class LengthDragRenderer extends LabelDragRenderer { + + private DasAxis xaxis, yaxis; + private DasPlot plot; + + NumberFormat nf; + + /** Creates a new instance of PointSlopeDragRenderer */ + public LengthDragRenderer(DasCanvasComponent parent, DasAxis xaxis, DasAxis yaxis) { + super( parent ); + this.plot= (DasPlot)parent; + this.xaxis= xaxis; + this.yaxis= yaxis; + } + + // kludge to handle the fact that datums are not always displayed with their units!!! + private String datumString( Datum d ) { + String result= d.toString(); + /*if ( d.getUnits()!= Units.dimensionless && !d.getUnits().isConvertableTo(Units.us2000) ) { + result+= " "+d.getUnits(); + }*/ + return result; + } + + public Rectangle[] renderDrag(java.awt.Graphics g1, java.awt.Point p1, java.awt.Point p2) { + Graphics2D g= ( Graphics2D ) g1; + g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); + double atan= Math.atan2( p2.y-p1.y, p2.x-p1.x ); + + Line2D line= new Line2D.Double( p1.x + (int)(4.0 * Math.cos(atan)), (int)(p1.y + 4.0*Math.sin(atan)), p2.x, p2.y ); + g.draw( line ); + g.draw( new Ellipse2D.Double( p1.x-4, p1.y-4, 8, 8 ) ); + + Rectangle myDirtyBounds= new Rectangle(); + + myDirtyBounds.setRect( p1.x-3, p1.y-3, 7, 7 ); + myDirtyBounds.add(p2.x-2,p2.y-2); + myDirtyBounds.add(p2.x+2,p2.y+2); + + DasAxis xa= xaxis == null ? plot.getXAxis() : xaxis; + DasAxis ya= yaxis == null ? plot.getYAxis() : yaxis; + + if ( !p1.equals(p2) ) { + Datum x1= xa.invTransform(p2.x); + Datum x0= xa.invTransform(p1.x); + Datum run= x1.subtract(x0); + run= DatumUtil.asOrderOneUnits(run); + String runString; + if ( x1.getUnits()==run.getUnits() ) { + runString= x1.getFormatter().format(run); + } else { + runString= datumString(run); + } + + Datum y1= ya.invTransform(p2.y); + Datum y0= ya.invTransform(p1.y); + Datum rise= y1.subtract(y0); + + String riseString; + riseString= datumString(rise); + + String radString; + if ( rise.getUnits().isConvertableTo(run.getUnits()) ) { + Units u= run.getUnits(); + double rised= rise.doubleValue(u); + double rund= run.doubleValue(u); + double rad= Math.sqrt( rised * rised + rund * rund ); + double srised= rise.getResolution(u); + double srund= run.getResolution(u); + double res= rad * Math.sqrt( + Math.pow( srised / Math.max( Math.abs(rised), srised ), 2 ) + + Math.pow( srund / Math.max( Math.abs( rund ), srund ), 2 ) ); + Datum radDatum= Datum.create( rad, u, res/100. ); + + radString= "!cR:" + radDatum; + } else { + radString= ""; + } + + String label= "\u0394x: " + runString + " \u0394y: " + riseString + radString ; + + if ( showSlope ) { + label += "!c m: "+ UnitsUtil.divideToString( rise, run ); + } + + if ( showFit ) { + // show y= m * ( x - x0 ) + y0 + Datum slope= rise.divide(run); + + String fit; + if ( yaxis.isLog() && xaxis.isLog() ) { + double ycycles= Math.log10( y1.divide(y0).doubleValue(Units.dimensionless) ); + double xcycles= Math.log10( x1.divide(x0).doubleValue(Units.dimensionless) ); + NumberFormat nf= new DecimalFormat("0.00"); + String sslope= nf.format( ycycles / xcycles ); + fit = "y= ( x/" +x1 +" )!A"+sslope + "!n * " + y1 ; + } else if ( yaxis.isLog() && !xaxis.isLog() ) { + NumberFormat nf= new DecimalFormat("0.00"); + Units u= run.getUnits(); + double drise= Math.log10(y1.divide(y0).doubleValue(Units.dimensionless) ); + double drun= x1.subtract(x0).doubleValue(u); + String sslope= nf.format( drise/drun ); + String su; + if ( u.isConvertableTo(Units.seconds) ) { + su= UnitsUtil.divideToString( Units.dimensionless.createDatum(drise), run ); + } else if ( u==Units.dimensionless ) { + su= sslope; + } else { + su= sslope + "/("+u+")"; + } + + fit= "!Cy="+ "10!A( x-("+x1+") )*"+su+"!n * " + y1; + + } else if ( !yaxis.isLog() && xaxis.isLog() ) { + fit = "n/a"; + } else { + fit= "y="+ slope + " * ( x - ("+x1+") ) + "+ y1; + } + label+= "!c" + fit; + } + + + setLabel( label ); + } else { + setLabel( "" ); + } + super.renderDrag( g, p1, p2 ); + return new Rectangle[] { dirtyBounds, myDirtyBounds }; + } + + /** + * Holds value of property showSlope. + */ + private boolean showSlope= false; + + /** + * Getter for property showSlope. + * @return Value of property showSlope. + */ + public boolean isShowSlope() { + return this.showSlope; + } + + /** + * Setter for property showSlope. + * @param showSlope New value of property showSlope. + */ + public void setShowSlope(boolean showSlope) { + this.showSlope = showSlope; + } + + protected boolean showFit = false; + + /** + * Get the value of showFit + * + * @return the value of showFit + */ + public boolean isShowFit() { + return showFit; + } + + /** + * Set the value of showFit + * + * @param showFit new value of showFit + */ + public void setShowFit(boolean showFit) { + this.showFit = showFit; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/MouseBoxEvent.java b/dasCore/src/main/java/org/das2/event/MouseBoxEvent.java new file mode 100644 index 000000000..92784bf58 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/MouseBoxEvent.java @@ -0,0 +1,64 @@ +/* File: MouseBoxEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import java.awt.*; + +public class MouseBoxEvent extends MouseDragEvent { + + private Point pressPoint; + private Point releasePoint; + + /** Creates a new instance of MouseBoxEvent */ + public MouseBoxEvent(Object source, Point pressPoint, Point releasePoint, boolean isModified) { + super(source); + this.pressPoint= pressPoint; + this.releasePoint= releasePoint; + } + + public Point getPressPoint() { + return pressPoint; + } + + public Point getPoint() { + return releasePoint; + } + + public int getXMinimum() { + return ( pressPoint.x < releasePoint.x ) ? pressPoint.x : releasePoint.x ; + } + + public int getXMaximum() { + return ( pressPoint.x > releasePoint.x ) ? pressPoint.x : releasePoint.x ; + } + + public int getYMinimum() { + return ( pressPoint.y < releasePoint.y ) ? pressPoint.y : releasePoint.y ; + } + + public int getYMaximum() { + return ( pressPoint.y > releasePoint.y ) ? pressPoint.y : releasePoint.y ; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/MouseDragEvent.java b/dasCore/src/main/java/org/das2/event/MouseDragEvent.java new file mode 100644 index 000000000..30ccf9d2d --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/MouseDragEvent.java @@ -0,0 +1,55 @@ +/* File: MouseDragEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author jbf + */ +public class MouseDragEvent extends DasMouseEvent { + + Gesture gesture; + + public MouseDragEvent(Object source) { + super(source); + gesture= Gesture.NONE; + } + + public MouseDragEvent(Object source, Gesture gesture ) { + super(source); + this.gesture= gesture; + } + + public boolean isGesture() { + return gesture!=Gesture.NONE; + } + + public Gesture getGesture() { + return gesture; + } + + public String toString() { + return isGesture() ? gesture.toString() : "MouseDragEvent source: "+source; + } +} diff --git a/dasCore/src/main/java/org/das2/event/MouseModule.java b/dasCore/src/main/java/org/das2/event/MouseModule.java new file mode 100755 index 000000000..f93ed5587 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/MouseModule.java @@ -0,0 +1,154 @@ +/* File: MouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.components.propertyeditor.Displayable; +import org.das2.components.propertyeditor.Editable; +import org.das2.graph.DasCanvasComponent; + +import java.awt.*; +import java.awt.event.*; +import java.util.Vector; + +/** A MouseModule is a pluggable unit that promotes simple + * mouse events into human events or actions that are useful + * for science analysis. Each component has a mouseInputAdapter + * that manages a set of mouseModules, one is active at any + * given time. + * + * The DasMouseInputAdapter will delegate mouse events, key events, and mouse wheel + * events to the active + * @author jbf + */ +public class MouseModule implements Editable, Displayable, KeyListener, MouseListener, MouseMotionListener, MouseWheelListener { + + //protected DasCanvasComponent parent; + protected DragRenderer dragRenderer; + private String label; + protected DasCanvasComponent parent; + + protected MouseModule() { + label= "unlabelled MM"; + dragRenderer= EmptyDragRenderer.renderer; + } + + public MouseModule(DasCanvasComponent parent) { + this( parent, EmptyDragRenderer.renderer, "unlabelled MM" ); + setLabel(this.getClass().getName()); + } + + public MouseModule(DasCanvasComponent parent, DragRenderer dragRenderer, String label) { + this.parent= parent; + this.dragRenderer= dragRenderer; + this.label= label; + } + + /** + * returns a string that identifies the module + */ + public String getLabel() { + return label; + } + + /** + * No longer used + * @deprecated No longer supported + */ + public Vector getHotSpots() { + return null; + } + + /** return a cursor that indicates the selected module. */ + public Cursor getCursor() { + return new Cursor(Cursor.DEFAULT_CURSOR); + } + + public void hotSpotPressed(Shape s) { + } + + public DragRenderer getDragRenderer() { + return dragRenderer; + } + + /** Action to take when a mouse range (click, drag, release) has been selected. */ + public void mouseRangeSelected(MouseDragEvent e) { + } + + /** Action to take when a point (click or drag) is selected. */ + public void mousePointSelected(MousePointSelectionEvent e) { + } + + public void setLabel(java.lang.String label) { + this.label= label; + } + + public javax.swing.Icon getListIcon() { + return null; + } + + public String getListLabel() { + return getLabel(); + } + + public void keyPressed(KeyEvent keyEvent) { + } + + public void keyReleased(KeyEvent keyEvent) { + } + + public void keyTyped(KeyEvent keyEvent) { + } + + public void mouseReleased(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseDragged(MouseEvent e) { + } + + public void mouseClicked(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + public void mouseMoved(MouseEvent e) { + } + + public void mouseWheelMoved(MouseWheelEvent e) { + } + + /** + * this should only be called from the mouse module constructor. (Until + * it is verified that it is okay to call it elsewhere.) + */ + protected void setDragRenderer(DragRenderer dragRenderer) { + this.dragRenderer = dragRenderer; + } +} diff --git a/dasCore/src/main/java/org/das2/event/MousePointSelectionEvent.java b/dasCore/src/main/java/org/das2/event/MousePointSelectionEvent.java new file mode 100644 index 000000000..2f81ab4a9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/MousePointSelectionEvent.java @@ -0,0 +1,57 @@ +/* File: MousePointSelectionEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author jbf + */ +public class MousePointSelectionEvent extends DasMouseEvent { + int x; + int y; + /** Creates a new instance of MousePointSelectionEvent */ + + public MousePointSelectionEvent(Object source, int x, int y ) { + super(source); + this.x= x; + this.y= y; + } + + public void set(int x, int y) { + this.x= x; + this.y= y; + } + + public int getX() { + return this.x; + } + + public int getY() { + return this.y; + } + + public String toString() { + return "[MousePointSelectionEvent x:"+x+" y:"+y+"]"; + } +} diff --git a/dasCore/src/main/java/org/das2/event/MouseRangeGestureSelectionEvent.java b/dasCore/src/main/java/org/das2/event/MouseRangeGestureSelectionEvent.java new file mode 100644 index 000000000..a20abccbf --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/MouseRangeGestureSelectionEvent.java @@ -0,0 +1,60 @@ +/* File: MouseRangeGestureSelectionEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author Owner + */ +public class MouseRangeGestureSelectionEvent extends MouseRangeSelectionEvent { + + private Gesture gesture; + + /** Creates a new instance of MouseRangeGestureSelectionEvent */ + public MouseRangeGestureSelectionEvent(Object source, int min, int max, Gesture g) { + super( source, min, max, false ); + this.gesture= g; + } + + public boolean isGesture() { + return gesture!=Gesture.NONE; + } + + public boolean isBack() { + return gesture==Gesture.BACK; + } + + public boolean isForward() { + return gesture==Gesture.FORWARD; + } + + public boolean isZoomOut() { + return gesture==Gesture.ZOOMOUT; + } + + public Gesture getGesture() { + return gesture; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/MouseRangeSelectionEvent.java b/dasCore/src/main/java/org/das2/event/MouseRangeSelectionEvent.java new file mode 100644 index 000000000..c47638293 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/MouseRangeSelectionEvent.java @@ -0,0 +1,62 @@ +/* File: MouseRangeSelectionEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + + + +/** + * + * @author eew + */ +public class MouseRangeSelectionEvent extends MouseDragEvent +{ + + private int min, max; + private boolean isModified; + + /** Creates a new instance of DasDevicePositionEvent */ + public MouseRangeSelectionEvent(Object source, int min, int max, boolean isModified) { + super(source); + if (min>max) { + int t= min; + min= max; + max= t; + } + + this.min= min; + this.max= max; + this.isModified= isModified; + } + + public int getMinimum() { + return min; + } + + public int getMaximum() { + return max; + } + public boolean isModified() { + return isModified; + } +} diff --git a/dasCore/src/main/java/org/das2/event/MoveComponentMouseModule.java b/dasCore/src/main/java/org/das2/event/MoveComponentMouseModule.java new file mode 100644 index 000000000..0e1bcd445 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/MoveComponentMouseModule.java @@ -0,0 +1,147 @@ +/* + * MoveComponentMouseModule.java + * + * Created on April 21, 2007, 7:10 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasColumn; +import org.das2.graph.DasRow; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.image.BufferedImage; + +/** + * + * @author jbf + */ +public class MoveComponentMouseModule extends MouseModule { + + Point p0; + + static class MoveRenderer implements DragRenderer { + + DasCanvasComponent c; + BufferedImage i; + + MoveRenderer(DasCanvasComponent c) { + this.c = c; + } + + private void refreshImage() { // this doesn't work. + Rectangle bounds = c.getActiveRegion().getBounds(); + i = new BufferedImage( bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB ); + Graphics g= i.getGraphics(); + g.translate( c.getX(), c.getY() ); + c.paint( g ); + + } + + private Point center(Shape s) { + long avgX = 0, avgY = 0; + double[] coords = new double[6]; + long count = 0; + for (PathIterator i = s.getPathIterator(null); !i.isDone(); i.next()) { + int type = i.currentSegment(coords); + if (type == PathIterator.SEG_LINETO) { + avgX += coords[0]; + avgY += coords[1]; + count++; + } + } + + avgX /= count; + avgY /= count; + + return new Point((int) avgX, (int) avgY); + } + + private Shape enlarge(Shape s, double scale) { + Point center = center(s); + AffineTransform at = new AffineTransform(); + + at.translate( +center.x, +center.y); + at.scale(scale, scale); + GeneralPath gp = new GeneralPath(s); + gp.transform(at); + at = new AffineTransform(); + at.translate(-center.x*scale, -center.y*scale); + gp.transform(at); + + return gp; + } + + public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { + Rectangle bounds = c.getActiveRegion().getBounds(); + bounds.translate(p2.x - p1.x, p2.y - p1.y); + Graphics2D g = (Graphics2D) g1; + + //g.drawImage( i, p2.x - p1.x, p2.y-p1.y, c ); + + g.setClip(null); + g.setColor(Color.BLACK); + g.draw(bounds); + Point p = center(bounds); + //g.drawOval(p.x - 2, p.y - 2, 5, 5); + //g.draw(enlarge(bounds.getBounds(), 1.2)); + //System.err.println("draw " + bounds.getBounds()); + return new Rectangle[] { enlarge(bounds.getBounds(), 1.2 ).getBounds()}; + } + + public void clear(Graphics g) { + } + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + return null; + } + + public boolean isPointSelection() { + return false; + } + + public boolean isUpdatingDragSelection() { + return true; + } + } + + /** Creates a new instance of MoveComponentMouseModule */ + public MoveComponentMouseModule(DasCanvasComponent parent) { + super(parent, new MoveRenderer(parent), "Move Component"); + } + + @Override + public void mouseReleased(MouseEvent e) { + super.mouseReleased(e); + Point p = e.getPoint(); + int dx = p.x - p0.x; + int dy = p.y - p0.y; + + DasRow row = this.parent.getRow(); + row.setDPosition(row.getDMinimum() + dy, row.getDMaximum() + dy); + + DasColumn col = this.parent.getColumn(); + col.setDPosition(col.getDMinimum() + dx, col.getDMaximum() + dx); + + p0 = null; + } + + @Override + public void mousePressed(MouseEvent e) { + super.mousePressed(e); + ((MoveRenderer)this.dragRenderer).refreshImage(); + p0 = e.getPoint(); + } + +} diff --git a/dasCore/src/main/java/org/das2/event/PeakDetectorMouseModule.java b/dasCore/src/main/java/org/das2/event/PeakDetectorMouseModule.java new file mode 100644 index 000000000..24caf880c --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/PeakDetectorMouseModule.java @@ -0,0 +1,599 @@ +/* + * Cutoff2MouseModule.java + * + * Created on November 10, 2005, 1:41 PM + * + * + */ + +package org.das2.event; + +import org.das2.DasException; +import org.das2.dataset.AverageTableRebinner; +import org.das2.dataset.ClippedTableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetConsumer; +import org.das2.dataset.DataSetRebinner; +import org.das2.dataset.DataSetUpdateEvent; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.NoDataInIntervalException; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.SingleVectorDataSet; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.dataset.test.PolynomialDataSetDescriptor; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasColumn; +import org.das2.graph.DasPlot; +import org.das2.graph.DasRow; +import org.das2.graph.Psym; +import org.das2.graph.PsymConnector; +import org.das2.graph.SymColor; +import org.das2.graph.SymbolLineRenderer; +import org.das2.math.QuadFitUtil; +import org.das2.system.DasLogger; +import org.das2.util.DasMath; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.util.Arrays; +import java.util.HashMap; +import java.util.logging.Logger; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; + +/** + * + * @author Jeremy + */ +public class PeakDetectorMouseModule extends BoxSelectorMouseModule { + + DasAxis xaxis, yaxis; + DataSetConsumer dataSetConsumer; + DatumRange xrange; + DatumRange yrange; + String lastComment; + PeakSlicer peakSlicer; + + + static Logger logger= DasLogger.getLogger( DasLogger.GUI_LOG ); + + public PeakDetectorMouseModule( DasPlot parent, DataSetConsumer consumer ) { + super( parent, parent.getXAxis(), parent.getYAxis(), consumer, new BoxRenderer(parent,true), "Peak Detector" ); + this.dataSetConsumer= consumer; + } + + @Override + protected void fireBoxSelectionListenerBoxSelected(BoxSelectionEvent event) { + + final DatumRange xrange0= xrange; + final DatumRange yrange0= yrange; + + xrange= event.getXRange(); + yrange= event.getYRange(); + if ( event.getPlane("keyChar")!=null ) { + lastComment= (String)event.getPlane("keyChar"); + } else { + lastComment= null; + } + + Runnable run= new Runnable() { + public void run() { + try { + recalculate(); + } catch ( RuntimeException ex ) { + xrange= xrange0; + yrange= yrange0; + throw ex; + } + } + }; + new Thread(run).start(); + } + + /** + * return RebinDescriptor that is on descrete, repeatable boundaries. + * get us2000, divide by resolution, truncate, multiply by resolution. + */ + private RebinDescriptor getRebinDescriptor( DatumRange range ) { + double res= xResolution.doubleValue(Units.microseconds); + double min= range.min().doubleValue(Units.us2000); + min= Math.floor( min / res ); + double max= range.max().doubleValue(Units.us2000); + max= Math.ceil( max / res ); + int nbin= (int)(max-min); + + RebinDescriptor ddx= new RebinDescriptor( min*res, max*res, Units.us2000, nbin, false ); + return ddx; + } + + private VectorDataSet toDb( VectorDataSet ds, Datum reference ) { + Units refUnits= reference.getUnits(); + double refValue= reference.doubleValue(refUnits); + Units yunits= Units.dB; + Units xunits= ds.getXUnits(); + VectorDataSetBuilder builder= new VectorDataSetBuilder( xunits, yunits ); + + for ( int i=0; i=xtag>max, or -1 if no peak is found. + */ + private static int peakIndex(VectorDataSet ds, Datum min, Datum max) { + Datum yMax = ds.getYUnits().createDatum(Double.NEGATIVE_INFINITY); + int iMax = -1; + int i0 = -1; //The first x index in range + int i1 = -1; //The last x index in range + for (int i = 0; i < ds.getXLength(); i++) { + Datum y = ds.getDatum(i); + Datum x = ds.getXTagDatum(i); + if ( x.ge(min) && x.lt(max)) { + if (i0 == -1) i0 = i; + i1 = i; + if (y.gt(yMax)) { + yMax = y; + iMax = i; + } + } + } + + // We don't want to record this value if it is the first or + // last within range, since this inidicates that the value + // probably isn't really a maximum. + if (iMax == i0 || iMax == i1) { + iMax = -1; + } + return iMax; + } + + private static int[] findFive(VectorDataSet ds, int peakIndex) { + int[] indices, result; + int nIndex; + double yLowPrev, yHighPrev; + Units yUnits = ds.getYUnits(); + + indices = new int[7]; + indices[0] = peakIndex; + nIndex = 1; + + yHighPrev = yLowPrev = ds.getDouble(peakIndex, yUnits); + + for (int i = 1; i <= 3 && nIndex < 5; i++) { + double yLow = (peakIndex - i) >= 0 ? ds.getDouble(peakIndex - i, yUnits) : Double.POSITIVE_INFINITY; + double yHigh = (peakIndex + i) < ds.getXLength() ? ds.getDouble(peakIndex + i, yUnits) : Double.POSITIVE_INFINITY; + if (yLow < yLowPrev) { + indices[nIndex++] = peakIndex - i; + yLowPrev = yLow; + } else { + yLowPrev = Double.NEGATIVE_INFINITY; + } + if (yHigh < yHighPrev) { + indices[nIndex++] = peakIndex + i; + yHighPrev = yHigh; + } else { + yHighPrev = Double.NEGATIVE_INFINITY; + } + } + + Arrays.sort(indices, 0, nIndex); + result = new int[nIndex]; + System.arraycopy(indices, 0, result, 0, nIndex); + + return result; + } + + /** + * returns a double[2] if a fit is possible, null otherwise. + * @param slice a VectorDataSet with yUnits convertable to Units.dB. + */ + private double[] getFit( VectorDataSet slice, int jMax) { + double[] px, py, w; + double[] c; + int [] indices; + Datum y = slice.getXTagDatum(jMax); + Datum z = slice.getDatum(jMax); + Units xUnits = slice.getXUnits(); + Units yUnits = Units.dB; + + indices = findFive(slice, jMax); + int peakOfFive= Arrays.binarySearch(indices, jMax); + px = new double[indices.length]; + py = new double[indices.length]; + w = new double[indices.length]; + for (int iIndex = 0; iIndex < indices.length; iIndex++) { + px[iIndex] = slice.getXTagDouble(indices[iIndex], xUnits); + py[iIndex] = slice.getDouble(indices[iIndex], yUnits); + w[iIndex] = Units.dB.convertDoubleTo(Units.dimensionless, py[iIndex]); + } + + double threeDown = py[peakOfFive] - levelMin.doubleValue(Units.dB); + if (threeDown < py[0] && threeDown < py[py.length - 1]) { + return null; + } + + //Arrays.fill(w, 1.0); + c = QuadFitUtil.quadfit(px, py, w); + + if (c[2] >= -0.0) { + return null; + } + + double peak = QuadFitUtil.quadPeak(c); + if (peak < slice.getXTagDouble(0, xUnits) || peak > slice.getXTagDouble(slice.getXLength() - 1, xUnits)) { + return null; + } + + return c; + } + + + private class PeakSlicer implements DataPointSelectionListener { + + DataPointSelectionEvent lastSelectedPoint; + FitDescriptor fit; + Datum yValue; + Datum xValue; + int selectedRecord; // + + SymbolLineRenderer levelRenderer; + SymbolLineRenderer fitRenderer; + SymbolLineRenderer fitPointRenderer; + + DasPlot topPlot; + JFrame frame; + + Action prevAction= new AbstractAction("<< Prev") { + @Override + public void actionPerformed( ActionEvent e ) { + Datum xnew= xValue.subtract( xResolution ); + DataPointSelectionEvent evNew= new DataPointSelectionEvent( this, xnew, yValue ); + PeakSlicer.this.dataPointSelected(evNew); + } + } ; + + Action nextAction= new AbstractAction("Next >>") { + @Override + public void actionPerformed( ActionEvent e ) { + Datum xnew= xValue.add( xResolution ); + DataPointSelectionEvent evNew= new DataPointSelectionEvent( this, xnew, yValue ); + PeakSlicer.this.dataPointSelected(evNew); + } + } ; + + + PeakSlicer( DasPlot parent, DasAxis xaxis ) { + frame= new JFrame("Peak Slice"); + JPanel contentPanel= new JPanel(); + contentPanel.setLayout( new BorderLayout() ); + DasCanvas canvas= new DasCanvas( 300, 300 ); + contentPanel.add( canvas, BorderLayout.CENTER ); + Box npBox= Box.createHorizontalBox(); + npBox.add( new JButton( prevAction ) ); + npBox.add( new JButton( nextAction ) ); + contentPanel.add( npBox, BorderLayout.NORTH ); + + frame.getContentPane().add( contentPanel ); + frame.pack(); + frame.setVisible(false); + frame.setDefaultCloseOperation( JFrame.HIDE_ON_CLOSE ); + + DasColumn col= DasColumn.create( canvas ); + DasRow row1= DasRow.create( canvas, 0, 1 ); + + DasAxis yaxis= new DasAxis( new DatumRange( 0.001, 1,Units.dimensionless ), DasAxis.VERTICAL ) ; + yaxis.setLog(true); + DasPlot plot= new PeakDasPlot( xaxis, yaxis ); + plot.getYAxis().setLabel("level"); + plot.getXAxis().setTickLabelsVisible(false); + levelRenderer= new SymbolLineRenderer(); + + fitRenderer= new SymbolLineRenderer(); + fitRenderer = new SymbolLineRenderer(); + fitRenderer.setColor(SymColor.blue); + + fitPointRenderer = new SymbolLineRenderer(); + fitPointRenderer.setColor(SymColor.red); + fitPointRenderer.setPsymConnector(PsymConnector.NONE); + fitPointRenderer.setPsym(Psym.TRIANGLES); + fitPointRenderer.setSymSize(3.0); + + plot.addRenderer(fitRenderer); + plot.addRenderer(levelRenderer); + + topPlot= plot; + + DataPointSelectorMouseModule tweakSlicer= + new DataPointSelectorMouseModule( topPlot, levelRenderer, + new VerticalSliceSelectionRenderer(topPlot), "tweak cutoff" ) { + @Override + public void keyPressed( KeyEvent event ) { + System.err.print(event); + if ( event.getKeyCode()==KeyEvent.VK_DOWN ) { + } else if ( event.getKeyCode()==KeyEvent.VK_UP ) { + Datum xnew= xValue.subtract( xResolution ); + DataPointSelectionEvent evNew= new DataPointSelectionEvent( this, xnew, yValue ); + PeakSlicer.this.dataPointSelected(evNew); + } + } + }; + tweakSlicer.setDragEvents(true); // only key events fire + tweakSlicer.addDataPointSelectionListener( new DataPointSelectionListener() { + @Override + public void dataPointSelected( DataPointSelectionEvent e ) { + Datum x= e.getX(); + HashMap properties= new HashMap(); + if ( e.getPlane("keyChar")!=null ) { + properties.put("comment",e.getPlane("keyChar")); + } else { + properties.put("comment","tweak"); + } + fireDataSetUpdateListenerDataSetUpdated( + new DataSetUpdateEvent(this, + new SingleVectorDataSet( xValue, e.getX(), properties ) ) ); + } + } ); + topPlot.addMouseModule( tweakSlicer ); + topPlot.getDasMouseInputAdapter().setPrimaryModule(tweakSlicer); + + DataPointSelectorMouseModule levelSlicer= + new DataPointSelectorMouseModule( topPlot, levelRenderer, + new HorizontalSliceSelectionRenderer(topPlot), "peak S/N level" ); + levelSlicer.addDataPointSelectionListener( new DataPointSelectionListener() { + @Override + public void dataPointSelected( DataPointSelectionEvent e ) { + Datum y= e.getY(); + PeakDetectorMouseModule.this.setLevelMin( y ); + } + } ); + levelSlicer.setDragEvents(false); + levelSlicer.setKeyEvents(false); + levelSlicer.setReleaseEvents(true); + topPlot.addMouseModule( levelSlicer ); + + canvas.add( plot, row1, col ); + + } + + + @Override + public void dataPointSelected(org.das2.event.DataPointSelectionEvent event) { + logger.fine("got DataPointSelectionEvent: "+event.getX() ); + this.lastSelectedPoint= event; + + TableDataSet tds= (TableDataSet)dataSetConsumer.getConsumedDataSet(); + + this.xValue= event.getX(); + this.yValue= event.getY(); + + if ( xrange==null ) return; + + DatumRange range= new DatumRange( event.getX(), event.getX() ); + try { + tds= conditionData( tds, range ); + } catch (NoDataInIntervalException ex) { + return; + } + + logger.fine("find closest column " ); + int i= DataSetUtil.closestColumn( tds, event.getX() ); + + + this.xValue= tds.getXTagDatum(i); + topPlot.setTitle( "" + xValue ); + + logger.fine("doDigitize"); + fit= doDigitize( tds, i ); + if ( fit!=null ) { + levelRenderer.setDataSet( fit.digitizedDataSet ); + Datum resLimit= topPlot.getXAxis().invTransform(1) .subtract( topPlot.getXAxis().invTransform(0) ); + PolynomialDataSetDescriptor dsd= new PolynomialDataSetDescriptor( fit.fitCoef, fit.peakX.getUnits(), + fit.digitizedDataSet.getYUnits(), resLimit ) ; + dsd.setYMin(fit.digitizedDataSet.getYUnits().createDatum( -5 )); + fitRenderer.setDataSetDescriptor( dsd ); + } + + showPopup(); + } + + private void showPopup() { + if ( !frame.isVisible() ) frame.setVisible(true); + } + + class PeakDasPlot extends DasPlot { + PeakDasPlot( DasAxis x, DasAxis y ) { + super(x,y); + } + @Override + protected void drawContent(java.awt.Graphics2D g) { + super.drawContent(g); + + if ( fit!=null ) { + g.setColor( Color.GRAY ); + int ix= (int)this.getXAxis().transform( fit.peakX ); + g.drawLine( ix, 0, ix, getHeight() ); + int iy= (int)this.getYAxis().transform( Units.dB.createDatum(-3) ); + g.drawLine( 0, iy, getWidth(), iy ); + + g.setColor( Color.pink ); + ix= (int)getXAxis().transform( yValue ); + g.drawLine( ix, 0, ix, getHeight() ); + } + + } + } + } + + public DataPointSelectionListener getSlicer( DasPlot plot, TableDataSetConsumer consumer ) { + DasAxis sourceYAxis = plot.getYAxis(); + DasAxis sourceZAxis = consumer.getZAxis(); + + DatumRange range= sourceYAxis.getDatumRange(); + DasAxis xAxis = sourceYAxis.createAttachedAxis( DasAxis.HORIZONTAL ); + peakSlicer= new PeakSlicer( plot, xAxis ); + return peakSlicer; + + } + + + private transient java.util.ArrayList dataSetUpdateListenerList; + + public synchronized void addDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + if (dataSetUpdateListenerList == null ) { + dataSetUpdateListenerList = new java.util.ArrayList(); + } + dataSetUpdateListenerList.add(listener); + } + + public synchronized void removeDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + if (dataSetUpdateListenerList != null ) { + dataSetUpdateListenerList.remove(listener); + } + } + + private void fireDataSetUpdateListenerDataSetUpdated(org.das2.dataset.DataSetUpdateEvent event) { + java.util.ArrayList list; + synchronized (this) { + if (dataSetUpdateListenerList == null) return; + list = (java.util.ArrayList)dataSetUpdateListenerList.clone(); + } + for (int i = 0; i < list.size(); i++) { + ((org.das2.dataset.DataSetUpdateListener)list.get(i)).dataSetUpdated(event); + } + } + + /** + * Holds value of property levelMin. + */ + private Datum levelMin= Units.dB.createDatum(-3.); + + /** + * Getter for property levelMin. + * @return Value of property levelMin. + */ + public Datum getLevelMin() { + return this.levelMin; + } + + /** + * Setter for property levelMin. + * @param levelMin New value of property levelMin. + */ + public void setLevelMin(Datum levelMin) { + this.levelMin = levelMin; + recalculate(); + } + + private Datum xResolution= Units.milliseconds.createDatum(500); + + public Datum getXResolution() { + return this.xResolution; + } + + public void setXResolution(Datum xResolution) { + this.xResolution = xResolution; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/PointSlopeDragRenderer.java b/dasCore/src/main/java/org/das2/event/PointSlopeDragRenderer.java new file mode 100644 index 000000000..a1cda6f5e --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/PointSlopeDragRenderer.java @@ -0,0 +1,61 @@ +/* + * PointSlopeDragRenderer.java + * + * Created on February 19, 2004, 11:31 PM + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasAxis; +import org.das2.datum.Datum; +import org.das2.util.GrannyTextRenderer; +import java.awt.*; +import java.text.*; + +/** + * + * @author Owner + */ +public class PointSlopeDragRenderer extends LabelDragRenderer { + + + DasAxis xaxis, yaxis; + NumberFormat nf; + + /** Creates a new instance of PointSlopeDragRenderer */ + public PointSlopeDragRenderer(DasCanvasComponent parent, DasAxis xaxis, DasAxis yaxis ) { + super( parent ); + this.parent= parent; + this.xaxis= xaxis; + this.yaxis= yaxis; + + gtr= new GrannyTextRenderer(); + nf= new DecimalFormat( "0.00E0" ); + } + + public Rectangle[] renderDrag(java.awt.Graphics g1, java.awt.Point p1, java.awt.Point p2) { + Graphics2D g= ( Graphics2D ) g1; + g1.drawLine( p1.x, p1.y, p2.x, p2.y ); + g1.drawOval(p1.x-1, p1.y-1, 3, 3 ) ; + + Rectangle myDirtyBounds= new Rectangle( p1.x-2, p1.y-2, 5, 5 ); + + myDirtyBounds.add(p2.x-2,p2.y-2); + myDirtyBounds.add(p2.x+2,p2.y+2); + + Datum run= xaxis.invTransform(p2.x).subtract(xaxis.invTransform(p1.x)); + Datum rise= yaxis.invTransform(p2.y).subtract(yaxis.invTransform(p1.y)); + + if ( !p1.equals(p2) ) { + Datum slope= rise.divide(run); + setLabel( "m="+slope ); + } else { + setLabel( "" ); + } + super.renderDrag( g, p1, p2 ); + return new Rectangle[] { dirtyBounds, myDirtyBounds } ; + + } + +} diff --git a/dasCore/src/main/java/org/das2/event/RangeAnnotatorMouseModule.java b/dasCore/src/main/java/org/das2/event/RangeAnnotatorMouseModule.java new file mode 100644 index 000000000..4e370c3c3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/RangeAnnotatorMouseModule.java @@ -0,0 +1,34 @@ +/* + * RangeAnnotatorMouseModule.java + * + * Created on November 1, 2005, 4:30 PM + * + * + */ + +package org.das2.event; + +import org.das2.graph.DasPlot; +import java.awt.event.MouseEvent; + +/** + * + * @author Jeremy + */ +public class RangeAnnotatorMouseModule extends HorizontalRangeSelectorMouseModule { + public RangeAnnotatorMouseModule( DasPlot parent ) { + super( parent, parent.getXAxis() ); + setLabel("Range Annotator"); + } + + public void mouseRangeSelected(MouseDragEvent e) { + super.mouseRangeSelected(e); + System.out.println(e); + } + + public void mouseReleased(MouseEvent e) { + super.mouseReleased(e); + System.out.println(e); + } + +} diff --git a/dasCore/src/main/java/org/das2/event/TimeRangeSelectionEvent.java b/dasCore/src/main/java/org/das2/event/TimeRangeSelectionEvent.java new file mode 100644 index 000000000..b5ac30fa8 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/TimeRangeSelectionEvent.java @@ -0,0 +1,51 @@ +/* File: TimeRangeSelectionEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; + +/** + * + * @author jbf + */ +public class TimeRangeSelectionEvent extends DasEvent { + + private DatumRange range = null; + + /** Creates a new instance of TimeRangeSelectionEvent */ + public TimeRangeSelectionEvent(Object source, DatumRange range ) { + super(source); + this.range= range; + } + + public DatumRange getRange() { + return range; + } + + public String toString() { + return "["+range+"]"; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/TimeRangeSelectionListener.java b/dasCore/src/main/java/org/das2/event/TimeRangeSelectionListener.java new file mode 100644 index 000000000..be0aa5028 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/TimeRangeSelectionListener.java @@ -0,0 +1,32 @@ +/* File: TimeRangeSelectionListener.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +/** + * + * @author jbf + */ +public abstract interface TimeRangeSelectionListener extends java.util.EventListener { + public void timeRangeSelected(TimeRangeSelectionEvent e); +} diff --git a/dasCore/src/main/java/org/das2/event/TimeRangeSelectorMouseModule.java b/dasCore/src/main/java/org/das2/event/TimeRangeSelectorMouseModule.java new file mode 100644 index 000000000..1f17d0055 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/TimeRangeSelectorMouseModule.java @@ -0,0 +1,137 @@ +/* File: TimeRangeSelectorMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.datum.DatumRange; +import org.das2.DasApplication; +import org.das2.datum.Datum; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasPlot; +import org.das2.system.DasLogger; +import javax.swing.event.EventListenerList; + + +/** + * + * @author jbf + * @deprecated Use HorizontalRangeSelectorMouseModule. + */ +public class TimeRangeSelectorMouseModule extends MouseModule { + + DasAxis timeAxis; + + /** Utility field used by event firing mechanism. */ + private EventListenerList listenerList = null; + + public String getLabel() { return "X Time Zoom"; } + + public TimeRangeSelectorMouseModule(DasCanvasComponent parent, DasAxis timeAxis) { + this.parent= parent; + this.dragRenderer= new HorizontalRangeGesturesRenderer(parent); + this.timeAxis= timeAxis; + } + + public static TimeRangeSelectorMouseModule create(DasPlot parent) { + DasAxis axis= parent.getXAxis(); + TimeRangeSelectorMouseModule result=null; + result= new TimeRangeSelectorMouseModule(parent, parent.getXAxis()); + return result; + } + + public void mouseRangeSelected(MouseRangeSelectionEvent e0) { + Datum tmin; + Datum tmax; + + if (!e0.isGesture()) { + MouseRangeSelectionEvent e= (MouseRangeSelectionEvent)e0; + Datum min= timeAxis.invTransform(e.getMinimum()); + Datum max= timeAxis.invTransform(e.getMaximum()); + + Datum nnMin= timeAxis.findTick(min,0,true); // nearest neighbor + Datum nnMax= timeAxis.findTick(max,0,true); + if (nnMin.equals(nnMax)) { + min= timeAxis.findTick(min,-1,true); + max= timeAxis.findTick(max,1,true); + } else { + min= nnMin; + max= nnMax; + } + TimeRangeSelectionEvent te= new TimeRangeSelectionEvent(parent,new DatumRange( min,max ) ); + fireTimeRangeSelectionListenerTimeRangeSelected(te); + } else if (e0.getGesture()==Gesture.BACK) { + timeAxis.setDataRangePrev(); + } else if (e0.getGesture()==Gesture.ZOOMOUT) { + timeAxis.setDataRangeZoomOut(); + } else if (e0.getGesture()==Gesture.FORWARD) { + timeAxis.setDataRangeForward(); + } else if (e0.getGesture()==Gesture.SCANPREV) { + DatumRange range0= timeAxis.getDatumRange(); + TimeRangeSelectionEvent te= new TimeRangeSelectionEvent(parent, range0.previous() ); + fireTimeRangeSelectionListenerTimeRangeSelected(te); + } else if (e0.getGesture()==Gesture.SCANNEXT) { + DatumRange range0= timeAxis.getDatumRange(); + TimeRangeSelectionEvent te= new TimeRangeSelectionEvent(parent, range0.next() ); + fireTimeRangeSelectionListenerTimeRangeSelected(te); + } else { + throw new RuntimeException("unrecognized gesture: "+e0.getGesture()); + } + + } + + /** Registers TimeRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addTimeRangeSelectionListener(org.das2.event.TimeRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new EventListenerList(); + } + listenerList.add(org.das2.event.TimeRangeSelectionListener.class, listener); + } + + /** Removes TimeRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeTimeRangeSelectionListener(org.das2.event.TimeRangeSelectionListener listener) { + listenerList.remove(org.das2.event.TimeRangeSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + + private void fireTimeRangeSelectionListenerTimeRangeSelected(TimeRangeSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.TimeRangeSelectionListener.class) { + String logmsg= "fire event: "+this.getClass().getName()+"-->"+listeners[i+1].getClass().getName()+" "+event; + DasLogger.getLogger( DasLogger.GUI_LOG ).fine(logmsg); + ((org.das2.event.TimeRangeSelectionListener)listeners[i+1]).timeRangeSelected(event); + } + } + } + +} diff --git a/dasCore/src/main/java/org/das2/event/VerticalRangeGesturesRenderer.java b/dasCore/src/main/java/org/das2/event/VerticalRangeGesturesRenderer.java new file mode 100644 index 000000000..8fce3fe2e --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/VerticalRangeGesturesRenderer.java @@ -0,0 +1,114 @@ +/* File: VerticalRangeGesturesRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; + +import java.awt.*; + +/** + * + * @author eew + */ +public class VerticalRangeGesturesRenderer implements DragRenderer { + + protected int xInitial; + protected int yInitial; + private GesturesRenderer gr; + private Rectangle dirtyBounds; + private DasCanvasComponent parent; + + public VerticalRangeGesturesRenderer(DasCanvasComponent parent) { + gr= new GesturesRenderer(parent); + this.parent= parent; + dirtyBounds= new Rectangle(); + } + + public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { + + Graphics2D g= (Graphics2D) g1; + + double dx= p2.x-p1.x; + double dy= -1* ( p2.y-p1.y ); + double angle= Math.atan2(dy, dx) * 180 / Math.PI; + double radius= Math.sqrt(dy*dy+dx*dx); + if ( radius<20 ) { + gr.renderDrag( g, p1, p2 ); + dirtyBounds.setBounds(gr.getDirtyBounds()); + } else { + + int y2 = p2.y; + int y1= p1.y; + if (y2 6 ) + g.drawLine(x, y1+3, x, y2-3); + g.drawLine(x+2, y1, x-2, y1 ); //serifs + g.drawLine(x+2, y2, x-2, y2 ); + + g.setStroke(new BasicStroke()); + g.setColor(color0); + + if ( height > 6 ) + g.drawLine(x, y1+3, x, y2-3); + g.drawLine(x+2, y1, x-2, y1 ); //serifs + g.drawLine(x+2, y2, x-2, y2 ); + dirtyBounds.setLocation(x-4,y1-2); + dirtyBounds.add(x+4,y2+2); + } + return new Rectangle[] { dirtyBounds }; + } + + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + double dx= p2.x-p1.x; + double dy= -1* ( p2.y-p1.y ); + double radius= Math.sqrt(dy*dy+dx*dx); + if ( gr.isGesture(p1,p2) ) { + return gr.getMouseDragEvent(source,p1,p2,isModified); + } else { + return new MouseRangeSelectionEvent(source,p1.y,p2.y, isModified ); + } + } + + public void clear(Graphics g) { + parent.paintImmediately(dirtyBounds); + } + + public boolean isPointSelection() { + return false; + } + + public boolean isUpdatingDragSelection() { + return false; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/VerticalRangeSelectorMouseModule.java b/dasCore/src/main/java/org/das2/event/VerticalRangeSelectorMouseModule.java new file mode 100644 index 000000000..d8697452a --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/VerticalRangeSelectorMouseModule.java @@ -0,0 +1,124 @@ +/* File: VerticalRangeSelectorMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasPlot; +import javax.swing.event.EventListenerList; + +/** + * + * @author jbf + */ +public class VerticalRangeSelectorMouseModule extends MouseModule { + + DasAxis axis; + + /** Utility field used by event firing mechanism. */ + private EventListenerList listenerList = null; + + public String getLabel() { return "Zoom Y"; }; + + public VerticalRangeSelectorMouseModule(DasCanvasComponent parent, DasAxis axis) { + if (axis.isHorizontal()) { + throw new IllegalArgumentException("Axis orientation is not vertical"); + } + this.parent= parent; + // this.dragRenderer= (DragRenderer)HorizontalRangeRenderer.renderer; + this.dragRenderer= new VerticalRangeGesturesRenderer(parent); + this.axis= axis; + } + + public static VerticalRangeSelectorMouseModule create(DasPlot parent) { + DasAxis axis= parent.getYAxis(); + VerticalRangeSelectorMouseModule result= + new VerticalRangeSelectorMouseModule(parent,parent.getYAxis()); + return result; + } + + public void mouseRangeSelected(MouseDragEvent e0) { + if (!e0.isGesture()) { + Datum min; + Datum max; + Datum nnMin; + Datum nnMax; + MouseRangeSelectionEvent e= (MouseRangeSelectionEvent)e0; + if (axis.isFlipped()) { + max= axis.invTransform(e.getMaximum()); + min= axis.invTransform(e.getMinimum()); + } + else { + min= axis.invTransform(e.getMaximum()); + max= axis.invTransform(e.getMinimum()); + } + DatumRange dr= new DatumRange( min, max ); + DatumRange nndr= axis.getTickV().enclosingRange(dr, true); + DataRangeSelectionEvent te= + new DataRangeSelectionEvent(parent,nndr.min(),nndr.max()); + fireDataRangeSelectionListenerDataRangeSelected(te); + } else if (e0.getGesture()==Gesture.BACK) { + axis.setDataRangePrev(); + } else if (e0.getGesture()==Gesture.ZOOMOUT) { + axis.setDataRangeZoomOut(); + } else if (e0.getGesture()==Gesture.FORWARD) { + axis.setDataRangeForward(); + } else { + } + } + + /** Registers DataRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new EventListenerList(); + } + listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Removes DataRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { + ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); + } + } + } + +} diff --git a/dasCore/src/main/java/org/das2/event/VerticalSliceSelectionRenderer.java b/dasCore/src/main/java/org/das2/event/VerticalSliceSelectionRenderer.java new file mode 100644 index 000000000..50b6547f6 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/VerticalSliceSelectionRenderer.java @@ -0,0 +1,84 @@ +/* File: VerticalSliceSelectionRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasRow; + +import java.awt.*; +/** + * + * @author eew + */ +public class VerticalSliceSelectionRenderer implements DragRenderer { + + DasCanvasComponent parent; + Rectangle dirtyBounds; + + /** Creates a new instance of VerticalLineSelectionRenderer */ + public VerticalSliceSelectionRenderer(DasCanvasComponent parent) { + this.parent= parent; + dirtyBounds= new Rectangle(); + } + + private void drawCrossHair(Graphics g0, Point p) { + + Graphics g= g0.create(); + + g.setColor(new Color(0,0,0)); + g.setXORMode(Color.white); + + DasRow row= parent.getRow(); + + g.drawLine( p.x, row.getDMinimum(), p.x, row.getDMaximum() ); + + g.dispose(); + + } + + public Rectangle[] renderDrag(Graphics g, Point p1, Point p2) { + drawCrossHair(g,p2); + //g.drawLine(p2.x, 0, p2.x, parent.getHeight()); + dirtyBounds.setRect(p2.x,0,1,parent.getHeight()); + return new Rectangle[] { dirtyBounds }; + } + + + public MouseDragEvent getMouseDragEvent(Object o,Point p1,Point p2,boolean isModified) { + return null; + } + + public void clear(Graphics g) { + parent.paintImmediately(dirtyBounds); + } + + public boolean isPointSelection() { + return true; + } + + public boolean isUpdatingDragSelection() { + return false; + } + +} diff --git a/dasCore/src/main/java/org/das2/event/VerticalSlicerMouseModule.java b/dasCore/src/main/java/org/das2/event/VerticalSlicerMouseModule.java new file mode 100755 index 000000000..0347f90e3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/VerticalSlicerMouseModule.java @@ -0,0 +1,111 @@ +/* File: VerticalSlicerMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; +import org.das2.dataset.DataSetConsumer; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasPlot; +import org.das2.graph.Renderer; +/** + * + * @author jbf + */ +public class VerticalSlicerMouseModule extends MouseModule { + + private org.das2.dataset.DataSet ds; + double offset; + private DasAxis xaxis; + private DasAxis yaxis; + + private org.das2.dataset.DataSetConsumer dataSetConsumer; + /** Creates a new instance of VerticalSlicerMouseModule */ + + private DataPointSelectionEvent de; + + /** Utility field used by event firing mechanism. */ + private javax.swing.event.EventListenerList listenerList = null; + + public VerticalSlicerMouseModule( DasCanvasComponent parent, + DataSetConsumer dataSetConsumer, DasAxis xaxis, DasAxis yaxis ) { + super( parent, new VerticalSliceSelectionRenderer(parent), "Vertical Slice" ); + this.dataSetConsumer= dataSetConsumer; + this.xaxis= xaxis; + this.yaxis= yaxis; + //TODO: this is silly, just create a new one each time... + this.de= new DataPointSelectionEvent(this,null,null); + } + + public static VerticalSlicerMouseModule create(DasPlot parent) { + DasAxis xaxis= parent.getXAxis(); + DasAxis yaxis= parent.getYAxis(); + return new VerticalSlicerMouseModule(parent,parent,xaxis,yaxis); + } + + public static VerticalSlicerMouseModule create( Renderer renderer ) { + DasPlot parent= renderer.getParent(); + return new VerticalSlicerMouseModule(parent,renderer,parent.getXAxis(),parent.getYAxis()); + } + + public void mousePointSelected(MousePointSelectionEvent e) { + de.birthMilli= System.currentTimeMillis(); + ds= dataSetConsumer.getConsumedDataSet(); + de.set(xaxis.invTransform(e.getX()),yaxis.invTransform(e.getY())); + + de.setDataSet(ds); + + fireDataPointSelectionListenerDataPointSelected(de); + } + + /** Registers DataPointSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } + listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** Removes DataPointSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + listenerList.remove(org.das2.event.DataPointSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataPointSelectionListenerDataPointSelected(DataPointSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataPointSelectionListener.class) { + ((org.das2.event.DataPointSelectionListener)listeners[i+1]).dataPointSelected(event); + } + } + } + +} diff --git a/dasCore/src/main/java/org/das2/event/ZoomOutMouseModule.java b/dasCore/src/main/java/org/das2/event/ZoomOutMouseModule.java new file mode 100644 index 000000000..e55c62833 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/ZoomOutMouseModule.java @@ -0,0 +1,62 @@ +/* + * ZoomOutMouseModule.java + * + * Created on June 29, 2007, 3:50 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.event; + +import org.das2.datum.DatumRange; +import org.das2.datum.DatumRangeUtil; +import org.das2.graph.DasAxis; + +/** + * + * @author jbf + */ +public class ZoomOutMouseModule extends BoxSelectorMouseModule { + + DasAxis parent; + + BoxSelectionListener createBoxSelectionListener() { + return new BoxSelectionListener() { + public void BoxSelected( BoxSelectionEvent event ) { + DatumRange outerRange= parent.getDatumRange(); + DatumRange range= parent.isHorizontal() ? event.getXRange() : event.getYRange(); + range= parent.getTickV().enclosingRange( range, true ); + DatumRange newRange; + if ( parent.isLog() ) { + double nmin= DatumRangeUtil.normalizeLog( range, outerRange.min() ); + double nmax= DatumRangeUtil.normalizeLog( range, outerRange.max() ); + nmin= nmin < -3 ? -3 : nmin; + nmax= nmax > 3 ? 3 : nmax; + newRange= DatumRangeUtil.rescaleLog( outerRange, nmin, nmax ); + } else { + double nmin= DatumRangeUtil.normalize( range, outerRange.min() ); + double nmax= DatumRangeUtil.normalize( range, outerRange.max() ); + nmin= nmin < -3 ? -3 : nmin; + nmax= nmax > 3 ? 3 : nmax; + newRange= DatumRangeUtil.rescale( outerRange, nmin, nmax ); + } + parent.setDatumRange( newRange ); + } + + }; + } + + /** Creates a new instance of ZoomOutMouseModule */ + public ZoomOutMouseModule( DasAxis axis ) { + super( axis, + axis.isHorizontal() ? axis : null, + axis.isHorizontal() ? null : axis, + null, + new BoxRenderer(axis), + "Zoom Out" ); + this.parent= axis; + addBoxSelectionListener( createBoxSelectionListener() ); + } + +} diff --git a/dasCore/src/main/java/org/das2/event/ZoomPanMouseModule.java b/dasCore/src/main/java/org/das2/event/ZoomPanMouseModule.java new file mode 100644 index 000000000..23db8ca0b --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/ZoomPanMouseModule.java @@ -0,0 +1,256 @@ +/* + * ZoomPanMouseModule.java + * + * Created on August 7, 2007, 8:53 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ +package org.das2.event; + +import java.awt.Component; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumRangeUtil; +import org.das2.datum.TimeLocationUnits; +import org.das2.datum.UnitsUtil; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.TickVDescriptor; +import java.awt.Cursor; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import org.das2.graph.DasDevicePosition; + +/** + * + * @author jbf + */ +public class ZoomPanMouseModule extends MouseModule { + + DasAxis xAxis; + DasAxis yAxis; + DasAxis.Lock xAxisLock; + DasAxis.Lock yAxisLock; + Point p0; + DatumRange xAxisRange0; + DatumRange yAxisRange0; + long t0, tbirth; + + /** Creates a new instance of ZoomPanMouseModule */ + public ZoomPanMouseModule(DasCanvasComponent parent, DasAxis horizontalAxis, DasAxis verticalAxis) { + super(parent); + setLabel("Zoom Pan"); + this.xAxis = horizontalAxis; + this.yAxis = verticalAxis; + t0 = System.nanoTime(); + tbirth = System.nanoTime(); + } + + private boolean axisIsAdjustable(DasAxis axis) { + return axis != null && (UnitsUtil.isIntervalMeasurement(axis.getUnits()) || UnitsUtil.isRatioMeasurement(axis.getUnits())); + } + + private enum Pos { + _null, beyondMin, min, middle, max, beyondMax + + + + }; + + private Pos position(DasDevicePosition ddp, int pos, int threshold) { + int max = ddp.getDMaximum(); + int min = ddp.getDMinimum(); + if (((max - min) / threshold) < 3) threshold = (max - min) / 3; + if (pos < min) { + return Pos.beyondMin; + } else if (pos < min + threshold ) { + return Pos.min; + } else if (pos <= max - threshold) { + return Pos.middle; + } else if (pos <= max) { + return Pos.max; + } else { + return Pos.beyondMax; + } + } + + /** + * mouse wheel events zoom or pan rapidly. With a physical wheel, I (jbf) found + * that I get 17ms per click, and this is managable. With a touchpad on a mac, + * these events come much faster, like 10ms per click, which can disorient the + * operator. So we limit the speed to 20ms per click, for now by dropping + * rapid clicks. + * + * @param e + */ + public void mouseWheelMoved(MouseWheelEvent e) { + double nmin, nmax; + + double xshift = 0., yshift = 0.; + + if ((e.isControlDown() || e.isShiftDown())) { + if (xAxis != null && yAxis != null) return; // this happens when mouse drifts onto plot during xaxis pan. + if (e.getWheelRotation() < 0) { + nmin = -0.20; // pan left on xaxis + nmax = +0.80; + } else { + nmin = +0.20; // pan right on xaxis + nmax = +1.20; + } + } else { + Point ep= SwingUtilities.convertPoint( e.getComponent(), e.getPoint(), parent.getCanvas() ); + + //ep.translate( e.getComponent().getX(), e.getComponent().getY() ); + Pos xpos = xAxis == null ? Pos._null : position(xAxis.getColumn(), ep.x, 20); + Pos ypos = yAxis == null ? Pos._null : position(yAxis.getRow(), ep.y, 20); + + if (e.getWheelRotation() < 0) { + nmin = 0.20; // zoom in + nmax = 0.80; + } else { + nmin = -0.25; // zoom out + nmax = 1.25; + } + switch (xpos) { + case min: + xshift = -nmin; + break; + case max: + xshift = nmin; + break; + } + switch (ypos) { + case min: + yshift = nmin; + break; + case max: + yshift = -nmin; + break; + } + } + + //int clickMag= Math.abs(e.getWheelRotation()); + int clickMag = 1; + final long t1 = System.nanoTime(); + long limitNanos = (long) 20e6; + if ((t1 - t0) / clickMag < limitNanos) { + clickMag = (int) Math.floor((t1 - t0) / limitNanos); + } + + if (clickMag == 0) return; + t0 = System.nanoTime(); + + //System.err.println(":ns: "+(System.nanoTime()-tbirth)+" "+clickMag); + if (axisIsAdjustable(xAxis)) { + DatumRange dr = xAxis.getDatumRange(); + for (int i = 0; i < clickMag; i++) { + if (xAxis.isLog()) { + dr = DatumRangeUtil.rescaleLog(dr, nmin+xshift, nmax+xshift); + } else { + dr = DatumRangeUtil.rescale(dr, nmin+xshift, nmax+xshift); + } + } + xAxis.setDatumRange(dr); + } + if (axisIsAdjustable(yAxis)) { + DatumRange dr = yAxis.getDatumRange(); + for (int i = 0; i < clickMag; i++) { + + if (yAxis.isLog()) { + dr = DatumRangeUtil.rescaleLog(dr, nmin+yshift, nmax+yshift); + } else { + dr = DatumRangeUtil.rescale(dr, nmin+yshift, nmax+yshift); + } + } + yAxis.setDatumRange(dr); + } + + super.mouseWheelMoved(e); + } + + public void mouseReleased(MouseEvent e) { + super.mouseReleased(e); + if (xAxis != null) { + xAxisLock.unlock(); + xAxisLock = null; + } + if (yAxis != null) { + yAxisLock.unlock(); + yAxisLock = null; + } + doPan(e, false); + parent.getCanvas().getGlassPane().setCursor(null); + } + + /** + * round to a nice boundaries. + */ + private static DatumRange doRound(DatumRange dr, DasAxis axis) { + TickVDescriptor ticks; + if (dr.getUnits() instanceof TimeLocationUnits) { + ticks = TickVDescriptor.bestTickVTime(dr.min(), dr.max(), axis.getDLength() / 2, axis.getDLength(), true); + } else if (axis.isLog()) { + ticks = TickVDescriptor.bestTickVLogNew(dr.min(), dr.max(), axis.getDLength() / 2, axis.getDLength(), true); + } else { + ticks = TickVDescriptor.bestTickVLinear(dr.min(), dr.max(), axis.getDLength() / 2, axis.getDLength(), true); + } + return ticks.enclosingRange(dr, true); + } + + private void doPan(final MouseEvent e, boolean round) { + Point p2 = e.getPoint(); + if (axisIsAdjustable(xAxis)) { + DatumRange dr; + if (xAxis.isLog()) { + Datum delta = xAxis.invTransform(p0.getX()).divide(xAxis.invTransform(p2.getX())); + dr = new DatumRange(xAxisRange0.min().multiply(delta), xAxisRange0.max().multiply(delta)); + } else { + Datum delta = xAxis.invTransform(p0.getX()).subtract(xAxis.invTransform(p2.getX())); + dr = new DatumRange(xAxisRange0.min().add(delta), xAxisRange0.max().add(delta)); + } + if (round) { + dr = doRound(dr, xAxis); + } + xAxis.setDatumRange(dr); + } + if (axisIsAdjustable(yAxis)) { + DatumRange dr; + if (yAxis.isLog()) { + Datum ydelta = yAxis.invTransform(p0.getY()).divide(yAxis.invTransform(p2.getY())); + dr = new DatumRange(yAxisRange0.min().multiply(ydelta), yAxisRange0.max().multiply(ydelta)); + } else { + Datum ydelta = yAxis.invTransform(p0.getY()).subtract(yAxis.invTransform(p2.getY())); + dr = new DatumRange(yAxisRange0.min().add(ydelta), yAxisRange0.max().add(ydelta)); + } + if (round) { + dr = doRound(dr, yAxis); + } + yAxis.setDatumRange(dr); + } + } + + public void mouseDragged(MouseEvent e) { + super.mouseDragged(e); + doPan(e, false); + } + + public void mousePressed(MouseEvent e) { + super.mousePressed(e); + p0 = e.getPoint(); + if (xAxis != null) { + xAxisRange0 = xAxis.getDatumRange(); + xAxisLock = xAxis.mutatorLock(); + xAxisLock.lock(); + } + if (yAxis != null) { + yAxisRange0 = yAxis.getDatumRange(); + yAxisLock = yAxis.mutatorLock(); + yAxisLock.lock(); + } + parent.getCanvas().getGlassPane().setCursor(new Cursor(Cursor.HAND_CURSOR)); + } +} diff --git a/dasCore/src/main/java/org/das2/event/package.html b/dasCore/src/main/java/org/das2/event/package.html new file mode 100644 index 000000000..12b4a49c2 --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/package.html @@ -0,0 +1,18 @@ + +

    Classes for adding interactivity to the application components. +Each DasCanvasComponent has a DasMouseInputAdapter that dispatches mouse +events. MouseModules are plug into a DasMouseInputAdapter and receive the +mouse events and perform a function based on the input. For example, the +CrossHairMouseModule looks up the X and Y coodinates of the pointer during a +mouse drag and displays them in a box. A VerticalSlicerMouseModule looks up +the TableDataSet column under the click and plots it in a popup window. +The MouseModules use DragRenderers to provide visual feedback about the pending +operation. +

    +

    A set of science abstraction level events is defined as well for communication +between application components. These include, for example, TimeRangeSelectionEvent +and BoxSelectionEvent. For example, a MouseModule might create a BoxSelectionEvent, +then send the event off to another module that begins an analysis for the data within +the selected box. +

    + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/event/scratchPad.txt b/dasCore/src/main/java/org/das2/event/scratchPad.txt new file mode 100755 index 000000000..d93f55c3c --- /dev/null +++ b/dasCore/src/main/java/org/das2/event/scratchPad.txt @@ -0,0 +1,18 @@ +Single Key MouseModule Activation: + X zoom X + Y zoom Y + V slice Vertical + H slice Horizontal + D crosshair digitizer + B bookmarks + E edge detector + F frequency measurer + + +Should DragRenderers only be allowed to communicate science events? If so, +then what is the purpose of a MouseModule? + +2007-01-29: eew + Ed suggests the mouseModules have a hook that is called when the MM is selected, so it can pop up a dialog, etc. + + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/fsm/FileStorageModel.java b/dasCore/src/main/java/org/das2/fsm/FileStorageModel.java new file mode 100644 index 000000000..704f5a812 --- /dev/null +++ b/dasCore/src/main/java/org/das2/fsm/FileStorageModel.java @@ -0,0 +1,774 @@ +/* + * FileStorageModel.java + * + * Created on March 31, 2004, 9:52 AM + */ + +package org.das2.fsm; + +import org.das2.datum.CalendarTime; +import org.das2.util.filesystem.FileObject; +import org.das2.util.filesystem.FileSystem; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.TimeUtil; +import org.das2.datum.Units; +import org.das2.util.DasExceptionHandler; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.monitor.NullProgressMonitor; +import org.das2.util.monitor.SubTaskMonitor; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.text.*; +import java.util.*; +import java.util.regex.*; + +/** + * Represents a method for storing data sets in a set of files by time. The + * client provides a regex for the files and how each group of the regex is + * interpreted as a time digit. The model can then be used to provide the set + * of files that cover a time range, etc. + * + * @author Jeremy + */ +public class FileStorageModel { + + private Pattern pattern; + private Pattern absPattern; + private String regex; + + private FieldHandler[] fieldHandlers; + + private CalendarTime.Step timeWidth; /* in TimeUtil enum */ + private int timeWidthMultiplier; /* 7 days */ + private Datum timePhase= null; /* a file boundary */ + + private boolean[] copyToEndTime; /* indexed by TimeUtil enum */ + private boolean useEndTime; + FileStorageModel parent; + FileSystem root; + + public static final int StartYear4=100; + public static final int StartYear2=101; + public static final int StartMonth=102; + public static final int StartMonthName=108; + public static final int StartDay=103; + public static final int StartDoy=104; + public static final int StartHour=105; + public static final int StartMinute=106; + public static final int StartSecond=107; + + public static final int EndYear4=200; + public static final int EndYear2=201; + public static final int EndMonth=202; + public static final int EndMonthName=208; + public static final int EndDay=203; + public static final int EndDoy=204; + public static final int EndHour=205; + public static final int EndMinute=206; + public static final int EndSecond=207; + + public static final int Ignore=300; + + HashMap fileNameMap=null; + + /* need to map back to TimeUtil's enums, note that we have an extra for the 2 digit year */ + private CalendarTime.Step toTimeUtilEnum( int i ) { + if( i<100 || i > 300 ) + throw new IllegalArgumentException( "enumeration is not of the correct type"); + + switch(i % 100){ + case 0: + case 1: return CalendarTime.Step.YEAR; + case 2: return CalendarTime.Step.MONTH; + // Day handled below + case 5: return CalendarTime.Step.HOUR; + case 6: return CalendarTime.Step.MINUTE; + case 7: + case 8: return CalendarTime.Step.SECOND; + } + return CalendarTime.Step.DAY; + } + + //TODO: add + // public string format( DatumRange dr ); + // + public interface FieldHandler { + public void handle( String s, CalendarTime ts1, CalendarTime ts2 ); + public String format( CalendarTime ts1, CalendarTime ts2 ); + } + + public static abstract class IntegerFieldHandler implements FieldHandler { + public void handle( String s, CalendarTime ts1, CalendarTime ts2 ) { + handleInt( Integer.parseInt(s), ts1, ts2 ); + } + abstract void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ); + public abstract String format( CalendarTime ts1, CalendarTime ts2 ); + } + + static final NumberFormat nf4= new DecimalFormat("0000"); + static final NumberFormat nf3= new DecimalFormat("000"); + static final NumberFormat nf2= new DecimalFormat("00"); + + private final static String[] mons= new String [] { + "", "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec" } ; + private static final FieldHandler StartMonthNameHandler= new FieldHandler() { + public void handle( String s, CalendarTime ts1, CalendarTime ts2 ) { + try { + ts1.setMonth(TimeUtil.monthNumber( s )); + } catch ( ParseException e ) { + DasExceptionHandler.handle(e); + } + } + public String format(CalendarTime ts1, CalendarTime ts2) { + return mons[ ts1.month() ]; + } + }; + + private static final FieldHandler EndMonthNameHandler= new FieldHandler() { + public void handle( String s, CalendarTime ts1, CalendarTime ts2 ) { + try { + ts2.setMonth( TimeUtil.monthNumber( s ) ); + } catch ( ParseException e ) { + DasExceptionHandler.handle(e); + } + } + public String format(CalendarTime ts1, CalendarTime ts2) { + return mons[ ts2.month() ]; + } + + }; + + + private static final FieldHandler StartYear4Handler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setYear(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf4.format(ts1.year()); } + }; + + private static final FieldHandler StartYear2Handler= new IntegerFieldHandler() { + @Override + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setYear( i<58 ? i+2000 : i+1900); } + @Override + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.year() % 100 ); } + }; + + private static final FieldHandler StartMonthHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setMonth(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.month() ); } + }; + + private static final FieldHandler StartDayHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setDay(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.day() ); } + }; + private static final FieldHandler StartDoyHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setDayOfYear(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf3.format( ts1.dayOfYear() ); } + }; + private static final FieldHandler StartHourHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setHour(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.hour() ); } + }; + private static final FieldHandler StartMinuteHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setMinute(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.minute() ); } + }; + private static final FieldHandler StartSecondHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setSecond(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.second() ); } + }; + + private static final FieldHandler EndYear4Handler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setYear(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf4.format( ts2.year() ); } + }; + private static final FieldHandler EndYear2Handler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setYear( i<58 ? i+2000 : i+1900); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.year() ); } + }; + + private static final FieldHandler EndMonthHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setMonth(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.month() ); } + }; + private static final FieldHandler EndDayHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setDay(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.day() ); } + }; + private static final FieldHandler EndDoyHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setDayOfYear(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf3.format( ts2.dayOfYear() ); } + }; + private static final FieldHandler EndHourHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setHour(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.hour() ); } + }; + private static final FieldHandler EndMinuteHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setMinute(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.minute() ); } + }; + private static final FieldHandler EndSecondHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setSecond(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.second() ); } + }; + + private static final FieldHandler IgnoreHandler= new FieldHandler() { + public void handle( String s, CalendarTime ts1, CalendarTime ts2 ) { } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return "*"; } + }; + + + private void checkArgs( String regex, int[] digitList ) { + int startLsd=0, endLsd=0; + int[] startDigits= new int[7]; + int[] endDigits= new int[7]; + copyToEndTime= new boolean[8]; /* indexed by TimeUtil enum */ + int startBase=100; + int endBase=200; + int ignoreBase=300; + for ( int i=0; istartLsd ) startLsd= 103; + } else if ( digitList[i]==EndDoy ) { + endDigits[1]=1; endDigits[2]=1; + if ( 203>endLsd ) endLsd= 203; + } else if ( digitList[i]>=startBase && digitList[i]startLsd ) startLsd= digitList[i]; + } else if ( digitList[i]>=endBase && digitList[i]endLsd ) endLsd= digitList[i]; + } + } + if ( startDigits[StartYear2-startBase]==1 ) startDigits[StartYear4-startBase]=1; + if ( startDigits[StartYear4-startBase]==1 ) startDigits[StartYear2-startBase]=1; + + if ( startDigits[StartDoy-startBase]==1 ) { + startDigits[StartMonth-startBase]=1; + startDigits[StartDay-startBase]=1; + } + if ( endDigits[EndYear2-endBase]==1 ) endDigits[EndYear4-endBase]=1; + if ( startDigits[EndYear4-endBase]==1 ) startDigits[EndYear2-endBase]=1; + if ( endDigits[EndDoy-endBase]==1 ) { + endDigits[EndMonth-endBase]=1; + endDigits[EndDay-endBase]=1; + } + for ( int i=0; i0 && startDigits[i]==1 && startDigits[i-1]!=1 ) { + throw new IllegalArgumentException( "more significant digits missing in startTime"); + } + if ( i>0 && startDigits[i]==0 && startDigits[i-1]==1 ) { + timeWidth= toTimeUtilEnum( startLsd ); + timeWidthMultiplier= 1; + } + } + + boolean canUse= true; + for ( int i=startLsd-startBase; i>=0; i-- ) { + if ( endDigits[i]==0 ) canUse=false; + if ( !canUse ) endDigits[i]= 0; + } + + for ( int i=0; i=100 && digitList[i]<200 ) { + fieldHandlerList.add(i,startHandlers[digitList[i]-100]); + } else if (digitList[i]>=200 && digitList[i]<300 ) { + fieldHandlerList.add(i,endHandlers[digitList[i]-200]); + } else if ( digitList[i]==300 ) { + fieldHandlerList.add(i,IgnoreHandler); + } else { + throw new IllegalArgumentException("unknown field handler: "+digitList[i]); + } + } + return (FieldHandler[])fieldHandlerList.toArray( new FieldHandler[fieldHandlerList.size()] ); + } + + + /* + * extract time range for file or directory from its name. + * The least significant time digit is considered to be the implicitTimeWidth, + * and if the width is not stated explicitly, it will be used. When + * a set timeDigits are encountered twice, then the second occurrence + * is considered be the end time. + * + * .../FULL1/T8709_12/T871118.DAT + *'.../FULL1/T'YYMM_MM/TYYMMDD'.DAT' + */ + private DatumRange getDatumRangeFor( String filename ) { + + if ( fieldHandlers.length==0 ) { + // e.g. FULL1 doesn't constrain time + return DatumRange.newDatumRange( -1e30, 1e30, Units.mj1958 ); + } + + CalendarTime ts1= new CalendarTime(new int[]{1,1,1,0,0,0,0}); + + CalendarTime ts2= new CalendarTime(); + if ( File.separatorChar=='\\' ) filename= filename.replaceAll("\\\\", "/"); + + Matcher m= pattern.matcher(filename); + if ( m.matches() ) { + for ( int i=0; i0 ) { + String ff= names[i].equals("") ? files1[0] : names[i]+"/"+files1[0]; + if ( ff.endsWith("/") ) ff=ff.substring(0,ff.length()-1); + result= ff; + } + } + + return result; + } + + public File[] getFilesFor( final DatumRange targetRange ) throws IOException { + return getFilesFor( targetRange, new NullProgressMonitor() ); + } + + public DatumRange getRangeFor( String name ) { + return getDatumRangeFor( name ); + } + + /** + * returns true if the file came (or could come) from this FileStorageModel. + */ + public boolean containsFile( File file ) { + maybeCreateFileNameMap(); + if ( !fileNameMap.containsKey(file) ) { + return false; + } else { + String result= (String)fileNameMap.get(file); + String name= getNameFor( file ); + Matcher m= pattern.matcher( name ); + return m.matches(); + } + } + + /** + * Need a way to recover the model name of a file. The returned File from getFilesFor can be anywhere, + * so it would be good to provide a way to get it back into a FSM name. + */ + public String getNameFor( File file ) { + String result= (String)fileNameMap.get(file); + if ( result==null ) { + throw new IllegalArgumentException( "File didn't come from this FileStorageModel" ); + } else { + return result; + } + } + + private synchronized void maybeCreateFileNameMap() { + if ( fileNameMap==null ) fileNameMap= new HashMap(); + } + + /** + * retrieve the file for the name. + * @throws IOException if the file cannot be transferred. + */ + public File getFileFor( String name, ProgressMonitor monitor ) throws FileNotFoundException, IOException { + FileObject o= root.getFileObject( name ); + File file= o.getFile( monitor ); + + maybeCreateFileNameMap(); + + fileNameMap.put( file, name ); + return file; + + } + + /** + * returns a list of files that can be used + */ + public File[] getFilesFor( final DatumRange targetRange, ProgressMonitor monitor ) throws IOException { + String[] names= getNamesFor( targetRange ); + File[] files= new File[names.length]; + + maybeCreateFileNameMap(); + + if ( names.length>0 ) monitor.setTaskSize( names.length * 10 ); + for ( int i=0; i1 ) { + dirRegex= s[0]; + for ( int i=1; i 300 ) { + throw new IllegalArgumentException( "enumeration is not of the correct type"); + } + i= i % 100; + if ( i==0 ) i=1; + return i; + } + + + /** + * extract time range for file or directory from its name. + * The least significant time digit is considered to be the implicitTimeWidth, + * and if the width is not stated explicitly, it will be used. When + * a set timeDigits are encountered twice, then the second occurrence + * is considered be the end time. + * + * .../FULL1/T8709_12/T871118.DAT + *'.../FULL1/T'YYMM_MM/TYYMMDD'.DAT' + */ + private DatumRange getDatumRangeFor( String filename ) { + try { + if ( pattern.matcher(filename).matches() ) { + timeParser.parse( filename ); + return timeParser.getTimeRange(); + } else { + throw new IllegalArgumentException( "file name ("+filename+") doesn't match model specification ("+regex+")"); + } + } catch ( ParseException e ) { + IllegalArgumentException e2=new IllegalArgumentException( "file name ("+filename+") doesn't match model specification ("+regex+"), parse error in field",e); + throw e2; + } catch ( NumberFormatException e ) { + IllegalArgumentException e2=new IllegalArgumentException( "file name ("+filename+") doesn't match model specification ("+regex+"), parse error in field",e); + throw e2; + } + } + + public String getFilenameFor( Datum start, Datum end ) { + return timeParser.format( start, end ); + } + + public String[] getNamesFor( final DatumRange targetRange ) throws IOException { + return getNamesFor( targetRange, new NullProgressMonitor() ); + } + + public String[] getNamesFor( final DatumRange targetRange, ProgressMonitor monitor ) throws IOException { + + String listRegex; + + FileSystem[] fileSystems; + String[] names; + + if ( parent!=null ) { + names= parent.getNamesFor(targetRange); + fileSystems= new FileSystem[names.length]; + for ( int i=0; i0 ) monitor.setTaskSize( names.length * 10 ); + monitor.started(); + for ( int i=0; i1 ) { + dirRegex= s[0]; + for ( int i=1; i +fsm contains objects that model files that are stored in a FileSystem with the +filename parametric in time. For example, suppose there is a store of data +in http://cdaweb.gsfc.nasa.gov/istp_public/data/ace/swe/, where the data files are +stored by year and then in each yearly folder, the files contain the date encoded +within the filename. We can then model the storage with the template specification +%Y/ac_k0_swe_%Y%m%d_v01.cdf. A FileStorageModel object can be queried as to what +is available for a time range, and will use the FileSystem object to provide +local copies of the file. + +FileStorageModel is the production object providing this functionality. FileStorageModelNew +is an attempt to unify codes by treating filenames as formatted time range strings, which +should reduce code and run more efficiently. This code is not yet proven and should be used +with caution. FileStorageModelAvailabilityDataSetDescriptor provides a DataSetDescriptor +that can be used to get DataSet views describing when data is available. + + diff --git a/dasCore/src/main/java/org/das2/graph/Arrow.java b/dasCore/src/main/java/org/das2/graph/Arrow.java new file mode 100644 index 000000000..3cd8e5351 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/Arrow.java @@ -0,0 +1,90 @@ +/* + * Arrow.java + * + * Created on September 28, 2004, 2:13 PM + */ + +package org.das2.graph; + +import java.awt.*; +import java.awt.geom.*; + +/** + * + * @author Jeremy + */ +public class Arrow extends DasCanvasComponent { + + Point head, tail; + Stroke stroke; + double em=24; //pixels; + + public enum HeadStyle { + DRAFTING, FAT_TRIANGLE, THIN_TRIANGLE, + } + + public Arrow( DasCanvas c, Point head, Point tail ) { + this.head= head; + this.tail= tail; + setRow( new DasRow( c, head.getX(), tail.getX() ) ); + setColumn( new DasColumn( c, head.getY(), tail.getY() ) ); + } + + public void resize() { + Rectangle bounds= new Rectangle(); + bounds.add( head.x - em, head.y-em ); // account for stroke width + bounds.add( tail.x + em, tail.y+em ); + setBounds(bounds); + } + + public static void paintArrow( Graphics2D g, Point head, Point tail, double em, HeadStyle style ) { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); + + Line2D line= new Line2D.Double( head, tail ); + double dx= - ( head.getX() - tail.getX() ); + double dy= - ( head.getY() - tail.getY() ); + double dd= Math.sqrt( dx*dx + dy*dy ); + dx= dx * em / 4 / dd; + dy= dy * em / 4 / dd; + + double hx= head.getX(); + double hy= head.getY(); + + g.setStroke( new BasicStroke( (float)(em/8), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ); + + g.draw( line ); + + GeneralPath p= new GeneralPath(); + p.moveTo( (float)hx, (float)hy ); + + if ( style==HeadStyle.DRAFTING ) { + p.lineTo( (float)(hx+2*dx+0.5*dy), (float)(hy+2*dy-0.5*dx) ); + p.lineTo( (float)(hx+3*dx+dy), (float)(hy+3*dy-dx) ); + p.lineTo( (float)(hx+3*dx-dy), (float)(hy+3*dy+dx) ); + p.lineTo( (float)(hx+2*dx-0.5*dy), (float)(hy+2*dy+0.5*dx) ); + p.lineTo( (float)hx, (float)hy ); + } else if ( style==HeadStyle.FAT_TRIANGLE ) { + p.lineTo( (float)(hx+3*dx+1.5*dy), (float)(hy+3*dy-1.5*dx) ); + p.lineTo( (float)(hx+3*dx-1.5*dy), (float)(hy+3*dy+1.5*dx) ); + p.lineTo( (float)hx, (float)hy ); + } else if ( style==HeadStyle.THIN_TRIANGLE ) { + p.lineTo( (float)(hx+3*dx+dy), (float)(hy+3*dy-dx) ); + p.lineTo( (float)(hx+3*dx-dy), (float)(hy+3*dy+dx) ); + p.lineTo( (float)hx, (float)hy ); + } + + g.fill( p ); + + g.draw( p ); + + } + + protected void paintComponent(Graphics g1) { + Graphics2D g= (Graphics2D) g1.create(); + g.translate(-getX(),-getY()); + paintArrow( g, head, tail, em , HeadStyle.DRAFTING ); + getDasMouseInputAdapter().paint(g1); + } + + +} diff --git a/dasCore/src/main/java/org/das2/graph/AttachedLabel.java b/dasCore/src/main/java/org/das2/graph/AttachedLabel.java new file mode 100644 index 000000000..5c3f403f8 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/AttachedLabel.java @@ -0,0 +1,555 @@ +/* File: DasAxis.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.util.GrannyTextRenderer; +import java.awt.*; + +/** + * A canvas component for labeling things that is positioned just outside of a row,column box. + * + * @author eew + */ +public class AttachedLabel extends DasCanvasComponent implements Cloneable { + + + /* + * PUBLIC CONSTANT DECLARATIONS + */ + + /** This value indicates that the axis should be located at the top of its cell */ + public static final int TOP = 1; + + /** This value indicates that the axis should be located at the bottom of its cell */ + public static final int BOTTOM = 2; + + /** This value indicates that the axis should be located to the left of its cell */ + public static final int LEFT = 3; + + /** This value indicateds that the axis should be located to the right of its cell */ + public static final int RIGHT = 4; + + /** This value indicates that the axis should be oriented horizontally */ + public static final int HORIZONTAL = BOTTOM; + + /** This value indicates that the axis should be oriented vertically */ + public static final int VERTICAL = LEFT; + + /* GENERAL LABEL INSTANCE MEMBERS */ + private int orientation; + protected String axisLabel = ""; + + /* Rectangles representing different areas of the axis */ + private Rectangle blTitleRect; + private Rectangle trTitleRect; + + private double emOffset; + + private boolean flipLabel = false; + + private Font font = null; // Stays null unless setLabelFont is called + + /* DEBUGGING INSTANCE MEMBERS */ + private static final boolean DEBUG_GRAPHICS = false; + private static final Color[] DEBUG_COLORS; + static { + if (DEBUG_GRAPHICS) { + DEBUG_COLORS = new Color[] { + Color.BLACK, Color.RED, Color.GREEN, Color.BLUE, + Color.GRAY, Color.CYAN, Color.MAGENTA, Color.YELLOW, + }; + } else { + DEBUG_COLORS = null; + } + } + private int debugColorIndex = 0; + + /** constructs an AttachedLabel. + * @param label The granny string to be displayed. + * @param orientation identifies the side of the box. See TOP, BOTTOM, LEFT, RIGHT. + * @param emOffset The offset from the edge of the box to the label, in "ems"-- the roughly the width of a letter "M," and + * more precisely the size of the current font. + */ + public AttachedLabel( String label, int orientation, double emOffset) { + super(); + this.orientation = orientation; + this.emOffset = emOffset; + this.axisLabel = label; + setOpaque(false); + } + + /** Sets the side of the row,column box to locate the label. + * @param orientation should be one of AttachedLabel.TOP, AttachedLabel.BOTTOM, AttachedLabel.LEFT, AttachedLabel.RIGHT + */ + public void setOrientation(int orientation) { + boolean oldIsHorizontal = isHorizontal(); + setOrientationInternal(orientation); + } + + /* This is a private internal implementation for + * {@link #setOrientation(int)}. This method is provided + * to avoid calling a non-final non-private instance method + * from a constructor. Doing so can create problems if the + * method is overridden in a subclass. + */ + private void setOrientationInternal(int orientation) { + this.orientation = orientation; + } + + /** Mutator method for the title property of this axis. + * + * The title for this axis is displayed below the ticks for horizontal axes + * or to left of the ticks for vertical axes. + * @param t The new title for this axis + */ + public void setLabel(String t) { + if (t == null) throw new NullPointerException("axis label cannot be null"); + Object oldValue = axisLabel; + axisLabel = t; + update(); + firePropertyChange("label", oldValue, t); + } + + /** + * Accessor method for the title property of this axis. + * + * @return A String instance that contains the title displayed + * for this axis, or null if the axis has no title. + */ + public String getLabel() { + return axisLabel; + } + + /** + * @return the pixel position of the label. + * @deprecated It's not clear how this should be used, and it does not appear to be used within dasCore and dasApps. + */ + public final int getDevicePosition() { + if (orientation == BOTTOM) { + return getRow().getDMaximum(); + } else if (orientation == TOP) { + return getRow().getDMinimum(); + } else if (orientation == LEFT) { + return getColumn().getDMinimum(); + } else { + return getColumn().getDMaximum(); + } + } + + /** + * @return returns the length in pixels of the axis. + */ + public int getDLength() { + if (isHorizontal()) + return getColumn().getWidth(); + else + return getRow().getHeight(); + } + + /** + * paints the axis component. The tickV's and bounds should be calculated at this point + * @param graphics + */ + protected void paintComponent(Graphics graphics) { + + /* This was code was keeping axes from being printed on PC's + Shape saveClip = null; + if (getCanvas().isPrintingThread()) { + saveClip = graphics.getClip(); + graphics.setClip(null); + } + */ + + Graphics2D g = (Graphics2D)graphics.create(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.translate(-getX(), -getY()); + g.setColor(getForeground()); + + /* Debugging code */ + /* The compiler will optimize it out if DEBUG_GRAPHICS == false */ + if (DEBUG_GRAPHICS) { + g.setStroke(new BasicStroke(3f, BasicStroke.CAP_BUTT, BasicStroke.CAP_BUTT, 1f, new float[]{3f, 3f}, 0f)); + g.setColor(Color.BLUE); + g.setColor(Color.LIGHT_GRAY); + if (trTitleRect != null) g.draw(trTitleRect); + g.setStroke(new BasicStroke(1f)); + g.setColor(DEBUG_COLORS[debugColorIndex]); + debugColorIndex++; + if (debugColorIndex >= DEBUG_COLORS.length) { debugColorIndex = 0; }; + } + /* End debugging code */ + + if (isHorizontal()) { + paintHorizontalLabel(g); + } else { + paintVerticalLabel(g); + } + + g.dispose(); + getDasMouseInputAdapter().paint(graphics); + } + + /** Paint the axis if it is horizontal */ + protected void paintHorizontalLabel(Graphics2D g) { + Rectangle clip = g.getClipBounds(); + if (clip == null) { + clip = new Rectangle(getX(), getY(), getWidth(), getHeight()); + } + + boolean bottomLabel = ((orientation == BOTTOM && !axisLabel.equals("")) && blTitleRect != null && blTitleRect.intersects(clip)); + boolean topLabel = ((orientation == TOP && !axisLabel.equals("")) && trTitleRect != null && trTitleRect.intersects(clip)); + + int topPosition = getRow().getDMinimum() - 1; + int bottomPosition = getRow().getDMaximum(); + int DMax= getColumn().getDMaximum(); + int DMin= getColumn().getDMinimum(); + + Font labelFont = getLabelFont(); + + int tickLengthMajor = labelFont.getSize() * 2 / 3; + int tickLengthMinor = tickLengthMajor / 2; + + if (!axisLabel.equals("")) { + Graphics2D g2 = (Graphics2D)g.create(); + int titlePositionOffset = getTitlePositionOffset(); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(g, axisLabel); + int titleWidth = (int)gtr.getWidth(); + int baseline; + int leftEdge; + g2.setFont(getLabelFont()); + if (bottomLabel) { + leftEdge = DMin + (DMax-DMin - titleWidth)/2; + baseline = bottomPosition + titlePositionOffset; + gtr.draw(g2, (float)leftEdge, (float)baseline); + } + if (topLabel) { + leftEdge = DMin + (DMax-DMin - titleWidth)/2; + baseline = topPosition - titlePositionOffset; + gtr.draw(g2, (float)leftEdge, (float)baseline); + } + g2.dispose(); + } + } + + /** Paint the axis if it is vertical */ + protected void paintVerticalLabel(Graphics2D g) { + Rectangle clip = g.getClipBounds(); + if (clip == null) { + clip = new Rectangle(getX(), getY(), getWidth(), getHeight()); + } + + boolean leftLabel = ((orientation == LEFT && !axisLabel.equals("")) && blTitleRect != null && blTitleRect.intersects(clip)); + boolean rightLabel = ((orientation == RIGHT && !axisLabel.equals("")) && trTitleRect != null && trTitleRect.intersects(clip)); + + int leftPosition = getColumn().getDMinimum() - 1; + int rightPosition = getColumn().getDMaximum(); + int DMax= getRow().getDMaximum(); + int DMin= getRow().getDMinimum(); + + Font labelFont = getLabelFont(); + + int tickLengthMajor= labelFont.getSize()*2/3; + int tickLengthMinor = tickLengthMajor / 2; + + if (!axisLabel.equals("")) { + Graphics2D g2 = (Graphics2D)g.create(); + int titlePositionOffset = getTitlePositionOffset(); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(g, axisLabel); + int titleWidth = (int)gtr.getWidth(); + int baseline; + int leftEdge; + g2.setFont(getLabelFont()); + if (leftLabel) { + g2.rotate(-Math.PI/2.0); + leftEdge = -DMax + (DMax-DMin - titleWidth)/2; + baseline = leftPosition - titlePositionOffset; + gtr.draw(g2, (float)leftEdge, (float)baseline); + //leftEdge = DMin + (DMax-DMin - titleWidth)/2; + //baseline = topPosition - titlePositionOffset; + //gtr.draw(g2, (float)leftEdge, (float)baseline); + } + if (rightLabel) { + if (flipLabel) { + g2.rotate(-Math.PI/2.0); + leftEdge = -DMax + (DMax-DMin - titleWidth)/2; + baseline = rightPosition + titlePositionOffset + + (int)Math.ceil(gtr.getHeight()) + - 2*(int)Math.ceil(gtr.getDescent()); + gtr.draw(g2, (float)leftEdge, (float)baseline); + //leftEdge = DMin + (DMax-DMin - titleWidth)/2; + //baseline = bottomPosition + titlePositionOffset; + //gtr.draw(g2, (float)leftEdge, (float)baseline); + } + else { + g2.rotate(Math.PI/2.0); + leftEdge = DMin + (DMax-DMin - titleWidth)/2; + baseline = - rightPosition - titlePositionOffset; + gtr.draw(g2, (float)leftEdge, (float)baseline); + } + } + g2.dispose(); + } + } + + /** calculates the distance from the box to the label. + * @return integer distance in pixels. + */ + protected int getTitlePositionOffset() { + Font labelFont = getLabelFont(); + + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(labelFont, axisLabel); + + int offset = (int)Math.ceil(emOffset * labelFont.getSize()); + + if (orientation == BOTTOM) { + offset += gtr.getAscent(); + } + return offset; + } + + public boolean isLabelFlipped() { + return flipLabel; + } + + public void setLabelFlipped(boolean flipLabel) { + this.flipLabel = flipLabel; + update(); + } + + /** get the current font of the component. + * @return Font of the component. + */ + public Font getLabelFont() { + if(font != null) return font; + return this.getFont(); + } + + /** set the font of the label. + * @param labelFont Font for the component. Currently this is ignored. + */ + public void setLabelFont(Font labelFont) { + font = labelFont; + repaint(); + } + + /** clones the component + * @return clone of this. + */ + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new Error("Assertion failure"); + } + } + + /** + * revalidate component after resize. + */ + public void resize() { + setBounds(getLabelBounds()); + invalidate(); + validate(); + } + + /** get the Rectangle precisely enclosing the label. + * @return Rectangle in canvas space. + */ + protected Rectangle getLabelBounds() { + Rectangle bounds; + if (isHorizontal()) { + bounds = getHorizontalLabelBounds(); + } else { + bounds = getVerticalLabelBounds(); + } + return bounds; + } + + private Rectangle getHorizontalLabelBounds() { + int topPosition = getRow().getDMinimum() - 1; + int bottomPosition = getRow().getDMaximum(); + DasDevicePosition range = getColumn(); + int DMax = range.getDMaximum(); + int DMin = range.getDMinimum(); + + boolean bottomLabel = (orientation == BOTTOM && !axisLabel.equals("")); + boolean topLabel = (orientation == TOP && !axisLabel.equals("")); + + Rectangle bounds; + + //Add room for the axis label + Font labelFont = getLabelFont(); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(labelFont, getLabel()); + int offset = getTitlePositionOffset(); + if (bottomLabel) { + int x = 0; + int y = getRow().getDMaximum() + offset - (int)Math.ceil(gtr.getAscent()); + int width = getCanvas().getWidth(); + int height = (int)Math.ceil(gtr.getHeight()); + blTitleRect = setRectangleBounds(blTitleRect, x, y, width, height); + } + if (topLabel) { + int x = 0; + int y = getRow().getDMinimum() - offset - (int)Math.ceil(gtr.getAscent()); + int width = getCanvas().getWidth(); + int height = (int)Math.ceil(gtr.getHeight()); + trTitleRect = setRectangleBounds(trTitleRect, x, y, width, height); + } + + bounds = new Rectangle((orientation == BOTTOM) ? blTitleRect : trTitleRect); + + return bounds; + } + + private Rectangle getVerticalLabelBounds() { + boolean leftLabel = (orientation == LEFT && !axisLabel.equals("")); + boolean rightLabel = (orientation == RIGHT && !axisLabel.equals("")); + + int leftPosition = getColumn().getDMinimum() - 1; + int rightPosition = getColumn().getDMaximum(); + int DMax= getRow().getDMaximum(); + int DMin= getRow().getDMinimum(); + + Rectangle bounds; + + int offset = getTitlePositionOffset(); + + //Add room for the axis label + Font labelFont = getLabelFont(); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(labelFont, getLabel()); + if (leftLabel) { + int x = getColumn().getDMinimum() - offset - (int)Math.ceil(gtr.getAscent()); + int y = 0; + int width = (int)Math.ceil(gtr.getHeight()); + int height = getCanvas().getHeight(); + blTitleRect = setRectangleBounds(blTitleRect, x, y, width, height); + } + if (rightLabel) { + int x = getColumn().getDMaximum() + offset - (int)Math.ceil(gtr.getDescent()); + int y = 0; + int width = (int)Math.ceil(gtr.getHeight()); + int height = getCanvas().getHeight(); + trTitleRect = setRectangleBounds(trTitleRect, x, y, width, height); + } + + bounds = new Rectangle((orientation == LEFT) ? blTitleRect : trTitleRect); + return bounds; + } + + private static Rectangle setRectangleBounds(Rectangle rc, int x, int y, int width, int height) { + if (rc == null) { + return new Rectangle(x, y, width, height); + } else { + rc.setBounds(x, y, width, height); + return rc; + } + } + + /** return orientation int + * @return AttachedLabel.TOP, etc. + */ + public int getOrientation() { + return orientation; + } + + + /** true if the label is horizontal + * @return true if the label is horizontal + */ + public boolean isHorizontal() { + return orientation == BOTTOM || orientation == TOP; + } + + /** return a string for the int orientation encoding. + * @param i + * @return "top", "right", etc. + */ + protected static String orientationToString(int i) { + switch (i) { + case TOP: + return "top"; + case BOTTOM: + return "bottom"; + case LEFT: + return "left"; + case RIGHT: + return "right"; + default: throw new IllegalStateException("invalid orienation: " + i); + } + } + + /** + * @param orientationString left, right, horizontal, etc. + * @return the int encoding for the orientation parameter, + */ + protected static int parseOrientationString(String orientationString) { + if (orientationString.equals("horizontal")) { + return HORIZONTAL; + } else if (orientationString.equals("vertical")) { + return VERTICAL; + } else if (orientationString.equals("left")) { + return LEFT; + } else if (orientationString.equals("right")) { + return RIGHT; + } else if (orientationString.equals("top")) { + return TOP; + } else if (orientationString.equals("bottom")) { + return BOTTOM; + } else { + throw new IllegalArgumentException("Invalid orientation: " + orientationString); + } + } + + /** + * @return the bounds of the label + */ + public Shape getActiveRegion() { + return getLabelBounds(); + } + + /** + * Getter for property emOffset. + * @return Value of property emOffset. + */ + public double getEmOffset() { + + return this.emOffset; + } + + /** + * Setter for property emOffset. + * @param emOffset New value of property emOffset. + */ + public void setEmOffset(double emOffset) { + + this.emOffset = emOffset; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/Auralizor.java b/dasCore/src/main/java/org/das2/graph/Auralizor.java new file mode 100644 index 000000000..b14705593 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/Auralizor.java @@ -0,0 +1,90 @@ +/* + * Auralizor.java + * + * Created on April 2, 2004, 3:31 PM + */ + +package org.das2.graph; + +import org.das2.dataset.VectorDataSet; +import org.das2.datum.Units; +import org.das2.system.DasLogger; +import javax.sound.sampled.*; + +/** + * + * @author Owner + */ +public class Auralizor { + + private static final int EXTERNAL_BUFFER_SIZE = 1000; + byte[] buffer; + SourceDataLine line = null; + int bufferInputIndex; + double min; + double max; + Units yUnits; + + VectorDataSet ds; + + void setDataSet( VectorDataSet ds ) { + this.ds= ds; + } + + public void playSound() { + float sampleRate= (float) ( 1. / ds.getXTagDatum(1).subtract(ds.getXTagDatum(0)).doubleValue(Units.seconds) ) ; + DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("sampleRate= "+sampleRate); + AudioFormat audioFormat= new AudioFormat( sampleRate, 8, 1, true, false ); + + DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); + try { + line = (SourceDataLine) AudioSystem.getLine(info); + line.open(audioFormat); + line.addLineListener(getLineListener()); + } + catch (Exception e) { + throw new RuntimeException(e); + } + buffer = new byte[EXTERNAL_BUFFER_SIZE]; + bufferInputIndex= 0; + line.start(); + + int i=0; + int ibuf=0; + while ( i xhigh2 ) return; + + GeneralPath gp= new GeneralPath(); + GeneralPath fillPath= new GeneralPath(); + + gp.moveTo( xlow1,y1-hlen ); fillPath.moveTo( xlow1,y1-hlen ); + gp.lineTo( xlow1,y1 ); fillPath.lineTo( xlow1,y1 ); + gp.lineTo( xlow2,y2 ); fillPath.lineTo( xlow2,y2 ); + gp.lineTo( xlow2,y3 ); fillPath.lineTo( xlow2,y3 ); + gp.moveTo( xhigh2, y3 ); + fillPath.lineTo( xhigh2,y3 ); + gp.lineTo( xhigh2,y2 ); fillPath.lineTo( xhigh2,y2 ); + gp.lineTo( xhigh1,y1 ); fillPath.lineTo( xhigh1,y1 ); + gp.lineTo( xhigh1,y1-hlen ); fillPath.lineTo( xhigh1,y1-hlen ); + + if ( fill ) { + g.setColor( fillColor ); + g.fill(fillPath); + } + + g.setColor( getForeground() ); + + g.draw( gp ); + + if ( bottomCurtain && topPlot.getYAxis().getUnits().isConvertableTo( bottomPlot.getYAxis().getUnits() ) ) { + + DatumRange drtop= topPlot.getYAxis().getDatumRange(); + DatumRange yaxisRange= bottomPlot.getYAxis().getDatumRange(); + + drtop= DatumRangeUtil.sloppyIntersection( yaxisRange, drtop ); + + int y5,y6; + + y5= (int)bottomPlot.getYAxis().transform( drtop.max() ); + y6= (int)bottomPlot.getYAxis().transform( drtop.min() ); + + if ( curtainOpacityPercent > 0 ) { + int xLeft= (int)topPlot.getXAxis().getColumn().getDMinimum(); + int xRight= (int)bottomPlot.getXAxis().getColumn().getDMaximum(); + Color canvasColor= getCanvas().getBackground(); + Color curtainColor= new Color( canvasColor.getRed(), canvasColor.getGreen(), canvasColor.getBlue(), + curtainOpacityPercent * 255 / 100 ); + + GeneralPath gpfill= new GeneralPath( DasRow.toRectangle(bottomPlot.getRow(),bottomPlot.getColumn() ) ); + gpfill.append( new Rectangle( xlow2, y5, xhigh2-xlow2, y6-y5 ), false ); + gpfill.setWindingRule( GeneralPath.WIND_EVEN_ODD ); + + g.setColor( curtainColor ); + g.fill( gpfill ); + //g.fillRect( xLeft, y3+1, xlow2-xLeft, y4-y3-1 ); + //g.fillRect( xhigh2+1, y3+1, xRight-xhigh2-1, y4-y3-1 ); + g.setColor( getForeground() ); + } + + if ( yaxisRange.contains(drtop.max()) )g.drawLine( xlow2, y5, xhigh2, y5 ); + if ( yaxisRange.contains(drtop.min()) && drtop.min().gt( yaxisRange.min() ) ) g.drawLine( xlow2, y6, xhigh2, y6 ); + g.drawLine( xlow2, y3, xlow2, y4 ); + g.drawLine( xhigh2, y3, xhigh2, y4 ); + + + } + + g.dispose(); + + getDasMouseInputAdapter().paint(g1); + } + + public void propertyChange(java.beans.PropertyChangeEvent propertyChangeEvent) { + markDirty(); + update(); + } + + /** + * Holds value of property fillColor. + */ + private Color fillColor= new Color( 240,240,240,255 ); + + /** + * Utility field used by bound properties. + */ + private java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); + + /** + * Adds a PropertyChangeListener to the listener list. + * @param l The listener to add. + */ + public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.addPropertyChangeListener(l); + } + + /** + * Removes a PropertyChangeListener from the listener list. + * @param l The listener to remove. + */ + public void removePropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.removePropertyChangeListener(l); + } + + /** + * Getter for property fillColor. + * @return Value of property fillColor. + */ + public Color getFillColor() { + return this.fillColor; + } + + /** + * Setter for property fillColor. + * @param fillColor New value of property fillColor. + */ + public void setFillColor(Color fillColor) { + Color oldFillColor = this.fillColor; + this.fillColor = fillColor; + repaint(); + propertyChangeSupport.firePropertyChange("fillColor", oldFillColor, fillColor); + } + + /** + * Holds value of property fill. + */ + private boolean fill=false; + + /** + * Getter for property fill. + * @return Value of property fill. + */ + public boolean isFill() { + return this.fill; + } + + /** + * Setter for property fill. + * @param fill New value of property fill. + */ + public void setFill(boolean fill) { + boolean oldFill = this.fill; + this.fill = fill; + repaint(); + propertyChangeSupport.firePropertyChange("fill", new Boolean(oldFill), new Boolean(fill)); + } + + /** + * Holds value of property bottomCurtain. + */ + private boolean bottomCurtain; + + /** + * Getter for property bottomCurtain. + * @return Value of property bottomCurtain. + */ + public boolean isBottomCurtain() { + return this.bottomCurtain; + } + + /** + * Setter for property bottomCurtain. + * @param bottomCurtain New value of property bottomCurtain. + */ + public void setBottomCurtain(boolean bottomCurtain) { + boolean oldBottomCurtain = this.bottomCurtain; + this.bottomCurtain = bottomCurtain; + repaint(); + propertyChangeSupport.firePropertyChange("bottomCurtain", new Boolean(oldBottomCurtain), new Boolean(bottomCurtain)); + } + + /** + * Holds value of property curtainOpacityPercent. + */ + private int curtainOpacityPercent= 40; + + /** + * Getter for property curtainOpacityPercent. + * @return Value of property curtainOpacityPercent. + */ + public int getCurtainOpacityPercent() { + return this.curtainOpacityPercent; + } + + /** + * Setter for property curtainOpacityPercent. + * @param curtainOpacityPercent New value of property curtainOpacityPercent. + */ + public void setCurtainOpacityPercent(int curtainOpacityPercent) { + int oldCurtainOpacityPercent = this.curtainOpacityPercent; + this.curtainOpacityPercent = Math.max( 0, Math.min( 100, curtainOpacityPercent ) ); + repaint(); + propertyChangeSupport.firePropertyChange("curtainOpacityPercent", new Integer(oldCurtainOpacityPercent), new Integer(curtainOpacityPercent)); + } + +} + diff --git a/dasCore/src/main/java/org/das2/graph/ContoursRenderer.java b/dasCore/src/main/java/org/das2/graph/ContoursRenderer.java new file mode 100755 index 000000000..bc3a89f49 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/ContoursRenderer.java @@ -0,0 +1,455 @@ +/* + * ContoursRenderer.java + * + * Created on December 7, 2007, 2:47 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ +package org.das2.graph; + +import org.das2.DasException; +import org.das2.components.propertyeditor.Displayable; +import org.das2.dataset.AverageTableRebinner; +import org.das2.dataset.ClippedTableDataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.DatumVector; +import org.das2.datum.Units; +import org.das2.math.Contour; +import java.awt.BasicStroke; +import org.das2.util.monitor.ProgressMonitor; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.ArrayList; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Renderer for making contour plots + * @author jbf + */ +public class ContoursRenderer extends Renderer implements Displayable { + + /** Creates a new instance of ContoursRenderer */ + public ContoursRenderer() { + } + GeneralPath[] paths; + String[] pathLabels; + + public synchronized void render(Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + + Graphics2D g = (Graphics2D) g1; + + if (parent.getCanvas().isAntiAlias()) { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } + + if (paths == null) { + return; + } + g.setColor(color); + g.setStroke( new BasicStroke((float)lineThick) ); + + if (drawLabels) { + Area labelClip = paintLabels(g); + + Shape rclip = g.getClip() == null ? new Rectangle(parent.getX(), parent.getY(), parent.getWidth(), parent.getHeight()) : g.getClip(); + Area clip = new Area(rclip); + clip.subtract(labelClip); + g.setClip(clip); + //g.draw( labelClip ); + + } + + for (int i = 0; i < paths.length; i++) { + if (paths[i] != null) { + g.draw(paths[i]); + } + } + + } + + /** + * returns clip, in the canvas reference frame + */ + private Area paintLabels(final Graphics2D g) { + + Area clip = new Area(); + + Font font0 = g.getFont(); + + // do labels + AffineTransform at0 = g.getTransform(); + + Font font = font0.deriveFont(8f); + + g.setFont(font); + + String[] cons = this.contours.trim().split(","); + + double minLength= 20; + for (int i = 0; i < paths.length; i++) { + if (paths[i] == null) { + continue; + } + String label = pathLabels[i]; + GeneralPath p = paths[i]; + + if (p != null) { + + PathIterator it1 = p.getPathIterator(null); + PathIterator it2 = p.getPathIterator(null); + + while (!it1.isDone()) { + + double len = GraphUtil.pointsAlongCurve(it1, null, null, null, true); + + int nlabel = 1 + (int) Math.floor( len / this.labelCadence ); + + double phase = (len - ( nlabel-1 ) * labelCadence ) / 2; + + if ( len < minLength ) { + //advance it2. + GraphUtil.pointsAlongCurve(it2, null, null, null, true); + + } else { + double[] lens = new double[nlabel*2]; + double labelWidth=10; // approx. + if ( labelWidth > labelCadence ) labelWidth= labelCadence * 0.99; + for (int ilabel = 0; ilabel < nlabel; ilabel++) { + lens[ilabel*2] = phase + labelCadence * ilabel; + lens[ilabel*2+1] = phase + labelCadence * ilabel + labelWidth; + } + Point2D.Double[] points = new Point2D.Double[nlabel*2]; + double[] orient = new double[nlabel*2]; + + //advance it2. + GraphUtil.pointsAlongCurve(it2, lens, points, orient, true); + + for (int ilabel = 0; ilabel < nlabel; ilabel++) { + AffineTransform at = new AffineTransform(); + at.translate(points[ilabel*2].x, points[ilabel*2].y); + //double dx= points[ilabel*2+1].x - points[ilabel*2].x; + //double dy= points[ilabel*2+1].y - points[ilabel*2].y; + //double orient1= Math.atan2(dy,dx); + at.rotate(orient[ilabel*2]); + //at.rotate(orient1); + + + Rectangle2D sbounds = g.getFontMetrics().getStringBounds(label, g); + double w = sbounds.getWidth(); + + GeneralPath rect = new GeneralPath(sbounds); + rect.transform(AffineTransform.getTranslateInstance(-w / 2, 0)); + rect.transform(at); + clip.add(new Area(rect)); + + AffineTransform gat= new AffineTransform(at0); + gat.concatenate(at); + + g.setTransform( gat ); + g.setColor(color); + g.drawString(label, (int) (-w / 2), 0); + } + } + } + } + } + g.setTransform(at0); + + return clip; + } + + protected void installRenderer() { + } + + protected void uninstallRenderer() { + } + + protected Element getDOMElement(Document document) { + return null; + } + + public Icon getListIcon() { + return new ImageIcon(SpectrogramRenderer.class.getResource("/images/icons/contoursRenderer.png")); + } + + public synchronized void updatePlotImage(DasAxis xAxis, DasAxis yAxis, ProgressMonitor monitor) throws DasException { + super.updatePlotImage(xAxis, yAxis, monitor); + + TableDataSet tds = (TableDataSet) getDataSet(); + + if (tds == null) { + return; + } + tds = new ClippedTableDataSet(tds, xAxis.getDatumRange(), yAxis.getDatumRange()); + + Units units = tds.getZUnits(); + + String[] cons = this.contours.trim().split(","); + double[] dcons = new double[cons.length]; + for (int i = 0; i < cons.length; i++) { + if (cons[i].trim().equals("")) { + continue; + } + double c = Double.parseDouble(cons[i]); + dcons[i] = c; + } + DatumVector dv = DatumVector.newDatumVector(dcons, tds.getZUnits()); + + final boolean rebin= false; + if (rebin) { + RebinDescriptor xRebinDescriptor; + xRebinDescriptor = new RebinDescriptor( + xAxis.getDataMinimum(), xAxis.getDataMaximum(), + xAxis.getWidth() / 2, + xAxis.isLog()); + + RebinDescriptor yRebinDescriptor = new RebinDescriptor( + yAxis.getDataMinimum(), yAxis.getDataMaximum(), + yAxis.getHeight() / 2, + yAxis.isLog()); + + if (DataSetUtil.guessXTagWidth(tds).gt(xRebinDescriptor.binWidthDatum())) { + xRebinDescriptor = null; + } + if (TableUtil.guessYTagWidth(tds).gt(yRebinDescriptor.binWidthDatum())) { + yRebinDescriptor = null; + } + AverageTableRebinner rebinner = new AverageTableRebinner(); + rebinner.setInterpolate(false); + + if (xRebinDescriptor != null || yRebinDescriptor != null) { + tds = (TableDataSet) rebinner.rebin(tds, xRebinDescriptor, yRebinDescriptor, null); + } + } + + VectorDataSet vds = Contour.contour(tds, dv); + + paths = new GeneralPath[dv.getLength()]; + + double d0 = units.getFillDouble(); + int ii = -1; + + VectorDataSet xds = (VectorDataSet) vds.getPlanarView(Contour.PLANE_X); + VectorDataSet yds = (VectorDataSet) vds.getPlanarView(Contour.PLANE_Y); + + Units xunits = xAxis.getUnits(); + Units yunits = yAxis.getUnits(); + + ArrayList list = new ArrayList(); + ArrayList labels = new ArrayList(); + + GeneralPath currentPath = null; + + double n0 = 0; // node counter. Breaks are indicated by increment, so keep track of the last node. + + double slen = 0.; // path length + + float fx0 = 0f, fy0 = 0f; // for calculating path length + + NumberFormat nf = new DecimalFormat("0.00"); + + for (int i = 0; i < vds.getXLength(); i++) { + double d = vds.getDouble(i, units); + int n = (int) vds.getXTagDouble(i, Units.dimensionless); + + float fx = (float) xAxis.transform(xds.getDatum(i)); + float fy = (float) yAxis.transform(yds.getDatum(i)); + + if (d != d0) { + ii++; + + if ( currentPath!=null && simplifyPaths ) { + GeneralPath newPath= new GeneralPath(); + newPath= GraphUtil.reducePath( currentPath.getPathIterator(null), newPath ); + list.set( list.indexOf(currentPath), newPath ); + } + + currentPath = new GeneralPath(); + list.add(currentPath); + labels.add(nf.format(d)); + + d0 = d; + currentPath.moveTo(fx, fy); + slen = 0.; + fx0 = fx; + fy0 = fy; + } + if (n != (n0 + 1)) { + currentPath.moveTo(fx, fy); + fx0 = fx; + fy0 = fy; + slen = 0.; + } else { + currentPath.lineTo(fx, fy); + } + n0 = n; + } + + paths = (GeneralPath[]) list.toArray(new GeneralPath[list.size()]); + pathLabels = (String[]) labels.toArray(new String[labels.size()]); + } + /** + * Holds value of property contours. + */ + private String contours = "-.7,-.6,-.5,-.4,-.3,-.2,-.1,0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9"; + + /** + * Getter for property contours. + * @return Value of property contours. + */ + public String getContours() { + return this.contours; + } + + /** + * Setter for property contours. + * @param contours New value of property contours. + */ + public void setContours(String contours) { + String oldContours = this.contours; + this.contours = contours; + update(); + propertyChangeSupport.firePropertyChange("contours", oldContours, contours); + } + /** + * property labelCadence, the inter-label distance, in ems. + */ + private double labelCadence = 100; + + /** + * Getter for property labelCadence. + * @return Value of property labelCadence. + */ + public double getLabelCadence() { + return this.labelCadence; + } + + /** + * Setter for property labelCadence. + * @param labelCadence New value of property labelCadence. + */ + public void setLabelCadence(double labelCadence) { + double oldLabelCadence = this.labelCadence; + this.labelCadence = labelCadence; + update(); + propertyChangeSupport.firePropertyChange("labelCadence", new Double(oldLabelCadence), new Double(labelCadence)); + } + + public boolean acceptContext(int x, int y) { + if (paths == null) { + return false; + } + for (int i = 0; i < paths.length; i++) { + if (paths[i] != null) { + if (paths[i].intersects(x - 2, y - 2, 5, 5)) { + return true; + } + } + } + return false; + } + /** + * Holds value of property drawLabels. + */ + private boolean drawLabels; + + /** + * Getter for property drawLabels. + * @return Value of property drawLabels. + */ + public boolean isDrawLabels() { + return this.drawLabels; + } + + /** + * Setter for property drawLabels. + * @param drawLabels New value of property drawLabels. + */ + public void setDrawLabels(boolean drawLabels) { + boolean oldDrawLabels = this.drawLabels; + this.drawLabels = drawLabels; + update(); + propertyChangeSupport.firePropertyChange("drawLabels", new Boolean(oldDrawLabels), new Boolean(drawLabels)); + } + /** + * Holds value of property color. + */ + private Color color = Color.BLACK; + + /** + * Getter for property color. + * @return Value of property color. + */ + public Color getColor() { + return this.color; + } + + /** + * Setter for property color. + * @param color New value of property color. + */ + public void setColor(Color color) { + Color oldColor = this.color; + this.color = color; + update(); + propertyChangeSupport.firePropertyChange("color", oldColor, color); + } + + public String getListLabel() { + return "Contours Renderer"; + } + + + private boolean simplifyPaths = true; + + public static final String PROP_SIMPLIFYPATHS = "simplifyPaths"; + + public boolean isSimplifyPaths() { + return this.simplifyPaths; + } + + public void setSimplifyPaths(boolean newsimplifyPaths) { + boolean oldsimplifyPaths = simplifyPaths; + this.simplifyPaths = newsimplifyPaths; + update(); + propertyChangeSupport.firePropertyChange(PROP_SIMPLIFYPATHS, oldsimplifyPaths, newsimplifyPaths); + } + + + private double lineThick = 1.0; + + public static final String PROP_LINETHICK = "lineThick"; + + public double getLineThick() { + return this.lineThick; + } + + public void setLineThick(double newlineThick) { + double oldlineThick = lineThick; + this.lineThick = newlineThick; + propertyChangeSupport.firePropertyChange(PROP_LINETHICK, oldlineThick, newlineThick); + } + + +} diff --git a/dasCore/src/main/java/org/das2/graph/CurveRenderer.java b/dasCore/src/main/java/org/das2/graph/CurveRenderer.java new file mode 100644 index 000000000..ccb6bbfa2 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/CurveRenderer.java @@ -0,0 +1,199 @@ +/* File: TickCurveRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on November 3, 2003, 11:43 AM by __FULLNAME__ <__EMAIL__> + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.Units; +import org.das2.DasException; +import org.das2.util.monitor.ProgressMonitor; +import java.awt.*; +import java.awt.geom.*; + +/** + * + * @author jbf + */ +public class CurveRenderer extends Renderer { + + private String xplane; + private String yplane; + + private Units xunits; // xUnits of the axis + private Units yunits; // yUnits of the axis + private double[][] idata; // data transformed to pixel space + + private boolean antiAliased= true; + private SymColor color= SymColor.black; + private PsymConnector psymConnector = PsymConnector.SOLID; + private Psym psym = Psym.NONE; + private double symSize = 1.0; // radius in pixels + private float lineWidth = 1.5f; // width in pixels + + private GeneralPath path; + + /** The dataset descriptor should return a Vector data set with planes identified + * by xplane and yplane. + */ + public CurveRenderer( DataSetDescriptor dsd, String xplane, String yplane ) { + super(dsd); + + setLineWidth( 1.0f ); + + this.xplane= xplane; + this.yplane= yplane; + } + + protected void uninstallRenderer() { + } + + protected void installRenderer() { + } + + public void render(java.awt.Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + long timer0= System.currentTimeMillis(); + + VectorDataSet dataSet= (VectorDataSet)getDataSet(); + + if (dataSet == null || dataSet.getXLength() == 0) { + return; + } + + VectorDataSet xds= (VectorDataSet)dataSet.getPlanarView(xplane); + VectorDataSet yds= (VectorDataSet)dataSet.getPlanarView(yplane); + + Graphics2D graphics= (Graphics2D) g1.create(); + + RenderingHints hints0= graphics.getRenderingHints(); + if ( antiAliased ) { + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } else { + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + } + + graphics.setColor(color.toColor()); + + if (path != null) { + psymConnector.draw(graphics, path, (float)lineWidth); + } + + Dimension d; + + double xmin, xmax, ymin, ymax; + + org.das2.datum.Units xUnits= xAxis.getUnits(); + org.das2.datum.Units yUnits= yAxis.getUnits(); + + Rectangle r= g1.getClipBounds(); + + if ( r==null ) { + xmax= xAxis.getDataMaximum().doubleValue(xUnits); + xmin= xAxis.getDataMinimum().doubleValue(xUnits); + ymax= yAxis.getDataMaximum().doubleValue(yUnits); + ymin= yAxis.getDataMinimum().doubleValue(yUnits); + } else { + xmin= xAxis.invTransform((int)r.getX()).doubleValue(xUnits); + xmax= xAxis.invTransform((int)(r.getX()+r.getWidth())).doubleValue(xUnits); + ymin= yAxis.invTransform((int)r.getY()).doubleValue(yUnits); + ymax= yAxis.invTransform((int)(r.getY()+r.getHeight())).doubleValue(yUnits); + } + + + for (int index = 0; index < xds.getXLength(); index++) { + if ( ! yUnits.isFill(yds.getDouble(index,yUnits)) ) { + double i = xAxis.transform(xds.getDouble(index,xUnits),xUnits); + double j = yAxis.transform(yds.getDouble(index,yUnits),yUnits); + if ( Double.isNaN(j) ) { + //DasApplication.getDefaultApplication().getDebugLogger().warning("got NaN"); + } else { + psym.draw( g1, i, j, (float)symSize ); + } + } + } + + graphics.setRenderingHints(hints0); + } + + public void updatePlotImage(DasAxis xAxis, DasAxis yAxis, ProgressMonitor monitor) throws DasException { + super.updatePlotImage( xAxis, yAxis, monitor ); + + VectorDataSet dataSet= (VectorDataSet)getDataSet(); + + if (dataSet == null || dataSet.getXLength() == 0) { + return; + } + + VectorDataSet xds= (VectorDataSet)dataSet.getPlanarView(xplane); + VectorDataSet yds= (VectorDataSet)dataSet.getPlanarView(yplane); + + path= GraphUtil.getPath( xAxis, yAxis, xds, yds, false ); + } + + /** Getter for property lineWidth. + * @return Value of property lineWidth. + * + */ + public double getLineWidth() { + return this.lineWidth; + } + + /** Setter for property lineWidth. + * @param lineWidth New value of property lineWidth. + * + */ + public void setLineWidth(double lineWidth) { + this.lineWidth = (float)lineWidth; + } + + protected org.w3c.dom.Element getDOMElement(org.w3c.dom.Document document) { + throw new UnsupportedOperationException(); + } + + public PsymConnector getPsymConnector() { + return psymConnector; + } + + public void setPsymConnector(PsymConnector p) { + psymConnector = p; + refreshImage(); + } + + /** Getter for property psym. + * @return Value of property psym. + */ + public Psym getPsym() { + return this.psym; + } + + + /** Setter for property psym. + * @param psym New value of property psym. + */ + public void setPsym(Psym psym) { + if (psym == null) throw new NullPointerException("psym cannot be null"); + Object oldValue = this.psym; + this.psym = psym; + refreshImage(); + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/DasAnnotation.java b/dasCore/src/main/java/org/das2/graph/DasAnnotation.java new file mode 100644 index 000000000..9a87cbf86 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasAnnotation.java @@ -0,0 +1,377 @@ +/* + * DasAnnotation.java + * + * Created on December 20, 2004, 2:32 PM + */ +package org.das2.graph; + +import org.das2.util.GrannyTextRenderer; +import org.das2.datum.Datum; +import org.das2.event.ArrowDragRenderer; +import org.das2.event.MouseModule; +import org.das2.event.MoveComponentMouseModule; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JMenuItem; + +/** + * This component-izes a GrannyTextRenderer, and composes with an Arrow. + * @author Jeremy + */ +public class DasAnnotation extends DasCanvasComponent { + + String templateString; + GrannyTextRenderer gtr; + /** + * point at this thing + */ + private DasAnnotation.PointDescriptor pointAt; + private MouseModule arrowToMouseModule; + + /** Creates a new instance of DasAnnotation */ + public DasAnnotation(String string) { + super(); + this.gtr = new GrannyTextRenderer(); + this.templateString = string; + + Action removeArrowAction = new AbstractAction("remove arrow") { + + public void actionPerformed(ActionEvent e) { + pointAt = null; + repaint(); + } + }; + + this.getDasMouseInputAdapter().addMenuItem(new JMenuItem(removeArrowAction)); + + Action removeMeAction = new AbstractAction("remove") { + + public void actionPerformed(ActionEvent e) { + DasCanvas canvas = getCanvas(); + // TODO: confirm dialog + canvas.remove(DasAnnotation.this); + canvas.revalidate(); + } + }; + + this.getDasMouseInputAdapter().addMenuItem(new JMenuItem(removeMeAction)); + + MouseModule mm = new MoveComponentMouseModule(this); + this.getDasMouseInputAdapter().setPrimaryModule(mm); + + arrowToMouseModule = createArrowToMouseModule(this); + this.getDasMouseInputAdapter().setSecondaryModule(arrowToMouseModule); + } + + public static class DatumPairPointDescriptor implements PointDescriptor { + + DasPlot p; + Datum x; + Datum y; + + public DatumPairPointDescriptor(DasPlot p, Datum x, Datum y) { + this.x = x; + this.y = y; + this.p = p; + } + + public Point getPoint() { + int ix = (int) (p.getXAxis().transform(x)); + int iy = (int) (p.getYAxis().transform(y)); + return new Point(ix, iy); + } + + public String getLabel() { + return "" + x + "," + y; + } + } + + private MouseModule createArrowToMouseModule(final DasAnnotation anno) { + return new MouseModule(DasAnnotation.this, new ArrowDragRenderer(), "Point At") { + + Point head; + Point tail; + + @Override + public void mousePressed(MouseEvent e) { + super.mousePressed(e); + tail= e.getPoint(); + tail.translate(anno.getX(), anno.getY()); + Rectangle r= DasAnnotation.this.getActiveRegion().getBounds(); + if ( !r.contains(tail) ) { + tail= null; + } + } + + @Override + public void mouseReleased(MouseEvent e) { + super.mouseReleased(e); + if ( tail==null ) return; + head = e.getPoint(); + head.translate(anno.getX(), anno.getY()); + DasCanvasComponent c = parent.getCanvas().getCanvasComponentAt(head.x, head.y); + if (c instanceof DasPlot) { + final DasPlot p = (DasPlot) c; + final Datum x = p.getXAxis().invTransform(head.x); + final Datum y = p.getYAxis().invTransform(head.y); + anno.setPointAt(new DatumPairPointDescriptor(p, x, y)); + setBounds(calcBounds()); + } + + } + }; + } + + public void setText(String string) { + this.templateString = string; + if ( this.getGraphics()!=null ) { + gtr.setString( this.getGraphics(), getString() ); + calcBounds(); + } + + repaint(); + + } + + public String getText() { + return templateString; + } + + @Override + public void resize() { + super.resize(); + this.gtr.setString(this.getGraphics(), getString() ); + Rectangle r= calcBounds(); + setBounds(r); + } + + @Override + public Shape getActiveRegion() { + Rectangle r = gtr.getBounds(); + int em = (int) getEmSize() / 2; + r = new Rectangle(r.x, r.y + (int) gtr.getAscent(), r.width + 2 * em + 3, r.height + 2 * em + 3); + r.translate(getColumn().getDMinimum(), getRow().getDMinimum()); + return r; + } + + @Override + public boolean acceptContext(int x, int y) { + if ( getActiveRegion().contains( x, y ) ) { + return true; + } else if ( pointAt!=null ) { + if ( pointAt.getPoint().distance(x,y) < 5 ) { + return true; + } + } + return false; + } + + + private Rectangle calcBounds() { + Rectangle r = (Rectangle)getActiveRegion(); + int em = (int) getEmSize() / 2; + if (pointAt != null) { + Point head = pointAt.getPoint(); + r.add(head); + } + r.x-= em; + r.y-= em; + r.width+= em*2; + r.height+= em*2; + + return r; + } + + @Override + public void paintComponent(Graphics g1) { + + // TODO: need to draw based on row, col, not on bounds which may move with arrow. + + Graphics2D g = (Graphics2D) g1.create(); //SVG bug + + g.translate( getColumn().getDMinimum()-getX(), getRow().getDMinimum()-getY() ); + + Color fore = g.getColor(); + + Color canvasColor = getCanvas().getBackground(); + Color back = new Color(canvasColor.getRed(), canvasColor.getGreen(), canvasColor.getBlue(), + 80 * 255 / 100); + if ( fontSize>0 ) g.setFont( getFont().deriveFont(fontSize) ); + + int em = (int) getEmSize() / 2; + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + gtr.setString( g, getString() ); + Rectangle r = gtr.getBounds(); + + r.x = em; + r.y = em; + + r = new Rectangle(r.x - em + 1, r.y - em + 1, r.width + 2 * em - 1, r.height + 2 * em - 1); + + //r.translate( em, em + (int) gtr.getAscent()); + g.setColor(back); + + if (borderType == BorderType.RECTANGLE || borderType == BorderType.NONE) { + g.fill(r); + } else if (borderType == BorderType.ROUNDED_RECTANGLE) { + g.fillRoundRect(r.x, r.y, r.width, r.height, em * 2, em * 2); + } + + g.setColor(fore); + + gtr.draw(g, em, em + (float) gtr.getAscent()); + + if (pointAt != null) { + double em2 = getCanvas().getFont().getSize(); + g.setStroke(new BasicStroke((float) (em2 / 8))); + //g.drawLine( r.x, r.y+r.height, r.x+r.width, r.y+r.height ); + + Point head = pointAt.getPoint(); + head.translate(-getColumn().getDMinimum(), -getRow().getDMinimum()); + int tx = Math.min(head.x, r.x + r.width * 2 / 3); + tx = Math.max(tx, r.x + r.width * 1 / 3); + Point tail = new Point(tx, r.y + r.height); + Graphics2D g2 = (Graphics2D) g.create(); + g2.setClip(null); + //Arrow.paintArrow(g2, head, tail, em2); + + Point2D tail2d= new Point2D.Double( r.x + r.width/2, r.y + r.y + r.height/2 ); + Point2D head2d= new Point2D.Double( head.x, head.y ); + Rectangle2D rect2d= new Rectangle2D.Double(r.x, r.y, r.width, r.height ); + Point2D p2d= GraphUtil.lineRectangleIntersection( tail2d, head2d, rect2d ); + Point p= p2d==null ? head : new Point( (int)p2d.getX(), (int)p2d.getY() ); + Arrow.paintArrow(g2, head, p, em2, this.arrowStyle ); + + } + + if (borderType != BorderType.NONE) { + if (borderType == BorderType.RECTANGLE) { + g.draw(r); + } else if (borderType == BorderType.ROUNDED_RECTANGLE) { + g.drawRoundRect(r.x, r.y, r.width, r.height, em * 2, em * 2); + } + + } + + /*r= DasColumn.toRectangle( getRow(), getColumn() ); + r.translate( -getX(), -getY() ); + r.width--; + r.height--; + ((Graphics2D)g1).draw( r ); + */ + + g.dispose(); + + getDasMouseInputAdapter().paint(g1); + + } + + public interface PointDescriptor { + + Point getPoint(); + + String getLabel(); + } + + public void setPointAt(PointDescriptor p) { + this.pointAt = p; + repaint(); + } + + private String getString() { + String s = templateString; + if (this.templateString != null && this.templateString.contains("%") && pointAt!=null ) { + s = templateString.replace("%p", pointAt.getLabel() ); + } + return s; + } + + public PointDescriptor getPointAt() { + return this.pointAt; + } + + @Override + protected void installComponent() { + super.installComponent(); + this.gtr.setString( this.getFont(), getString() ); + } + + float fontSize= 0; + + /** + * Getter for property fontSize. + * @return Value of property fontSize. + */ + public float getFontSize() { + return fontSize; + } + + /** + * override the canvas font size. If zero, then use the canvas size, otherwise, + * use this size. + * + * @param fontSize New value of property fontSize. + */ + public void setFontSize(float fontSize) { + this.fontSize= fontSize; + Font f = getFont(); + if (f == null) { + f = getCanvas().getBaseFont(); + } + Font newFont= f; + if ( fontSize>0 ) f= f.deriveFont(fontSize); + Graphics g= this.getGraphics(); + if ( g==null ) return; + g.setFont(newFont); + gtr.setString( g, getString() ); + setBounds(calcBounds()); + repaint(); + + } + + + + public enum BorderType { + NONE, RECTANGLE, ROUNDED_RECTANGLE + } + private BorderType borderType = BorderType.NONE; + public static final String PROP_BORDERTYPE = "borderType"; + + public BorderType getBorderType() { + return this.borderType; + } + + public void setBorderType(BorderType newborderType) { + BorderType oldborderType = borderType; + this.borderType = newborderType; + repaint(); + + firePropertyChange(PROP_BORDERTYPE, oldborderType, newborderType); + } + + + + private Arrow.HeadStyle arrowStyle = Arrow.HeadStyle.DRAFTING; + + public static final String PROP_ARROWSTYLE = "arrowStyle"; + + public Arrow.HeadStyle getArrowStyle() { + return this.arrowStyle; + } + + public void setArrowStyle( Arrow.HeadStyle newarrowStyle) { + Arrow.HeadStyle oldarrowStyle = arrowStyle; + this.arrowStyle = newarrowStyle; + repaint(); + firePropertyChange(PROP_ARROWSTYLE, oldarrowStyle, newarrowStyle); + } + + +} diff --git a/dasCore/src/main/java/org/das2/graph/DasAnnotationBeanInfo.java b/dasCore/src/main/java/org/das2/graph/DasAnnotationBeanInfo.java new file mode 100644 index 000000000..4a1200659 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasAnnotationBeanInfo.java @@ -0,0 +1,82 @@ +/* File: DasColorBarBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.beans.DasCanvasComponentBeanInfo; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; +import org.das2.components.propertyeditor.EnumerationEditor; + +/** + * BeanInfo class for DasColorBar + * + * @author Edward West + */ +public class DasAnnotationBeanInfo extends SimpleBeanInfo { + + private static PropertyDescriptor[] properties; + static { + try { + properties = new PropertyDescriptor[] { + new PropertyDescriptor("text", DasAnnotation.class), + new PropertyDescriptor("borderType", DasAnnotation.class), + new PropertyDescriptor("arrowStyle", DasAnnotation.class ), + new PropertyDescriptor("fontSize", DasAnnotation.class ), + }; + properties[1].setPropertyEditorClass( EnumerationEditor.class ); + properties[2].setPropertyEditorClass( EnumerationEditor.class ); + System.err.println("yeah!!!"); + } catch ( IntrospectionException e) { + e.printStackTrace(); + properties= null; + } + + } + + + @Override + public PropertyDescriptor[] getPropertyDescriptors() { + return properties; + } + + @Override + public BeanInfo[] getAdditionalBeanInfo() { + BeanInfo[] additional = { + new DasCanvasComponentBeanInfo(), + }; + return additional; + + /*try { + BeanInfo[] additional = { + Introspector.getBeanInfo( DasAxis.class ), + }; + return additional; + } catch ( IntrospectionException e ) { + throw new RuntimeException(e); + }*/ + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/DasAxis.java b/dasCore/src/main/java/org/das2/graph/DasAxis.java new file mode 100755 index 000000000..33906eb5e --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasAxis.java @@ -0,0 +1,3399 @@ +/* File: DasAxis.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.graph; + +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.*; +import java.util.*; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.*; + +import javax.swing.*; +import javax.swing.border.*; +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.DasNameException; +import org.das2.DasProperties; +import org.das2.DasPropertyException; +import org.das2.NameContext; +import org.das2.dasml.FormBase; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.DataSetUpdateEvent; +import org.das2.dataset.DataSetUpdateListener; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumRangeUtil; +import org.das2.datum.DatumVector; +import org.das2.datum.InconvertibleUnitsException; +import org.das2.datum.TimeLocationUnits; +import org.das2.datum.TimeUtil; +import org.das2.datum.Units; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.DatumFormatterFactory; +import org.das2.datum.format.DefaultDatumFormatterFactory; +import org.das2.datum.format.TimeDatumFormatterFactory; +import org.das2.event.DataRangeSelectionEvent; +import org.das2.event.DataRangeSelectionListener; +import org.das2.event.HorizontalRangeSelectorMouseModule; +import org.das2.event.MouseModule; +import org.das2.event.TimeRangeSelectionEvent; +import org.das2.event.TimeRangeSelectionListener; +import org.das2.event.VerticalRangeSelectorMouseModule; +import org.das2.event.ZoomPanMouseModule; +import org.das2.system.DasLogger; +import org.das2.util.DasExceptionHandler; +import org.das2.util.DasMath; +import org.das2.util.Entities; +import org.das2.util.GrannyTextRenderer; +import org.das2.util.monitor.NullProgressMonitor; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * One dimensional axis component that transforms data to device space and back, + * and provides controls for nagivating the 1-D data space. + * @author eew + */ +public class DasAxis extends DasCanvasComponent + implements DataRangeSelectionListener, TimeRangeSelectionListener, Cloneable { + + public static final String PROP_LABEL = "label"; + public static final String PROP_Y_LABEL = "yLabel"; + public static final String PROP_LOG = "log"; + public static final String PROP_OPPOSITE_AXIS_VISIBLE = "oppositeAxisVisible"; + public static final String PROP_BOUNDS = "bounds"; + + /* + * PUBLIC CONSTANT DECLARATIONS + */ + /** This value indicates that the axis should be located at the top of its cell */ + public static final int TOP = 1; + /** This value indicates that the axis should be located at the bottom of its cell */ + public static final int BOTTOM = 2; + /** This value indicates that the axis should be located to the left of its cell */ + public static final int LEFT = 3; + /** This value indicateds that the axis should be located to the right of its cell */ + public static final int RIGHT = 4; + /** This value indicates that the axis should be oriented horizontally */ + public static final int HORIZONTAL = BOTTOM; + /** This value indicates that the axis should be oriented vertically */ + public static final int VERTICAL = LEFT; + /** */ + public static final int UP = 995; + /** */ + public static final int DOWN = 996; + /* Constants defining the action commands and labels for the scan buttons. */ + private static final String SCAN_PREVIOUS_LABEL = "<< scan"; + private static final String SCAN_NEXT_LABEL = "scan >>"; + + public static String PROP_UNITS= "units"; + + /* GENERAL AXIS INSTANCE MEMBERS */ + protected DataRange dataRange; + public static String PROPERTY_TICKS = "ticks"; + + public DatumFormatter getUserDatumFormatter() { + return userDatumFormatter; + } + + public void setUserDatumFormatter(DatumFormatter userDatumFormatter) { + this.userDatumFormatter = userDatumFormatter; + updateTickV(); + repaint(); + } + /** + * if non-null, try to use this formatter. + */ + private DatumFormatter userDatumFormatter = null; + + /** + * until we switch to java 1.5, use this lock object instead of + * java.util.concurrent.lock + */ + public interface Lock { + + public void lock(); + + public void unlock(); + } + /* Affine Transform, dependent on min, max and axis position + * pixel= at_m * data + at_b + * where data is data point in linear space (i.e. log property implemented) + */ + double at_m; + double at_b; + private int orientation; + private int tickDirection = 1; // 1=down or left, -1=up or right + protected String axisLabel = ""; + protected TickVDescriptor tickV; + protected boolean autoTickV = true; + private boolean ticksVisible = true; + private boolean tickLabelsVisible = true; + private boolean oppositeAxisVisible = false; + protected DatumFormatter datumFormatter = DefaultDatumFormatterFactory.getInstance().defaultFormatter(); + private MouseModule zoom = null; + private PropertyChangeListener dataRangePropertyListener; + protected JPanel primaryInputPanel; + protected JPanel secondaryInputPanel; + private ScanButton scanPrevious; + private ScanButton scanNext; + private boolean animated = ("on".equals(DasProperties.getInstance().get("visualCues"))); + /* Rectangles representing different areas of the axis */ + private Rectangle blLineRect; + private Rectangle trLineRect; + private Rectangle blTickRect; + private Rectangle trTickRect; + private Rectangle blLabelRect; + private Rectangle trLabelRect; + private Rectangle blTitleRect; + private Rectangle trTitleRect; + /** TODO: Currently under implemented! */ + private boolean flipped; + /* TIME LOCATION UNITS RELATED INSTANCE MEMBERS */ + private javax.swing.event.EventListenerList timeRangeListenerList = null; + private TimeRangeSelectionEvent lastProcessedEvent = null; + /* TCA RELATED INSTANCE MEMBERS */ + private DataSetDescriptor dsd; + private VectorDataSet[] tcaData = new VectorDataSet[0]; + private DatumFormatter[] tcaFormatters = new DatumFormatter[0]; + private String dataset = ""; + private boolean drawTca; + private static DatumFormatterFactory formatterFactory + = DefaultDatumFormatterFactory.getInstance(); + private static DatumFormatterFactory timeFormatterFactory + = TimeDatumFormatterFactory.getInstance(); + + public static String PROPERTY_DATUMRANGE = "datumRange"; + + /* DEBUGGING INSTANCE MEMBERS */ + private static boolean DEBUG_GRAPHICS = false; + private static final Color[] DEBUG_COLORS; + + static { + if (DEBUG_GRAPHICS) { + DEBUG_COLORS = new Color[]{ + Color.BLACK, Color.RED, Color.GREEN, Color.BLUE, + Color.GRAY, Color.CYAN, Color.MAGENTA, Color.YELLOW, + }; + } else { + DEBUG_COLORS = null; + } + } + private int debugColorIndex = 0; + private DasPlot dasPlot; + private JMenu favoritesMenu; + private JMenu backMenu; + private static final Logger logger = DasLogger.getLogger(DasLogger.GRAPHICS_LOG); + + /** TODO + * @param min + * @param max + * @param orientation DasAxis.VERTICAL, DasAxis.HORIZONTAL, DasAxis.RIGHT, etc. + */ + public DasAxis(Datum min, Datum max, int orientation) { + this(min, max, orientation, false); + } + + /** TODO Tell us if these are screen or data coordinates + * @param min + * @param max + * @param orientation + * @param log + */ + public DasAxis(Datum min, Datum max, int orientation, boolean log) { + this(orientation); + dataRange = new DataRange(this, min, max, log); + addListenersToDataRange(dataRange, dataRangePropertyListener); + copyFavorites(); + copyHistory(); + } + + /** TODO + * @param range + * @param orientation + */ + protected DasAxis(DataRange range, int orientation) { + this(orientation); + dataRange = range; + addListenersToDataRange(range, dataRangePropertyListener); + copyFavorites(); + copyHistory(); + } + + private void addListenersToDataRange(DataRange range, PropertyChangeListener listener) { + range.addPropertyChangeListener(PROP_LOG, listener); + range.addPropertyChangeListener("minimum", listener); + range.addPropertyChangeListener("maximum", listener); + range.addPropertyChangeListener(DataRange.PROPERTY_DATUMRANGE, listener); + range.addPropertyChangeListener("history", listener); + range.addPropertyChangeListener("favorites", listener); + } + + public DasAxis(DatumRange range, int orientation) { + this(range.min(), range.max(), orientation); + } + + private DasAxis(int orientation) { + super(); + setOpaque(false); + setOrientationInternal(orientation); + installMouseModules(); + if (!DasApplication.getDefaultApplication().isHeadless()) { + backMenu = new JMenu("Back"); + mouseAdapter.addMenuItem(backMenu); + favoritesMenu = new JMenu("Favorites"); + mouseAdapter.addMenuItem(favoritesMenu); + } + dataRangePropertyListener = createDataRangePropertyListener(); + setLayout(new AxisLayoutManager()); + maybeInitializeInputPanels(); + maybeInitializeScanButtons(); + add(primaryInputPanel); + add(secondaryInputPanel); + } + + public void addToFavorites(final DatumRange range) { + dataRange.addToFavorites(range); + copyFavorites(); + } + + private void copyFavorites() { + if (DasApplication.getDefaultApplication().isHeadless()) { + return; + } + favoritesMenu.removeAll(); + List favorites = dataRange.getFavorites(); + for (Iterator i = favorites.iterator(); i.hasNext();) { + final DatumRange r = (DatumRange) i.next(); // copied code from addToFavorites + Action action = new AbstractAction(r.toString()) { + + public void actionPerformed(ActionEvent e) { + DasAxis.this.setDatumRange(r); + } + }; + JMenuItem menuItem = new JMenuItem(action); + favoritesMenu.add(menuItem); + } + Action action = new AbstractAction("add to favorites") { + + public void actionPerformed(ActionEvent e) { + DasAxis.this.addToFavorites(DasAxis.this.getDatumRange()); + } + }; + JMenuItem addItem = new JMenuItem(action); + favoritesMenu.add(addItem); + } + + private void copyHistory() { + if (DasApplication.getDefaultApplication().isHeadless()) { + return; + } + backMenu.removeAll(); + List history = dataRange.getHistory(); + int ii = 0; + for (Iterator i = history.iterator(); i.hasNext();) { + final int ipop = ii; + final DatumRange r = (DatumRange) i.next(); // copied code from addToFavorites + Action action = new AbstractAction(r.toString()) { + + public void actionPerformed(ActionEvent e) { + dataRange.popHistory(ipop); + DasAxis.this.setDataRangePrev(); + } + }; + JMenuItem menuItem = new JMenuItem(action); + backMenu.add(menuItem); + ii++; + } + } + + /* PRIVATE INITIALIZATION FUNCTIONS */ + private void maybeInitializeInputPanels() { + if (primaryInputPanel == null) { + primaryInputPanel = new JPanel(); + primaryInputPanel.setOpaque(false); + } + if (secondaryInputPanel == null) { + secondaryInputPanel = new JPanel(); + secondaryInputPanel.setOpaque(false); + } + } + + private void maybeInitializeScanButtons() { + if (!DasApplication.getDefaultApplication().isHeadless()) { + scanPrevious = new DasAxis.ScanButton(SCAN_PREVIOUS_LABEL); + scanNext = new DasAxis.ScanButton(SCAN_NEXT_LABEL); + ActionListener al = createScanActionListener(); + scanPrevious.addActionListener(al); + scanNext.addActionListener(al); + add(scanPrevious); + add(scanNext); + } + } + + private ActionListener createScanActionListener() { + return new ActionListener() { + + public void actionPerformed(ActionEvent e) { + String command = e.getActionCommand(); + DasLogger.getLogger(DasLogger.GUI_LOG).fine("event " + command); + if (command.equals(SCAN_PREVIOUS_LABEL)) { + scanPrevious(); + } else if (command.equals(SCAN_NEXT_LABEL)) { + scanNext(); + } + } + }; + } + + private PropertyChangeListener createDataRangePropertyListener() { + return new PropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent e) { + String propertyName = e.getPropertyName(); + Object oldValue = e.getOldValue(); + Object newValue = e.getNewValue(); + if (propertyName.equals(PROP_LOG)) { + update(); + firePropertyChange(PROP_LOG, oldValue, newValue); + } else if (propertyName.equals("minimum")) { + update(); + firePropertyChange("dataMinimum", oldValue, newValue); + } else if (propertyName.equals("maximum")) { + update(); + firePropertyChange("dataMaximum", oldValue, newValue); + } else if (propertyName.equals("favorites")) { + copyFavorites(); + } else if (propertyName.equals(DataRange.PROPERTY_DATUMRANGE)) { + update(); + firePropertyChange(PROPERTY_DATUMRANGE, oldValue, newValue); + } else if (propertyName.equals("history")) { + if (!dataRange.valueIsAdjusting()) { + copyHistory(); + } + } + markDirty(); + } + }; + } + + private void installMouseModules() { + if (zoom instanceof HorizontalRangeSelectorMouseModule) { + ((HorizontalRangeSelectorMouseModule) zoom).removeDataRangeSelectionListener(this); + mouseAdapter.removeMouseModule(zoom); + } else if (zoom instanceof VerticalRangeSelectorMouseModule) { + ((VerticalRangeSelectorMouseModule) zoom).removeDataRangeSelectionListener(this); + mouseAdapter.removeMouseModule(zoom); + } + if (isHorizontal()) { + zoom = new HorizontalRangeSelectorMouseModule(this, this); + ((HorizontalRangeSelectorMouseModule) zoom).addDataRangeSelectionListener(this); + mouseAdapter.addMouseModule(zoom); + mouseAdapter.setPrimaryModule(zoom); + + MouseModule zoomPan = new ZoomPanMouseModule(this, this, null); + mouseAdapter.addMouseModule(zoomPan); + mouseAdapter.setSecondaryModule(zoomPan); + } else { + zoom = new VerticalRangeSelectorMouseModule(this, this); + ((VerticalRangeSelectorMouseModule) zoom).addDataRangeSelectionListener(this); + mouseAdapter.addMouseModule(zoom); + mouseAdapter.setPrimaryModule(zoom); + + MouseModule zoomPan = new ZoomPanMouseModule(this, null, this); + mouseAdapter.addMouseModule(zoomPan); + mouseAdapter.setSecondaryModule(zoomPan); + } + } + + /** TODO + * @param orientation + */ + public void setOrientation(int orientation) { + boolean oldIsHorizontal = isHorizontal(); + setOrientationInternal(orientation); + if (oldIsHorizontal != isHorizontal()) { + installMouseModules(); + } + } + + /* This is a private internal implementation for + * {@link #setOrientation(int)}. This method is provided + * to avoid calling a non-final non-private instance method + * from a constructor. Doing so can create problems if the + * method is overridden in a subclass. + */ + private void setOrientationInternal(int orientation) { + this.orientation = orientation; + if (orientation == TOP) { + setTickDirection(UP); + } else if (orientation == BOTTOM) { + setTickDirection(DOWN); + } else if (orientation == LEFT) { + setTickDirection(RIGHT); + } else if (orientation == RIGHT) { + setTickDirection(LEFT); + } else { + throw new IllegalArgumentException("Invalid value for orientation"); + } + } + + public void setDatumRange(DatumRange dr) { + if ( getUnits().isConvertableTo(dr.getUnits()) ) { + this.setDataRange(dr.min(), dr.max()); + } else { + Units oldUnits= getUnits(); + this.resetRange(dr); + firePropertyChange( PROP_UNITS, oldUnits, dr.getUnits() ); + } + + } + + public DatumRange getDatumRange() { + return dataRange.getDatumRange(); + } + + /* + * @returns true is the range is acceptible, false otherwise. This method + * is overriden by DasLabelAxis. + */ + protected boolean rangeIsAcceptable(DatumRange dr) { + return dr.min().lt(dr.max()); + } + + /** TODO + * @param minimum + * @param maximum + */ + public void setDataRange(Datum minimum, Datum maximum) { + + Units units = dataRange.getUnits(); + if (minimum.getUnits() != units) { + minimum = minimum.convertTo(units); + maximum = maximum.convertTo(units); + } + + DatumRange newRange = new DatumRange(minimum, maximum); + logger.fine("enter dasAxis.setDataRange( " + newRange + " )"); + + if (!rangeIsAcceptable(newRange)) { + logger.warning("invalid range ignored"); + return; + } + + double min, max, min0, max0; + + min0 = dataRange.getMinimum(); + max0 = dataRange.getMaximum(); + + if (dataRange.isLog()) { + min = DasMath.log10(minimum.doubleValue(getUnits())); + max = DasMath.log10(maximum.doubleValue(getUnits())); + } else { + min = minimum.doubleValue(getUnits()); + max = maximum.doubleValue(getUnits()); + } + + if (!valueIsAdjusting()) { + animateChange(min0, max0, min, max); + } + DatumRange oldRange = dataRange.getDatumRange(); + dataRange.setRange(newRange); + + update(); + createAndFireRangeSelectionEvent(); + firePropertyChange(PROPERTY_DATUMRANGE, oldRange, newRange); + } + + public void clearHistory() { + dataRange.clearHistory(); + } + + private void createAndFireRangeSelectionEvent() { + if (getUnits() instanceof TimeLocationUnits) { + logger.fine("firing rangeSelectionEvent"); + TimeRangeSelectionEvent e = new TimeRangeSelectionEvent(this, new DatumRange(this.getDataMinimum(), this.getDataMaximum())); + fireTimeRangeSelectionListenerTimeRangeSelected(e); + } + } + + /** TODO */ + public void setDataRangePrev() { + logger.fine("enter dasAxis.setDataRangePrev()"); + DatumRange oldRange = dataRange.getDatumRange(); + double min0 = dataRange.getMinimum(); + double max0 = dataRange.getMaximum(); + dataRange.setRangePrev(); + DatumRange newRange = dataRange.getDatumRange(); + double min1 = dataRange.getMinimum(); + double max1 = dataRange.getMaximum(); + animateChange(min0, max0, min1, max1); + update(); + createAndFireRangeSelectionEvent(); + firePropertyChange(PROPERTY_DATUMRANGE, oldRange, newRange); + } + + /** TODO */ + public void setDataRangeForward() { + logger.fine("enter dasAxis.setDataRangeForward()"); + double min0 = dataRange.getMinimum(); + double max0 = dataRange.getMaximum(); + DatumRange oldRange = dataRange.getDatumRange(); + dataRange.setRangeForward(); + DatumRange newRange = dataRange.getDatumRange(); + double min1 = dataRange.getMinimum(); + double max1 = dataRange.getMaximum(); + animateChange(min0, max0, min1, max1); + update(); + createAndFireRangeSelectionEvent(); + firePropertyChange(PROPERTY_DATUMRANGE, oldRange, newRange); + } + + /** TODO */ + public void setDataRangeZoomOut() { + logger.fine("enter dasAxis.setDataRangeZoomOut()"); + double t1 = dataRange.getMinimum(); + double t2 = dataRange.getMaximum(); + double delta = t2 - t1; + double min = t1 - delta; + double max = t2 + delta; + animateChange(t1, t2, min, max); + DatumRange oldRange = dataRange.getDatumRange(); + dataRange.setRange(min, max); + DatumRange newRange = dataRange.getDatumRange(); + update(); + createAndFireRangeSelectionEvent(); + firePropertyChange(PROPERTY_DATUMRANGE, oldRange, newRange); + } + + /** TODO + * @return + */ + public DataRange getDataRange() { + return this.dataRange; + } + + /** TODO */ + protected void deviceRangeChanged() { + } + + /** TODO + * @return + */ + public Datum getDataMinimum() { + return dataRange.getDatumRange().min(); + } + + /** TODO + * @return + */ + public Datum getDataMaximum() { + return dataRange.getDatumRange().max(); + } + + /* + * + */ + /** + * This is the preferred method for getting the range of the axis. + * @return a DatumRange indicating the range of the axis. + * @deprecated use getDatumRange instead. + */ + public DatumRange getRange() { + return dataRange.getDatumRange(); + } + + /** TODO + * @param units + * @return + */ + public double getDataMaximum(Units units) { + return getDataMaximum().doubleValue(units); + } + + /** TODO + * @param units + * @return + */ + public double getDataMinimum(Units units) { + return getDataMinimum().doubleValue(units); + } + + /** TODO + * @param max + */ + public void setDataMaximum(Datum max) { + dataRange.setMaximum(max); + update(); + } + + /** TODO + * @param min + */ + public void setDataMinimum(Datum min) { + dataRange.setMinimum(min); + update(); + } + + /** TODO + * @return + */ + public boolean isLog() { + return dataRange.isLog(); + } + + /** + * Set the axis to be log or linear. If necessary, axis range will be + * adjusted to make the range valid. + * @param log + */ + public void setLog(boolean log) { + boolean oldLog = isLog(); + DatumRange range= getDatumRange(); + dataRange.setLog(log); + update(); + if (log != oldLog) { + firePropertyChange(PROP_LOG, oldLog, log); + } + // switching log can change the axis range. + if ( ! range.equals(getDatumRange()) ) { + firePropertyChange( PROPERTY_DATUMRANGE, range, getDatumRange() ); + } + } + + /** TODO + * @return + */ + public Units getUnits() { + return dataRange.getUnits(); + } + + public void setUnits(Units newUnits) { + dataRange.setUnits(newUnits); + } + + /** + * changes the units of the axis to a new unit. + */ + public synchronized void resetRange(DatumRange range) { + if (range.getUnits() != this.getUnits()) { + if (dasPlot != null) { + dasPlot.invalidateCacheImage(); + } + logger.finest("replaceRange(" + range + ")"); + dataRange.resetRange(range); + } else { + dataRange.setRange(range); + } + updateTickV(); + markDirty(); + update(); + } + + /** TODO + * @param visible + */ + public void setOppositeAxisVisible(boolean visible) { + if (visible == oppositeAxisVisible) { + return; + } + boolean oldValue = oppositeAxisVisible; + oppositeAxisVisible = visible; + revalidate(); + repaint(); + firePropertyChange(PROP_OPPOSITE_AXIS_VISIBLE, oldValue, visible); + } + + /** TODO + * @return + */ + public boolean isOppositeAxisVisible() { + return oppositeAxisVisible; + } + + /** Mutator method for the title property of this axis. + * + * The title for this axis is displayed below the ticks for horizontal axes + * or to left of the ticks for vertical axes. + * @param t The new title for this axis + */ + public void setLabel(String t) { + if (t == null) { + throw new NullPointerException("axis label cannot be null"); + } + Object oldValue = axisLabel; + axisLabel = t; + update(); + firePropertyChange(PROP_LABEL, oldValue, t); + } + + /** + * Accessor method for the title property of this axis. + * + * @return A String instance that contains the title displayed + * for this axis, or null if the axis has no title. + */ + public String getLabel() { + return axisLabel; + } + + /** Getter for property animated. + * @return Value of property animated. + */ + public boolean isAnimated() { + return this.animated; + } + + /** Setter for property animated. + * @param animated new value of property animated. + */ + public void setAnimated(boolean animated) { + this.animated = animated; + } + + public boolean getDrawTca() { + return drawTca; + } + + public void setDrawTca(boolean b) { + boolean oldValue = drawTca; + if (b && getOrientation() != BOTTOM) { + throw new IllegalArgumentException("Vertical time axes cannot have annotations"); + } + if (drawTca == b) { + return; + } + drawTca = b; + markDirty(); + update(); + firePropertyChange("showTca", oldValue, b); + } + + public String getDataPath() { + return dataset; + } + + /** + * + * @param dataset The URL identifier string of a TCA data set, or "" for no TCAs. + */ + public void setDataPath(String dataset) { + if (dataset == null) { + throw new NullPointerException("null dataPath string not allowed"); + } + Object oldValue = this.dataset; + if (dataset.equals(this.dataset)) { + return; + } + this.dataset = dataset; + if (dataset.equals("")) { + dsd = null; + } else { + try { + dsd = DataSetDescriptor.create(dataset); + } catch ( org.das2.DasException de) { + DasExceptionHandler.handle(de); + } + } + markDirty(); + update(); + firePropertyChange("dataPath", oldValue, dataset); + } + + /** Add auxiliary data to an axis (usually OA data for a time axis). + * This function does the same thing as setDataPath, but with a different interface. + * dsdAux must return a non-empty string from getDataSetID() + * @param dsdAux will be called upon to generate auxiliary data sets. To avoid nonsensical + * graphs the X axis for this dataset must be the same as the that handed to the + * renderer. + */ + public void setDataSetDescriptor(DataSetDescriptor dsdAux) { + if (dsdAux == null) { + throw new NullPointerException("null DataSetDescriptor not allowed"); + } + DataSetDescriptor oldVal = dsd; + dsd = dsdAux; + markDirty(); + update(); + + String oldDataset = dataset; + dataset = dsd.getDataSetID(); + firePropertyChange("dataSetDescriptor", oldVal, dsd); + firePropertyChange("dataPath", oldDataset, dataset); + } + + public DataSetDescriptor getDataSetDescriptor() { + return dsd; + } + + public void setTcaData(VectorDataSet ds) { + logger.fine("got TCADataSet"); + List itemList = (List) ds.getProperty("plane-list"); + VectorDataSet[] newData = new VectorDataSet[itemList.size()]; + DatumFormatter[] newFormatters = new DatumFormatter[itemList.size()]; + newData[0] = ds; + for (int i = 1; i < itemList.size(); i++) { + newData[i] = (VectorDataSet) ds.getPlanarView((String) itemList.get(i)); + } + for (int i = 0; i < newData.length; i++) { + String format = (String)newData[i].getProperty("format"); + newFormatters[i] = getFormatter(format, newData[i].getYUnits()); + } + tcaData = newData; + tcaFormatters = newFormatters; + update(); + } + + private DatumFormatter getFormatter(String format, Units u) { + if (format == null) return null; + try { + if (u.isConvertableTo(Units.t2000)) { + return timeFormatterFactory.newFormatter(format); + } + return formatterFactory.newFormatter(format); + } + catch (ParseException ex) { + return null; + } + catch (IllegalArgumentException ex) { + return null; + } + } + + private final DataSetUpdateListener tcaListener = new DataSetUpdateListener() { + + @Override + public void dataSetUpdated(DataSetUpdateEvent e) { + VectorDataSet ds = (VectorDataSet) e.getDataSet(); + if (ds == null) { + logger.warning(String.valueOf(e.getException())); + return; + } + setTcaData(ds); + } + }; + + private void updateTCADataSet() { + logger.fine("updateTCADataSet"); + double[] tickV = getTickV().tickV.toDoubleArray(getUnits()); + Datum data_minimum; + Datum data_maximum; + Datum iinterval; + if (tickV.length == 1) { + data_minimum = Datum.create(tickV[0], getTickV().units); + data_maximum = Datum.create(tickV[0], getTickV().units); + iinterval = data_maximum.subtract(data_minimum); + } else { + data_minimum = Datum.create(tickV[0], getTickV().units); + data_maximum = Datum.create(tickV[tickV.length - 1], getTickV().units); + iinterval = (data_maximum.subtract(data_minimum)).divide(tickV.length - 1); + } + data_maximum = data_maximum.add(iinterval); + final Datum interval = iinterval; + tcaData = null; + tcaFormatters = null; + + this.dsd.requestDataSet(data_minimum, data_maximum.add(Datum.create(1.0, Units.seconds)), interval, new NullProgressMonitor(), getCanvas(), tcaListener); + + } + + /** TODO + * @return + */ + public final int getDevicePosition() { + if (orientation == BOTTOM) { + return getRow().getDMaximum(); + } else if (orientation == TOP) { + return getRow().getDMinimum(); + } else if (orientation == LEFT) { + return getColumn().getDMinimum(); + } else { + return getColumn().getDMaximum(); + } + } + + /** + * @return returns the length in pixels of the axis. + */ + public int getDLength() { + if (isHorizontal()) { + return getColumn().getWidth(); + } else { + return getRow().getHeight(); + } + } + + /** TODO + * @return + */ + public DasAxis getMasterAxis() { + return dataRange.getCreator(); + } + + /** TODO + * @param axis + */ + public void attachTo(DasAxis axis) { + DataRange oldRange = dataRange; + dataRange = axis.dataRange; + oldRange.removePropertyChangeListener(PROP_LOG, dataRangePropertyListener); + oldRange.removePropertyChangeListener("minimum", dataRangePropertyListener); + oldRange.removePropertyChangeListener("maximum", dataRangePropertyListener); + oldRange.removePropertyChangeListener(DataRange.PROPERTY_DATUMRANGE, dataRangePropertyListener); + dataRange.addPropertyChangeListener(PROP_LOG, dataRangePropertyListener); + dataRange.addPropertyChangeListener("minimum", dataRangePropertyListener); + dataRange.addPropertyChangeListener("maximum", dataRangePropertyListener); + dataRange.addPropertyChangeListener(DataRange.PROPERTY_DATUMRANGE, dataRangePropertyListener); + if (oldRange.isLog() != dataRange.isLog()) { + firePropertyChange(PROP_LOG, oldRange.isLog(), dataRange.isLog()); + } + firePropertyChange("minimum", oldRange.getMinimum(), dataRange.getMinimum()); + firePropertyChange("maximum", oldRange.getMaximum(), dataRange.getMaximum()); + copyFavorites(); + copyHistory(); + } + + /** TODO */ + public void detach() { + dataRange.removePropertyChangeListener(PROP_LOG, dataRangePropertyListener); + dataRange.removePropertyChangeListener("minimum", dataRangePropertyListener); + dataRange.removePropertyChangeListener("maximum", dataRangePropertyListener); + dataRange.removePropertyChangeListener(DataRange.PROPERTY_DATUMRANGE, dataRangePropertyListener); + DataRange newRange = new DataRange(this, Datum.create(dataRange.getMinimum(), dataRange.getUnits()), + Datum.create(dataRange.getMaximum(), dataRange.getUnits()), + dataRange.isLog()); + dataRange = newRange; + dataRange.addPropertyChangeListener(PROP_LOG, dataRangePropertyListener); + dataRange.addPropertyChangeListener("minimum", dataRangePropertyListener); + dataRange.addPropertyChangeListener("maximum", dataRangePropertyListener); + dataRange.addPropertyChangeListener(DataRange.PROPERTY_DATUMRANGE, dataRangePropertyListener); + copyFavorites(); + copyHistory(); + } + + /** TODO + * @return + */ + public boolean isAttached() { + return this != getMasterAxis(); + } + + /** TODO + * @return + */ + public TickVDescriptor getTickV() { + if (tickV == null) { + updateTickV(); + } + return tickV; + } + + /** + * Sets the TickVDescriptor for this axis. If null is passed in, the + * axis will put into autoTickV mode, where the axis will attempt to + * determine ticks using an appropriate algortithm. + * + * @param tickV the new ticks for this axis, or null + */ + public void setTickV(TickVDescriptor tickV) { + checkTickV(tickV); + this.tickV = tickV; + if (tickV == null) { + autoTickV = true; + updateTickV(); + } else { + autoTickV = false; + } + update(); + } + + /** TODO: implement this + */ + private void checkTickV(TickVDescriptor tickV) throws IllegalArgumentException { + } + + private void updateTickVLog() { + + double min = getDataMinimum().doubleValue(getUnits()); + double max = getDataMaximum().doubleValue(getUnits()); + + double dMinTick = DasMath.roundNFractionalDigits(DasMath.log10(min), 4); + int minTick = (int) Math.ceil(dMinTick); + double dMaxTick = DasMath.roundNFractionalDigits(DasMath.log10(max), 4); + int maxTick = (int) Math.floor(dMaxTick); + + GrannyTextRenderer idlt = new GrannyTextRenderer(); + idlt.setString(this.getTickLabelFont(), "10!U-10"); + + int nTicksMax; + if (isHorizontal()) { + nTicksMax = (int) Math.floor(getColumn().getWidth() / (idlt.getWidth())); + } else { + nTicksMax = (int) Math.floor(getRow().getHeight() / (idlt.getHeight())); + } + + nTicksMax = (nTicksMax < 7) ? nTicksMax : 7; + + tickV = TickVDescriptor.bestTickVLogNew(getDataMinimum(), getDataMaximum(), 3, nTicksMax, true); + datumFormatter = resolveFormatter(tickV); + + return; + + } + + private void updateTickVLinear() { + int nTicksMax; + int axisSize; + if (isHorizontal()) { + int tickSizePixels = (int) (getFontMetrics(getTickLabelFont()).stringWidth("0.0000") * 1.5); + axisSize = getColumn().getWidth(); + nTicksMax = axisSize / tickSizePixels; + } else { + int tickSizePixels = getFontMetrics(getTickLabelFont()).getHeight() + 6; + axisSize = getRow().getHeight(); + nTicksMax = axisSize / tickSizePixels; + } + + nTicksMax = (nTicksMax < 7) ? nTicksMax : 7; + + this.tickV = TickVDescriptor.bestTickVLinear(getDataMinimum(), getDataMaximum(), 3, nTicksMax, false); + + DatumFormatter tdf = resolveFormatter(tickV); + + boolean once = true; + + while (once) { + + Rectangle maxBounds = getMaxBounds(tdf, tickV); + + if (isHorizontal()) { + int tickSizePixels = (int) (maxBounds.width + getEmSize() * 2); + nTicksMax = axisSize / tickSizePixels; + } else { + int tickSizePixels = (int) (maxBounds.height); + nTicksMax = axisSize / tickSizePixels; + } + + this.tickV = TickVDescriptor.bestTickVLinear(getDataMinimum(), getDataMaximum(), 3, nTicksMax, true); + datumFormatter = resolveFormatter(tickV); + + once = false; + } + + return; + + } + + private DatumFormatter resolveFormatter(TickVDescriptor tickV) { + return getUserDatumFormatter() == null ? tickV.getFormatter() : getUserDatumFormatter(); + } + + private Rectangle getMaxBounds(DatumFormatter tdf, TickVDescriptor tickV) { + String[] granny = tdf.axisFormat(tickV.tickV, getDatumRange()); + GrannyTextRenderer idlt = new GrannyTextRenderer(); + Rectangle bounds = new Rectangle(); + for (int i = 0; i < granny.length; i++) { + idlt.setString(this.getTickLabelFont(), granny[i]); + bounds.add(idlt.getBounds()); + } + return bounds; + } + + private void updateTickVTime() { + + int nTicksMax; + + DatumRange dr = getDatumRange(); + Datum pixel = dr.width().divide(getDLength()); + DatumFormatter tdf; + + if (isHorizontal()) { + // two passes to avoid clashes -- not guarenteed + tickV = TickVDescriptor.bestTickVTime(dr.min().subtract(pixel), dr.max().add(pixel), 3, 8, false); + + tdf = resolveFormatter(tickV); + Rectangle bounds = getMaxBounds(tdf, tickV); + + int tickSizePixels = (int) (bounds.width + getEmSize() * 2); + + if (drawTca) { + FontMetrics fm = getFontMetrics(getTickLabelFont()); + String item = format(99999.99, "(f8.2)"); + int width = fm.stringWidth(item) + (int) (getEmSize() * 2); + if (width > tickSizePixels) { + tickSizePixels = width; + } + } + + int axisSize = getColumn().getWidth(); + nTicksMax = Math.max(2, axisSize / tickSizePixels); + + tickV = TickVDescriptor.bestTickVTime(getDataMinimum(), getDataMaximum(), 2, nTicksMax, false); + tdf = resolveFormatter(tickV); + + bounds = getMaxBounds(tdf, tickV); + tickSizePixels = (int) (bounds.getWidth() + getEmSize() * 2); + + if (drawTca) { + FontMetrics fm = getFontMetrics(getTickLabelFont()); + String item = format(99999.99, "(f8.2)"); + int width = fm.stringWidth(item); + if (width > tickSizePixels) { + tickSizePixels = width; + } + } + //nTicksMax = (int) Math.floor( 0.2 + 1.* axisSize / tickSizePixels ); + + nTicksMax = (nTicksMax > 1 ? nTicksMax : 2); + nTicksMax = (nTicksMax < 10 ? nTicksMax : 10); + + boolean overlap = true; + while (overlap && nTicksMax > 2) { + + tickV = TickVDescriptor.bestTickVTime(getDataMinimum(), getDataMaximum(), 2, nTicksMax, false); + + if (tickV.getMajorTicks().getLength() <= 1) { + // we're about to have an assertion error, time to debug; + System.err.println("about to assert error: " + tickV.getMajorTicks()); + } + assert (tickV.getMajorTicks().getLength() > 1); + + tdf = resolveFormatter(tickV); + + bounds = getMaxBounds(tdf, tickV); + tickSizePixels = (int) (bounds.getWidth() + getEmSize() * 2); + + double x0 = transform(tickV.getMajorTicks().get(0)); + double x1 = transform(tickV.getMajorTicks().get(1)); + + if (x1 - x0 > tickSizePixels) { + overlap = false; + } else { + nTicksMax = nTicksMax - 1; + } + } + tickV = TickVDescriptor.bestTickVTime(getDataMinimum(), getDataMaximum(), 2, nTicksMax, true); + + } else { + int tickSizePixels = getFontMetrics(getTickLabelFont()).getHeight(); + int axisSize = getRow().getHeight(); + nTicksMax = axisSize / tickSizePixels; + + nTicksMax = (nTicksMax > 1 ? nTicksMax : 2); + nTicksMax = (nTicksMax < 10 ? nTicksMax : 10); + + tickV = TickVDescriptor.bestTickVTime(getDataMinimum(), getDataMaximum(), 3, nTicksMax, true); + + } + + datumFormatter = resolveFormatter(tickV); + + if (drawTca && !dataset.equals("") && dsd != null) { + updateTCADataSet(); + } + } + + public synchronized void updateTickV() { + if ( !valueIsAdjusting() ) { + if (autoTickV) { + TickVDescriptor oldTicks = this.tickV; + if (getUnits() instanceof TimeLocationUnits) { + updateTickVTime(); + } else if (dataRange.isLog()) { + updateTickVLog(); + } else { + updateTickVLinear(); + } + firePropertyChange(PROPERTY_TICKS, oldTicks, this.tickV); + } + } + + } + private String errorMessage; + + /** + * checks the validity of the state, setting variable errorMessage to non-null if there is a problem. + */ + private void checkState() { + double dmin = getDataMinimum(dataRange.getUnits()); + double dmax = getDataMaximum(dataRange.getUnits()); + + String em = ""; + + if (Double.isNaN(dmin)) { + em += "dmin is NaN, "; + } + if (Double.isNaN(dmax)) { + em += "dmax is NaN, "; + } + if (Double.isInfinite(dmin)) { + em += "dmin is infinite, "; + } + if (Double.isInfinite(dmax)) { + em += "dmax is infinite, "; + } + if (dmin >= dmax) { + em += "min => max, "; + } + if (dataRange.isLog() && dmin <= 0) { + em += "min<= 0 and log, "; + } + + if (em.length() == 0) { + this.errorMessage = null; + } else { + this.errorMessage = em; + } + } + + /** paints the axis component. The tickV's and bounds should be calculated at this point */ + protected void paintComponent(Graphics graphics) { + logger.fine("enter DasAxis.paintComponent"); + /* This was code was keeping axes from being printed on PC's + Shape saveClip = null; + if (getCanvas().isPrintingThread()) { + saveClip = graphics.getClip(); + graphics.setClip(null); + } + */ + logger.fine("DasAxis clip=" + graphics.getClip() + " @ "+getX()+","+getY() ); + + Graphics2D g = (Graphics2D) graphics.create(); + //g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); + //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + //g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + g.translate(-getX(), -getY()); + g.setColor(getForeground()); + + /* Debugging code */ + /* The compiler will optimize it out if DEBUG_GRAPHICS == false */ + if (DEBUG_GRAPHICS) { + g.setStroke(new BasicStroke(3f, BasicStroke.CAP_BUTT, BasicStroke.CAP_BUTT, 1f, new float[]{3f, 3f}, 0f)); + g.setColor(Color.BLUE); + if (blLabelRect != null) { + g.draw(blLabelRect); + } + g.setColor(Color.RED); + if (blLineRect != null) { + g.draw(blLineRect); + } + g.setColor(Color.GREEN); + if (blTickRect != null) { + g.draw(blTickRect); + } + g.setColor(Color.LIGHT_GRAY); + if (blTitleRect != null) { + g.draw(blTitleRect); + } + g.setColor(Color.BLUE); + if (trLabelRect != null) { + g.draw(trLabelRect); + } + g.setColor(Color.RED); + if (trLineRect != null) { + g.draw(trLineRect); + } + g.setColor(Color.GREEN); + if (trTickRect != null) { + g.draw(trTickRect); + } + g.setColor(Color.LIGHT_GRAY); + if (trTitleRect != null) { + g.draw(trTitleRect); + } + g.setStroke(new BasicStroke(1f)); + g.setColor(DEBUG_COLORS[debugColorIndex]); + debugColorIndex++; + if (debugColorIndex >= DEBUG_COLORS.length) { + debugColorIndex = 0; + } + } + /* End debugging code */ + + if (tickV == null || tickV.tickV.getUnits().isConvertableTo(getUnits())) { + if (isHorizontal()) { + paintHorizontalAxis(g); + } else { + paintVerticalAxis(g); + } + } else { + //System.err.println("inconvertable units"); + } + + Rectangle clip = g.getClipBounds(); + if (clip == null) { + clip = new Rectangle(getX(), getY(), getWidth(), getHeight()); + } + + if (drawTca && getOrientation() == BOTTOM && tcaData != null && blLabelRect != null && blLabelRect.intersects(clip)) { + + int position = getRow().getDMaximum(); + int DMin = getColumn().getDMinimum(); + Font tickLabelFont = getTickLabelFont(); + FontMetrics tickLabelFontMetrics = getFontMetrics(tickLabelFont); + int tickLength = tickLabelFont.getSize() * 2 / 3; + int tickLabelGap = tickLabelFontMetrics.stringWidth(" "); + int lineHeight = tickLabelFont.getSize() + getLineSpacing(); + + int baseLine = position + tickLength + tickLabelGap + tickLabelFont.getSize(); + int rightEdge = DMin - tickLabelFontMetrics.stringWidth("000") - tickLabelGap; // Note assumes top label is date but lower labels are shorter. + + GrannyTextRenderer idlt = new GrannyTextRenderer(); + /* + idlt.setString(this.getGraphics(), "SCET"); + int width = (int)Math.ceil(idlt.getWidth()); + int leftEdge = rightEdge - width; + idlt.draw(g, (float)leftEdge, (float)baseLine); + */ + + + int width, leftEdge; + + for (int i = 0; i < tcaData.length; i++) { + String label = (String) tcaData[i].getProperty(DataSet.PROPERTY_Y_LABEL); + if (label == null) { + label = (String) tcaData[i].getProperty("label"); + } + if (label == null) { + label = ""; + } + baseLine += lineHeight; + idlt.setString(g, label); + width = (int) Math.floor(idlt.getWidth() + 0.5); + leftEdge = rightEdge - width; + idlt.draw(g, (float) leftEdge, (float) baseLine); + } + } + + g.dispose(); + getDasMouseInputAdapter().paint(graphics); + + /* This was code was keeping axes from being printed on PC's + if (getCanvas().isPrintingThread()) { + g.setClip(saveClip); + } + */ + } + + /** Paint the axis if it is horizontal */ + protected void paintHorizontalAxis(Graphics2D g) { + try { + Rectangle clip = g.getClipBounds(); + if (clip == null) { + clip = new Rectangle(getX(), getY(), getWidth(), getHeight()); + } + + boolean bottomLine = ((orientation == BOTTOM || oppositeAxisVisible) && blLineRect != null && blLineRect.intersects(clip)); + boolean bottomTicks = ((orientation == BOTTOM || oppositeAxisVisible) && blTickRect != null && blTickRect.intersects(clip)); + boolean bottomTickLabels = ((orientation == BOTTOM && tickLabelsVisible) && blLabelRect != null && blLabelRect.intersects(clip)); + boolean bottomLabel = ((orientation == BOTTOM && !axisLabel.equals("")) && blTitleRect != null && blTitleRect.intersects(clip)); + boolean topLine = ((orientation == TOP || oppositeAxisVisible) && trLineRect != null && trLineRect.intersects(clip)); + boolean topTicks = ((orientation == TOP || oppositeAxisVisible) && trTickRect != null && trTickRect.intersects(clip)); + boolean topTickLabels = ((orientation == TOP && tickLabelsVisible) && trLabelRect != null && trLabelRect.intersects(clip)); + boolean topLabel = ((orientation == TOP && !axisLabel.equals("")) && trTitleRect != null && trTitleRect.intersects(clip)); + + int topPosition = getRow().getDMinimum() - 1; + int bottomPosition = getRow().getDMaximum(); + int DMax = getColumn().getDMaximum(); + int DMin = getColumn().getDMinimum(); + + Font labelFont = getTickLabelFont(); + + TickVDescriptor ticks = getTickV(); + + if (bottomLine) { + g.drawLine(DMin, bottomPosition, DMax, bottomPosition); + } + if (topLine) { + g.drawLine(DMin, topPosition, DMax, topPosition); + } + + int tickLengthMajor = labelFont.getSize() * 2 / 3; + int tickLengthMinor = tickLengthMajor / 2; + int tickLength; + + String[] labels = tickFormatter(ticks.tickV, getDatumRange()); + + for (int i = 0; i < ticks.tickV.getLength(); i++) { + Datum tick1 = ticks.tickV.get(i); + int tickPosition = (int) Math.floor(transform(tick1)); + if (DMin <= tickPosition && tickPosition <= DMax) { + tickLength = tickLengthMajor; + if (bottomTicks) { + g.drawLine(tickPosition, bottomPosition, tickPosition, bottomPosition + tickLength); + } + if (bottomTickLabels) { + drawLabel(g, tick1, labels[i], i, tickPosition, bottomPosition + tickLength); + } + if (topTicks) { + g.drawLine(tickPosition, topPosition, tickPosition, topPosition - tickLength); + } + if (topTickLabels) { + drawLabel(g, tick1, labels[i], i, tickPosition, topPosition - tickLength + 1); + } + } + } + + for (int i = 0; i < ticks.minorTickV.getLength(); i++) { + Datum tick = ticks.minorTickV.get(i); + int tickPosition = (int) Math.floor(transform(tick)); + if (DMin <= tickPosition && tickPosition <= DMax) { + tickLength = tickLengthMinor; + if (bottomTicks) { + g.drawLine(tickPosition, bottomPosition, tickPosition, bottomPosition + tickLength); + } + if (topTicks) { + g.drawLine(tickPosition, topPosition, tickPosition, topPosition - tickLength); + } + } + } + + if (!axisLabel.equals("")) { + Graphics2D g2 = (Graphics2D) g.create(); + int titlePositionOffset = getTitlePositionOffset(); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(g2, axisLabel); + int titleWidth = (int) gtr.getWidth(); + int baseline; + int leftEdge; + g2.setFont(getLabelFont()); + if (bottomLabel) { + leftEdge = DMin + (DMax - DMin - titleWidth) / 2; + baseline = bottomPosition + titlePositionOffset; + gtr.draw(g2, (float) leftEdge, (float) baseline); + } + if (topLabel) { + leftEdge = DMin + (DMax - DMin - titleWidth) / 2; + baseline = topPosition - titlePositionOffset; + gtr.draw(g2, (float) leftEdge, (float) baseline); + } + g2.dispose(); + } + } catch (InconvertibleUnitsException ex) { + logger.log(Level.WARNING, ex.getMessage(), ex); + } + } + + /** Paint the axis if it is vertical */ + protected void paintVerticalAxis(Graphics2D g) { + try { + Rectangle clip = g.getClipBounds(); + if (clip == null) { + clip = new Rectangle(getX(), getY(), getWidth(), getHeight()); + } + + boolean leftLine = ((orientation == LEFT || oppositeAxisVisible) && blLineRect != null && blLineRect.intersects(clip)); + boolean leftTicks = ((orientation == LEFT || oppositeAxisVisible) && blTickRect != null && blTickRect.intersects(clip)); + boolean leftTickLabels = ((orientation == LEFT && tickLabelsVisible) && blLabelRect != null && blLabelRect.intersects(clip)); + boolean leftLabel = ((orientation == LEFT && !axisLabel.equals("")) && blTitleRect != null && blTitleRect.intersects(clip)); + boolean rightLine = ((orientation == RIGHT || oppositeAxisVisible) && trLineRect != null && trLineRect.intersects(clip)); + boolean rightTicks = ((orientation == RIGHT || oppositeAxisVisible) && trTickRect != null && trTickRect.intersects(clip)); + boolean rightTickLabels = ((orientation == RIGHT && tickLabelsVisible) && trLabelRect != null && trLabelRect.intersects(clip)); + boolean rightLabel = ((orientation == RIGHT && !axisLabel.equals("")) && trTitleRect != null && trTitleRect.intersects(clip)); + + int leftPosition = getColumn().getDMinimum() - 1; + int rightPosition = getColumn().getDMaximum(); + int DMax = getRow().getDMaximum(); + int DMin = getRow().getDMinimum(); + + Font labelFont = getTickLabelFont(); + + + TickVDescriptor ticks = getTickV(); + + if (leftLine) { + g.drawLine(leftPosition, DMin, leftPosition, DMax); + } + if (rightLine) { + g.drawLine(rightPosition, DMin, rightPosition, DMax); + } + + int tickLengthMajor = labelFont.getSize() * 2 / 3; + int tickLengthMinor = tickLengthMajor / 2; + int tickLength; + + String[] labels = tickFormatter(ticks.tickV, getDatumRange()); + for (int i = 0; i < ticks.tickV.getLength(); i++) { + Datum tick1 = ticks.tickV.get(i); + int tickPosition = (int) Math.floor(transform(tick1)); + if (DMin <= tickPosition && tickPosition <= DMax) { + + tickLength = tickLengthMajor; + if (leftTicks) { + g.drawLine(leftPosition, tickPosition, leftPosition - tickLength, tickPosition); + } + if (leftTickLabels) { + drawLabel(g, tick1, labels[i], i, leftPosition - tickLength, tickPosition); + } + if (rightTicks) { + g.drawLine(rightPosition, tickPosition, rightPosition + tickLength, tickPosition); + } + if (rightTickLabels) { + drawLabel(g, tick1, labels[i], i, rightPosition + tickLength, tickPosition); + } + } + } + + for (int i = 0; i < ticks.minorTickV.getLength(); i++) { + tickLength = tickLengthMinor; + double tick1 = ticks.minorTickV.doubleValue(i, getUnits()); + int tickPosition = (int) Math.floor(transform(tick1, ticks.units)); + if (DMin <= tickPosition && tickPosition <= DMax) { + tickLength = tickLengthMinor; + if (leftTicks) { + g.drawLine(leftPosition, tickPosition, leftPosition - tickLength, tickPosition); + } + if (rightTicks) { + g.drawLine(rightPosition, tickPosition, rightPosition + tickLength, tickPosition); + } + } + } + + + if (!axisLabel.equals("")) { + Graphics2D g2 = (Graphics2D) g.create(); + int titlePositionOffset = getTitlePositionOffset(); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(g2, axisLabel); + int titleWidth = (int) gtr.getWidth(); + int baseline; + int leftEdge; + g2.setFont(getLabelFont()); + if (leftLabel) { + g2.rotate(-Math.PI / 2.0); + leftEdge = -DMax + (DMax - DMin - titleWidth) / 2; + baseline = leftPosition - titlePositionOffset; + gtr.draw(g2, (float) leftEdge, (float) baseline); + } + if (rightLabel) { + if ( flipLabel ) { + g2.rotate( -Math.PI / 2.0); + leftEdge = DMin + (DMax - DMin + titleWidth) / 2; + baseline = rightPosition + titlePositionOffset; + gtr.draw(g2, (float) -leftEdge, (float) baseline); + g2.getClipBounds(); + } else { + g2.rotate( Math.PI / 2.0 ); + leftEdge = DMin + (DMax - DMin - titleWidth) / 2; + baseline = -rightPosition - titlePositionOffset; + gtr.draw(g2, (float) leftEdge, (float) baseline); + } + } + g2.dispose(); + } + } catch (InconvertibleUnitsException e) { + // do nothing + } + } + + /** TODO + * @return + */ + protected int getTitlePositionOffset() { + Font tickLabelFont = getTickLabelFont(); + FontMetrics fm = getFontMetrics(tickLabelFont); + Font labelFont = getLabelFont(); + int tickLength = tickLabelFont.getSize() * 2 / 3; + + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(labelFont, axisLabel); + + int offset; + + if (orientation == BOTTOM) { + offset = tickLabelFont.getSize() + tickLength + fm.stringWidth(" ") + labelFont.getSize() + labelFont.getSize() / 2; + } else if (orientation == TOP) { + offset = tickLength + fm.stringWidth(" ") + labelFont.getSize() + labelFont.getSize() / 2 + (int) gtr.getDescent(); + } else if (orientation == LEFT) { + //offset = tickLength + (int)this.blLabelRect.getWidth() + fm.stringWidth(" ") + labelFont.getSize() / 2 + (int) gtr.getDescent(); + offset= getColumn().getDMinimum() - blLabelRect.x + labelFont.getSize() / 2 + (int) gtr.getDescent(); + } else { + offset= this.trLabelRect.x + this.trLabelRect.width - getColumn().getDMaximum() + labelFont.getSize() / 2 + + (int) ( flipLabel ? gtr.getAscent() : gtr.getDescent() ); + /*if ( !flipLabel ) { + offset = tickLength + (int)this.trLabelRect.getWidth() + fm.stringWidth(" ") + labelFont.getSize() / 2 + (int) gtr.getDescent(); + } else { + offset = tickLength + (int)this.trLabelRect.getWidth() + fm.stringWidth(" ") + labelFont.getSize() / 2 + (int) gtr.getAscent(); + offset= this.trLabelRect.x + this.trLabelRect.width - getColumn().getDMaximum() + labelFont.getSize() / 2 + }*/ + } + if (getOrientation() == BOTTOM && drawTca && tcaData != null) { + offset += tcaData.length * (tickLabelFont.getSize() + getLineSpacing()); + } + return offset; + } + + public int getLineSpacing() { + return getTickLabelFont().getSize() / 4; + } + + /** TODO */ + protected void drawLabel(Graphics2D g, Datum value, String label, int index, int x, int y) { + + if (!tickLabelsVisible) { + return; + } + + g.setFont(getTickLabelFont()); + GrannyTextRenderer idlt = new GrannyTextRenderer(); + idlt.setString(g, label); + + int width; + width = (int) (isHorizontal() ? idlt.getLineOneWidth() : idlt.getWidth()); + int height = (int) idlt.getHeight(); + int ascent = (int) idlt.getAscent(); + + int tick_label_gap = getFontMetrics(getTickLabelFont()).stringWidth(" "); + + if (orientation == BOTTOM) { + x -= width / 2; + y += getTickLabelFont().getSize() + tick_label_gap; + } else if (orientation == TOP) { + x -= width / 2; + y -= tick_label_gap + idlt.getDescent(); + } else if (orientation == LEFT) { + x -= (width + tick_label_gap); + y += ascent - height / 2; + } else { + x += tick_label_gap; + y += ascent - height / 2; + } + Color c = g.getColor(); + idlt.draw(g, x, y); + if (orientation == BOTTOM && drawTca && tcaData != null) { + drawTCAItems(g, value, x, y, width); + } + } + + private void drawTCAItems(Graphics g, Datum value, int x, int y, int width) { + int index; + + int baseLine, leftEdge, rightEdge; + double pixelSize; + double tcaValue; + + if (tcaData == null || tcaData.length == 0) { + return; + } + + baseLine = y; + leftEdge = x; + rightEdge = leftEdge + width; + + index = DataSetUtil.closestColumn(tcaData[0], value); + if (index < 0 || index > tcaData[0].getXLength()) { + return; + } + pixelSize = getDatumRange().width().divide(getDLength()).doubleValue( + getUnits().getOffsetUnits()); + + if ( tcaData[0].getXLength()==0 ) { + g.drawString("tca data is empty", leftEdge, baseLine); + return; + } + + tcaValue = tcaData[0].getXTagDouble(index, getUnits()); + + //Added in to say take nearest nieghbor as long as the distance to the nieghbor is + //not more than the xtagwidth. + double xTagWidth = DataSetUtil.guessXTagWidth(tcaData[0]).doubleValue( + getUnits().getOffsetUnits()); + double limit = Math.max(xTagWidth, pixelSize); + + if (Math.abs(tcaValue - value.doubleValue(getUnits())) > limit) { + return; + } + + Font tickLabelFont = getTickLabelFont(); + FontMetrics fm = getFontMetrics(tickLabelFont); + int lineHeight = tickLabelFont.getSize() + getLineSpacing(); + for (int i = 0; i < tcaData.length; i++) { + baseLine += lineHeight; + String item; + if (tcaFormatters[i] == null) { + item = format(tcaData[i].getDouble(index, tcaData[i].getYUnits()), "(f8.2)"); + } + else { + item = tcaFormatters[i].format(tcaData[i].getDatum(index)); + } + width = fm.stringWidth(item); + leftEdge = rightEdge - width; + g.drawString(item, leftEdge, baseLine); + } + } + + /** TODO + * @return + */ + public Font getTickLabelFont() { + return this.getFont(); + } + + /** TODO + * @param tickLabelFont + */ + public void setTickLabelFont(Font tickLabelFont) { + } + + /** TODO + * @return + */ + public Font getLabelFont() { + return this.getFont(); + } + + /** TODO + * @param labelFont + */ + public void setLabelFont(Font labelFont) { + // TODO: whah?--jbf + } + + public static class Memento { + + private DatumRange range; + private int dmin, dmax; + private boolean log; + private boolean flipped; + private DasAxis axis; + + public boolean equals(Object o) { + Memento m = (Memento) o; + return this == m || (this.range.equals(m.range) && + this.dmin == m.dmin && + this.dmax == m.dmax && + this.log == m.log && + this.flipped == m.flipped && + this.axis == m.axis); + } + + public String toString() { + return (log ? "log " : "") + range.toString() + " " + (dmax - dmin) + " pixels @ " + dmin; + } + } + + public Memento getMemento() { + Memento result = new Memento(); + result.range = this.getDatumRange(); + if (isHorizontal()) { + if (getColumn() != DasColumn.NULL) { + result.dmin = getColumn().getDMinimum(); + result.dmax = getColumn().getDMaximum(); + } else { + result.dmin = 0; + result.dmax = 0; + } + } else { + if (getRow() != DasRow.NULL) { + result.dmin = getRow().getDMinimum(); + result.dmax = getRow().getDMaximum(); + } else { + result.dmin = 0; + result.dmax = 0; + } + } + result.log = this.isLog(); + result.flipped = flipped; + result.axis = this; + return result; + } + + /** + * return the AffineTransform, or null. The transform will be applied after the input + * transform is applied. So to just get the transform, pass in identity. + */ + public AffineTransform getAffineTransform(Memento memento, AffineTransform at) { + if (at == null) { + return null; + } + if (memento.log != isLog()) { + return null; + } + if (memento.flipped != flipped) { + return null; + } + if (!memento.range.getUnits().isConvertableTo(getUnits())) { + return null; + } + + //TODO: remove cut-n-paste code + //return getAffineTransform(memento.range, false, at); + + double dmin0, dmax0; + + dmin0 = transform(memento.range.min()); + dmax0 = transform(memento.range.max()); + + if (!(isHorizontal() ^ flipped)) { + double tmp = dmin0; + dmin0 = dmax0; + dmax0 = tmp; + } + + if (!isHorizontal()) { + double dmin1 = getRow().getDMinimum(); + double dmax1 = getRow().getDMaximum(); + double scaley = (dmin0 - dmax0) / (dmin1 - dmax1); + double transy = -1 * dmin1 * scaley + dmin0; + at.translate(0., transy); + at.scale(1., scaley); + + } else { + double dmin1 = getColumn().getDMinimum(); + double dmax1 = getColumn().getDMaximum(); + + double scalex = (dmin0 - dmax0) / (dmin1 - dmax1); + double transx = -1 * dmin1 * scalex + dmin0; + at.translate(transx, 0); + at.scale(scalex, 1.); + + } + + if (at.getDeterminant() == 0.000) { + return null; + } else { + return at; + } + } + + /** TODO + * @return + */ + public Object clone() { + try { + DasAxis result = (DasAxis) super.clone(); + result.dataRange = (DataRange) result.dataRange.clone(); + return result; + } catch (CloneNotSupportedException e) { + throw new Error("Assertion failure"); + } + } + + private void setTickDirection(int direction) { + if (direction == UP || direction == RIGHT) { + tickDirection = -1; + } else if (direction == DOWN || direction == LEFT) { + tickDirection = 1; + } else { + throw new IllegalArgumentException("Invalid tick direction"); + } + } + + /** + * calculate the biggest label width + * @return the width in pixels of the widest label. + */ + private int getMaxLabelWidth() { + try { + Font f = getTickLabelFont(); + TickVDescriptor ticks = getTickV(); + DatumVector tickv = ticks.tickV; + int size = Integer.MIN_VALUE; + Graphics g = this.getGraphics(); + for (int i = 0; i < tickv.getLength(); i++) { + String label = tickFormatter(tickv.get(i)); + GrannyTextRenderer idlt = new GrannyTextRenderer(); + idlt.setString(f, label); + int labelSize = (int) Math.round(idlt.getWidth()); + if (labelSize > size) { + size = labelSize; + } + } + return size; + } catch (InconvertibleUnitsException ex) { + return 10; + } + } + + /** TODO + * @param fm + * @deprecated use getMaxLabelWidth() + * @return the width in pixels of the widest label. + */ + protected int getMaxLabelWidth(FontMetrics fm) { + try { + TickVDescriptor ticks = getTickV(); + DatumVector tickv = ticks.tickV; + int size = Integer.MIN_VALUE; + Graphics g = this.getGraphics(); + for (int i = 0; i < tickv.getLength(); i++) { + String label = tickFormatter(tickv.get(i)); + GrannyTextRenderer idlt = new GrannyTextRenderer(); + idlt.setString(g, label); + int labelSize = (int) Math.round(idlt.getWidth()); + if (labelSize > size) { + size = labelSize; + } + } + return size; + } catch (InconvertibleUnitsException ex) { + return 10; + } + } + + /** TODO */ + public void resize() { + resetTransform(); + Rectangle oldBounds = this.getBounds(); + setBounds(getAxisBounds()); + //setBounds(getAxisBoundsNew()); + invalidate(); + if (tickV == null || tickV.tickV.getUnits().isConvertableTo(getUnits())) { + validate(); + } + firePropertyChange(PROP_BOUNDS, oldBounds, getBounds()); + } + + + /** + * calculate the bounds of the labels. This should including regions that + * the labels could occupy if the axis were panned, so that result doesn't + * change during panning. + * + * @return Rectangle in the canvas coordinate frame. + */ + protected synchronized Rectangle getLabelBounds( Rectangle bounds ) { + String[] labels = tickFormatter(this.getTickV().tickV, getDatumRange()); + + int majorTickLength = (int) getEmSize(); + + GrannyTextRenderer gtr = new GrannyTextRenderer(); + + Font labelFont = this.getLabelFont(); + + int tickLen = (int) getEmSize(); + + double dmin= transform(getDataMinimum()); + double dmax= transform(getDataMaximum()); + + DatumVector ticks= this.getTickV().tickV; + for (int i = 0; i < labels.length; i++) { + Datum d = ticks.get(i); + DatumRange dr= getDatumRange(); + if (DatumRangeUtil.sloppyContains(dr, d)) { + gtr.setString(labelFont, labels[i]); + Rectangle rmin = gtr.getBounds(); + Rectangle rmax= new Rectangle(rmin); // same bound, but positioned at the axis max. + double flw = gtr.getLineOneWidth(); + + if (isHorizontal()) { + if (getOrientation() == BOTTOM) { + rmin.translate((int) (dmin - flw / 2), getRow().bottom() + tickLen + labelFont.getSize() ); + rmax.translate((int) (dmax - flw / 2), getRow().bottom() + tickLen + labelFont.getSize() ); + } else { + rmin.translate((int) (dmin - flw / 2), getRow().top() - tickLen - (int) rmin.getHeight()); + rmin.translate((int) (dmax - flw / 2), getRow().top() - tickLen - (int) rmax.getHeight()); + } + bounds.add(rmin); + bounds.add(rmax); + } else { + if (getOrientation() == LEFT) { + rmin.translate(-(int) rmin.getWidth() - tickLen + getColumn().left(), + (int) (dmin + getEmSize() / 2)); + rmax.translate(-(int) rmax.getWidth() - tickLen + getColumn().left(), + (int) (dmax + getEmSize() / 2)); + } else { + rmin.translate(tickLen + getColumn().right(), (int) (dmin + getEmSize() / 2)); + rmax.translate(tickLen + getColumn().right(), (int) (dmax + getEmSize() / 2)); + } + bounds.add(rmin); + bounds.add(rmax); + } + } + } + return bounds; + } + + /** + * Calculate the rectangle that bounds the axis including its labels. + * When the axis is drawn on both sides of the plot, this rectangle will + * extend across the plot. + * @return Rectangle containing the axes and its labels. + */ + protected Rectangle getAxisBounds() { + Rectangle bounds; + if (isHorizontal()) { + bounds = getHorizontalAxisBounds(); + } else { + bounds = getVerticalAxisBounds(); + } + if (getOrientation() == BOTTOM && areTickLabelsVisible()) { + if (drawTca && tcaData != null && tcaData.length != 0) { + int DMin = getColumn().getDMinimum(); + int DMax = getColumn().getDMaximum(); + Font tickLabelFont = getTickLabelFont(); + int tick_label_gap = getFontMetrics(tickLabelFont).stringWidth(" "); + int tcaHeight = (tickLabelFont.getSize() + getLineSpacing()) * tcaData.length; + int maxLabelWidth = getMaxLabelWidth(); + bounds.height += tcaHeight; + blLabelRect.height += tcaHeight; + if (blTitleRect != null) { + blTitleRect.y += tcaHeight; + } + GrannyTextRenderer idlt = new GrannyTextRenderer(); + idlt.setString(tickLabelFont, "SCET"); + int tcaLabelWidth = (int) Math.floor(idlt.getWidth() + 0.5); + for (int i = 0; i < tcaData.length; i++) { + String label = (String)tcaData[i].getProperty(DataSet.PROPERTY_Y_LABEL); + if (label == null) { + label = (String)tcaData[i].getProperty("label"); + } + if (label == null) { + label = ""; + } + idlt.setString(tickLabelFont, label); + int width = (int) Math.floor(idlt.getWidth() + 0.5); + tcaLabelWidth = Math.max(tcaLabelWidth, width); + } + tcaLabelWidth += 50; + if (tcaLabelWidth > 0) { + int tcaLabelSpace = DMin - tcaLabelWidth - tick_label_gap; + int minX = Math.min(tcaLabelSpace - maxLabelWidth / 2, bounds.x); + int maxX = bounds.x + bounds.width; + bounds.x = minX; + bounds.width = maxX - minX; + blLabelRect.x = minX; + blLabelRect.width = maxX - minX; + } + } + + for (int i = 0; i < tickV.tickV.getLength(); i++) { + if (false) { // this is unnecessary? I think it's a kludge for multiline ticks... + bounds.height += getTickLabelFont().getSize() + getLineSpacing(); + if (getTickDirection() == -1) { + bounds.y -= getTickLabelFont().getSize() + getLineSpacing(); + } + } + } + } + return bounds; + } + + private Rectangle getHorizontalAxisBounds() { + int topPosition = getRow().getDMinimum() - 1; + int bottomPosition = getRow().getDMaximum(); + DasDevicePosition range = getColumn(); + int DMax = range.getDMaximum(); + int DMin = range.getDMinimum(); + int DWidth= DMax- DMin; + + boolean bottomTicks = (orientation == BOTTOM || oppositeAxisVisible); + boolean bottomTickLabels = (orientation == BOTTOM && tickLabelsVisible); + boolean bottomLabel = (bottomTickLabels && !axisLabel.equals("")); + boolean topTicks = (orientation == TOP || oppositeAxisVisible); + boolean topTickLabels = (orientation == TOP && tickLabelsVisible); + boolean topLabel = (topTickLabels && !axisLabel.equals("")); + + Rectangle bounds; + + Font tickLabelFont = getTickLabelFont(); + + int tickSize = tickLabelFont.getSize() * 2 / 3; + + if (bottomTicks) { + if (blLineRect == null) { + blLineRect = new Rectangle(); + } + blLineRect.setBounds(DMin, bottomPosition, DWidth + 1, 1); + } + if (topTicks) { + if (trLineRect == null) { + trLineRect = new Rectangle(); + } + trLineRect.setBounds(DMin, topPosition, DWidth + 1, 1); + } + + //Add room for ticks + if (bottomTicks) { + int x = DMin; + int y = bottomPosition + 1; + int width = DWidth; + int height = tickSize; + //The last tick is at position (x + width), so add 1 to width + blTickRect = setRectangleBounds(blTickRect, x, y, width + 1, height); + } + if (topTicks) { + int x = DMin; + int y = topPosition - tickSize; + int width = DWidth; + int height = tickSize; + //The last tick is at position (x + width), so add 1 to width + trTickRect = setRectangleBounds(trTickRect, x, y, width + 1, height); + } + + //int maxLabelWidth = getMaxLabelWidth(); + //int tick_label_gap = getFontMetrics(tickLabelFont).stringWidth(" "); + + if (bottomTickLabels) { + blLabelRect = getLabelBounds( new Rectangle( DMin, blTickRect.y, DWidth, 10 ) ); + //int x = DMin - maxLabelWidth / 2; + //int y = blTickRect.y + blTickRect.height; + //int width = DMax - DMin + maxLabelWidth; + //int height = tickLabelFont.getSize() * 3 / 2 + tick_label_gap; + //blLabelRect = setRectangleBounds(blLabelRect, x, y, width, height); + } + if (topTickLabels) { + trLineRect = getLabelBounds( new Rectangle( DMin, topPosition-10, DWidth, 10 ) ); + //int x = DMin - maxLabelWidth / 2; + //int y = topPosition - (tickLabelFont.getSize() * 3 / 2 + tick_label_gap + 1); + //int width = DMax - DMin + maxLabelWidth; + //int height = tickLabelFont.getSize() * 3 / 2 + tick_label_gap; + //trLabelRect = setRectangleBounds(trLabelRect, x, y, width, height); + } + + //Add room for the axis label + Font labelFont = getLabelFont(); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(labelFont, getLabel()); + int labelSpacing = (int) gtr.getHeight() + labelFont.getSize() / 2; + if (bottomLabel) { + int x = DMin; + int y = blLabelRect.y + blLabelRect.height; + int width = DMax - DMin; + int height = labelSpacing; + blTitleRect = setRectangleBounds(blTitleRect, x, y, width, height); + } + if (topLabel) { + int x = DMin; + int y = trLabelRect.y - labelSpacing; + int width = DMax - DMin; + int height = labelSpacing; + trTitleRect = setRectangleBounds(trTitleRect, x, y, width, height); + } + + bounds = new Rectangle((orientation == BOTTOM) ? blLineRect : trLineRect); + if (bottomTicks) { + bounds.add(blLineRect); + bounds.add(blTickRect); + } + if (bottomTickLabels) { + bounds.add(blLabelRect); + } + if (bottomLabel) { + bounds.add(blTitleRect); + } + if (topTicks) { + bounds.add(trLineRect); + bounds.add(trTickRect); + } + if (topTickLabels) { + bounds.add(trLabelRect); + } + if (topLabel) { + bounds.add(trTitleRect); + } + + //Add room for the scan buttons (if present) + if (scanPrevious != null && scanNext != null) { + Dimension prevSize = scanPrevious.getPreferredSize(); + Dimension nextSize = scanPrevious.getPreferredSize(); + int minX = Math.min(DMin - prevSize.width, bounds.x); + int maxX = Math.max(DMax + nextSize.width, bounds.x + bounds.width); + bounds.x = minX; + bounds.width = maxX - minX; + } + + return bounds; + } + + private Rectangle getVerticalAxisBounds() { + boolean leftTicks = (orientation == LEFT || oppositeAxisVisible); + boolean leftTickLabels = (orientation == LEFT && tickLabelsVisible); + boolean leftLabel = (orientation == LEFT && !axisLabel.equals("")); + boolean rightTicks = (orientation == RIGHT || oppositeAxisVisible); + boolean rightTickLabels = (orientation == RIGHT && tickLabelsVisible); + boolean rightLabel = (orientation == RIGHT && !axisLabel.equals("")); + + int leftPosition = getColumn().getDMinimum() - 1; + int rightPosition = getColumn().getDMaximum(); + int DMax = getRow().getDMaximum(); + int DMin = getRow().getDMinimum(); + int DWidth= DMax-DMin; + + Rectangle bounds; + + Font tickLabelFont = getTickLabelFont(); + + int tickSize = tickLabelFont.getSize() * 2 / 3; + + if (leftTicks) { + if (blLineRect == null) { + blLineRect = new Rectangle(); + } + blLineRect.setBounds(leftPosition, DMin, 1, DWidth + 1); + } + if (rightTicks) { + if (trLineRect == null) { + trLineRect = new Rectangle(); + } + trLineRect.setBounds(rightPosition, DMin, 1, DWidth + 1); + } + + //Add room for ticks + if (leftTicks) { + int x = leftPosition - tickSize; + int y = DMin; + int width = tickSize; + int height = DWidth; + //The last tick is at position (y + height), so add 1 to height + blTickRect = setRectangleBounds(blTickRect, x, y, width, height + 1); + } + if (rightTicks) { + int x = rightPosition + 1; + int y = DMin; + int width = tickSize; + int height = DWidth; + //The last tick is at position (y + height), so add 1 to height + trTickRect = setRectangleBounds(trTickRect, x, y, width, height + 1); + } + + //int maxLabelWidth = getMaxLabelWidth(); + //int tick_label_gap = getFontMetrics(tickLabelFont).stringWidth(" "); + + //Add room for tick labels + if (leftTickLabels) { + //int x = blTickRect.x - (maxLabelWidth + tick_label_gap); + //int y = DMin - tickLabelFont.getSize(); + //int width = maxLabelWidth + tick_label_gap; + //int height = DMax - DMin + tickLabelFont.getSize() * 2; + //blLabelRect = setRectangleBounds(blLabelRect, x, y, width, height); + blLabelRect= getLabelBounds( new Rectangle(blTickRect.x-10,DMin,10,DWidth) ); + } + if (rightTickLabels) { + trLabelRect= getLabelBounds( new Rectangle(trTickRect.x+trTickRect.width,DMin,10,DWidth) ); + //int x = trTickRect.x + trTickRect.width; + //int y = DMin - tickLabelFont.getSize(); + //int width = maxLabelWidth + tick_label_gap; + //int height = DMax - DMin + tickLabelFont.getSize() * 2; + //trLabelRect = setRectangleBounds(trLabelRect, x, y, width, height); + } + + //Add room for the axis label + Font labelFont = getLabelFont(); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(labelFont, getLabel()); + int labelSpacing = (int) gtr.getHeight() + labelFont.getSize() / 2; + if (leftLabel) { + int x = blLabelRect.x - labelSpacing; + int y = DMin; + int width = labelSpacing; + int height = DWidth; + blTitleRect = setRectangleBounds(blTitleRect, x, y, width, height); + } + if (rightLabel) { + int x = trLabelRect.x + trLabelRect.width; + int y = DMin; + int width = labelSpacing; + int height = DWidth; + trTitleRect = setRectangleBounds(trTitleRect, x, y, width, height); + } + + bounds = new Rectangle((orientation == LEFT) ? blLineRect : trLineRect); + if (leftTicks) { + bounds.add(blLineRect); + bounds.add(blTickRect); + } + if (leftTickLabels) { + bounds.add(blLabelRect); + } + if (leftLabel) { + bounds.add(blTitleRect); + } + if (rightTicks) { + bounds.add(trLineRect); + bounds.add(trTickRect); + } + if (rightTickLabels) { + bounds.add(trLabelRect); + } + if (rightLabel) { + bounds.add(trTitleRect); + } + + return bounds; + } + + private static Rectangle setRectangleBounds(Rectangle rc, int x, int y, int width, int height) { + if (rc == null) { + return new Rectangle(x, y, width, height); + } else { + rc.setBounds(x, y, width, height); + return rc; + } + } + + /** + * returns the orientation of the axis, which is one of BOTTOM,TOP,LEFT or RIGHT. + * @return BOTTOM,TOP,LEFT or RIGHT. + */ + public int getOrientation() { + return orientation; + } + + /** + * test if the axis is horizontal. + * @return true if the orientation is BOTTOM or TOP. + */ + public boolean isHorizontal() { + return orientation == BOTTOM || orientation == TOP; + } + + /** TODO + * @return + */ + public int getTickDirection() { + return tickDirection; + } + + /** TODO + * @return + */ + public DatumFormatter getDatumFormatter() { + return datumFormatter; + } + + /** Transforms a Datum in data coordinates to a horizontal or vertical + * position on the parent canvas. + * @param datum a data value + * @return Horizontal or vertical position on the canvas. + */ + public double transform(Datum datum) { + return transform(datum.doubleValue(getUnits()), getUnits()); + } + + protected double transformFast(double data, Units units) { + if (dataRange.isLog()) { + if (data <= 0.) { + data = dataRange.getMinimum() - 3; // TODO verify that dataRange.getMinimum() is log. + } else { + data = DasMath.log10(data); + } + } + double result = at_m * data + at_b; + return result; + } + + /** Transforms a double in the given units in data coordinates to a horizontal or vertical + * position on the parent canvas. + * @param data a data value + * @param units the units of the given data value. + * @return Horizontal or vertical position on the canvas. + */ + protected double transform(double data, Units units) { + DasDevicePosition range; + // TODO: consider optimization here + if (isHorizontal()) { + range = getColumn(); + return transform(data, units, range.getDMinimum(), range.getDMaximum()); + } else { + range = getRow(); + return transform(data, units, range.getDMaximum(), range.getDMinimum()); + } + } + + protected double transform(double data, Units units, int dmin, int dmax) { + if (units != dataRange.getUnits()) { + data = units.convertDoubleTo(dataRange.getUnits(), data); + } + + double device_range = (dmax - dmin); + double result; + + if (dataRange.isLog()) { + if (data <= 0.) { + data = -1e308; + } else { + data = DasMath.log10(data); + } + } + + double minimum = dataRange.getMinimum(); + double maximum = dataRange.getMaximum(); + double data_range = maximum - minimum; + + if (flipped) { + result = dmax - (device_range * (data - minimum) / data_range); + } else { + result = (device_range * (data - minimum) / data_range) + dmin; + } + + if (result > 10000) { + result = 10000; + } + if (result < -10000) { + result = -10000; + } + return result; + } + + public Datum invTransform(double idata) { + double data; + DasDevicePosition range = (isHorizontal() + ? (DasDevicePosition) getColumn() + : (DasDevicePosition) getRow()); + + double alpha = (idata - range.getDMinimum()) / (double) getDLength(); + if (!isHorizontal()) { + alpha = 1.0 - alpha; + } + if (flipped) { + alpha = 1.0 - alpha; + } + + double minimum = dataRange.getMinimum(); + double maximum = dataRange.getMaximum(); + double data_range = maximum - minimum; + data = data_range * alpha + minimum; + + double resolution = data_range / getDLength(); + if (dataRange.isLog()) { + data = DasMath.exp10(data); + resolution = data * (DasMath.exp10(resolution) - 1); + } + + Datum result = Datum.create(data, dataRange.getUnits(), resolution); + + return result; + } + + /** + * return a label for this datum and visible range. This is intended + * to be overriden to change behavior. Note that both tickFormatter methods + * should be overridden. + * @param tickv + * @return string, possibly with Granny control characters. + */ + protected String tickFormatter(Datum d) { + // TODO: label the axis with the Unit! + return datumFormatter.grannyFormat(d, d.getUnits()); + + } + + /** + * return the tick labels for these datums and visible range. This is intended + * to be overriden to change behavior. Note that both tickFormatter methods + * should be overridden. + * @param tickV + * @param datumRange + * @return Strings, possibly with Granny control characters. + */ + protected String[] tickFormatter(DatumVector tickV, DatumRange datumRange) { + return datumFormatter.axisFormat(tickV, datumRange); + } + + /** TODO + * @param e + */ + public void dataRangeSelected(DataRangeSelectionEvent e) { + this.setDataRange(e.getMinimum(), e.getMaximum()); + } + + /** TODO + * @param xDatum + * @param direction + * @param minor + * @return + * + * @depricated. Use getTickVDescriptor.findTick + */ + public Datum findTick(Datum xDatum, double direction, boolean minor) { + return getTickV().findTick(xDatum, direction, minor); + } + + /** TODO + * @param min0 + * @param max0 + * @param min1 + * @param max1 + */ + private void animateChange(double min0, double max0, double min1, double max1) { + + if (animated && EventQueue.isDispatchThread()) { + + + + + logger.fine("animate axis"); + + boolean drawTca0 = getDrawTca(); + setDrawTca(false); + + long t0 = System.currentTimeMillis(); + long frames = 0; + + DataRange dataRange0 = dataRange; + DataRange tempRange = DataRange.getAnimationDataRange(dataRange.getDatumRange(), dataRange.isLog()); + + this.dataRange = tempRange; + + double transitionTime = 300; // millis + //double transitionTime= 1500; // millis + double alpha = (System.currentTimeMillis() - t0) / transitionTime; + + while (alpha < 1.0) { + alpha = (System.currentTimeMillis() - t0) / transitionTime; + + final double[] aa = new double[]{0.0, 0.3, 0.85, 1.0}; + final double[] aa1 = new double[]{0.0, 0.05, 0.90, 1.0}; + + double f1 = DasMath.findex(aa, alpha, 0); + double a1 = DasMath.interpolate(aa1, f1); + double a0 = 1 - a1; + + tempRange.setRange(min0 * a0 + min1 * a1, max0 * a0 + max1 * a1); + + //updateTickV(); + this.paintImmediately(0, 0, this.getWidth(), this.getHeight()); + + if (dasPlot != null) { + dasPlot.paintImmediately(0, 0, dasPlot.getWidth(), dasPlot.getHeight()); + } + frames++; + } + + logger.fine("animation frames/sec= " + (1000. * frames / transitionTime)); + setDrawTca(drawTca0); + + this.dataRange = dataRange0; + } + } + + /** TODO */ + protected void updateImmediately() { + super.updateImmediately(); + logger.finer("" + getDatumRange() + " " + isLog()); + resetTransform(); + updateTickV(); + } + + /** TODO + * @return + * @deprecated use isTickLabelsVisible + */ + public boolean areTickLabelsVisible() { + return tickLabelsVisible; + } + + /** + * true if the tick labels should be drawn. + * @return + */ + public boolean isTickLabelsVisible() { + return tickLabelsVisible; + } + + /** TODO + * @param b + */ + public void setTickLabelsVisible(boolean b) { + if (tickLabelsVisible == b) { + return; + } + boolean oldValue = ticksVisible; + tickLabelsVisible = b; + update(); + firePropertyChange("tickLabelsVisible", oldValue, b); + } + + /** TODO */ + protected void installComponent() { + super.installComponent(); + } + + /** TODO */ + protected void uninstallComponent() { + super.uninstallComponent(); + } + + /** Process an <axis> element. + * + * @param element The DOM tree node that represents the element + */ + static DasAxis processAxisElement(Element element, FormBase form) throws DasPropertyException, DasNameException, ParseException { + String name = element.getAttribute("name"); + boolean log = element.getAttribute(PROP_LOG).equals("true"); + Datum dataMinimum; + Datum dataMaximum; + if ("TIME".equals(element.getAttribute("units"))) { + String min = element.getAttribute("dataMinimum"); + String max = element.getAttribute("dataMaximum"); + dataMinimum = (min == null || min.equals("") ? TimeUtil.create("1979-02-26") : TimeUtil.create(min)); + dataMaximum = (max == null || max.equals("") ? TimeUtil.create("1979-02-27") : TimeUtil.create(max)); + } else { + String min = element.getAttribute("dataMinimum"); + String max = element.getAttribute("dataMaximum"); + dataMinimum = (min == null || min.equals("") ? Datum.create(1.0) : Datum.create(Double.parseDouble(min))); + dataMaximum = (max == null || max.equals("") ? Datum.create(10.0) : Datum.create(Double.parseDouble(max))); + } + int orientation = parseOrientationString(element.getAttribute("orientation")); + DasAxis axis = new DasAxis(dataMinimum, dataMaximum, orientation, log); + String rowString = element.getAttribute("row"); + if (!rowString.equals("")) { + DasRow row = (DasRow) form.checkValue(rowString, DasRow.class, ""); + axis.setRow(row); + } + String columnString = element.getAttribute("column"); + if (!columnString.equals("")) { + DasColumn column = (DasColumn) form.checkValue(columnString, DasColumn.class, ""); + axis.setColumn(column); + } + + axis.setLabel(element.getAttribute(PROP_LABEL)); + axis.setOppositeAxisVisible(!element.getAttribute(PROP_OPPOSITE_AXIS_VISIBLE).equals("false")); + axis.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); + + axis.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, axis); + + return axis; + } + + /** TODO + * @param i + * @return + */ + protected static String orientationToString(int i) { + switch (i) { + case TOP: + return "top"; + case BOTTOM: + return "bottom"; + case LEFT: + return "left"; + case RIGHT: + return "right"; + default: + throw new IllegalStateException("invalid orienation: " + i); + } + } + + /** TODO + * @param orientationString + * @return + */ + protected static int parseOrientationString(String orientationString) { + if (orientationString.equals("horizontal")) { + return HORIZONTAL; + } else if (orientationString.equals("vertical")) { + return VERTICAL; + } else if (orientationString.equals("left")) { + return LEFT; + } else if (orientationString.equals("right")) { + return RIGHT; + } else if (orientationString.equals("top")) { + return TOP; + } else if (orientationString.equals("bottom")) { + return BOTTOM; + } else { + throw new IllegalArgumentException("Invalid orientation: " + orientationString); + } + } + + /** TODO + * @param document + * @return + */ + public Element getDOMElement(Document document) { + Element element; + if (this.isAttached()) { + element = document.createElement("attachedaxis"); + } else { + element = document.createElement("axis"); + } + if (this.isAttached()) { + element.setAttribute("ref", this.getMasterAxis().getDasName()); + } else { + String minimumStr = getDataMinimum().toString(); + element.setAttribute("dataMinimum", minimumStr); + String maximumStr = getDataMaximum().toString(); + element.setAttribute("dataMaximum", maximumStr); + } + + element.setAttribute("name", getDasName()); + element.setAttribute("row", getRow().getDasName()); + element.setAttribute("column", getColumn().getDasName()); + + element.setAttribute(PROP_LABEL, getLabel()); + element.setAttribute(PROP_LOG, Boolean.toString(isLog())); + element.setAttribute("tickLabelsVisible", Boolean.toString(areTickLabelsVisible())); + element.setAttribute(PROP_OPPOSITE_AXIS_VISIBLE, Boolean.toString(isOppositeAxisVisible())); + element.setAttribute("animated", Boolean.toString(isAnimated())); + element.setAttribute("orientation", orientationToString(getOrientation())); + + return element; + } + + /** Create a new axis that uses the same DataRange object as this axis. + * + * @return A new DasAxis with the same "backing store" (i.e. the DataRange object) + * as this axis. + */ + public DasAxis createAttachedAxis() { + return new DasAxis(this.dataRange, this.getOrientation()); + } + + /** + * Note: This is commonly used for pop-up slicer windows. + * @return + */ + public DasAxis createAttachedAxis(int orientation) { + return new DasAxis(this.dataRange, orientation); + } + + /** Process a <attachedaxis> element. + * + * @param element The DOM tree node that represents the element + */ + static DasAxis processAttachedaxisElement(Element element, FormBase form) throws DasPropertyException, DasNameException { + String name = element.getAttribute("name"); + DasAxis ref = (DasAxis) form.checkValue(element.getAttribute("ref"), DasAxis.class, ""); + int orientation = (element.getAttribute("orientation").equals("horizontal") ? HORIZONTAL : DasAxis.VERTICAL); + + DasAxis axis = ref.createAttachedAxis(orientation); + + String rowString = element.getAttribute("row"); + if (!rowString.equals("")) { + DasRow row = (DasRow) form.checkValue(rowString, DasRow.class, ""); + axis.setRow(row); + } + String columnString = element.getAttribute("column"); + if (!columnString.equals("")) { + DasColumn column = (DasColumn) form.checkValue(columnString, DasColumn.class, ""); + axis.setColumn(column); + } + + axis.setDataPath(element.getAttribute("dataPath")); + axis.setDrawTca(element.getAttribute("showTca").equals("true")); + axis.setLabel(element.getAttribute(PROP_LABEL)); + axis.setOppositeAxisVisible(!element.getAttribute(PROP_OPPOSITE_AXIS_VISIBLE).equals("false")); + axis.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); + + axis.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, axis); + + return axis; + } + + public void setPlot(DasPlot p) { + dasPlot = p; + } + + /** TODO + * @param name + * @return + */ + public static DasAxis createNamedAxis(String name) { + DasAxis axis = new DasAxis(Datum.create(1.0, Units.dimensionless), Datum.create(10.0, Units.dimensionless), DasAxis.HORIZONTAL); + if (name == null) { + name = "axis_" + Integer.toHexString(System.identityHashCode(axis)); + } + try { + axis.setDasName(name); + } catch (DasNameException dne) { + DasExceptionHandler.handle(dne); + } + return axis; + } + + /** TODO */ + public void scanPrevious() { + Datum delta = (getDataMaximum().subtract(getDataMinimum())).multiply(1.0); + Datum tmin = getDataMinimum().subtract(delta); + Datum tmax = getDataMaximum().subtract(delta); + setDataRange(tmin, tmax); + } + + /** TODO */ + public void scanNext() { + Datum delta = (getDataMaximum().subtract(getDataMinimum())).multiply(1.0); + Datum tmin = getDataMinimum().add(delta); + Datum tmax = getDataMaximum().add(delta); + setDataRange(tmin, tmax); + } + + /** TODO + * @return + */ + public Shape getActiveRegion() { + Rectangle primaryBounds = primaryInputPanel.getBounds(); + primaryBounds.translate(getX(), getY()); + if (oppositeAxisVisible) { + Rectangle secondaryBounds = secondaryInputPanel.getBounds(); + secondaryBounds.translate(getX(), getY()); + GeneralPath path = new GeneralPath(primaryBounds); + path.setWindingRule(GeneralPath.WIND_EVEN_ODD); + path.append(secondaryBounds, false); + return path; + } else { + return primaryBounds; + } + } + + /** + * Adds a MouseWheelListener to the DasAxis. Special care must be taken + * with the DasAxis, because it is composed of two sub panels, and their + * parent panel (this), must not recieve the events. (This is because + * the DasPlot between them should get the events, and the DasPlot does + * not have a simple rectangular boundary. + */ + public void addMouseWheelListener(MouseWheelListener l) { + maybeInitializeInputPanels(); + primaryInputPanel.addMouseWheelListener(l); + secondaryInputPanel.addMouseWheelListener(l); + } + + public void removeMouseWheelListener(MouseWheelListener l) { + maybeInitializeInputPanels(); + primaryInputPanel.removeMouseWheelListener(l); + secondaryInputPanel.removeMouseWheelListener(l); + } + + /** TODO + * @param l + */ + public void addMouseListener(MouseListener l) { + maybeInitializeInputPanels(); + primaryInputPanel.addMouseListener(l); + secondaryInputPanel.addMouseListener(l); + } + + /** TODO + * @param l + */ + public void removeMouseListener(MouseListener l) { + maybeInitializeInputPanels(); + primaryInputPanel.removeMouseListener(l); + secondaryInputPanel.removeMouseListener(l); + } + + /** TODO + * @param l + */ + public void addMouseMotionListener(MouseMotionListener l) { + maybeInitializeInputPanels(); + primaryInputPanel.addMouseMotionListener(l); + secondaryInputPanel.addMouseMotionListener(l); + } + + /** TODO + * @param l + */ + public void removeMouseMotionListener(MouseMotionListener l) { + maybeInitializeInputPanels(); + primaryInputPanel.removeMouseMotionListener(l); + secondaryInputPanel.removeMouseMotionListener(l); + } + + public void timeRangeSelected(TimeRangeSelectionEvent e) { + if (e.getSource() != this && !e.equals(lastProcessedEvent)) { + setDatumRange(e.getRange()); // setDatumRange fires the event + lastProcessedEvent = e; + } + } + + /** Registers TimeRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addTimeRangeSelectionListener(org.das2.event.TimeRangeSelectionListener listener) { + if (timeRangeListenerList == null) { + timeRangeListenerList = new javax.swing.event.EventListenerList(); + } + timeRangeListenerList.add(org.das2.event.TimeRangeSelectionListener.class, listener); + } + + /** Removes TimeRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeTimeRangeSelectionListener(org.das2.event.TimeRangeSelectionListener listener) { + timeRangeListenerList.remove(org.das2.event.TimeRangeSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireTimeRangeSelectionListenerTimeRangeSelected(TimeRangeSelectionEvent event) { + if (timeRangeListenerList == null) { + return; + } + Object[] listeners = timeRangeListenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == org.das2.event.TimeRangeSelectionListener.class) { + String logmsg = "fire event: " + this.getClass().getName() + "-->" + listeners[i + 1].getClass().getName() + " " + event; + DasLogger.getLogger(DasLogger.GUI_LOG).fine(logmsg); + ((org.das2.event.TimeRangeSelectionListener) listeners[i + 1]).timeRangeSelected(event); + } + } + } + + static DasAxis processTimeaxisElement(Element element, FormBase form) throws org.das2.DasPropertyException,org.das2.DasNameException, DasException, java.text.ParseException { + String name = element.getAttribute("name"); + Datum timeMinimum = TimeUtil.create(element.getAttribute("timeMinimum")); + Datum timeMaximum = TimeUtil.create(element.getAttribute("timeMaximum")); + int orientation = parseOrientationString(element.getAttribute("orientation")); + + DasAxis timeaxis = new DasAxis(timeMinimum, timeMaximum, orientation); + + String rowString = element.getAttribute("row"); + if (!rowString.equals("")) { + DasRow row = (DasRow) form.checkValue(rowString, DasRow.class, ""); + timeaxis.setRow(row); + } + String columnString = element.getAttribute("column"); + if (!columnString.equals("")) { + DasColumn column = (DasColumn) form.checkValue(columnString, DasColumn.class, ""); + timeaxis.setColumn(column); + } + + timeaxis.setDataPath(element.getAttribute("dataPath")); + timeaxis.setDrawTca(element.getAttribute("showTca").equals("true")); + timeaxis.setLabel(element.getAttribute(PROP_LABEL)); + timeaxis.setOppositeAxisVisible(!element.getAttribute(PROP_OPPOSITE_AXIS_VISIBLE).equals("false")); + timeaxis.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); + + timeaxis.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, timeaxis); + + return timeaxis; + } + private static final java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\([eEfF]\\d+.\\d+\\)"); + + private static String format(double d, String f) { + Matcher m = pattern.matcher(f); + if (!m.matches()) { + throw new IllegalArgumentException("\"" + f + "\" is not a valid format specifier"); + } + int length = Integer.parseInt(f.substring(2, f.indexOf('.'))); + int fracLength = Integer.parseInt(f.substring(f.indexOf('.') + 1, f.indexOf(')'))); + char[] buf = new char[length]; + String result; + if (f.charAt(1) == 'f' || f.charAt(1) == 'F') { + int i = 0; + while (i < length - fracLength - 2) { + buf[i] = '#'; + i++; + } + buf[i] = '0'; + i++; + buf[i] = '.'; + i++; + while (i < length) { + buf[i] = '0'; + i++; + } + DecimalFormat form = new DecimalFormat(new String(buf)); + result = form.format(d); + } else { + int i = 0; + while (i < length - fracLength - 6) { + buf[i] = '#'; + i++; + } + buf[i] = '0'; + i++; + buf[i] = '.'; + i++; + while (i < length - 5) { + buf[i] = '0'; + i++; + } + buf[i] = 'E'; + buf[i + 1] = (d > -1.0 && d < 1.0 ? '-' : '+'); + buf[i + 2] = '0'; + buf[i + 3] = '0'; + java.text.DecimalFormat form = new java.text.DecimalFormat(new String(buf)); + result = form.format(d); + } + + if (result.length() > length) { + java.util.Arrays.fill(buf, '*'); + return new String(buf); + } + + while (result.length() < length) { + result = " " + result; + } + return result; + } + + public String toString() { + String retValue; + retValue = super.toString() + "(" + getUnits() + ")"; + return retValue; + } + + protected class AxisLayoutManager implements LayoutManager { + //NOOP + /** TODO + * @param name + * @param comp + */ + public void addLayoutComponent(String name, Component comp) { + } + + /** TODO + * @param parent + */ + public void layoutContainer(Container parent) { + if (DasAxis.this != parent) { + throw new IllegalArgumentException(); + } + if (DasAxis.this.isHorizontal()) { + horizontalLayout(); + } else { + verticalLayout(); + } + if (drawTca && getOrientation() == BOTTOM && tcaData != null) { + Rectangle bounds = primaryInputPanel.getBounds(); + int tcaHeight = (getTickLabelFont().getSize() + getLineSpacing()) * tcaData.length; + bounds.height += tcaHeight; + primaryInputPanel.setBounds(bounds); + } + } + + /** TODO */ + protected void horizontalLayout() { + int topPosition = getRow().getDMinimum() - 1; + int bottomPosition = getRow().getDMaximum(); + int DMax = getColumn().getDMaximum(); + int DMin = getColumn().getDMinimum(); + + + boolean bottomTicks = (orientation == BOTTOM || oppositeAxisVisible); + boolean bottomTickLabels = (orientation == BOTTOM && tickLabelsVisible); + boolean topTicks = (orientation == TOP || oppositeAxisVisible); + boolean topTickLabels = (orientation == TOP && tickLabelsVisible); + Rectangle bottomBounds = null; + Rectangle topBounds = null; + Font tickLabelFont = getTickLabelFont(); + int tickSize = tickLabelFont.getSize() * 2 / 3; + //Initialize bounds rectangle + if (bottomTicks) { + bottomBounds = new Rectangle(DMin, bottomPosition, DMax - DMin + 1, 1); + } + if (topTicks) { + topBounds = new Rectangle(DMin, topPosition, DMax - DMin + 1, 1); + } + //Add room for ticks + if (bottomTicks) { + bottomBounds.height += tickSize; + } + if (topTicks) { + topBounds.height += tickSize; + topBounds.y -= tickSize; + } + int tick_label_gap = getFontMetrics(tickLabelFont).stringWidth(" "); + //Add room for tick labels + if (bottomTickLabels) { + bottomBounds.height += tickLabelFont.getSize() * 3 / 2 + tick_label_gap; + } + if (topTickLabels) { + topBounds.y -= (tickLabelFont.getSize() * 3 / 2 + tick_label_gap); + topBounds.height += tickLabelFont.getSize() * 3 / 2 + tick_label_gap; + } + + Rectangle primaryBounds = (orientation == BOTTOM ? bottomBounds : topBounds); + Rectangle secondaryBounds = (orientation == BOTTOM ? topBounds : bottomBounds); + + primaryBounds.translate(-DasAxis.this.getX(), -DasAxis.this.getY()); + if (oppositeAxisVisible) { + secondaryBounds.translate(-DasAxis.this.getX(), -DasAxis.this.getY()); + } + + primaryInputPanel.setBounds(primaryBounds); + if (oppositeAxisVisible) { + secondaryInputPanel.setBounds(secondaryBounds); + } else { + secondaryInputPanel.setBounds(-100, -100, 0, 0); + } + + if (scanPrevious != null && scanNext != null) { + Dimension preferred = scanPrevious.getPreferredSize(); + int x = DMin - preferred.width - DasAxis.this.getX(); + int y = (orientation == BOTTOM ? bottomPosition : topPosition - preferred.height) - DasAxis.this.getY(); + scanPrevious.setBounds(x, y, preferred.width, preferred.height); + preferred = scanNext.getPreferredSize(); + x = DMax - DasAxis.this.getX(); + scanNext.setBounds(x, y, preferred.width, preferred.height); + } + } + + /** TODO */ + protected void verticalLayout() { + boolean leftTicks = (orientation == LEFT || oppositeAxisVisible); + boolean leftTickLabels = (orientation == LEFT && tickLabelsVisible); + boolean rightTicks = (orientation == RIGHT || oppositeAxisVisible); + boolean rightTickLabels = (orientation == RIGHT && tickLabelsVisible); + int leftPosition = getColumn().getDMinimum() - 1; + int rightPosition = getColumn().getDMaximum(); + int DMax = getRow().getDMaximum(); + int DMin = getRow().getDMinimum(); + Rectangle leftBounds = null; + Rectangle rightBounds = null; + Font tickLabelFont = getTickLabelFont(); + int tickSize = tickLabelFont.getSize() * 2 / 3; + //Initialize bounds rectangle(s) + if (leftTicks) { + leftBounds = new Rectangle(leftPosition, DMin, 1, DMax - DMin + 1); + } + if (rightTicks) { + rightBounds = new Rectangle(rightPosition, DMin, 1, DMax - DMin + 1); + } + //Add room for ticks + if (leftTicks) { + leftBounds.width += tickSize; + leftBounds.x -= tickSize; + } + if (rightTicks) { + rightBounds.width += tickSize; + } + int maxLabelWidth = getMaxLabelWidth(); + int tick_label_gap = getFontMetrics(tickLabelFont).stringWidth(" "); + //Add room for tick labels + if (leftTickLabels) { + leftBounds.x -= (maxLabelWidth + tick_label_gap); + leftBounds.width += maxLabelWidth + tick_label_gap; + //bounds.y -= tickLabelFont.getSize(); + //bounds.height += tickLabelFont.getSize()*2; + } + if (rightTickLabels) { + rightBounds.width += maxLabelWidth + tick_label_gap; + //bounds.y -= tickLabelFont.getSize(); + //bounds.height += tickLabelFont.getSize()*2; + } + + Rectangle primaryBounds = (orientation == LEFT ? leftBounds : rightBounds); + Rectangle secondaryBounds = (orientation == LEFT ? rightBounds : leftBounds); + + primaryBounds.translate(-DasAxis.this.getX(), -DasAxis.this.getY()); + if (oppositeAxisVisible) { + secondaryBounds.translate(-DasAxis.this.getX(), -DasAxis.this.getY()); + } + + primaryInputPanel.setBounds(primaryBounds); + if (oppositeAxisVisible) { + secondaryInputPanel.setBounds(secondaryBounds); + } else { + secondaryInputPanel.setBounds(-100, -100, 0, 0); + } + } + + /** TODO + * @param parent + * @return + */ + public Dimension minimumLayoutSize(Container parent) { + return new Dimension(); + } + + /** TODO + * @param parent + * @return + */ + public Dimension preferredLayoutSize(Container parent) { + return new Dimension(); + } + //NOOP + /** TODO + * @param comp + */ + public void removeLayoutComponent(Component comp) { + } + } + + private static class ScanButton extends JButton { + + private boolean hover; + private boolean pressed; + + /** TODO + * @param text + */ + public ScanButton(String text) { + setOpaque(true); + setContentAreaFilled(false); + setText(text); + setFocusable(false); + setBorder(new CompoundBorder( + new LineBorder(Color.BLACK), + new EmptyBorder(2, 2, 2, 2))); + this.addMouseListener(new MouseAdapter() { + + public void mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + setForeground(Color.LIGHT_GRAY); + pressed = true; + repaint(); + } + } + + public void mouseReleased(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + setForeground(Color.BLACK); + pressed = false; + repaint(); + } + } + + public void mouseEntered(MouseEvent e) { + hover = true; + repaint(); + } + + public void mouseExited(MouseEvent e) { + hover = false; + repaint(); + } + }); + } + + /** TODO + * @param g + */ + protected void paintComponent(Graphics g) { + if (hover || pressed) { + Graphics2D g2 = (Graphics2D) g; + g2.setColor(Color.white); + g2.fillRect(0, 0, getWidth(), getHeight()); + Object aaHint = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + Object aaOn = RenderingHints.VALUE_ANTIALIAS_ON; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaOn); + super.paintComponent(g2); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaHint); + } + } + + /** TODO + * @param g + */ + protected void paintBorder(Graphics g) { + if (hover || pressed) { + super.paintBorder(g); + } + } + } + + public boolean isFlipped() { + return flipped; + } + + public void setFlipped(boolean b) { + update(); + this.flipped = b; + } + protected String formatString = ""; + public static final String PROP_FORMATSTRING = "formatString"; + + public String getFormat() { + return formatString; + } + + protected boolean flipLabel = false; + public static final String PROP_FLIPLABEL = "flipLabel"; + + public boolean isFlipLabel() { + return flipLabel; + } + + public void setFlipLabel(boolean flipLabel) { + boolean oldFlipLabel = this.flipLabel; + this.flipLabel = flipLabel; + repaint(); + firePropertyChange(PROP_FLIPLABEL, oldFlipLabel, flipLabel); + } + + + /** + * set a hint at the format string. Examples include: + * 0.000 + * %H:%M!c%Y-%j + * @param formatString + */ + public void setFormat(String formatString) { + try { + String oldFormatString = this.formatString; + this.formatString = formatString; + if (formatString.equals("")) { + setUserDatumFormatter(null); + } else { + setUserDatumFormatter(getUnits().getDatumFormatterFactory().newFormatter(formatString)); + } + updateTickV(); + repaint(); + firePropertyChange(PROP_FORMATSTRING, oldFormatString, formatString); + } catch (ParseException e) { + setUserDatumFormatter(null); + } + } + + private void resetTransform() { + DasDevicePosition pos; + if (isHorizontal()) { + pos = getColumn(); + } else { + pos = getRow(); + } + double dmin = pos.getDMinimum(); + double dmax = pos.getDMaximum(); + if (isFlipped()) { + double t = dmin; + dmin = dmax; + dmax = t; + } + double[] at = GraphUtil.getSlopeIntercept(dataRange.getMinimum(), dmin, dataRange.getMaximum(), dmax); + at_m = at[0]; + at_b = at[1]; + } + + public Lock mutatorLock() { + return dataRange.mutatorLock(); + } + + /** + * true if a lock is out and an object is rapidly mutating the object. + * clients listening for property changes can safely ignore property + * changes while valueIsAdjusting is true, as they should receive a + * final propertyChangeEvent after the lock is released. (note it's not + * clear who is responsible for this. + * See http://www.das2.org/wiki/index.php/Das2.valueIsAdjusting) + */ + public boolean valueIsAdjusting() { + return dataRange.valueIsAdjusting(); + } +} diff --git a/dasCore/src/main/java/org/das2/graph/DasCanvas.java b/dasCore/src/main/java/org/das2/graph/DasCanvas.java new file mode 100755 index 000000000..675ab01dc --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasCanvas.java @@ -0,0 +1,2394 @@ +/* File: DasCanvas.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.graph; + +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.DasNameException; +import org.das2.DasProperties; +import org.das2.DasPropertyException; +import org.das2.NameContext; +import org.das2.dasml.FormBase; +import org.das2.dasml.FormComponent; +import org.das2.dasml.ParsedExpressionException; +import org.das2.event.DragRenderer; +import org.das2.graph.dnd.TransferableCanvasComponent; +import org.das2.system.DasLogger; +import org.das2.system.RequestProcessor; +import org.das2.util.AboutUtil; +import org.das2.util.DasExceptionHandler; +import org.das2.util.DasPNGConstants; +import org.das2.util.DasPNGEncoder; +import org.das2.util.awt.EventQueueBlocker_1; +import org.das2.util.awt.GraphicsOutput; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.LayoutManager; +import java.awt.Paint; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; +import java.util.prefs.Preferences; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JLayeredPane; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.LookAndFeel; +import javax.swing.Scrollable; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.event.MouseInputAdapter; +import javax.swing.event.MouseInputListener; +import javax.swing.filechooser.FileFilter; +import org.das2.components.propertyeditor.Editable; +import org.das2.components.propertyeditor.PropertyEditor; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** Canvas for das2 graphics. The DasCanvas contains any number of DasCanvasComponents + * such as axes, plots, colorbars, etc. + * + * A basic hierarchy of ownership is: + *
    + * DasCanvas
    + *  |
    + *  +- {@link DasDevicePosition} (2-N objects)
    + *  |   |
    + *  |   +- {@link DasDevicePosition} (sub positions, 0-N objects)
    + *  |
    + *  +- {@link DasCanvasComponent} (0-N objects)
    + *
    + * For example: For a 2-D line plot the basic components are: + *
    + * DasCavas
    + *  |
    + *  +- {@link DasRow} (a DasDevicePosition)
    + *  |
    + *  +- {@link DasColumn} (a DasDevicePosition)
    + *  |
    + *  +- {@link DasAxis} (a DasCanvasComponent)
    + *  |
    + *  +- {@link DasAxis} (a DasCanvasComponent)
    + *  |
    + *  +- {@link DasPlot} (a DasCanvasComponent)
    + *     |
    + *     +- {@link SymbolLineRenderer} (Helper for DasPlot, implements a data painting style.)
    + *
    + * Note that {@link DasPlot}s don't know how to paint data, that job is delegated to one or + * more {@link Renderer} objects. {@link SymbolLineRenderer}s know how to draw simple line + * plots. + * + * @see DasColumn + * @see DasRow + * @see DasAxis + * @see DasPlot + * @see Renderer + * @author eew + */ +public class DasCanvas extends JLayeredPane implements Printable, Editable, FormComponent, Scrollable { + + /** Default drawing layer of the JLayeredPane */ + public static final Integer DEFAULT_LAYER = JLayeredPane.DEFAULT_LAYER; + /** Z-Layer for drawing the plot. */ + public static final Integer PLOT_LAYER = new Integer(300); + /** Z-Layer for vertical axis. Presently lower than the horizontal axis, presumably to remove ambiguity */ + public static final Integer VERTICAL_AXIS_LAYER = new Integer(400); + /** Z-Layer */ + public static final Integer HORIZONTAL_AXIS_LAYER = new Integer(500); + /** Z-Layer */ + public static final Integer AXIS_LAYER = VERTICAL_AXIS_LAYER; + /** Z-Layer */ + public static final Integer ANNOTATION_LAYER = new Integer(1000); + /** Z-Layer */ + public static final Integer GLASS_PANE_LAYER = new Integer(30000); + private static final Paint PAINT_ROW = new Color(0xff, 0xb2, 0xb2, 0x92); + private static final Paint PAINT_COLUMN = new Color(0xb2, 0xb2, 0xff, 0x92); + private static final Paint PAINT_SELECTION = Color.GRAY; + private static final Stroke STROKE_DASHED; + + + static { + float thick = 3.0f; + int cap = BasicStroke.CAP_SQUARE; + int join = BasicStroke.JOIN_MITER; + float[] dash = new float[]{thick * 4.0f, thick * 4.0f}; + STROKE_DASHED = new BasicStroke(thick, cap, join, thick, dash, 0.0f); + } + + /* Canvas actions */ + protected static abstract class CanvasAction extends AbstractAction { + + protected static DasCanvas currentCanvas; + + CanvasAction(String label) { + super(label); + } + } + + private static FileFilter getFileNameExtensionFilter(final String description, final String ext) { + return new FileFilter() { + + @Override + public boolean accept(File f) { + return f.isDirectory() || f.toString().endsWith(ext); + } + + @Override + public String getDescription() { + return description; + } + }; + } + private static File currentFile; + public static final Action SAVE_AS_PNG_ACTION = new CanvasAction("Save as PNG") { + + @Override + public void actionPerformed(ActionEvent e) { + final JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle("Write to PNG"); + fileChooser.setFileFilter(getFileNameExtensionFilter("png files", "png")); + Preferences prefs = Preferences.userNodeForPackage(DasCanvas.class); + String savedir = prefs.get("savedir", null); + if (savedir != null) fileChooser.setCurrentDirectory(new File(savedir)); + if (currentFile != null) fileChooser.setSelectedFile(currentFile); + int choice = fileChooser.showSaveDialog(currentCanvas); + if (choice == JFileChooser.APPROVE_OPTION) { + final DasCanvas canvas = currentCanvas; + String fname = fileChooser.getSelectedFile().toString(); + if (!fname.toLowerCase().endsWith(".png")) fname += ".png"; + final String ffname = fname; + prefs.put("savedir", new File(ffname).getParent()); + currentFile = new File(ffname.substring(0, ffname.length() - 4)); + Runnable run = new Runnable() { + + public void run() { + try { + canvas.writeToPng(ffname); + } catch (java.io.IOException ioe) { + org.das2.util.DasExceptionHandler.handle(ioe); + } + } + }; + new Thread(run, "writePng").start(); + } + } + }; + public static final Action SAVE_AS_SVG_ACTION = new CanvasAction("Save as SVG") { + + @Override + public void actionPerformed(ActionEvent e) { + final JFileChooser fileChooser = new JFileChooser(); + fileChooser.setApproveButtonText("Select File"); + fileChooser.setDialogTitle("Write to SVG"); + fileChooser.setFileFilter(getFileNameExtensionFilter("svg files", "svg")); + Preferences prefs = Preferences.userNodeForPackage(DasCanvas.class); + String savedir = prefs.get("savedir", null); + if (savedir != null) fileChooser.setCurrentDirectory(new File(savedir)); + if (currentFile != null) fileChooser.setSelectedFile(currentFile); + int choice = fileChooser.showSaveDialog(currentCanvas); + if (choice == JFileChooser.APPROVE_OPTION) { + final DasCanvas canvas = currentCanvas; + String fname = fileChooser.getSelectedFile().toString(); + if (!fname.toLowerCase().endsWith(".svg")) fname += ".svg"; + final String ffname = fname; + prefs.put("savedir", new File(ffname).getParent()); + currentFile = new File(ffname.substring(0, ffname.length() - 4)); + Runnable run = new Runnable() { + + public void run() { + try { + canvas.writeToSVG(ffname); + } catch (java.io.IOException ioe) { + org.das2.util.DasExceptionHandler.handle(ioe); + } + } + }; + new Thread(run, "writeSvg").start(); + } + } + }; + public static final Action SAVE_AS_PDF_ACTION = new CanvasAction("Save as PDF") { + + @Override + public void actionPerformed(ActionEvent e) { + final JFileChooser fileChooser = new JFileChooser(); + fileChooser.setApproveButtonText("Select File"); + fileChooser.setDialogTitle("Write to PDF"); + fileChooser.setFileFilter(getFileNameExtensionFilter("pdf files", "pdf")); + Preferences prefs = Preferences.userNodeForPackage(DasCanvas.class); + String savedir = prefs.get("savedir", null); + if (savedir != null) fileChooser.setCurrentDirectory(new File(savedir)); + if (currentFile != null) fileChooser.setSelectedFile(currentFile); + int choice = fileChooser.showDialog(currentCanvas, "Select File"); + if (choice == JFileChooser.APPROVE_OPTION) { + final DasCanvas canvas = currentCanvas; + String fname = fileChooser.getSelectedFile().toString(); + if (!fname.toLowerCase().endsWith(".pdf")) fname += ".pdf"; + final String ffname = fname; + prefs.put("savedir", new File(ffname).getParent()); + currentFile = new File(ffname.substring(0, ffname.length() - 4)); + Runnable run = new Runnable() { + + public void run() { + try { + canvas.writeToPDF(ffname); + } catch (java.io.IOException ioe) { + org.das2.util.DasExceptionHandler.handle(ioe); + } + } + }; + new Thread(run, "writePdf").start(); + } + } + }; + public static final Action EDIT_DAS_PROPERTIES_ACTION = new AbstractAction("DAS Properties") { + + @Override + public void actionPerformed(ActionEvent e) { + org.das2.DasProperties.showEditor(); + } + }; + public static final Action PRINT_ACTION = new CanvasAction("Print...") { + + @Override + public void actionPerformed(ActionEvent e) { + Printable p = currentCanvas.getPrintable(); + PrinterJob pj = PrinterJob.getPrinterJob(); + pj.setPrintable(p); + if (pj.printDialog()) { + try { + pj.print(); + } catch (PrinterException pe) { + Object[] message = {"Error printing", pe.getMessage()}; + JOptionPane.showMessageDialog(null, message, "ERROR", JOptionPane.ERROR_MESSAGE); + } + } + } + }; + public static final Action REFRESH_ACTION = new CanvasAction("Refresh") { + + public void actionPerformed(ActionEvent e) { + DasCanvasComponent[] comps = currentCanvas.getCanvasComponents(); + for (int i = 0; i < comps.length; i++) { + comps[i].update(); + } + } + }; + + public static final Action ABOUT_ACTION = new CanvasAction("About") { + + @Override + public void actionPerformed(ActionEvent e) { + String aboutContent = AboutUtil.getAboutHtml(); + + JOptionPane.showConfirmDialog(currentCanvas, aboutContent, "about das2", JOptionPane.PLAIN_MESSAGE); + } + }; + public final Action PROPERTIES_ACTION = new CanvasAction("properties") { + + public void actionPerformed(ActionEvent e) { + PropertyEditor editor = new PropertyEditor(DasCanvas.this); + editor.showDialog(DasCanvas.this); + } + }; + + public static Action[] getActions() { + return new Action[]{ + ABOUT_ACTION, + REFRESH_ACTION, + EDIT_DAS_PROPERTIES_ACTION, + PRINT_ACTION, + SAVE_AS_PNG_ACTION, + SAVE_AS_SVG_ACTION, + SAVE_AS_PDF_ACTION, + }; + } + private DasApplication application; + private static final Logger logger = DasLogger.getLogger(DasLogger.GRAPHICS_LOG); + private final GlassPane glassPane; + private String dasName; + private JPopupMenu popup; + private boolean editable; + private int printing = 0; + List devicePositionList = new ArrayList(); + org.das2.util.DnDSupport dndSupport; + DasCanvasStateSupport stateSupport; + /** The set of Threads that are currently printing this canvas. + * This set is used to determine of certain operations that are only + * appropriate in printing situations should occur. + */ + private Set printingThreads; + + /** Creates a new instance of DasCanvas + * TODO + */ + public DasCanvas() { + LookAndFeel.installColorsAndFont(this, "Panel.background", "Panel.foreground", "Panel.font"); + application = DasApplication.getDefaultApplication(); + String name = application.suggestNameFor(this); + setName(name); + setOpaque(true); + setLayout(new RowColumnLayout()); + addComponentListener(createResizeListener()); + setBackground(Color.white); + setPreferredSize(new Dimension(400, 300)); + this.setDoubleBuffered(true); + glassPane = new GlassPane(); + add(glassPane, GLASS_PANE_LAYER); + if (!application.isHeadless()) { + popup = createPopupMenu(); + this.addMouseListener(createMouseInputAdapter()); + + try { + dndSupport = new CanvasDnDSupport(); + } catch (SecurityException ex) { + dndSupport = new CanvasDnDSupport(); + } + } + CanvasAction.currentCanvas = this; + stateSupport = new DasCanvasStateSupport(this); + } + + private MouseInputAdapter createMouseInputAdapter() { + return new MouseInputAdapter() { + + public void mousePressed(MouseEvent e) { + Point primaryPopupLocation = e.getPoint(); + CanvasAction.currentCanvas = DasCanvas.this; + if (SwingUtilities.isRightMouseButton(e)) + popup.show(DasCanvas.this, e.getX(), e.getY()); + } + }; + } + + private JPopupMenu createPopupMenu() { + JPopupMenu popup = new JPopupMenu(); + + JMenuItem props = new JMenuItem(PROPERTIES_ACTION); + popup.add(props); + + popup.addSeparator(); + + Action[] actions = getActions(); + for (int iaction = 0; iaction < actions.length; iaction++) { + JMenuItem item = new JMenuItem(); + item.setAction(actions[iaction]); + popup.add(item); + } + + popup.addSeparator(); + + JMenuItem close = new JMenuItem("close"); + close.setToolTipText("close this popup"); + popup.add(close); + + return popup; + } + + /** returns the GlassPane above all other components. This is used for drawing dragRenderers, etc. + * @return + */ + public Component getGlassPane() { + return glassPane; + } + + /** TODO + * @return + */ + public List getDevicePositionList() { + return Collections.unmodifiableList(devicePositionList); + } + private int displayLockCount = 0; + private Object displayLockObject = new String("DISPLAY_LOCK_OBJECT"); + + /** + * Lock the display for this canvas. All Mouse and Key events are captured + * and swallowed by the glass pane. + * + * @param o the Object requesting the lock. + * @see #freeDisplay(Object); + */ + synchronized void lockDisplay(Object o) { + synchronized (displayLockObject) { + displayLockCount++; + //if (displayLockCount == 1) { + // glassPane.setBlocking(true); + //} + } + } + + /** + * Frees the lock the specified object has requested on the display. + * The display will not be freed until all locks have been freed. + * + * @param o the object releasing its lock on the display + * @see #lockDisplay(Object) + */ + synchronized void freeDisplay(Object o) { + synchronized (displayLockObject) { + displayLockCount--; + if (displayLockCount == 0) { + // glassPane.setBlocking(false); + displayLockObject.notifyAll(); + } + } + } + + /** Creates a new instance of DasCanvas with the specified width and height + * TODO + * @param width The width of the DasCanvas + * @param height The height of the DasCanvas + */ + public DasCanvas(int width, int height) { + this(); + setPreferredSize(new Dimension(width, height)); + } + + public DasApplication getApplication() { + return application; + } + + public void setApplication(DasApplication application) { + this.application = application; + } + + /** simply returns getPreferredSize(). + * @return getPreferredSize() + */ + @Override + public Dimension getMaximumSize() { + return getPreferredSize(); + } + + /** paints the canvas itself. If printing, stamps the date on it as well. + * @param gl the Graphics object + */ + @Override + protected void paintComponent(Graphics g1) { + logger.fine("entering DasCanvas.paintComponent"); + + Graphics2D g = (Graphics2D) g1; + + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + textAntiAlias ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + antiAlias ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF); + + + if (!(isPrintingThread() && getBackground().equals(Color.WHITE))) { + g.setColor(getBackground()); + //g.fillRect(0, 0, getWidth(), getHeight()); + Graphics2D g2 = g; + g2.fill(g2.getClipBounds()); + } + g.setColor(getForeground()); + if (isPrintingThread()) { + int width, height; + Date now; + SimpleDateFormat dateFormat; + Font font, oldFont; + FontMetrics metrics; + String s; + + if (!printingTag.equals("")) { + now = new Date(); + dateFormat = new SimpleDateFormat(printingTag); + s = dateFormat.format(now); + + oldFont = g.getFont(); + font = oldFont.deriveFont((float) oldFont.getSize() / 2); + metrics = g.getFontMetrics(font); + width = metrics.stringWidth(s); + height = metrics.getHeight(); + + g.setFont(font); + g.drawString(s, getWidth() - width, getHeight() -height ); + g.setFont(oldFont); + } + } + } + + /** Prints the canvas, scaling and possibly rotating it to improve fit. + * @param printGraphics the Graphics object. + * @param format the PageFormat object. + * @param pageIndex should be 0, since the image will be on one page. + * @return Printable.PAGE_EXISTS or Printable.NO_SUCH_PAGE + */ + @Override + public int print(Graphics printGraphics, PageFormat format, int pageIndex) { + + if (pageIndex != 0) return NO_SUCH_PAGE; + + Graphics2D g2 = (Graphics2D) printGraphics; + double canvasWidth = (double) getWidth(); + double canvasHeight = (double) getHeight(); + double printableWidth = format.getImageableWidth(); + double printableHeight = format.getImageableHeight(); + + g2.translate(format.getImageableX(), format.getImageableY()); + + double canvasMax = Math.max(canvasWidth, canvasHeight); + double canvasMin = Math.min(canvasWidth, canvasHeight); + double printableMax = Math.max(printableWidth, printableHeight); + double printableMin = Math.min(printableWidth, printableHeight); + + double maxScaleFactor = printableMax / canvasMax; + double minScaleFactor = printableMin / canvasMin; + double scaleFactor = Math.min(maxScaleFactor, minScaleFactor); + g2.scale(scaleFactor, scaleFactor); + + if ((canvasWidth == canvasMax) ^ (printableWidth == printableMax)) { + g2.rotate(Math.PI / 2.0); + g2.translate(0.0, -canvasHeight); + } + + print(g2); + + return PAGE_EXISTS; + + } + + /** + * Layout manager for managing the Row, Column layout implemented by swing. + * This will probably change in the future when we move away from using + * swing to handle the DasCanvasComponents. + */ + protected static class RowColumnLayout implements LayoutManager { + + @Override + public void layoutContainer(Container target) { + synchronized (target.getTreeLock()) { + int count = target.getComponentCount(); + for (int i = 0; i < count; i++) { + Component c = target.getComponent(i); + if (c instanceof DasCanvasComponent) { + ((DasCanvasComponent) c).update(); + } else if (c == ((DasCanvas) target).glassPane) { + Dimension size = target.getSize(); + c.setBounds(0, 0, size.width, size.height); + } + } + } + } + + @Override + public Dimension minimumLayoutSize(Container target) { + return new Dimension(0, 0); + } + + @Override + public Dimension preferredLayoutSize(Container target) { + return new Dimension(400, 300); + } + + @Override + public void addLayoutComponent(String name, Component comp) { + } + + @Override + public void removeLayoutComponent(Component comp) { + } + } + + /** + * @return true if the current thread is registered as the one printing this component. + */ + protected final boolean isPrintingThread() { + synchronized (this) { + return printingThreads == null ? false : printingThreads.contains(Thread.currentThread()); + } + } + + @Override + public void print(Graphics g) { + synchronized (this) { + if (printingThreads == null) { + printingThreads = new HashSet(); + } + printingThreads.add(Thread.currentThread()); + } + try { + setOpaque(false); + logger.fine("*** print graphics: " + g); + logger.fine("*** print graphics clip: " + g.getClip()); + + for (int i = 0; i < getComponentCount(); i++) { + Component c = getComponent(i); + if (c instanceof DasPlot) { + DasPlot p = (DasPlot) c; + logger.fine(" DasPlot.isDirty()=" + p.isDirty()); + logger.fine(" DasPlot.getBounds()=" + p.getBounds()); + /* System.err.println(" DasPlot.isDirty()=" + p.isDirty()); + System.err.println(" DasPlot.getBounds()=" + p.getBounds()); */ + } + } + super.print(g); + } finally { + setOpaque(true); + synchronized (this) { + printingThreads.remove(Thread.currentThread()); + } + } + } + + /** Returns an instance of java.awt.print.Printable that can + * be used to render this canvas to a printer. The current implementation + * returns a reference to this canvas. This method is provided so that in + * the future, the canvas can delegate it's printing to another object. + * @return a Printable instance for rendering this component. + */ + public Printable getPrintable() { + return this; + } + + /** + * uses getImage to get an image of the canvas and encodes it + * as a png. + * + * TODO: this should take an output stream, and then a helper class + * manages the file. (e.g. web servers) + * + * @param filename the specified filename + * @throws IOException if there is an error opening the file for writing + */ + public void writeToPng(String filename) throws IOException { + writeToPng(filename, Collections.EMPTY_MAP); + } + + public void writeToPng(String filename, Map txt) throws IOException { + + final FileOutputStream out = new FileOutputStream(filename); + + logger.fine("Enter writeToPng"); + + Image image = getImage(getWidth(), getHeight()); + + DasPNGEncoder encoder = new DasPNGEncoder(); + encoder.addText(DasPNGConstants.KEYWORD_CREATION_TIME, new Date().toString()); + for (String key : txt.keySet()) { + encoder.addText(key, txt.get(key)); + } + try { + logger.fine("Encoding image into png"); + encoder.write((BufferedImage) image, out); + logger.fine("write png file " + filename); + } catch (IOException ioe) { + } finally { + try { + out.close(); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + + } + + public void writeToPDF(String filename) throws IOException { + try { + writeToGraphicsOutput(filename, "org.das2.util.awt.PdfGraphicsOutput"); + DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("write pdf file " + filename); + } catch (NoClassDefFoundError cnfe) { + DasExceptionHandler.handle(new RuntimeException("PDF output is not available", cnfe)); + } catch (ClassNotFoundException cnfe) { + DasExceptionHandler.handle(new RuntimeException("PDF output is not available", cnfe)); + } catch (InstantiationException ie) { + DasExceptionHandler.handleUncaught(ie); + } catch (IllegalAccessException iae) { + DasExceptionHandler.handleUncaught(iae); + } + } + + /** + * write to various graphics devices such as png, pdf and svg. This handles the synchronization and + * parameter settings. + * @param out OutputStream to receive the data + * @param go GraphicsOutput object. + */ + public void writeToGraphicsOutput(OutputStream out, GraphicsOutput go) throws IOException, IllegalAccessException { + go.setOutputStream(out); + go.setSize(getWidth(), getHeight()); + go.start(); + print(go.getGraphics()); + go.finish(); + } + + public void writeToGraphicsOutput(String filename, String graphicsOutput) + throws IOException, ClassNotFoundException, + InstantiationException, IllegalAccessException { + FileOutputStream out = new FileOutputStream(filename); + Class goClass = Class.forName(graphicsOutput); + GraphicsOutput go = (GraphicsOutput) goClass.newInstance(); + writeToGraphicsOutput(out, go); + + } + + /** + * @param filename the specified filename + * @throws IOException if there is an error opening the file for writing + */ + public void writeToSVG(String filename) throws IOException { + try { + writeToGraphicsOutput(filename, "org.das2.util.awt.SvgGraphicsOutput"); + DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("write svg file " + filename); + } catch (ClassNotFoundException cnfe) { + DasExceptionHandler.handle(new RuntimeException("SVG output is not available", cnfe)); + } catch (InstantiationException ie) { + DasExceptionHandler.handleUncaught(ie); + } catch (IllegalAccessException iae) { + DasExceptionHandler.handleUncaught(iae); + } + } + + /** + * returns true if work needs to be done to make the canvas clean. This checks each component's + * isDirty. + * + * @return + */ + public boolean isDirty() { + DasCanvasComponent[] cc= this.getCanvasComponents(); + boolean result= false; + for ( int i=0; iDasCanvasComponent to this canvas. + * @param c the component to be added to this canvas + * Note that the canvas will need to be revalidated after the component + * is added. + * @param row DasRow specifying the layout of the component. + * @param column DasColumn specifying the layout of the component. + */ + public void add(DasCanvasComponent c, DasRow row, DasColumn column) { + if (c.getRow() == DasRow.NULL || c.getRow().getParent() != this) { + c.setRow(row); + } + if (c.getColumn() == DasColumn.NULL || c.getColumn().getParent() != this) { + c.setColumn(column); + } + add(c); + PropertyChangeListener positionListener = new PropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent evt) { + repaint(); + } + }; + + if(row != null) + row.addPropertyChangeListener(positionListener); + + if(column != null) + column.addPropertyChangeListener(positionListener); + } + + /** TODO + * @param comp + * @param constraints + * @param index + */ + @Override + protected void addImpl(Component comp, Object constraints, int index) { + if (comp == null) { + org.das2.util.DasDie.println("NULL COMPONENT"); + Thread.dumpStack(); + return; + } + if (index < 0) index = 0; + + Integer layer = (Integer) ((JComponent) comp).getClientProperty(LAYER_PROPERTY); + if (layer == null) { + if (comp instanceof DasPlot) { + ((DasPlot) comp).putClientProperty(LAYER_PROPERTY, PLOT_LAYER); + } else if (comp instanceof DasAxis) { + ((DasAxis) comp).putClientProperty(LAYER_PROPERTY, AXIS_LAYER); + } else if (comp instanceof Legend) { + ((Legend) comp).putClientProperty(LAYER_PROPERTY, AXIS_LAYER); + } else if (comp instanceof DasAnnotation) { + ((DasAnnotation) comp).putClientProperty(LAYER_PROPERTY, ANNOTATION_LAYER); + } else if (comp instanceof JComponent) { + ((JComponent) comp).putClientProperty(LAYER_PROPERTY, DEFAULT_LAYER); + } + } + super.addImpl(comp, constraints, index); + if (comp instanceof DasCanvasComponent) { + ((DasCanvasComponent) comp).installComponent(); + } + } + + /** + * Sets the preferred width of the canvas to the specified width. + * + * @param width the specified width. + */ + public void setPreferredWidth(int width) { + Dimension pref = getPreferredSize(); + pref.width = width; + setPreferredSize(pref); + if (getParent() != null) ((JComponent) getParent()).revalidate(); + } + + /** Sets the preferred height of the canvas to the specified height. + * + * @param height the specified height + */ + public void setPreferredHeight(int height) { + Dimension pref = getPreferredSize(); + pref.height = height; + setPreferredSize(pref); + if (getParent() != null) ((JComponent) getParent()).revalidate(); + } + + + protected boolean scaleFonts = true; + + /** + * The font used should be the base font scaled based on the canvas size. + * If this is false, then the canvas font is simply the base font. + */ + public static final String PROP_SCALEFONTS = "scaleFonts"; + + public boolean isScaleFonts() { + return scaleFonts; + } + + public void setScaleFonts(boolean scaleFonts) { + boolean oldScaleFonts = this.scaleFonts; + this.scaleFonts = scaleFonts; + setBaseFont(getBaseFont()); + firePropertyChange(PROP_SCALEFONTS, oldScaleFonts, scaleFonts); + } + + private Font baseFont = null; + + /** TODO + * @return + */ + public Font getBaseFont() { + if (baseFont == null) { + baseFont = getFont(); + } + return this.baseFont; + } + + /** + * The base font is the font from which all other fonts should be derived. When the + * canvas is resized, the base font size is scaled. + * @param font the font used to derive all other fonts. + */ + public void setBaseFont(Font font) { + Font oldFont = getFont(); + this.baseFont = font; + if ( scaleFonts ) { + setFont(getFontForSize(getWidth(), getHeight())); + } else { + setFont( font ); + } + firePropertyChange("font", oldFont, getFont()); //TODO: really? + repaint(); + } + private static final int R_1024_X_768 = 1024 * 768; + private static final int R_800_X_600 = 800 * 600; + private static final int R_640_X_480 = 640 * 480; + private static final int R_320_X_240 = 320 * 240; + + private Font getFontForSize(int width, int height) { + int area = width * height; + Font f; + + float baseFontSize = getBaseFont().getSize2D(); + + if (area >= (R_1024_X_768 - R_800_X_600) / 2 + R_800_X_600) { + f = getBaseFont().deriveFont(baseFontSize / 12f * 18f); //new Font("Serif", Font.PLAIN, 18); + } else if (area >= (R_800_X_600 - R_640_X_480) / 2 + R_640_X_480) { + f = getBaseFont().deriveFont(baseFontSize / 12f * 14f); //new Font("Serif", Font.PLAIN, 14); + } else if (area >= (R_640_X_480 - R_320_X_240) / 2 + R_320_X_240) { + f = getBaseFont().deriveFont(baseFontSize / 12f * 12f); //new Font("Serif", Font.PLAIN, 12); + } else if (area >= (R_320_X_240) / 2) { + f = getBaseFont().deriveFont(baseFontSize / 12f * 8f); //new Font("Serif", Font.PLAIN, 8); + } else { + f = getBaseFont().deriveFont(baseFontSize / 12f * 6f); //new Font("Serif", Font.PLAIN, 6); + } + return f; + } + + private ComponentListener createResizeListener() { + return new ComponentAdapter() { + + @Override + public void componentResized(ComponentEvent e) { + Font aFont; + if ( scaleFonts ) { + aFont= getFontForSize(getWidth(), getHeight()); + } else { + aFont= getBaseFont(); + } + if (!aFont.equals(getFont())) { + setFont(aFont); + } + } + }; + } + + /** TODO + * @return + * @param document + */ + @Override + public Element getDOMElement(Document document) { + Element element = document.createElement("canvas"); + Dimension size = getPreferredSize(); + element.setAttribute("name", getDasName()); + element.setAttribute("width", Integer.toString(size.width)); + element.setAttribute("height", Integer.toString(size.height)); + + for (int index = 0; index < devicePositionList.size(); index++) { + Object obj = devicePositionList.get(index); + if (obj instanceof DasRow) { + DasRow row = (DasRow) obj; + element.appendChild(row.getDOMElement(document)); + } else if (obj instanceof DasColumn) { + DasColumn column = (DasColumn) obj; + element.appendChild(column.getDOMElement(document)); + } + } + + Component[] components = getComponents(); + Map elementMap = new LinkedHashMap(); + + //THREE PASS ALGORITHM. + //1. Process all DasAxis components. + // Add all , , elements to elementList. + //2. Process all DasColorBar components. + // Remove all elements that correspond to axis property of colorbars. + // Add all elements to elementList. + //3. Process all DasSpectrogramPlot and DasPlot components. + // Remove all , , , and elements + // that correspond to xAxis, yAxis, and colorbar properties of + // plots spectrograms and spectrogram renderers. + // Add all elements to elementList. + + for (int index = 0; index < components.length; index++) { + if (components[index] instanceof DasAxis) { + DasAxis axis = (DasAxis) components[index]; + elementMap.put(axis.getDasName(), axis.getDOMElement(document)); + } + } + for (int index = 0; index < components.length; index++) { + if (components[index] instanceof DasColorBar) { + DasColorBar colorbar = (DasColorBar) components[index]; + elementMap.put(colorbar.getDasName(), colorbar.getDOMElement(document)); + } + } + for (int index = 0; index < components.length; index++) { + if (components[index] instanceof DasPlot) { + DasPlot plot = (DasPlot) components[index]; + elementMap.remove(plot.getXAxis().getDasName()); + elementMap.remove(plot.getYAxis().getDasName()); + Renderer[] renderers = plot.getRenderers(); + for (int i = 0; i < renderers.length; i++) { + if (renderers[i] instanceof SpectrogramRenderer) { + SpectrogramRenderer spectrogram = (SpectrogramRenderer) renderers[i]; + elementMap.remove(spectrogram.getColorBar().getDasName()); + } + } + elementMap.put(plot.getDasName(), plot.getDOMElement(document)); + } + } + + for (Iterator iterator = elementMap.values().iterator(); iterator.hasNext();) { + Element e = (Element) iterator.next(); + if (e != null) { + element.appendChild(e); + } + } + return element; + } + + /** Process a <canvas> element. + * + * @param form + * @param element The DOM tree node that represents the element + * @throws DasPropertyException + * @throws DasNameException + * @throws ParsedExpressionException + * @return + */ + public static DasCanvas processCanvasElement(Element element, FormBase form) + throws DasPropertyException, DasNameException, DasException, ParsedExpressionException, java.text.ParseException { + try { + Logger log = DasLogger.getLogger(DasLogger.DASML_LOG); + + String name = element.getAttribute("name"); + int width = Integer.parseInt(element.getAttribute("width")); + int height = Integer.parseInt(element.getAttribute("height")); + + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + + DasCanvas canvas = new DasCanvas(width, height); + + NodeList children = element.getChildNodes(); + int childCount = children.getLength(); + for (int index = 0; index < childCount; index++) { + Node node = children.item(index); + log.fine("node=" + node.getNodeName()); + if (node instanceof Element) { + String tagName = node.getNodeName(); + if (tagName.equals("row")) { + DasRow row = DasRow.processRowElement((Element) node, canvas, form); + } else if (tagName.equals("column")) { + DasColumn column = DasColumn.processColumnElement((Element) node, canvas, form); + } else if (tagName.equals("axis")) { + DasAxis axis = DasAxis.processAxisElement((Element) node, form); + canvas.add(axis); + } else if (tagName.equals("timeaxis")) { + DasAxis timeaxis = DasAxis.processTimeaxisElement((Element) node, form); + canvas.add(timeaxis); + } else if (tagName.equals("attachedaxis")) { + DasAxis attachedaxis = DasAxis.processAttachedaxisElement((Element) node, form); + canvas.add(attachedaxis); + } else if (tagName.equals("colorbar")) { + DasColorBar colorbar = DasColorBar.processColorbarElement((Element) node, form); + canvas.add(colorbar); + } else if (tagName.equals("plot")) { + DasPlot plot = DasPlot.processPlotElement((Element) node, form); + canvas.add(plot); + } else if (tagName.equals("spectrogram")) { + DasPlot plot = DasPlot.processPlotElement((Element) node, form); + canvas.add(plot); + } + + } + } + canvas.setDasName(name); + nc.put(name, canvas); + + return canvas; + } catch (org.das2.DasPropertyException dpe) { + if (!element.getAttribute("name").equals("")) { + dpe.setObjectName(element.getAttribute("name")); + } + throw dpe; + } + } + + /** + * @param name + * @param width + * @param height + * @return DasCanvas with a name. + */ + public static DasCanvas createFormCanvas(String name, int width, int height) { + DasCanvas canvas = new DasCanvas(width, height); + if (name == null) { + name = "canvas_" + Integer.toHexString(System.identityHashCode(canvas)); + } + try { + canvas.setDasName(name); + } catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + return canvas; + } + + /** Returns the DasCanvasComponent that contains the (x, y) location. + * If there is no component at that location, this method + * returns null + * @param x the x coordinate + * @param y the y coordinate + * @return the component at the specified point, or null + */ + public DasCanvasComponent getCanvasComponentAt(int x, int y) { + Component[] components = getComponents(); + for (int index = 1; index < components.length; index++) { + Component c = components[index]; + if (c instanceof DasCanvasComponent) { + DasCanvasComponent cc = (DasCanvasComponent) c; + if (cc.getActiveRegion().contains((double) x, (double) y)) { + return cc; + } + } + } + return null; + } + + /** + * Removes the component, specified by index, + * from this container. + * @param index the index of the component to be removed. + */ + @Override + public void remove(int index) { + Component comp = this.getComponent(index); + super.remove(index); + if (comp instanceof DasCanvasComponent) { + ((DasCanvasComponent) comp).uninstallComponent(); + } + } + + private class CanvasDnDSupport extends org.das2.util.DnDSupport { + + CanvasDnDSupport() { + super(DasCanvas.this, DnDConstants.ACTION_COPY_OR_MOVE, null); + } + private List acceptList = Arrays.asList(new DataFlavor[]{ + org.das2.graph.dnd.TransferableCanvasComponent.PLOT_FLAVOR, + org.das2.graph.dnd.TransferableCanvasComponent.AXIS_FLAVOR, + org.das2.graph.dnd.TransferableCanvasComponent.COLORBAR_FLAVOR + }); + + private Rectangle getAxisRectangle(Rectangle rc, Rectangle t, int x, int y) { + if (t == null) { + t = new Rectangle(); + } + int o = getAxisOrientation(rc, x, y); + switch (o) { + case DasAxis.TOP: + t.width = rc.width; + t.height = 3 * getFont().getSize(); + t.x = rc.x; + t.y = rc.y - t.height; + break; + case DasAxis.RIGHT: + t.width = 3 * getFont().getSize(); + t.height = rc.height; + t.x = rc.x + rc.width; + t.y = rc.y; + break; + case DasAxis.LEFT: + t.width = 3 * getFont().getSize(); + t.height = rc.height; + t.x = rc.x - t.width; + t.y = rc.y; + break; + case DasAxis.BOTTOM: + t.width = rc.width; + t.height = 3 * getFont().getSize(); + t.x = rc.x; + t.y = rc.y + rc.height; + break; + default: + throw new RuntimeException("invalid orientation: " + o); + } + return t; + } + + private int getAxisOrientation(Rectangle rc, int x, int y) { + int nx = (x - rc.x) * rc.height; + int ny = (y - rc.y) * rc.width; + int a = rc.width * rc.height; + boolean b = nx + ny < a; + return (nx > ny ? (b ? DasAxis.TOP : DasAxis.RIGHT) : (b ? DasAxis.LEFT : DasAxis.BOTTOM)); + } + + @Override + protected int canAccept(DataFlavor[] flavors, int x, int y, int action) { + glassPane.setAccepting(true); + List flavorList = java.util.Arrays.asList(flavors); + Cell cell = getCellAt(x, y); + Rectangle cellBounds = (cell == null ? null : cell.getCellBounds()); + Rectangle target = glassPane.target; + if (flavorList.contains(TransferableCanvasComponent.COLORBAR_FLAVOR)) { + return action; + } else if (flavorList.contains(TransferableCanvasComponent.AXIS_FLAVOR)) { + if (target != cellBounds && (target == null || !target.equals(cellBounds))) { + if (target != null) { + glassPane.repaint(target.x - 1, target.y - 1, target.width + 2, target.height + 2); + } + if (cellBounds != null) { + target = glassPane.target = getAxisRectangle(cellBounds, target, x, y); + glassPane.repaint(target.x - 1, target.y - 1, target.width + 2, target.height + 2); + } else { + glassPane.target = null; + } + } + return action; + } else if (flavorList.contains(TransferableCanvasComponent.CANVAS_COMPONENT_FLAVOR)) { + if (target != cellBounds && (target == null || !target.equals(cellBounds))) { + if (target != null) { + glassPane.repaint(target.x - 1, target.y - 1, target.width + 2, target.height + 2); + } + target = glassPane.target = cellBounds; + if (cellBounds != null) { + glassPane.repaint(target.x - 1, target.y - 1, target.width + 2, target.height + 2); + } + } + return action; + } + return -1; + } + + @Override + protected void done() { + glassPane.setAccepting(false); + if (glassPane.target != null) { + Rectangle target = glassPane.target; + glassPane.target = null; + glassPane.repaint(target.x - 1, target.y - 1, target.width + 2, target.height + 2); + } + } + + @Override + protected boolean importData(Transferable t, int x, int y, int action) { + boolean success = false; + try { + if (t.isDataFlavorSupported(TransferableCanvasComponent.COLORBAR_FLAVOR)) { + Cell c = getCellAt(x, y); + if (c != null) { + DasCanvasComponent comp = (DasCanvasComponent) t.getTransferData(TransferableCanvasComponent.CANVAS_COMPONENT_FLAVOR); + comp.setRow(c.getRow()); + comp.setColumn(c.getColumn()); + add(comp); + revalidate(); + success = true; + } + } else if (t.isDataFlavorSupported(TransferableCanvasComponent.AXIS_FLAVOR)) { + Cell c = getCellAt(x, y); + if (c != null) { + DasAxis axis = (DasAxis) t.getTransferData(TransferableCanvasComponent.AXIS_FLAVOR); + axis.setRow(c.getRow()); + axis.setColumn(c.getColumn()); + Rectangle cellBounds = c.getCellBounds(); + int orientation = getAxisOrientation(cellBounds, x, y); + axis.setOrientation(orientation); + add(axis); + revalidate(); + success = true; + } + } else if (t.isDataFlavorSupported(TransferableCanvasComponent.CANVAS_COMPONENT_FLAVOR)) { + Cell c = getCellAt(x, y); + if (c != null) { + DasCanvasComponent comp = (DasCanvasComponent) t.getTransferData(TransferableCanvasComponent.CANVAS_COMPONENT_FLAVOR); + comp.setRow(c.getRow()); + comp.setColumn(c.getColumn()); + add(comp); + revalidate(); + success = true; + } + } + } catch (UnsupportedFlavorException ufe) { + } catch (IOException ioe) { + } + return success; + } + + @Override + protected Transferable getTransferable(int x, int y, int action) { + DasCanvasComponent component = DasCanvas.this.getCanvasComponentAt(x, y); + if (component instanceof DasColorBar) { + return new TransferableCanvasComponent((DasColorBar) component); + } else if (component instanceof DasAxis) { + return new TransferableCanvasComponent((DasAxis) component); + } else if (component instanceof DasPlot) { + return new TransferableCanvasComponent((DasPlot) component); + } else { + return null; + } + } + + @Override + protected void exportDone(Transferable t, int action) { + } + } + + /** + * JPanel that lives above all other components, and is capable of blocking keyboard and mouse input from + * all components underneath. + */ + public static class GlassPane extends JPanel implements MouseInputListener, KeyListener { + + boolean blocking = false; + boolean accepting = false; + Rectangle target; + DragRenderer dragRenderer = null; + Point p1 = null, p2 = null; + + public GlassPane() { + setOpaque(false); + setLayout(null); + } + + DasCanvas getCanvas() { + return (DasCanvas) getParent(); + } + + void setBlocking(boolean b) { + if (b != blocking) { + blocking = b; + if (b) { + addMouseListener(this); + addMouseMotionListener(this); + } else { + removeMouseListener(this); + removeMouseMotionListener(this); + } + repaint(); + } + } + + void setAccepting(boolean b) { + if (b != accepting) { + accepting = b; + repaint(); + } + } + + public void setDragRenderer(DragRenderer r, Point p1, Point p2) { + this.dragRenderer = r; + this.p1 = p1; + this.p2 = p2; + } + + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + if (blocking) { + paintLoading(g2); + } + if (((DasCanvas) getParent()).getEditingMode()) { + paintRowColumn(g2); + } + if (accepting && target != null) { + paintDnDTarget(g2); + } + if (dragRenderer != null) { + dragRenderer.renderDrag(g2, p1, p2); + } + } + + private void paintRowColumn(Graphics2D g2) { + g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, + RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); + DasCanvas canvas = getCanvas(); + for (Iterator i = canvas.devicePositionList.iterator(); i.hasNext();) { + DasDevicePosition d = (DasDevicePosition) i.next(); + double minimum = d.getMinimum(); + double maximum = d.getMaximum(); + int cWidth = canvas.getWidth(); + int cHeight = canvas.getHeight(); + int x, y, width, height; + Paint paint; + if (d instanceof DasRow) { + x = 0; + width = cWidth; + y = (int) Math.floor(minimum * cHeight + 0.5); + height = (int) Math.floor(maximum * cHeight + 0.5) - y; + paint = PAINT_ROW; + } else { + x = (int) Math.floor(minimum * cWidth + 0.5); + width = (int) Math.floor(maximum * cWidth + 0.5) - x; + y = 0; + height = cHeight; + paint = PAINT_COLUMN; + } + g2.setPaint(paint); + g2.fillRect(x, y, width, height); + } + } + + private void paintDnDTarget(Graphics2D g2) { + g2.setStroke(STROKE_DASHED); + g2.setPaint(PAINT_SELECTION); + g2.drawRect(target.x + 1, target.y + 1, target.width - 2, target.height - 2); + } + + private void paintLoading(Graphics2D g2) { + g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, + RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); + g2.setColor(new Color(0xdcFFFFFF, true)); + Rectangle rect = g2.getClipBounds(); + if (rect == null) { + g2.fillRect(0, 0, getWidth(), getHeight()); + } else { + g2.fillRect(rect.x, rect.y, rect.width, rect.height); + } + } + + @Override + public void mouseClicked(MouseEvent e) { + } + + @Override + public void mouseDragged(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + @Override + public void mouseMoved(MouseEvent e) { + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + /** Invoked when a key has been pressed. + * See the class description for {@link KeyEvent} for a definition of + * a key pressed event. + */ + @Override + public void keyPressed(KeyEvent e) { + } + + /** Invoked when a key has been released. + * See the class description for {@link KeyEvent} for a definition of + * a key released event. + */ + @Override + public void keyReleased(KeyEvent e) { + } + + /** Invoked when a key has been typed. + * See the class description for {@link KeyEvent} for a definition of + * a key typed event. + */ + @Override + public void keyTyped(KeyEvent e) { + } + } + private HashSet horizontalLineSet = new HashSet(); + private HashSet verticalLineSet = new HashSet(); + private HashSet cellSet = new HashSet(); + + /** TODO + * @param x + * @param y + * @return + */ + public HotLine getLineAt(int x, int y) { + Iterator iterator = horizontalLineSet.iterator(); + while (iterator.hasNext()) { + HotLine line = (HotLine) iterator.next(); + if (y >= line.position - 1 && y <= line.position + 1) { + return line; + } + } + iterator = verticalLineSet.iterator(); + while (iterator.hasNext()) { + HotLine line = (HotLine) iterator.next(); + if (x >= line.position - 1 && x <= line.position + 1) { + return line; + } + } + return null; + } + + /** TODO + * @param x + * @param y + * @return + */ + public Cell getCellAt(int x, int y) { + Cell best = null; + Point bestCenter = null; + Point boxCenter = null; + Iterator iterator = cellSet.iterator(); + while (iterator.hasNext()) { + Cell box = (Cell) iterator.next(); + Rectangle rc = box.rc; + if (rc.contains(x, y)) { + if (best == null) { + best = box; + } else { + if (bestCenter == null) { + bestCenter = new Point(); + boxCenter = new Point(); + } + if (best.rc.contains(rc)) { + best = box; + } else { + bestCenter.setLocation(best.rc.x + best.rc.width / 2, best.rc.y + best.rc.height / 2); + boxCenter.setLocation(rc.x + rc.width / 2, rc.y + rc.height / 2); + int bestDistance = distanceSquared(x, y, bestCenter.x, bestCenter.y); + int boxDistance = distanceSquared(x, y, boxCenter.x, boxCenter.y); + if (boxDistance < bestDistance) { + best = box; + } else if (boxDistance == bestDistance) { + if (rc.width * rc.height < best.rc.width * best.rc.height) { + best = box; + } + } + } + } + } + } + return best; + } + + private static int distanceSquared(int x1, int y1, int x2, int y2) { + return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); + } + + /** + * This method should only be called in a constructor defined in the + * DasDevicePosition class. + * + * @param position the DasDevicePosition object calling this method. + */ + void addDevicePosition(DasDevicePosition position) { + devicePositionList.add(position); + if (position instanceof DasRow) { + addRow((DasRow) position); + } else if (position instanceof DasColumn) { + addColumn((DasColumn) position); + } + } + + private void addRow(DasRow row) { + HotLine min = new HotLine(row, HotLine.MIN); + HotLine max = new HotLine(row, HotLine.MAX); + horizontalLineSet.add(min); + horizontalLineSet.add(max); + Iterator iterator = devicePositionList.iterator(); + while (iterator.hasNext()) { + DasDevicePosition position = (DasDevicePosition) iterator.next(); + if (position instanceof DasColumn) { + DasColumn column = (DasColumn) position; + cellSet.add(new Cell(row, column)); + } + } + } + + private void addColumn(DasColumn column) { + HotLine min = new HotLine(column, HotLine.MIN); + HotLine max = new HotLine(column, HotLine.MAX); + verticalLineSet.add(min); + verticalLineSet.add(max); + Iterator iterator = devicePositionList.iterator(); + while (iterator.hasNext()) { + DasDevicePosition position = (DasDevicePosition) iterator.next(); + if (position instanceof DasRow) { + DasRow row = (DasRow) position; + cellSet.add(new Cell(row, column)); + } + } + } + + /** TODO + * @param position + */ + public void removepwDevicePosition(DasDevicePosition position) { + + devicePositionList.remove(position); + if (position instanceof DasRow) { + removeRow((DasRow) position); + } else if (position instanceof DasColumn) { + removeColumn((DasColumn) position); + } + } + + private void removeRow(DasRow row) { + for (Iterator i = horizontalLineSet.iterator(); i.hasNext();) { + HotLine line = (HotLine) i.next(); + if (line.devicePosition == row) { + i.remove(); + } + } + for (Iterator i = cellSet.iterator(); i.hasNext();) { + Cell cell = (Cell) i.next(); + if (cell.row == row) { + i.remove(); + } + } + } + + private void removeColumn(DasColumn column) { + for (Iterator i = verticalLineSet.iterator(); i.hasNext();) { + HotLine line = (HotLine) i.next(); + if (line.devicePosition == column) { + i.remove(); + } + } + for (Iterator i = cellSet.iterator(); i.hasNext();) { + Cell cell = (Cell) i.next(); + if (cell.column == column) { + i.remove(); + } + } + } + + /** TODO + * @return + */ + @Override + public FormBase getForm() { + Component parent = getParent(); + if (parent instanceof FormComponent) { + return ((FormComponent) parent).getForm(); + } + return null; + } + + /** TODO + * @return + */ + @Override + public boolean getEditingMode() { + return editable; + } + + /** TODO + * @param b + */ + @Override + public void setEditingMode(boolean b) { + if (editable == b) { + return; + } + editable = b; + revalidate(); + } + + /** TODO + * @return + */ + @Override + public org.das2.util.DnDSupport getDnDSupport() { + return dndSupport; + } + + /** TODO + * @param x + * @param y + * @param action + * @param evt + * @return + */ + @Override + public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { + for (int i = 0; i < getComponentCount(); i++) { + if (getComponent(i).getBounds().contains(x, y)) { + dndSupport.startDrag(x, y, action, evt); + return true; + } + } + return false; + } + + /** TODO + * @return + */ + @Override + public String getDasName() { + return dasName; + } + + /** TODO + * @param name + * @throws DasNameException + */ + @Override + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = getDasApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + @Override + public void deregisterComponent() { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + for (Iterator i = devicePositionList.iterator(); i.hasNext();) { + DasDevicePosition dp = (DasDevicePosition) i.next(); + try { + if (nc.get(dp.getDasName()) == dp) { + nc.remove(dp.getDasName()); + } + } catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + for (int index = 0; index < getComponentCount(); index++) { + Component c = getComponent(index); + if (c instanceof DasCanvasComponent) { + DasCanvasComponent cc = (DasCanvasComponent) c; + try { + if (nc.get(cc.getDasName()) == cc) { + nc.remove(cc.getDasName()); + } + } catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + } + try { + if (nc.get(getDasName()) == this) { + nc.remove(getDasName()); + } + } catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + } + + @Override + public DasApplication getDasApplication() { + Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent) p).getDasApplication(); + } else { + return null; + } + } + + @Override + public void registerComponent() throws org.das2.DasException { + try { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + for (Iterator i = devicePositionList.iterator(); i.hasNext();) { + DasDevicePosition dp = (DasDevicePosition) i.next(); + nc.put(dp.getDasName(), dp); + } + for (int index = 0; index < getComponentCount(); index++) { + Component c = getComponent(index); + if (c instanceof DasCanvasComponent) { + DasCanvasComponent cc = (DasCanvasComponent) c; + nc.put(cc.getDasName(), cc); + } + } + nc.put(getDasName(), this); + } + } catch (DasNameException dne) { + deregisterComponent(); + throw dne; + } + } + + /** Support reloading and refreshing all data on the canvas + */ + public void reload(){ + int nLen = getComponentCount(); + Component cmp; + for(int i = 0; i < nLen; i++){ + cmp = getComponent(i); + if( cmp instanceof DasCanvasComponent) + ((DasCanvasComponent)cmp).reload(); + } + } + + public DasCanvasComponent getCanvasComponents(int index) { + return (DasCanvasComponent) getComponent(index + 1); + } + + public DasCanvasComponent[] getCanvasComponents() { + int n = getComponentCount() - 1; + DasCanvasComponent[] result = new DasCanvasComponent[n]; + for (int i = 0; i < n; i++) { + result[i] = getCanvasComponents(i); + } + return result; + } + + @Override + public String toString() { + return "[DasCanvas " + this.getWidth() + "x" + this.getHeight() + " " + this.getDasName() + "]"; + } + + /** TODO */ + public static class HotLine implements PropertyChangeListener { + + /** TODO */ + public static final int MIN = -1; + /** TODO */ + public static final int NONE = 0; + /** TODO */ + public static final int MAX = 1; + int position; + DasDevicePosition devicePosition; + int minOrMax; + + HotLine(DasDevicePosition devicePosition, int minOrMax) { + this.devicePosition = devicePosition; + this.minOrMax = minOrMax; + refresh(); + devicePosition.addPropertyChangeListener((minOrMax == MIN ? "dMinimum" : "dMaximum"), this); + } + + void refresh() { + position = (minOrMax == MIN + ? (int) Math.floor(devicePosition.getDMinimum() + 0.5) + : (int) Math.floor(devicePosition.getDMaximum() + 0.5)); + } + + /** TODO + * @param e + */ + public void propertyChange(PropertyChangeEvent e) { + refresh(); + } + + /** TODO + * @param o + * @return + */ + public boolean equals(Object o) { + if (o instanceof HotLine) { + HotLine h = (HotLine) o; + return h.devicePosition == devicePosition && h.minOrMax == minOrMax; + } + return false; + } + + /** TODO + * @return + */ + public int hashCode() { + return minOrMax * devicePosition.hashCode(); + } + + /** TODO + * @return + */ + public String toString() { + return "{" + devicePosition.getDasName() + (minOrMax == MIN ? ", MIN, " : ", MAX, ") + position + "}"; + } + + /** TODO + * @return + */ + public DasDevicePosition getDevicePosition() { + return devicePosition; + } + + /** TODO + * @return + */ + public int getMinOrMax() { + return minOrMax; + } + } + + /** TODO */ + public static class Cell implements PropertyChangeListener { + + Rectangle rc; + DasRow row; + DasColumn column; + + Cell(DasRow row, DasColumn column) { + this.row = row; + this.column = column; + rc = new Rectangle(); + row.addPropertyChangeListener("dMinimum", this); + row.addPropertyChangeListener("dMaximum", this); + column.addPropertyChangeListener("dMinimum", this); + column.addPropertyChangeListener("dMaximum", this); + rc.x = (int) Math.floor(column.getDMinimum() + 0.5); + rc.y = (int) Math.floor(row.getDMinimum() + 0.5); + rc.width = (int) Math.floor(column.getDMaximum() + 0.5) - rc.x; + rc.height = (int) Math.floor(row.getDMaximum() + 0.5) - rc.y; + } + + /** TODO + * @param e + */ + @Override + public void propertyChange(PropertyChangeEvent e) { + if (e.getSource() == row) { + rc.y = (int) Math.floor(row.getDMinimum() + 0.5); + rc.height = (int) Math.floor(row.getDMaximum() + 0.5) - rc.y; + } else { + rc.x = (int) Math.floor(column.getDMinimum() + 0.5); + rc.width = (int) Math.floor(column.getDMaximum() + 0.5) - rc.x; + } + } + + /** TODO + * @param o + * @return + */ + @Override + public boolean equals(Object o) { + if (o instanceof Cell) { + Cell box = (Cell) o; + return box.row == row && box.column == column; + } + return false; + } + + /** TODO + * @return + */ + @Override + public String toString() { + return "{" + row.getDasName() + " x " + column.getDasName() + ": " + rc.toString() + "}"; + } + + /** TODO + * @return + */ + public Rectangle getCellBounds() { + return new Rectangle(rc); + } + + /** TODO + * @param r + * @return + */ + public Rectangle getCellBounds(Rectangle r) { + if (r == null) { + return getCellBounds(); + } + r.setBounds(rc); + return r; + } + + /** TODO + * @return + */ + public DasRow getRow() { + return row; + } + + /** TODO + * @return + */ + public DasColumn getColumn() { + return column; + } + } + /** + * printingTag is the DateFormat string to use to tag printed images. + */ + private String printingTag = "'UIOWA 'yyyyMMdd"; + + /** + * printingTag is the DateFormat string to use to tag printed images. + * @return Value of property printingTag. + */ + public String getPrintingTag() { + return this.printingTag; + } + + /** + * printingTag is the DateFormat string to use to tag printed images. + * @param printingTag New value of property printingTag. + */ + public void setPrintingTag(String printingTag) { + String old = this.printingTag; + this.printingTag = printingTag; + firePropertyChange("printingTag", old, printingTag); + } + /** + * Holds value of property textAntiAlias. + */ + private boolean textAntiAlias = true; + + /** + * Getter for property textAntiAlias. + * @return Value of property textAntiAlias. + */ + public boolean isTextAntiAlias() { + return this.textAntiAlias; + } + + /** + * Setter for property textAntiAlias. + * @param textAntiAlias New value of property textAntiAlias. + */ + public void setTextAntiAlias(boolean textAntiAlias) { + boolean old = this.textAntiAlias; + this.textAntiAlias = textAntiAlias; + firePropertyChange("textAntiAlias", old, textAntiAlias); + } + /** + * Holds value of property antiAlias. + */ + private boolean antiAlias = "on".equals(DasProperties.getInstance().get("antiAlias")); + + /** + * Getter for property antiAlias. + * @return Value of property antiAlias. + */ + public boolean isAntiAlias() { + return this.antiAlias; + } + + /** + * Setter for property antiAlias. + * @param antiAlias New value of property antiAlias. + */ + public void setAntiAlias(boolean antiAlias) { + boolean old = this.antiAlias; + this.antiAlias = antiAlias; + firePropertyChange("antiAlias", old, antiAlias); + } + private boolean fitted; + + /** + * If true, and the canvas was added to a scrollpane, the canvas + * will size itself to fit within the scrollpane. + * + * @return value of fitted property + */ + public boolean isFitted() { + return fitted; + } + + public void setFitted(boolean fitted) { + boolean oldValue = this.fitted; + this.fitted = fitted; + firePropertyChange("fitted", oldValue, fitted); + revalidate(); + } + + @Override + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); + } + + @Override + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + switch (orientation) { + case SwingConstants.HORIZONTAL: + return visibleRect.width / 10; + case SwingConstants.VERTICAL: + return visibleRect.height / 10; + default: + return 10; + } + } + + @Override + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + switch (orientation) { + case SwingConstants.HORIZONTAL: + return visibleRect.width; + case SwingConstants.VERTICAL: + return visibleRect.height; + default: + return 10; + } + } + + @Override + public boolean getScrollableTracksViewportWidth() { + return fitted; + } + + @Override + public boolean getScrollableTracksViewportHeight() { + return fitted; + } + + public void registerPendingChange(Object client, Object lockObject) { + stateSupport.registerPendingChange(client, lockObject); + } + + public void performingChange(Object client, Object lockObject) { + stateSupport.performingChange(client, lockObject); + } + + public void changePerformed(Object client, Object lockObject) { + stateSupport.changePerformed(client, lockObject); + } + + public boolean isPendingChanges() { + return stateSupport.isPendingChanges(); + } +} diff --git a/dasCore/src/main/java/org/das2/graph/DasCanvasComponent.java b/dasCore/src/main/java/org/das2/graph/DasCanvasComponent.java new file mode 100755 index 000000000..4fd00dd25 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasCanvasComponent.java @@ -0,0 +1,504 @@ +/* File: DasCanvasComponent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.DasApplication; +import org.das2.event.DasMouseInputAdapter; +import org.das2.event.MouseModule; +import org.das2.graph.event.DasUpdateListener; +import org.das2.system.DasLogger; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.logging.*; +import org.das2.components.propertyeditor.Editable; +import org.das2.components.propertyeditor.PropertyEditor; + +/** + * + * @author eew + */ +public abstract class DasCanvasComponent extends JComponent implements Editable { + + private Logger logger= DasLogger.getLogger(DasLogger.GUI_LOG); + + protected static abstract class CanvasComponentAction extends DasCanvas.CanvasAction { + private static DasCanvasComponent currentCanvasComponent; + public CanvasComponentAction(String label) { + super(label); + } + public static DasCanvasComponent getCurrentComponent() { + return currentCanvasComponent; + } + } + + private static final MouseListener currentComponentListener = new MouseAdapter() { + public void mousePressed(MouseEvent e) { + DasCanvasComponent dcc; + if (e.getSource() instanceof DasCanvasComponent) { + dcc = (DasCanvasComponent)e.getComponent(); + } else { + dcc = (DasCanvasComponent)SwingUtilities.getAncestorOfClass(DasCanvasComponent.class, e.getComponent()); + } + CanvasComponentAction.currentCanvasComponent = dcc; + DasCanvas canvas = dcc.getCanvas(); + DasCanvas.CanvasAction.currentCanvas = canvas; + } + }; + + public static final Action PROPERTIES_ACTION = new CanvasComponentAction("Properties") { + public void actionPerformed(ActionEvent e) { + if (getCurrentComponent() != null) { + getCurrentComponent().showProperties(); + } + } + }; + + private DasRow row; + private DasColumn column; + private ResizeListener rl; + protected DasMouseInputAdapter mouseAdapter; + private String dasName; + + /** + * constructs a DasCanvasComponent, creating the + * DasMouseInputAdapter for it and assigning a + * default name to it. + */ + public DasCanvasComponent() { + setOpaque(false); + rl = new ResizeListener(); + + row= DasRow.NULL; + column= DasColumn.NULL; + + setDasMouseInputAdapter( new DasMouseInputAdapter(this) ); + + try { + String name= DasApplication.getDefaultApplication().suggestNameFor(this); + setDasName(name); + } catch (org.das2.DasNameException dne) { + } + } + + /** + * Add the MouseModule to the list of MouseModules + * attached to the component via the DasMouseInputAdapter. + * MouseModules will appear the in the order that they + * are added. + */ + public void addMouseModule(MouseModule module) { + mouseAdapter.addMouseModule(module); + } + + /** + * Remove the MouseModule from the list of MouseModules + * attached to the component via the DasMouseInputAdapter. + */ + public void removeMouseModule(MouseModule module) { + mouseAdapter.removeMouseModule(module); + } + + + /** + * accessor for the DasRow used for positioning the component. + * @return DasRow used for positioning the component. + */ + public DasRow getRow() { + return row; + } + + /** + * accessor for the DasColumn used for positioning the component. + * @return DasColumn used for positioning the component. + */ + public DasColumn getColumn() { + return column; + } + + /** Called by the DasCanvas layout manager to request this component + * to set its bounds. + */ + public void resize() { + if (column == DasColumn.NULL || row == DasRow.NULL ) { + logger.warning("Null row and/or column in resize: row=" + row + + " column=" + column); + } else { + setBounds(column.getDMinimum(),row.getDMinimum(), + (column.getDMaximum()-column.getDMinimum()), + (row.getDMaximum()-row.getDMinimum())); + } + } + + @Override + public void setBounds(int x, int y, int width, int height) { + if ( getDasName().startsWith("plot_") ) { + //new Exception().printStackTrace(); + //System.err.println( getDasName() + " setBounds(" + new Rectangle(x, y, width, height) + ")" ); + } + super.setBounds(x, y, width, height); + } + + @Override + public void setBounds(Rectangle r) { + //if ( getDasName().startsWith("plot_") ) System.err.println( getDasName() + " setBounds(" + r ); + super.setBounds(r); + } + + + /** + * class for handling resize events. + */ + private class ResizeListener implements DasUpdateListener { + public void update(org.das2.graph.event.DasUpdateEvent e) { + logger.fine("component row or column moved: "+e.getSource()); + markDirty(); + DasCanvasComponent.this.update(); + } + + } + + /** + * set the DasRow for positioning the component vertically. + * The current row is disconnected, and a propertyChange is + * fired. + */ + public void setRow(DasRow r) { + if (row == r) { + return; + } + Object oldValue = row; + if (row != DasRow.NULL ) { + row.removepwUpdateListener(rl); + } + row = r; + if (row != DasRow.NULL ) { + row.addpwUpdateListener(rl); + } /*else { + throw new IllegalArgumentException("null row is not allowed for the meantime"); + }*/ + firePropertyChange("row", oldValue, r); + } + + /** + * set the DasColumn for positioning the component horizontally. + * The current column is disconnected, and a propertyChange is + * fired. + */ + public void setColumn(DasColumn c) { + if (column == c) { + return; + } + Object oldValue = column; + if (column != DasColumn.NULL ) { + column.removepwUpdateListener(rl); + } + column = c; + if (column != DasColumn.NULL ) { + column.addpwUpdateListener(rl); + } /*else { + throw new IllegalArgumentException("null column is not allowed for the meantime"); + }*/ + firePropertyChange("column", oldValue, c); + } + + /** + * popup the PropertyEditor for editing the state + * of this component. + */ + public void showProperties() { + PropertyEditor editor = new PropertyEditor(this); + editor.showDialog(this); + } + + /** + * @return a concise String representation of the object. + */ + public String toString() { + return getClass().getName()+"'"+getDasName()+"'"; + } + + /** + * This method is called when a DasUpdateEvent is processed. + * The default implementation does nothing. If a subclass + * needs to do any expensive operations involved in updating, + * they should be done by overriding this method so that + * the AWT Event Queue can coalesce update events. + */ + protected void updateImmediately() { + logger.finer("updateImmediately for "+this.getClass().getName() ); + } + + private org.das2.event.DasUpdateEvent devt; + + /** + * posts an update event on the SystemEventQueue, indicating that work needs to be + * done to get the get the component back into a valid state. + */ + public void update() { + logger.finer("update for "+this.getClass().getName() ); + java.awt.EventQueue eventQueue = + Toolkit.getDefaultToolkit().getSystemEventQueue(); + if (devt == null) devt = new org.das2.event.DasUpdateEvent(this); + eventQueue.postEvent(devt); + } + + /** Cause the component to reload, recalculate and repaint. + * This method is meant to support user-initiated data reloading, tick recalculation, + * etc. Override this to provide a refresh support. + */ + public void reload(){ + repaint(); + } + + /** Processes events occurring on this component. By default this + * method calls the appropriate + * process<event type>Event + * method for the given class of event. + *

    Note that if the event parameter is null + * the behavior is unspecified and may result in an + * exception. + * + * @param e the event + * @see java.awt.Component#processComponentEvent + * @see java.awt.Component#processFocusEvent + * @see java.awt.Component#processKeyEvent + * @see java.awt.Component#processMouseEvent + * @see java.awt.Component#processMouseMotionEvent + * @see java.awt.Component#processInputMethodEvent + * @see java.awt.Component#processHierarchyEvent + * @see java.awt.Component#processMouseWheelEvent + * @see #processDasUpdateEvent + */ + protected void processEvent(AWTEvent e) { + super.processEvent(e); + if (e instanceof org.das2.event.DasUpdateEvent) { + processDasUpdateEvent((org.das2.event.DasUpdateEvent)e); + } + } + + protected void processDasUpdateEvent(org.das2.event.DasUpdateEvent e) { + if (isDisplayable()) { + if (isDirty()) { + markClean(); + updateImmediately(); + } + resize(); + repaint(); + } + } + + /** Potentially coalesce an event being posted with an existing + * event. This method is called by EventQueue.postEvent + * if an event with the same ID as the event to be posted is found in + * the queue (both events must have this component as their source). + * This method either returns a coalesced event which replaces + * the existing event (and the new event is then discarded), or + * null to indicate that no combining should be done + * (add the second event to the end of the queue). Either event + * parameter may be modified and returned, as the other one is discarded + * unless null is returned. + *

    + * This implementation of coalesceEvents coalesces + * DasUpdateEvents, returning the existingEvent parameter + * + * @param existingEvent the event already on the EventQueue + * @param newEvent the event being posted to the + * EventQueue + * @return a coalesced event, or null indicating that no + * coalescing was done + */ + protected AWTEvent coalesceEvents(AWTEvent existingEvent, AWTEvent newEvent) { + if (existingEvent instanceof org.das2.event.DasUpdateEvent && newEvent instanceof org.das2.event.DasUpdateEvent) { + return existingEvent; + } + return super.coalesceEvents(existingEvent, newEvent); + } + + protected void installComponent() {} + + protected void uninstallComponent() {} + + public Font getFont() { + return (getParent() == null ? super.getFont() : getParent().getFont()); + } + + /** + * convenient method intended to encourage use of em's. returns the em size for the canvas. + * We define the em size as the height of the component's font. + * @return the height of the component's font. + */ + public double getEmSize() { + return getFont().getSize2D(); + } + + boolean dirty = true; + + /** + * set the dirty flag indicating the state has changed and work is to be + * done to restore a valid state. For example, a DasAxis' minimum is + * changed, so we will need to recalculate the ticks. (But we don't want + * to recalculate the ticks immediately, since the maximum may change + * as well. + */ + void markDirty() { + dirty = true; + } + /** + * @return true if the component has been marked as dirty, meaning + * work needs to be done to restore it to a valid state. + */ + boolean isDirty() { + return dirty; + } + + /** + * clear the dirty flag, indicating the component is in a self-consistent + * state. + */ + void markClean() { + dirty = false; + } + + /** + * get the DasCanvas which contains this DasCanvasComponent. + * @return the DasCanvas which contains this DasCanvasComponent. + */ + public DasCanvas getCanvas() { + return (DasCanvas)getParent(); + } + + /** + * Get the String identifier for the component which identifies + * the component within the application. This name should be + * consistent between sessions of an application, where + * applicable, for persistent state support. + * + * @return the name of the component. + */ + public String getDasName() { + return dasName; + } + + /** + * Set the String identifier for the component which identifies + * the component within the application. This name should be + * consistent between sessions of an application, where + * applicable, for persistent state support. For example, + * "timeAxis1" or "theTimeAxis" + * @param name unique String identifying the component within + * the application. + * @throws org.das2.DasNameException + */ + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = DasApplication.getDefaultApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + /** + * returns the active region of the canvas component, which is not necessarily the bounds. + */ + public Shape getActiveRegion() { + int x = getColumn().getDMinimum(); + int y = getRow().getDMinimum(); + int width = getColumn().getDMaximum() - x; + int height = getRow().getDMaximum() - y; + return new Rectangle(x, y, width, height); + } + + /** + * returns true if the component is suitable context for the point. For example, + * the operator right-clicks at the point, is this point a transparent region of + * the component, and accepting context would be confusing to the operator? This + * was first introduced to support the annotation component, which draws a compact + * background bubble around a message, which is typically smaller than its bounds, + * plus an arrow. + * @param x + * @param y + * @return true if the component accepts the context at this point. + */ + public boolean acceptContext( int x, int y ) { + return true; + } + + /** + * accessor to the DasMouseInputAdapter handling mouse input for the component. + * Note there is also getDasMouseInputAdapter. + * @return DasMouseInputAdaptor handling mouse input for the component. + * @deprecated use getDasMouseInputAdapter instead + */ + public DasMouseInputAdapter getMouseAdapter() { + return mouseAdapter; + } + + public Action[] getActions() { + return new Action[] { + PROPERTIES_ACTION, + }; + } + + /** + * Getter for property dasMouseInputAdapter, the DasMouseInputAdapter handling mouse input for the component. + * @return Value of property dasMouseInputAdapter. + */ + public DasMouseInputAdapter getDasMouseInputAdapter() { + return this.mouseAdapter; + } + + /** + * Setter for property dasMouseInputAdapter. + * @param dasMouseInputAdapter New value of property dasMouseInputAdapter. + */ + public synchronized void setDasMouseInputAdapter(DasMouseInputAdapter dasMouseInputAdapter) { + if ( mouseAdapter!=null ) { + removeMouseListener(mouseAdapter); + removeMouseMotionListener(mouseAdapter); + removeMouseListener(currentComponentListener); + removeKeyListener(mouseAdapter.getKeyAdapter()); + removeMouseWheelListener(mouseAdapter); + } + this.mouseAdapter = dasMouseInputAdapter; + if ( ! DasApplication.getDefaultApplication().isHeadless() ) { + addMouseListener(mouseAdapter); + addMouseMotionListener(mouseAdapter); + addMouseListener(currentComponentListener); + addKeyListener(mouseAdapter.getKeyAdapter()); + addMouseWheelListener(mouseAdapter); + } + + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/DasCanvasComponentInterface.java b/dasCore/src/main/java/org/das2/graph/DasCanvasComponentInterface.java new file mode 100644 index 000000000..cd27c103e --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasCanvasComponentInterface.java @@ -0,0 +1,32 @@ +/* + * DasCanvasComponentInterface.java + * + * Created on November 4, 2004, 5:26 PM + */ + +package org.das2.graph; + +import java.awt.*; + +/** + * All entities on the DasCanvas are DasCanvasComponents. + * @author Jeremy + */ +public interface DasCanvasComponentInterface { + + /** + * this paints the component, the point 0,0 always refers to the upper-left corner + * of the canvas. + * @param g + */ + void paintComponent( Graphics g ); + + /** + * This is called when the canvas is resized or something has happened to make the + * boundries change. This code should call the setBounds( Rectangle ) + */ + void resize(); + + + +} diff --git a/dasCore/src/main/java/org/das2/graph/DasCanvasStateSupport.java b/dasCore/src/main/java/org/das2/graph/DasCanvasStateSupport.java new file mode 100755 index 000000000..cee0ff352 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasCanvasStateSupport.java @@ -0,0 +1,100 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.graph; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +/** + * DasCanvasStateSupport is a registery where objects can tell the canvas they + * intend to mutate the canvas, so canvas clients should know that its result + * may be incomplete. This is first introduced to support server-side + * processing, where in Autoplot the Autolayout feature knows then layout is + * going to be adjusted, but doesn't have enough information to perform the + * function. + * + * Also, this may be used to address bugs like + * https://bugs-pw.physics.uiowa.edu/mantis/view.php?id=303, + * "strange intermediate states in transitions," since canvas painting can + * be halted while the change is being performed. + * + * @author jbf + */ +public class DasCanvasStateSupport { + DasCanvas canvas; + Map changesPending; + private static final Logger logger= Logger.getLogger( "das2.graphics" ); + + DasCanvasStateSupport( DasCanvas canvas ) { + this.canvas= canvas; + this.changesPending= new HashMap(); // client->lock + } + + /** + * the client knows a change will be coming, and the canvas' clients should + * know that its current state will change soon. Example pending changes + * would be: + * layout because tick labels are changing + * data is loading + * + * @param client the object that will perform the change. This allows the + * canvas (and developers) identify who has registered the change. + * @param lockObject object identifying the change. + */ + synchronized void registerPendingChange( Object client, Object lockObject ) { + logger.fine( "registerPendingChange "+lockObject+" by "+client); + Object existingClient= changesPending.get(lockObject); + if ( existingClient!=null ) { + if ( existingClient!=client ) { + throw new IllegalStateException( "lock object in use: "+lockObject + ", by "+changesPending.get(lockObject) ); + } else { + return; + } + } + boolean oldVal= this.isPendingChanges(); + changesPending.put( lockObject, client ); + canvas.firePropertyChange( PROP_PENDINGCHANGES, oldVal, true ); + } + + /** + * performingChange tells that the change is about to be performed. This + * is a place holder in case we use a mutator lock, but currently does + * nothing. + * @param lockObject + */ + synchronized void performingChange( Object client, Object lockObject ) { + + } + + /** + * the change is complete, and as far as the client is concerned, the canvas + * is valid. + * @param lockObject + */ + synchronized void changePerformed( Object client, Object lockObject ) { + logger.fine( "clearPendingChange "+lockObject+" by "+client); + if ( changesPending.get(lockObject)==null ) { + // throw new IllegalStateException( "no such lock object: "+lockObject ); //TODO: handle multiple registrations by the same client + } + boolean oldVal= this.isPendingChanges(); + changesPending.remove(lockObject); + canvas.firePropertyChange( PROP_PENDINGCHANGES, oldVal, true ); + } + + // --- properties + + /** + * someone has registered a pending change. + */ + public static final String PROP_PENDINGCHANGES = "pendingChanges"; + + public boolean isPendingChanges() { + return changesPending.size() > 0; + } + +} + diff --git a/dasCore/src/main/java/org/das2/graph/DasColorBar.java b/dasCore/src/main/java/org/das2/graph/DasColorBar.java new file mode 100644 index 000000000..6257cd705 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasColorBar.java @@ -0,0 +1,657 @@ +/* File: DasColorBar.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.components.propertyeditor.Displayable; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.datum.DatumRangeUtil; +import org.das2.datum.TimeUtil; +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.components.propertyeditor.Enumeration; +import org.das2.dasml.FormBase; +import org.das2.event.DataRangeSelectionEvent; +import org.das2.event.HorizontalSliceSelectionRenderer; +import org.das2.event.MouseModule; +import org.das2.event.MousePointSelectionEvent; +import java.awt.image.IndexColorModel; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import javax.swing.event.EventListenerList; + +/** + * + * @author jbf + */ +public class DasColorBar extends DasAxis { + + public static final String PROPERTY_TYPE = "type"; + public static final String PROPERTY_FILL_COLOR= "fillColor"; + + private BufferedImage image; + private DasColorBar.Type type; + private static int fillColor= Color.LIGHT_GRAY.getRGB(); + private int fillColorIndex; + private int ncolor; + + private static final int COLORTABLE_SIZE=240; + + public DasColorBar( Datum min, Datum max, boolean isLog) { + this(min, max, RIGHT, isLog); + } + + public DasColorBar( Datum min, Datum max, int orientation, boolean isLog) { + super(min, max, orientation, isLog); + setLayout(new ColorBarLayoutManager()); + setType(DasColorBar.Type.COLOR_WEDGE); + } + + public DasColorBar( DatumRange range, int orientation, boolean isLog) { + super(range, orientation); + setLog(isLog); + setLayout(new ColorBarLayoutManager()); + setType(DasColorBar.Type.COLOR_WEDGE); + } + + public DasColorBar( DatumRange range, boolean isLog) { + this(range, RIGHT, isLog); + } + + public int rgbTransform(double x, Units units) { + int icolor= (int)transform(x,units,0, ncolor); + + if ( units.isFill(x) ) { + return fillColor; + } else { + icolor= (icolor<0)?0:icolor; + icolor= (icolor>=ncolor)?(ncolor-1):icolor; + return type.getRGB(icolor); + } + } + + public int indexColorTransform( double x, Units units ) { + if ( units.isFill(x) ) { + return fillColorIndex; + } else { + int icolor= (int)transform(x,units,0,ncolor); + icolor= (icolor<0)?0:icolor; + icolor= (icolor>=ncolor)?(ncolor-1):icolor; + return icolor; + } + } + + public IndexColorModel getIndexColorModel() { + return new IndexColorModel( 8, type.getColorCount()+1, type.colorTable, 0, true, -1, DataBuffer.TYPE_BYTE ); + } + + public int getFillColorIndex() { + return fillColorIndex; + } + + public DasColorBar.Type getType() { + return type; + } + + + public void setType(DasColorBar.Type type) { + if (this.type == type) { + return; + } + DasColorBar.Type oldValue = this.type; + this.type = type; + this.ncolor= type.getColorCount(); + image = null; + fillColorIndex= getType().getColorCount(); + fillColor= getType().getRGB(fillColorIndex); + markDirty(); + update(); + firePropertyChange( PROPERTY_TYPE, oldValue,type); + } + + protected void paintComponent(Graphics g) { + int x = (int)Math.round(getColumn().getDMinimum()); + int y = (int)Math.round(getRow().getDMinimum()); + int width = (int)Math.round(getColumn().getDMaximum()) - x; + int height = (int)Math.round(getRow().getDMaximum()) - y; + //if (image == null || image.getWidth() != width || image.getHeight() != height) { + if (isHorizontal()) { + image = type.getHorizontalScaledImage(width, height); + } else { + image = type.getVerticalScaledImage(width, height); + } + //} + g.translate(-getX(), -getY()); + if (!isHorizontal()) { + y++; + } + g.drawImage(image, x, y, this); + g.translate(getX(), getY()); + super.paintComponent(g); + } + + protected Rectangle getAxisBounds() { + int x = (int)Math.round(getColumn().getDMinimum()); + int y = (int)Math.round(getRow().getDMinimum()); + int width = (int)Math.round(getColumn().getDMaximum()) - x; + int height = (int)Math.round(getRow().getDMaximum()) - y; + Rectangle rc = new Rectangle(x, y, width, height); + Rectangle bounds = super.getAxisBounds(); + bounds.add(rc); + return bounds; + } + + public static DasColumn getColorBarColumn(DasColumn column) { + return new DasColumn( null, column, 1.0, 1.0, 1, 2, 0, 0 ); + } + + /** Process a <colorbar> element. + * + * @param element The DOM tree node that represents the element + */ + static DasColorBar processColorbarElement(Element element, FormBase form) throws org.das2.DasPropertyException,org.das2.DasNameException, java.text.ParseException { + String name = element.getAttribute("name"); + boolean log = element.getAttribute("log").equals("true"); + String unitStr = element.getAttribute("units"); + if (unitStr == null) { + unitStr = ""; + } + Datum dataMinimum; + Datum dataMaximum; + if (unitStr.equals("TIME")) { + String min = element.getAttribute("dataMinimum"); + String max = element.getAttribute("dataMaximum"); + dataMinimum = (min == null || min.equals("") ? TimeUtil.create("1979-02-26") : TimeUtil.create(min)); + dataMaximum = (max == null || max.equals("") ? TimeUtil.create("1979-02-27") : TimeUtil.create(max)); + } else { + Units units = Units.lookupUnits(unitStr); + String min = element.getAttribute("dataMinimum"); + String max = element.getAttribute("dataMaximum"); + dataMinimum = (min == null || min.equals("") ? Datum.create(1.0, units) : Datum.create(Double.parseDouble(min), units)); + dataMaximum = (max == null || max.equals("") ? Datum.create(10.0, units) : Datum.create(Double.parseDouble(max), units)); + } + int orientation = parseOrientationString(element.getAttribute("orientation")); + + DasColorBar cb = new DasColorBar(dataMinimum, dataMaximum, orientation, log); + + String rowString = element.getAttribute("row"); + if (!rowString.equals("")) { + DasRow row = (DasRow)form.checkValue(rowString, DasRow.class, ""); + cb.setRow(row); + } + String columnString = element.getAttribute("column"); + if (!columnString.equals("")) { + DasColumn column = (DasColumn)form.checkValue(columnString, DasColumn.class, ""); + cb.setColumn(column); + } + + cb.setLabel(element.getAttribute("label")); + cb.setOppositeAxisVisible(!element.getAttribute("oppositeAxisVisible").equals("false")); + cb.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); + cb.setType(DasColorBar.Type.parse(element.getAttribute(PROPERTY_TYPE))); + + cb.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, cb); + + return cb; + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("colorbar"); + String minimumStr = getDataMinimum().toString(); + element.setAttribute("dataMinimum", minimumStr); + String maximumStr = getDataMaximum().toString(); + element.setAttribute("dataMaximum", maximumStr); + + element.setAttribute("name", getDasName()); + element.setAttribute("row", getRow().getDasName()); + element.setAttribute("column", getColumn().getDasName()); + + element.setAttribute("label", getLabel()); + element.setAttribute("log", Boolean.toString(isLog())); + element.setAttribute("tickLabelsVisible", Boolean.toString(isTickLabelsVisible())); + element.setAttribute("oppositeAxisVisible", Boolean.toString(isOppositeAxisVisible())); + element.setAttribute("animated", Boolean.toString(isAnimated())); + element.setAttribute("orientation", orientationToString(getOrientation())); + element.setAttribute(PROPERTY_TYPE, getType().toString()); + + return element; + } + + public static DasColorBar createNamedColorBar(String name) { + DasColorBar cb = new DasColorBar(Datum.create(1.0, Units.dimensionless), Datum.create(10.0, Units.dimensionless), false); + if (name == null) { + name = "colorbar_" + Integer.toHexString(System.identityHashCode(cb)); + } + try { + cb.setDasName(name); + } catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + return cb; + } + + public Shape getActiveRegion() { + int x = (int)Math.round(getColumn().getDMinimum()); + int y = (int)Math.round(getRow().getDMinimum()); + int width = (int)Math.round(getColumn().getDMaximum()) - x; + int height = (int)Math.round(getRow().getDMaximum()) - y; + Rectangle bounds = primaryInputPanel.getBounds(); + bounds.translate(getX(), getY()); + Rectangle middleBounds = new Rectangle(x, y, width, height); + bounds.add(middleBounds); + if (isOppositeAxisVisible()) { + Rectangle secondaryBounds = secondaryInputPanel.getBounds(); + secondaryBounds.translate(getX(), getY()); + bounds.add(secondaryBounds); + } + return bounds; + } + + protected class ColorBarLayoutManager extends AxisLayoutManager { + + public void layoutContainer(Container parent) { + super.layoutContainer(parent); + int x = (int)Math.round(getColumn().getDMinimum()); + int y = (int)Math.round(getRow().getDMinimum()); + int width = (int)Math.round(getColumn().getDMaximum()) - x; + int height = (int)Math.round(getRow().getDMaximum()) - y; + Rectangle rc = new Rectangle(x - getX(), y - getY(), width, height); + Rectangle bounds = primaryInputPanel.getBounds(); + bounds.add(rc); + primaryInputPanel.setBounds(bounds); + } + + } + + public static final class Type implements Enumeration, Displayable { + + public static final Type COLOR_WEDGE = new Type("color_wedge"); + //public static final Type BLUE_TO_ORANGE = new Type("blue_to_orange"); + public static final Type GRAYSCALE = new Type("grayscale"); + public static final Type INVERSE_GRAYSCALE = new Type("inverse_grayscale"); + public static final Type WRAPPED_COLOR_WEDGE = new Type("wrapped_color_wedge"); + + private BufferedImage image; + private int[] colorTable; + private final String desc; + private javax.swing.Icon icon; + + private Type(String desc) { + this.desc = desc; + } + + public javax.swing.Icon getListIcon() { + maybeInitializeIcon(); + return icon; + } + + public void maybeInitializeIcon() { + if (icon == null) { + icon = new javax.swing.ImageIcon(getVerticalScaledImage(16, 16)); + } + } + + public String toString() { + return desc; + } + + public String getListLabel() { + return desc; + } + + /* It's understood that the colors indeces from 0 to getColorCount()-1 are the color wedge, and getColorCount() is the fill color. */ + public int getColorCount() { + maybeInitializeColorTable(); + return colorTable.length-1; + } + + public int getRGB(int index) { + maybeInitializeColorTable(); + return colorTable[index]; + } + + public BufferedImage getHorizontalScaledImage(int width, int height) { + maybeInitializeImage(); + BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + AffineTransform at = new AffineTransform(); + at.scale((double)width / (double)getColorCount(), (double)height); + at.rotate(-Math.PI/2.0); + at.translate(-1.0, 0.0); + AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + op.filter(image, scaled); + return scaled; + } + + public BufferedImage getVerticalScaledImage(int width, int height) { + maybeInitializeImage(); + BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + AffineTransform at = new AffineTransform(); + at.scale((double)width, -(double)height / (double)getColorCount()); + at.translate(0.0, -(double)getColorCount()); + AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + op.filter(image, scaled); + return scaled; + } + + private void maybeInitializeImage() { + if (image == null) { + maybeInitializeColorTable(); + image = new BufferedImage(1, getColorCount(), BufferedImage.TYPE_INT_RGB); + image.setRGB(0, 0, 1, getColorCount(), colorTable, 0, 1); + } + } + + // returns a color table with interpolated colors for the wedge from 0 to ncolor-1, and at ncolor, the fill color. + private static int[] makeColorTable( int [] index, int[] red, int[] green, int[] blue, int ncolor, int bottom, int top ) { + // index should go from 0-255. + // truncate when ncolor>COLORTABLE_SIZE + int[] colorTable = new int[ncolor]; + + int ii= 0; + for (int i = 0; i < ncolor-1; i++) { + float comp= ( i - bottom ) * 255 / ( top - bottom ); + if ( comp > index[ii + 1]) { + ii++; + } + + double a; + if ( ii>=(index.length-1) ) { + a= 1; + ii= index.length-2; + } else { + a= (comp-index[ii]) / (double)(index[ii+1]-index[ii]); + } + if ( a>1. ) a=1.; + if ( a<0. ) a=0.; + double rr= (red[ii]*(1-a) + red[ii+1]*a)/(double)255.; + double gg= (green[ii]*(1-a) + green[ii+1]*a)/(double)255.; + double bb= (blue[ii]*(1-a) + blue[ii+1]*a)/(double)255.; + colorTable[i]= new Color((float)rr,(float)gg,(float)bb).getRGB(); + } + + colorTable[ncolor-1]= fillColor; + return colorTable; + } + + private void maybeInitializeColorTable() { + if (colorTable == null) { + initializeColorTable(COLORTABLE_SIZE,0,COLORTABLE_SIZE); + } + } + + private void initializeColorTable( int size, int bottom, int top ) { + if (this == COLOR_WEDGE) { + initializeColorWedge(size, bottom, top); + } else if (this == GRAYSCALE) { + initializeGrayScale(size, bottom, top); + } else if (this == INVERSE_GRAYSCALE) { + initializeInverseGrayScale(size, bottom, top); + } else if (this == WRAPPED_COLOR_WEDGE) { + initializeWrappedColorWedge(size, bottom, top); + //} else if (this == BLUE_TO_ORANGE ) { + // initializeBlueToOrange(size, bottom, top); + } + } + + private void initializeColorWedge( int size, int bottom, int top ) { + int[] index = { 0, 30, 63, 126, 162, 192, 221, 255 }; + int[] red = { 0, 0, 0, 0, 255, 255, 255, 255 }; + int[] green = { 0, 0, 255, 255, 255, 185, 84, 0 }; + int[] blue = { 137, 255, 255, 0, 0, 0, 0, 0 }; + colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); + colorTable[0] = ( colorTable[0] & 0xFFFFFF00 ) | 1; + } + + private void initializeBlueToOrange( int size, int bottom, int top ) { + // cat | awk '{ print $3 "," }' | xargs + int[] index = { 0, 23, 46, 69, 92, 115, 139, 162, 185, 208, 231, 255 }; + int[] red = { 0, 25, 50, 101, 153, 204, 255, 255, 255, 255, 255, 255 }; + int[] green = { 42, 101, 153, 204, 237, 255, 255, 238, 204, 153, 102, 42 }; + int[] blue = { 255, 255, 255, 255, 255, 255, 204, 153, 101, 50, 25, 0 }; + colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); + colorTable[0] = ( colorTable[0] & 0xFFFFFF00 ) | 1; + } + + private void initializeWrappedColorWedge( int size, int bottom, int top ) { + int[] index = { 0, 32, 64, 96, 128, 160, 192, 224, 255, }; + int[] red = { 225, 0, 0, 0, 255, 255, 255, 255, 255, }; + int[] green = { 0, 0, 255, 255, 255, 185, 84, 0, 0, }; + int[] blue = { 225, 255, 255, 0, 0, 0, 0, 0, 255, }; + colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); + } + + private void initializeInverseGrayScale( int size, int bottom, int top ) { + int [] index= { 0, 255 }; + int [] red= { 0, 255 }; + int [] green= { 0, 255 }; + int [] blue= { 0, 255 }; + colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); + } + + private void initializeGrayScale( int size, int bottom, int top ) { + int [] index= { 0, 255 }; + int [] red= { 255, 0 }; + int [] green= { 255, 0 }; + int [] blue= { 255, 0 }; + colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); + } + + public static Type parse(String s) { + if (s.equals("color_wedge")) { + return COLOR_WEDGE; + } else if (s.equals("grayscale")) { + return GRAYSCALE; + } else if (s.equals("inverse_grayscale")) { + return INVERSE_GRAYSCALE; + //} else if (s.equals("blue_to_orange")) { + // return BLUE_TO_ORANGE; + } else { + throw new IllegalArgumentException("invalid DasColorBar.Type string: " + s); + } + } + + } + + public MouseModule getRepaletteMouseModule( Renderer r ) { + return new ColorBarRepaletteMouseModule( r, this ); + } + + public class ColorBarRepaletteMouseModule extends MouseModule { + + DasColorBar colorBar; + Renderer parent; + DatumRange range0; + int lastTopColor; + int lastBottomColor; + + boolean animated0; + int state; + int STATE_IGNORE=300; + int STATE_TOP=200; + int STATE_BOTTOM=100; + + /** Utility field used by event firing mechanism. */ + private EventListenerList listenerList = null; + + public String getLabel() { return "Repalette"; }; + + public ColorBarRepaletteMouseModule( Renderer parent, DasColorBar colorBar ) { + if (colorBar.isHorizontal()) { + throw new IllegalArgumentException("Axis orientation is not vertical"); + } + if ( parent==null ) { + throw new IllegalArgumentException("Parent is null"); + } + this.parent= parent; + // this.dragRenderer= (DragRenderer)HorizontalRangeRenderer.renderer; + this.dragRenderer= new HorizontalSliceSelectionRenderer(parent.getParent()); + this.colorBar= colorBar; + } + + private void setColorBar( int y ) { + + int bottomColor, topColor; + + DatumRange dr; + DasRow row= colorBar.getRow(); + + double alpha= ( row.getDMaximum() - y ) / (1.*row.getHeight()); + + if ( state==STATE_TOP ) { + topColor= (int)( COLORTABLE_SIZE * alpha ); + topColor= Math.max( COLORTABLE_SIZE / 20 + 1, topColor ); + bottomColor= 0; + } else if ( state==STATE_BOTTOM ) { + topColor= COLORTABLE_SIZE; + bottomColor= (int)( COLORTABLE_SIZE * alpha ); + bottomColor= Math.min( COLORTABLE_SIZE * 19 / 20, bottomColor ); + } else { + return; + } + + System.err.println( ""+bottomColor + " "+topColor ); + lastTopColor= topColor; + lastBottomColor= bottomColor; + + colorBar.type.initializeColorTable( COLORTABLE_SIZE, bottomColor, lastTopColor ); + + colorBar.image= null; + colorBar.type.image= null; + colorBar.repaint(); + parent.refreshImage(); + } + + public void mouseReleased( MouseEvent e ) { + if ( state!=STATE_IGNORE ) { + colorBar.setAnimated(animated0); + int lastTopColor= this.lastTopColor; + + DatumRange dr; + double a0= lastBottomColor / ( 1.*COLORTABLE_SIZE ); + double a1= lastTopColor / ( 1.*COLORTABLE_SIZE); + + if ( isLog() ) { + dr= DatumRangeUtil.rescaleLog( range0, a0, a1 ); + } else { + dr= DatumRangeUtil.rescale( range0, a0, a1); + } + + colorBar.setDatumRange( dr ); + + colorBar.type.initializeColorTable( COLORTABLE_SIZE, 0, COLORTABLE_SIZE ); + + colorBar.image= null; + colorBar.type.image= null; + colorBar.repaint(); + parent.refreshImage(); + + } + } + + public void mousePointSelected(MousePointSelectionEvent e) { + setColorBar( e.getY() ); + } + + /** Registers DataRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new EventListenerList(); + } + listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Removes DataRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { + ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); + } + } + } + + public void mousePressed(java.awt.event.MouseEvent e) { + super.mousePressed(e); + if ( DasColorBar.this.getColumn().contains(e.getX()+DasColorBar.this.getX()) ) { + if ( e.getY() + DasColorBar.this.getY() > DasColorBar.this.getRow().getDMiddle() ) { + state= STATE_BOTTOM; + } else { + state= STATE_TOP; + } + animated0= colorBar.isAnimated(); + colorBar.setAnimated(false); + range0= colorBar.getDatumRange(); + } else { + state= STATE_IGNORE; + } + } + + } + + /** + * Getter for property fillColor. + * @return Value of property fillColor. + */ + public Color getFillColor() { + return new Color( this.fillColor, true ); + } + + /** + * Setter for property fillColor. + * @param fillColor New value of property fillColor. + */ + public void setFillColor(Color fillColor) { + Color oldColor= new Color( this.fillColor ); + this.fillColor = fillColor.getRGB(); + this.type.initializeColorTable( COLORTABLE_SIZE, 0, this.type.getColorCount() ); + markDirty(); + update(); + firePropertyChange( PROPERTY_FILL_COLOR, oldColor,fillColor ); + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/DasColumn.java b/dasCore/src/main/java/org/das2/graph/DasColumn.java new file mode 100755 index 000000000..5722c2bdb --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasColumn.java @@ -0,0 +1,193 @@ +/* File: DasColumn.java + * Copyright (C) 2002-2014 The University of Iowa + * Created by: Jeremy Faden + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.dasml.FormBase; +import java.text.ParseException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** Define a vertical region on a DasCanvas. + * Extends from the top to the bottom of the canvas. Canvas components such as + * plots, axes, colorbars and labels use DasColumns and DasRows for positioning. + * + * @author jbf + * @author eew + * @since 2.0 + */ +public class DasColumn extends DasDevicePosition { + + /** Create a new vertical region on a DasCanvas. + * + * @param parent The canvas on which the region will be defined + * @param rLeft a value between 0.0 and 1.0 defining the left boundary of the column + * @param rRight a value between 0.0 and 1.0 defining the right boundary of the column + */ + public DasColumn(DasCanvas parent, double rLeft, double rRight) + { + super(parent,rLeft,rRight,true); + } + + /** Create a new vertical region on a DasCanvas + * + * @param canvas The canvas on which the region will be defined + * @param parent If not null, the column is defined relative to another column. So + * rMin = 0.0 is the left position of the parent column and rMax = 1.0 is the + * right position of the parent column. + * @param rLeft a value between 0.0 and 1.0 defining the left boundary of the column + * @param rRight a value between 0.0 and 1.0 defining the right boundary of the column + * @param emMin Offset from rLeft in "M's", adds with ptMin + * @param emMax Offset from rRight in "M's", adds with ptMax + * @param ptMin Offset from rLeft in pixels, adds with emMin + * @param ptMax Offset from rRight in pixels, adds with emMax + */ + public DasColumn(DasCanvas canvas, DasColumn parent, double rLeft, double rRight, + double emMin, double emMax, int ptMin, int ptMax ) + { + super( canvas, true, parent, rLeft, rRight, emMin, emMax, ptMin, ptMax ); + } + + + /** Makes a new DasColumn by parsing a formatted string + * makes a new DasColumn by parsing a string like "100%-5em+3pt" to get the offsets. + * The three qualifiers are "%", "em", and "pt", but "px" is allowed as well + * as surely people will use that by mistake. If an offset or the normal position + * is not specified, then 0 is used. + * + * @param canvas the canvas for the layout, ignored when a parent DasColumn is used. + * @param parent if non-null, this DasColumn is specified with respect to parent. + * @param minStr a string like "0%+5em" + * @param maxStr a string like "100%-7em" + * @throws IllegalArgumentException if the strings cannot be parsed + */ + public static DasColumn create(DasCanvas canvas, DasColumn parent, String minStr, + String maxStr ) + { + double[] min, max; + try { + min= parseFormatStr( minStr ); + } catch ( ParseException e ) { + throw new IllegalArgumentException("unable to parse min: \""+minStr+"\""); + } + try { + max= parseFormatStr( maxStr ); + } catch ( ParseException e ) { + throw new IllegalArgumentException("unable to parse max: \""+maxStr+"\""); + } + return new DasColumn( canvas, parent, min[0], max[0], min[1], max[1], (int)min[2], (int)max[2] ); + } + + public static final DasColumn NULL= new DasColumn(null,null,0,0,0,0,0,0); + + /** + * @deprecated This created a column that was not attached to anything, so + * it was simply a convenience method that didn't save much effort. + */ + public DasColumn createSubColumn( double pleft, double pright ) { + double left= getMinimum(); + double right= getMaximum(); + double delta= right-left; + return new DasColumn(getCanvas(),left+pleft*delta,left+pright*delta); + } + + public int getWidth() { + return getDMaximum()-getDMinimum(); + } + + public static DasColumn create(DasCanvas parent) { + return new DasColumn(parent,null,0.0,1.0,5,-5,0,0); + } + + public static DasColumn create( DasCanvas parent, int iplot, int nplot ) { + double min= 0.1 + iplot * ( 0.7 ) / nplot; + double max= 0.099 + ( iplot + 1 ) * ( 0.7 ) / nplot; + return new DasColumn( parent, min, max ); + } + + public DasColumn createAttachedColumn(double pleft, double pright) { + return new DasColumn(null,this,pleft,pright,0,0,0,0); + } + + /** + * create a child by parsing spec strings like "50%+3em" + * @throws IllegalArgumentException when the string is malformed. + * @param smin + * @param smax + * @return + */ + public DasColumn createChildColumn( String smin, String smax ) { + try { + double[] min= DasDevicePosition.parseFormatStr(smin); + double[] max= DasDevicePosition.parseFormatStr(smax); + return new DasColumn( null, this, min[0], max[0], min[1], max[1], (int)min[2], (int)max[2] ); + } catch ( ParseException ex ) { + throw new IllegalArgumentException(ex); + } + } + + /** Process a <column7gt; element. + * + * @param element The DOM tree node that represents the element + */ + static DasColumn processColumnElement(Element element, DasCanvas canvas, FormBase form) throws DasException { + String name = element.getAttribute("name"); + double minimum + = Double.parseDouble(element.getAttribute("minimum")); + double maximum + = Double.parseDouble(element.getAttribute("maximum")); + DasColumn column = new DasColumn(canvas, minimum, maximum); + column.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, column); + return column; + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("column"); + element.setAttribute("name", getDasName()); + element.setAttribute("minimum", Double.toString(getMinimum())); + element.setAttribute("maximum", Double.toString(getMaximum())); + return element; + } + + /** + * return the left of the column. + * @return + */ + public int left() { + return getDMinimum(); + } + + /** + * return the right (non-inclusive) of the column. + * @return + */ + public int right() { + return getDMaximum(); + } +} + diff --git a/dasCore/src/main/java/org/das2/graph/DasDevicePosition.java b/dasCore/src/main/java/org/das2/graph/DasDevicePosition.java new file mode 100755 index 000000000..29877d791 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasDevicePosition.java @@ -0,0 +1,583 @@ +/* File: DasDevicePosition.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.DasApplication; +import org.das2.graph.event.DasUpdateEvent; +import org.das2.graph.event.DasUpdateListener; +import java.beans.PropertyChangeEvent; + +import javax.swing.event.EventListenerList; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.util.StringTokenizer; +import org.das2.components.propertyeditor.Editable; +import org.das2.system.MutatorLock; + +/** + * + * @author jbf + */ +public abstract class DasDevicePosition implements Editable, java.io.Serializable { + + protected transient DasCanvas canvas; + protected transient DasDevicePosition parent; + + private double minimum; + private double maximum; + + private boolean isWidth; + + private String dasName; + private transient PropertyChangeSupport propertyChangeDelegate; + + protected EventListenerList listenerList = new EventListenerList(); + + private PropertyChangeListener canvasListener= new PropertyChangeListener() { + public void propertyChange( PropertyChangeEvent ev ) { + if ( DasDevicePosition.this.emMinimum!=0 || DasDevicePosition.this.emMaximum!=0 ) { + revalidate(); + } + } + }; + + protected DasDevicePosition( DasCanvas canvas, boolean isWidth, DasDevicePosition parent, + double minimum, double maximum, + double emMinimum, double emMaximum, + int ptMinimum, int ptMaximum ) { + if ( minimum > maximum ) { + throw new IllegalArgumentException( "minimum>maximum" ); + } + + // isNull indicates this is the NULL row or column. + boolean isNull= ( canvas==null ) && ( parent==null ); + + if ( parent!=null ) { + canvas= parent.getCanvas(); + isWidth= parent.isWidth; + } + + if ( canvas==null & ( ! isNull ) ) { + throw new IllegalArgumentException("parent cannot be null"); + } + + this.canvas = canvas; + this.parent= parent; + this.minimum = minimum; + this.maximum = maximum; + this.emMinimum= emMinimum; + this.emMaximum= emMaximum; + this.ptMinimum= ptMinimum; + this.ptMaximum= ptMaximum; + this.isWidth = isWidth; + + this.dasName = DasApplication.getDefaultApplication().suggestNameFor(this); + this.propertyChangeDelegate = new PropertyChangeSupport(this); + if ( parent!=null ) { + parent.addPropertyChangeListener( new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + revalidate(); + } + } ); + } else { + if (canvas != null) { + canvas.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + revalidate(); + } + }); + canvas.addPropertyChangeListener( "font", canvasListener ); + canvas.addDevicePosition(this); + } + } + if ( !isNull ) { + revalidate(); + } + } + + /** + * parse position strings like "100%-5em+4pt" into npos, emoffset, pt_offset. + * Note px is acceptable, but pt is proper. + * Ems are rounded to the nearest hundredth. + * Percents are returned as normal (0-1) and rounded to the nearest thousandth. + */ + public static double[] parseFormatStr( String s ) throws ParseException { + double[] result= new double[] { 0, 0, 0 }; + StringTokenizer tok= new StringTokenizer( s, "%emptx", true ); + int pos=0; + while ( tok.hasMoreTokens() ) { + String ds= tok.nextToken(); + pos+=ds.length(); + double d= Double.parseDouble(ds); + String u= tok.nextToken(); + pos+=u.length(); + u.trim(); + if ( u.charAt(0)=='%' ) { + result[0]= d/100.; + } else if ( u.equals("e") ) { + String s2= tok.nextToken(); + if ( !s2.equals("m") ) throw new ParseException( "expected m following e",pos); + pos+= s2.length(); + result[1]= d; + } else if ( u.equals("p") ) { + String s2= tok.nextToken(); + if ( !( s2.equals("t") || s2.equals("x") ) ) throw new ParseException( "expected t following p",pos); + pos+= s2.length(); + result[2]= d; + } + } + result[0]= Math.round(result[0]*1000)/1000.; + result[1]= Math.round(result[1]*10)/10.; + return result; + } + + public static void parseLayoutStr( DasDevicePosition pos, String spec ) throws ParseException { + String[] ss= spec.split(","); + double[] pmin= parseFormatStr( ss[0] ); + double[] pmax= parseFormatStr( ss[1] ); + + MutatorLock lock= pos.mutatorLock(); + lock.lock(); + pos.setMinimum(pmin[0]); + pos.setEmMinimum(pmin[1]); + pos.setPtMinimum((int)pmin[2]); + pos.setMaximum(pmax[0]); + pos.setEmMaximum(pmax[1]); + pos.setPtMaximum((int)pmax[2]); + lock.unlock(); + } + + public static String formatLayoutStr( DasDevicePosition pos, boolean min ) { + StringBuffer buf= new StringBuffer(); + DecimalFormat nf2= new DecimalFormat("0.00"); + DecimalFormat nf1= new DecimalFormat("0.0"); + DecimalFormat nf0= new DecimalFormat("0"); + if ( min ) { + if ( pos.getMinimum()!=0 ) buf.append( nf2.format(pos.getMinimum()*100 )+"%" ); + if ( pos.getEmMinimum()!=0 ) buf.append( nf1.format(pos.getEmMinimum() )+"em" ); + if ( pos.getPtMinimum()!=0 ) buf.append( nf0.format(pos.getPtMinimum()) + "pt" ); + } else { + if ( pos.getMaximum()!=0 ) buf.append( nf2.format(pos.getMaximum()*100 )+"%" ); + if ( pos.getEmMaximum()!=0 ) buf.append( nf1.format(pos.getEmMaximum() )+"em" ); + if ( pos.getPtMaximum()!=0 ) buf.append( nf0.format(pos.getPtMaximum()) + "pt" ); + } + if ( buf.length()==0 ) return "0%"; + return buf.toString(); + } + + public DasDevicePosition(DasCanvas parent, double minimum, double maximum, boolean width) { + this( parent, width, null, minimum, maximum, 0., 0., 0, 0 ); + } + + protected DasCanvas getCanvas() { + return this.canvas; + } + + public void setDasName(String name) throws org.das2.DasNameException { + if (name.equals(dasName)) { + return; + } + String oldName = dasName; + dasName = name; + DasApplication app = canvas.getDasApplication(); + if (app != null) { + app.getNameContext().put(name, this); + if (oldName != null) { + app.getNameContext().remove(oldName); + } + } + this.firePropertyChange("name", oldName, name); + } + + public String getDasName() { + return dasName; + } + + /** + * returns the em size for the canvas. We define the em size as the height of the + * font. + * @return the em height in points. + */ + public int getEmSize() { + return canvas.getFont().getSize(); + } + + private int getParentMin() { + if ( parent==null ) { + return 0; + } else { + return parent.getDMinimum(); + } + } + + private int getParentMax() { + if ( parent==null ) { + return isWidth ? canvas.getWidth() : canvas.getHeight(); + } else { + return parent.getDMaximum(); + } + } + + private int dMinimum, dMaximum; + + /** + * recalculates dMinimum and dMaximum becased on the new values, and checks for + * correctness. Note if dMaximum<=dMinimum, we define dMaximum= dMinimum+1. + */ + private void revalidate() { + int oldmin= dMinimum; + int oldmax= dMaximum; + dMinimum= (int)( getParentMin() + minimum*getDeviceSize() + getEmSize() * emMinimum + ptMinimum ); + dMaximum= (int)( getParentMin() + maximum*getDeviceSize() + getEmSize() * emMaximum + ptMaximum ); + if ( dMaximum<=dMinimum ) dMaximum= dMinimum+1; + if ( dMinimum!=oldmin ) firePropertyChange( "dMinimum", oldmin, dMinimum ); + if ( dMaximum!=oldmax ) firePropertyChange( "dMaximum", oldmax, dMaximum ); + if ( dMinimum!=oldmin || dMaximum!=oldmax ) fireUpdate(); + canvas.repaint(); + } + + /** + * returns the pixel position of the minimum of the Row/Column. This is + * the left side of a column and the top of a row. + * @return the pixel position (pixel=point for now) + */ + public int getDMinimum() { + if ( canvas==null && parent==null ) { + String type= isWidth ? "column" : "row"; + throw new RuntimeException("null "+type+", "+type+" was not set before layout"); + } + return dMinimum; + } + + /** + * returns the pixel position of the maximum of the Row/Column. This is + * the right side of a column and the bottom of a row. + * @return the pixel position (pixel=point for now) + */ + public int getDMaximum() { + if ( canvas==null && parent==null ) { + String type= isWidth ? "column" : "row"; + throw new RuntimeException("null "+type+", "+type+" was not set before layout"); + } + return dMaximum; + } + + /** + * return the normal position control of the top/left. + * @return + */ + public double getMinimum() { + return minimum; + } + + public double getMaximum() { + return maximum; + } + + private void setPosition(double minimum, double maximum) { + double oldMin = this.minimum; + double oldMax = this.maximum; + double doldMin = this.minimum; + double doldMax = this.maximum; + this.minimum = Math.min(minimum, maximum); + this.maximum = Math.max(maximum, maximum); + revalidate(); + if (oldMin != this.minimum) { + firePropertyChange("minimum", oldMin, this.minimum); + } + if (oldMax != this.maximum) { + firePropertyChange("maximum", oldMax, this.maximum); + } + } + + public void setDPosition( int minimum, int maximum) { + int pmin= getParentMin(); + int pmax= getParentMax(); + int em= getEmSize(); + int length= pmax - pmin; + double nmin= ( minimum - emMinimum * em - ptMinimum - pmin ) / length; + double nmax= ( maximum - emMaximum * em - ptMaximum - pmin ) / length; + setPosition( nmin, nmax ); + } + + public void setMaximum(double maximum) { + if (maximum == this.maximum) { + return; + } + if (maximum < this.minimum) { + setPosition(maximum, this.minimum); + } else { + double oldValue = this.maximum; + this.maximum = maximum; + firePropertyChange("maximum", oldValue, maximum); + revalidate(); + } + } + + /** + * set the new pixel position of the bottom/right boundry. em and pt offsets + * are not modified, and the normal position is recalculated. + * @param maximum + */ + public void setDMaximum( int maximum) { + int pmin= getParentMin(); + int pmax= getParentMax(); + int em= getEmSize(); + int length= pmax - pmin; + double n= ( maximum - emMaximum * em - ptMaximum ) / length; + setMaximum( n ); + } + + public void setMinimum( double minimum) { + if (minimum == this.minimum) { + return; + } + if (minimum > this.maximum) { + setPosition(this.maximum, minimum); + } else { + double oldValue = this.minimum; + this.minimum = minimum; + firePropertyChange("minimum", oldValue, minimum); + revalidate(); + } + } + + /** + * set the new pixel position of the top/left boundry. em and pt offsets + * are not modified, and the normal position is recalculated. + * @param minimum + */ + public void setDMinimum( int minimum) { + int pmin= getParentMin(); + int pmax= getParentMax(); + int em= getEmSize(); + int length= pmax - pmin; + double n= ( minimum - emMinimum * em - ptMinimum ) / length; + setMinimum( n ); + } + + public DasCanvas getParent() { + return this.canvas; + } + + public void setParent(DasCanvas parent) { + this.canvas= parent; + fireUpdate(); + } + + private boolean valueIsAdjusting= false; + + protected synchronized MutatorLock mutatorLock() { + return new MutatorLock() { + public void lock() { + if ( isValueIsAdjusting() ) { + System.err.println("lock is already set!"); + } + valueIsAdjusting= true; + } + public void unlock() { + valueIsAdjusting= false; + propertyChangeDelegate.firePropertyChange( "mutatorLock", "locked", "unlocked"); + } + }; + } + + + public void addpwUpdateListener(DasUpdateListener l) { + listenerList.add(DasUpdateListener.class, l); + } + + public void removepwUpdateListener(DasUpdateListener l) { + listenerList.remove(DasUpdateListener.class, l); + } + + protected void fireUpdate() { + DasUpdateEvent e = new DasUpdateEvent(this); + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==DasUpdateListener.class) { + ((DasUpdateListener)listeners[i+1]).update(e); + } + } + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + propertyChangeDelegate.addPropertyChangeListener(listener); + } + + public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + propertyChangeDelegate.addPropertyChangeListener(propertyName, listener); + } + + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + propertyChangeDelegate.addPropertyChangeListener(propertyName, listener); + } + + protected void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { + firePropertyChange(propertyName, + (oldValue ? Boolean.TRUE : Boolean.FALSE), + (newValue ? Boolean.TRUE : Boolean.FALSE)); + } + + protected void firePropertyChange(String propertyName, int oldValue, int newValue) { + firePropertyChange(propertyName, new Integer(oldValue), new Integer(newValue)); + } + + protected void firePropertyChange(String propertyName, long oldValue, long newValue) { + firePropertyChange(propertyName, new Long(oldValue), new Long(newValue)); + } + + protected void firePropertyChange(String propertyName, float oldValue, float newValue) { + firePropertyChange(propertyName, new Float(oldValue), new Float(newValue)); + } + + protected void firePropertyChange(String propertyName, double oldValue, double newValue) { + firePropertyChange(propertyName, new Double(oldValue), new Double(newValue)); + } + + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + propertyChangeDelegate.firePropertyChange(propertyName, oldValue, newValue); + } + + protected int getDeviceSize() { + return getParentMax() - getParentMin(); + } + + public static java.awt.Rectangle toRectangle(DasRow row, DasColumn column) { + int xmin=column.getDMinimum(); + int ymin=row.getDMinimum(); + return new java.awt.Rectangle(xmin,ymin, + column.getDMaximum()-xmin, + row.getDMaximum()-ymin); + } + + public String toString() { + //String format="%.1f%%%+.1fem%+dpt"; + //String smin= String.format(format, minimum*100, emMinimum, ptMinimum ); + //String smax= String.format(format, maximum*100, emMaximum, ptMaximum ); + return getClass().getName() + " " + formatLayoutStr(this, true) + "," +formatLayoutStr(this, false) + " [dpos=" + getDMinimum() + "," + getDMaximum() + "]"; + } + + /** + * returns true if ( getDMinimum() <= x ) && ( x <= getDMaximum() ); + * @param x the pixel position + * @return + */ + public boolean contains( int x ) { + return ( getDMinimum() <= x ) && ( x <= getDMaximum() ); + } + + /** + * returns pixel position (device position) of the the middle of the row or column + * @return + */ + public int getDMiddle() { + return (getDMinimum()+getDMaximum())/2; + } + + /** + * property emMinimum, the em (font height * 2/3) offset from the minimum + */ + private double emMinimum; + + /** + * return the em offset that controls the position of the top/left boundry. + * @return + */ + public double getEmMinimum() { + return this.emMinimum; + } + + public void setEmMinimum(double emMinimum) { + double oldValue= this.emMinimum; + this.emMinimum = emMinimum; + firePropertyChange("emMinimum", oldValue, emMinimum); + revalidate(); + } + + /** + * property emMaximum, the em (font height * 2/3) offset from the maximum + */ + private double emMaximum; + + public double getEmMaximum() { + return this.emMaximum; + } + + public void setEmMaximum(double emMaximum) { + double oldValue= this.emMaximum; + this.emMaximum = emMaximum; + firePropertyChange("emMaximum", oldValue, emMaximum); + revalidate(); + } + + private int ptMinimum; + + /** + * return the points offset that controls the position of the top/left boundry. + * @return + */ + public int getPtMinimum() { + return this.ptMinimum; + } + + public void setPtMinimum(int ptMinimum) { + int oldValue= this.ptMinimum; + this.ptMinimum = ptMinimum; + firePropertyChange("ptMinimum", oldValue, ptMinimum); + revalidate(); + } + + /** + * property ptMaximum, the pixel offset from the maximum + */ + private int ptMaximum=0; + + /** + * return the points offset that controls the position of the bottom/right boundry. + * @return + */ + public int getPtMaximum() { + return this.ptMaximum; + } + public void setPtMaximum(int ptMaximum) { + int oldValue= this.ptMaximum; + this.ptMaximum = ptMaximum; + firePropertyChange("ptMaximum", oldValue, ptMaximum); + revalidate(); + } + + public DasDevicePosition getParentDevicePosition() { + return this.parent; + } + + public boolean isValueIsAdjusting() { + return valueIsAdjusting; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/DasEventsIndicator.java b/dasCore/src/main/java/org/das2/graph/DasEventsIndicator.java new file mode 100755 index 000000000..2bfcddd4d --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasEventsIndicator.java @@ -0,0 +1,71 @@ +/* + * DasEventsIndicator.java + * + * Created on April 6, 2004, 10:39 AM + */ + +package org.das2.graph; + +import org.das2.dataset.DataSetUpdateEvent; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.DataSetUpdateListener; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; + +/** + * + * @author Jeremy + * + * The DasEventsIndicator takes a DataSetDescriptor that produces VectorDataSets with a plane + * "xTagWidth." This plane should consist of Datums with the same Units as the xAxis offset Units. + * The y values of the DataSet will be toString'ed and this value will be the tooltip of the bar. + */ +public class DasEventsIndicator extends DasPlot implements DataSetUpdateListener { + EventsRenderer renderer; + + /** Creates a new instance of DasEventsIndicator */ + public DasEventsIndicator( DataSetDescriptor dsd, DasAxis axis, DasAxis yAxis, String planeId ) { + super( axis, yAxis ); + renderer= new EventsRenderer( dsd ) ; + addRenderer( renderer ); + } + + public DasEventsIndicator(DasAxis axis, DasAxis yAxis, String planeId ) { + super( axis, yAxis ); + renderer= new EventsRenderer( ) ; + addRenderer( renderer ); + } + + + /** Creates a new instance of a DasEventsIndicator that doesn't auto-request data + * + */ + public static DasEventsIndicator create(DasAxis xAxis, String planeId){ + DasAxis yAxis= new DasAxis( new DatumRange( 0,1, Units.dimensionless ), DasAxis.VERTICAL ); + yAxis.setVisible(false); + return new DasEventsIndicator(xAxis, yAxis, planeId ); + } + + /** + * This method replaces the old constructor. This is unavoidable + */ + public static DasEventsIndicator create( DataSetDescriptor dsd, DasAxis axis, String planeId ) { + DasAxis yAxis= new DasAxis( new DatumRange( 0,1, Units.dimensionless ), DasAxis.VERTICAL ); + yAxis.setVisible(false); + return new DasEventsIndicator( dsd, axis, yAxis, planeId ); + } + + + public void setDataSetDescriptor( DataSetDescriptor dsd ) { + renderer.setDataSetDescriptor(dsd); + } + + public DataSetDescriptor getDataSetDescriptor( ) { + return renderer.getDataSetDescriptor(); + } + + public void dataSetUpdated(DataSetUpdateEvent e) { + ((XAxisDataLoader)renderer.getDataLoader()).dataSetUpdated(e); + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/DasLabelAxis.java b/dasCore/src/main/java/org/das2/graph/DasLabelAxis.java new file mode 100755 index 000000000..78c7105ba --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasLabelAxis.java @@ -0,0 +1,470 @@ +/* File: DasLabelAxis.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.graph; + +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import org.das2.datum.DatumUtil; +import org.das2.util.GrannyTextRenderer; +import org.das2.datum.format.DatumFormatter; +import org.das2.graph.event.DasUpdateEvent; +import org.das2.graph.event.DasUpdateListener; +import java.awt.*; +import java.text.DecimalFormat; + + +public class DasLabelAxis extends DasAxis implements DasUpdateListener { + + DecimalFormat nfy = null; + DatumVector labels = null; + double[] labelValues = null; + Units labelUnits = null; + int[] labelPositions = null; + DatumFormatter df = null; + int indexMinimum; // first label to be displayed + int indexMaximum; + /** Holds value of property outsidePadding. */ + private int outsidePadding = 5; + /** Holds value of property floppyltemSpacing. */ + private boolean floppyItemSpacing = false; + // last label to be displayed + private void setLabels(DatumVector labels) { + if (labels.getLength() == 0) { + throw new IllegalArgumentException("labels can not be a zero-length array!"); + } + this.labels = labels; + this.labelPositions = new int[labels.getLength()]; + indexMinimum = 0; + indexMaximum = labels.getLength() - 1; + labelUnits = labels.getUnits(); + labelValues = labels.toDoubleArray(labelUnits); + this.df = DatumUtil.bestFormatter(labels); + } + + /** + * vg1pws needed a way to explicitly set this. + */ + public void setLabelFormatter(DatumFormatter df) { + this.df = df; + } + + protected DasLabelAxis(DatumVector labels, DataRange dataRange, int orientation) { + super(dataRange, orientation); + setLabels(labels); + getDataRange().addUpdateListener(this); + } + + public DasLabelAxis(DatumVector labels, int orientation) { + super(labels.get(0), labels.get(labels.getLength() - 1), orientation, false); + setLabels(labels); + getDataRange().addUpdateListener(this); + } + + public int[] getLabelPositions() { + return this.labelPositions; + } + + private void updateTickPositions() { + if (isDisplayable()) { + int nlabel = indexMaximum - indexMinimum + 1; + + int size; + int min; + + double interItemSpacing; + + if (this.getOrientation() == DasAxis.HORIZONTAL) { + size = getColumn().getWidth() - outsidePadding * 2; + interItemSpacing = ((float) size) / nlabel; + if (!floppyItemSpacing) { + interItemSpacing = (int) interItemSpacing; + } + min = (getColumn().getDMinimum() + outsidePadding + (int) (interItemSpacing / 2)); + } else { + size = getRow().getHeight() - outsidePadding * 2; + interItemSpacing = -1 * ((float) size) / nlabel; + if (!floppyItemSpacing) { + interItemSpacing = (int) interItemSpacing; + } + min = getRow().getDMaximum() - outsidePadding + (int) (interItemSpacing / 2); + } + + for (int i = 0; i < labelPositions.length; i++) { + labelPositions[i] = min + (int) (interItemSpacing * ((i - indexMinimum) + 0)); + } + + firePropertyChange("labelPositions", null, labelPositions); + } + } + + @Override + public Datum findTick(Datum xDatum, double direction, boolean minor) { + // somehow tickv.minor is set to non-zero, and Axis.findTick gets messed up. + // This is a work-around... + return xDatum; + } + + @Override + public void updateTickV() { + //super.updateTickV(); + updateTickPositions(); + } + + @Override + public TickVDescriptor getTickV() { + TickVDescriptor result = new TickVDescriptor(); + result.units = getUnits(); + result.tickV = labels.getSubVector(indexMinimum, indexMaximum + 1); + result.minorTickV = DatumVector.newDatumVector(new double[0], result.units); + return result; + } + + @Override + public double transform(double value, Units units) { + if (units != this.labelUnits) { + throw new IllegalArgumentException("units don't match"); + } + int iclose = findClosestIndex(labelValues, value); + return labelPositions[iclose]; + } + + private int findClosestIndex(int[] data, int searchFor) { + int iclose = 0; + double closest = Math.abs(data[iclose] - searchFor); + for (int i = 0; i < labelPositions.length; i++) { + double c1 = Math.abs(data[i] - searchFor); + if (c1 < closest) { + iclose = i; + closest = c1; + } + } + return iclose; + } + + private int findClosestIndex(double[] data, double searchFor) { + int iclose = 0; + double closest = Math.abs(data[iclose] - searchFor); + for (int i = 0; i < labelPositions.length; i++) { + double c1 = Math.abs(data[i] - searchFor); + if (c1 < closest) { + iclose = i; + closest = c1; + } + } + return iclose; + } + + @Override + public Datum invTransform(double d) { + int iclose = findClosestIndex(labelPositions, (int) d); + return labels.get(iclose); + } + + @Override + protected boolean rangeIsAcceptable(DatumRange dr) { + return true; + } + + @Override + protected String tickFormatter( Datum t ) { + return df.format( t ); + } + + @Override + protected String[] tickFormatter(DatumVector tickV, DatumRange datumRange) { + return df.axisFormat( tickV, datumRange ); + } + + public int getInterItemSpace() { + return (int) Math.abs(transform(labels.get(1)) - transform(labels.get(0))); + } + + public int getItemMin(Datum d) { + Units units = d.getUnits(); + double value = d.doubleValue(units); + + int iclose = findClosestIndex(labelValues, units.convertDoubleTo(this.getUnits(), value)); + int tickPosition = labelPositions[iclose]; + int w = getInterItemSpace(); + return tickPosition - w / 2; + } + + public int getItemMax(Datum d) { + int w = getInterItemSpace(); + return getItemMin(d) + w; + } + + public DasAxis createAttachedAxis(DasRow row, DasColumn column) { + DasLabelAxis result = new DasLabelAxis(labels, getDataRange(), this.getOrientation()); + return result; + } + + @Override + public DasAxis createAttachedAxis(int orientation) { + return new DasLabelAxis(labels, getDataRange(), orientation); + } + + @Override + public void update(DasUpdateEvent e) { + double minimum = getDataRange().getMinimum(); + double maximum = getDataRange().getMaximum(); + if (getDataRange().getUnits() != this.labelUnits) { + throw new IllegalArgumentException("units don't match"); + } + + this.indexMinimum = findClosestIndex(labelValues, minimum); + this.indexMaximum = findClosestIndex(labelValues, maximum); + + if (this.indexMinimum > this.indexMaximum) { + int t = this.indexMinimum; + this.indexMaximum = this.indexMinimum; + this.indexMinimum = t; + } + + + } + + @Override + protected void paintHorizontalAxis(java.awt.Graphics2D g) { + boolean bottomTicks = (getOrientation() == BOTTOM || isOppositeAxisVisible()); + boolean bottomTickLabels = (getOrientation() == BOTTOM && isTickLabelsVisible()); + boolean bottomLabel = (getOrientation() == BOTTOM && !axisLabel.equals("")); + boolean topTicks = (getOrientation() == TOP || isOppositeAxisVisible()); + boolean topTickLabels = (getOrientation() == TOP && isTickLabelsVisible()); + boolean topLabel = (getOrientation() == TOP && !axisLabel.equals("")); + + int topPosition = getRow().getDMinimum() - 1; + int bottomPosition = getRow().getDMaximum(); + int DMax = getColumn().getDMaximum(); + int DMin = getColumn().getDMinimum(); + + Font labelFont = getTickLabelFont(); + + double dataMax = dataRange.getMaximum(); + double dataMin = dataRange.getMinimum(); + + TickVDescriptor ticks = getTickV(); + + if (bottomTicks) { + g.drawLine(DMin, bottomPosition, DMax, bottomPosition); + } + if (topTicks) { + g.drawLine(DMin, topPosition, DMax, topPosition); + } + + int tickLengthMajor = labelFont.getSize() * 2 / 3; + int tickLengthMinor = tickLengthMajor / 2; + int tickLength; + + String[] llabels= tickFormatter( ticks.tickV, getDatumRange() ); + + for (int i = 0; i < ticks.tickV.getLength(); i++) { + Datum d = ticks.tickV.get(i); + int w = getInterItemSpace(); + int tickPosition = (int) Math.floor(transform(d) + 0.5) - w / 2; + tickLength = tickLengthMajor; + if (bottomTicks) { + g.drawLine(getItemMin(d), bottomPosition, getItemMin(d), bottomPosition + tickLength); + if (i == ticks.tickV.getLength() - 1) { + g.drawLine(getItemMax(d), bottomPosition, getItemMax(d), bottomPosition + tickLength); + } + if (bottomTickLabels) { + drawLabel(g, d, llabels[i], i, tickPosition + w / 2, bottomPosition + tickLength); + } + } + if (topTicks) { + g.drawLine(getItemMin(d), topPosition, getItemMin(d), topPosition - tickLength); + if (i == ticks.tickV.getLength() - 1) { + g.drawLine(getItemMax(d), topPosition, getItemMax(d), topPosition - tickLength); + } + if (topTickLabels) { + drawLabel(g, d, llabels[i], i, tickPosition + w / 2, topPosition - tickLength); + } + } + } + + + if (!axisLabel.equals("")) { + Graphics2D g2 = (Graphics2D) g.create(); + int titlePositionOffset = getTitlePositionOffset(); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(g2, axisLabel); + int titleWidth = (int) gtr.getWidth(); + int baseline; + int leftEdge; + g2.setFont(getLabelFont()); + if (bottomLabel) { + leftEdge = DMin + (DMax - DMin - titleWidth) / 2; + baseline = bottomPosition + titlePositionOffset; + gtr.draw(g2, (float) leftEdge, (float) baseline); + } + if (topLabel) { + leftEdge = DMin + (DMax - DMin - titleWidth) / 2; + baseline = topPosition - titlePositionOffset; + gtr.draw(g2, (float) leftEdge, (float) baseline); + } + g2.dispose(); + } + } + + @Override + protected void paintVerticalAxis(java.awt.Graphics2D g) { + boolean leftTicks = (getOrientation() == LEFT || isOppositeAxisVisible()); + boolean leftTickLabels = (getOrientation() == LEFT && isTickLabelsVisible()); + boolean leftLabel = (getOrientation() == LEFT && !axisLabel.equals("")); + boolean rightTicks = (getOrientation() == RIGHT || isOppositeAxisVisible()); + boolean rightTickLabels = (getOrientation() == RIGHT && isTickLabelsVisible()); + boolean rightLabel = (getOrientation() == RIGHT && !axisLabel.equals("")); + + int leftPosition = getColumn().getDMinimum() - 1; + int rightPosition = getColumn().getDMaximum(); + int DMax = getRow().getDMaximum(); + int DMin = getRow().getDMinimum(); + + Font labelFont = getTickLabelFont(); + + double dataMax = dataRange.getMaximum(); + double dataMin = dataRange.getMinimum(); + + TickVDescriptor ticks = getTickV(); + + if (leftTicks) { + g.drawLine(leftPosition, DMin, leftPosition, DMax); + } + if (rightTicks) { + g.drawLine(rightPosition, DMin, rightPosition, DMax); + } + + int tickLengthMajor = labelFont.getSize() * 2 / 3; + int tickLengthMinor = tickLengthMajor / 2; + int tickLength; + + String[] llabels= tickFormatter( ticks.tickV, getDatumRange() ); + for (int i = 0; i < ticks.tickV.getLength(); i++) { + Datum datum = ticks.tickV.get(i); + + int w = getInterItemSpace(); + int tickPosition = (getItemMax(datum) + getItemMin(datum)) / 2 - g.getFontMetrics().getAscent() / 5; + if (getRow().contains(tickPosition)) { + tickLength = tickLengthMajor; + if (leftTicks) { + if (i == ticks.tickV.getLength() - 1) { + g.drawLine(leftPosition, getItemMin(datum), leftPosition - tickLength, getItemMin(datum)); + } + g.drawLine(leftPosition, getItemMax(datum), leftPosition - tickLength, getItemMax(datum)); + if (leftTickLabels) { + drawLabel(g, datum, llabels[i], i, leftPosition - tickLength, tickPosition); + } + } + if (rightTicks) { + if (i == ticks.tickV.getLength() - 1) { + g.drawLine(rightPosition, getItemMin(datum), rightPosition + tickLength, getItemMin(datum)); + } + g.drawLine(rightPosition, getItemMax(datum), rightPosition + tickLength, getItemMax(datum)); + if (rightTickLabels) { + drawLabel(g, datum, llabels[i], i, rightPosition + tickLength, tickPosition); + } + } + } + + } + + if (!axisLabel.equals("")) { + Graphics2D g2 = (Graphics2D) g.create(); + int titlePositionOffset = getTitlePositionOffset(); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(g2, axisLabel); + int titleWidth = (int) gtr.getWidth(); + int baseline; + int leftEdge; + g2.setFont(getLabelFont()); + if (leftLabel) { + g2.rotate(-Math.PI / 2.0); + leftEdge = -DMax + (DMax - DMin - titleWidth) / 2; + baseline = leftPosition - titlePositionOffset; + gtr.draw(g2, (float) leftEdge, (float) baseline); + } + if (rightLabel) { + g2.rotate(Math.PI / 2.0); + leftEdge = DMin + (DMax - DMin - titleWidth) / 2; + baseline = -rightPosition - titlePositionOffset; + gtr.draw(g2, (float) leftEdge, (float) baseline); + } + g2.dispose(); + } + + } + + /** Getter for property outsidePadding. + * @return Value of property outsidePadding. + * + */ + public int getOutsidePadding() { + return this.outsidePadding; + } + + /** Setter for property outsidePadding. + * @param outsidePadding New value of property outsidePadding. + * + */ + public void setOutsidePadding(int outsidePadding) { + int oldValue = outsidePadding; + this.outsidePadding = outsidePadding; + firePropertyChange("setOutsidePadding", oldValue, outsidePadding); + updateTickPositions(); + update(); + } + + /** Getter for property floppyltemSpacing. + * @return Value of property floppyltemSpacing. + * + */ + public boolean isFloppyItemSpacing() { + return this.floppyItemSpacing; + } + + /** Setter for property floppyltemSpacing. + * @param floppyItemSpacing New value of property floppyltemSpacing. + * + */ + public void setFloppyItemSpacing(boolean floppyItemSpacing) { + boolean oldValue = this.floppyItemSpacing; + this.floppyItemSpacing = floppyItemSpacing; + firePropertyChange("floppyItemSpacing", oldValue, floppyItemSpacing); + updateTickPositions(); + update(); + } + + @Override + public java.awt.geom.AffineTransform getAffineTransform(Memento memento, java.awt.geom.AffineTransform at) { + return at; + //equals doesn't seem to work + /*if ( this.getMemento().equals( memento ) ) { + return at; + } else { + return null; + }*/ + } +} diff --git a/dasCore/src/main/java/org/das2/graph/DasNumberFormatter.java b/dasCore/src/main/java/org/das2/graph/DasNumberFormatter.java new file mode 100644 index 000000000..ecdebe9da --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasNumberFormatter.java @@ -0,0 +1,192 @@ +/* File: DasNumberFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * + * @author jessica + */ +package org.das2.graph; + +import java.text.DecimalFormat; + +public class DasNumberFormatter { + + /** Creates a new instance of DecFormat */ + public DasNumberFormatter() { + } + + /** + * @param args the command line arguments + */ + + + public static void main(String[] args) { + DecimalFormat dFormatter = new DecimalFormat(); + DecimalFormat dF = new DecimalFormat(); + String result; + double [] b = {123.123, 12.1234, 1.2345, 1234.12, 123.1}; + + dFormatter = createDF(b); + for(int i=0; i max_Num) + max_Num = testNum; + + + //if length of current fracDigit is larger than max, set max to current length + if(len_fracDigits > max_fracVal) + max_fracVal=len_fracDigits; + + intDigits = value[i].substring(0, index); //String of beginning integer digits of # + len_intDigits = intDigits.length(); //get the length of String of integer digits + + //if length of current intDigit is larger than max, set max to current length + if(len_intDigits > max_intVal) + max_intVal=len_intDigits; + }//for (int i=0; i last.length()){ + last = last + 0; + }//while + myPattern = "###." + last; + decFormat = new DecimalFormat(myPattern); + }//if (max_fracVal < 5) + + //if the largest fractional digit in series is 0, then return pattern ###, + //ex. 1.0 will be 1, 10.0 will be 10, it will drop unneeded decimal point and trailing 0 + if(max_Num == 0){ + myPattern = "###"; + decFormat = new DecimalFormat(myPattern); + }//(max_Num == 0) + + //scientific notation + int newMax = len_intDigits + len_fracDigits; + + if(max_intVal > 5){ + String subPattern = ""; + + for(int k=0; k<=newMax; k++){ + subPattern = subPattern + '#'; + if(newMax==3) + subPattern = subPattern + '.'; + }//for(int k=0; k<==nexMax; k++) + + //myPattern = subPattern; + myPattern = "###.####E0"; + decFormat = new DecimalFormat(myPattern); + decFormat.setMaximumIntegerDigits(2); + decFormat.setMinimumIntegerDigits(2); + }//if(max_intVal > 5) + + return decFormat; + }//static DecimalFormat createDF (double [] a) + + + + static final double LOG_10 = Math.log(10); + + static DecimalFormat makeNewDF(double [] a){ + DecimalFormat df = new DecimalFormat(); + String pattern; + int len_a = a.length; + int intDigit, fracDigit, first; + double intBase, fracBase, digit, second; + + for(int k=0; k + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.graph; + +import org.das2.event.MouseModule; +import org.das2.event.HorizontalRangeSelectorMouseModule; +import org.das2.event.LengthDragRenderer; +import org.das2.event.DisplayDataMouseModule; +import org.das2.event.CrossHairMouseModule; +import org.das2.event.BoxZoomMouseModule; +import org.das2.event.VerticalRangeSelectorMouseModule; +import org.das2.event.ZoomPanMouseModule; +import org.das2.dataset.DataSetConsumer; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.VectorUtil; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.CancelledOperationException; +import org.das2.DasProperties; +import org.das2.util.GrannyTextRenderer; +import org.das2.util.DasExceptionHandler; +import org.das2.util.DnDSupport; +import java.beans.PropertyChangeEvent; +import org.das2.util.monitor.NullProgressMonitor; +import org.das2.components.propertyeditor.Displayable; +import org.das2.components.propertyeditor.PropertyEditor; +import org.das2.datum.Datum; +import org.das2.dasml.FormBase; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumVector; +import org.das2.graph.dnd.TransferableRenderer; +import org.das2.system.DasLogger; +import java.awt.image.BufferedImage; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import javax.swing.event.MouseInputAdapter; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.event.*; +import java.awt.geom.AffineTransform; +import java.beans.PropertyChangeListener; +import java.io.*; +import java.io.IOException; +import java.nio.channels.*; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; +import org.das2.DasException; +import org.das2.DasNameException; +import org.das2.DasPropertyException; + +public class DasPlot extends DasCanvasComponent implements DataSetConsumer { + + public static String PROP_TITLE = "title"; + protected DataSetDescriptor dataSetDescriptor; + protected DataSet Data; + private DasAxis xAxis; + private DasAxis yAxis; + DasAxis.Memento xmemento; + DasAxis.Memento ymemento; + protected String offsetTime = ""; + protected String plotTitle = ""; + protected double[] psym_x; + protected double[] psym_y; + protected RebinListener rebinListener = new RebinListener(); + protected PropertyChangeListener ticksListener = new PropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent evt) { + if (drawGrid || drawMinorGrid) { + //invalidateCacheImage(); + } + } + }; + DnDSupport dndSupport; + final static Logger logger = DasLogger.getLogger(DasLogger.GRAPHICS_LOG); + private JMenuItem editRendererMenuItem; + /** + * cacheImage is a cached image that all the renderers have drawn on. This + * relaxes the need for renderers' render method to execute in + * animation-interactive time. + */ + boolean cacheImageValid = false; + BufferedImage cacheImage; + Rectangle cacheImageBounds; + /** + * property preview. If set, the cache image may be scaled to reflect + * the new axis position in animation-interactive time. + */ + boolean preview = false; + private int repaintCount = 0; + private int paintComponentCount = 0; + + public DasPlot(DasAxis xAxis, DasAxis yAxis) { + super(); + + addMouseListener(new MouseInputAdapter() { + + public void mousePressed(MouseEvent e) { + //if (e.getButton() == MouseEvent.BUTTON3) { + if (editRendererMenuItem != null) { + //TODO: check out SwingUtilities, I think this is wrong: + int ir = findRendererAt(getX() + e.getX(), getY() + e.getY()); + editRendererMenuItem.setText("Renderer Properties"); + if (ir > -1) { + editRendererMenuItem.setEnabled(true); + Renderer r = renderers.get(ir); + if (r instanceof Displayable) { + Displayable d = (Displayable) r; + editRendererMenuItem.setIcon(d.getListIcon()); + } else { + editRendererMenuItem.setIcon(null); + } + setFocusRenderer(r); + } else { + editRendererMenuItem.setEnabled(false); + editRendererMenuItem.setIcon(null); + setFocusRenderer(null); + } + } + //} + } + }); + + setOpaque(false); + + this.renderers = new ArrayList(); + this.xAxis = xAxis; + if (xAxis != null) { + if (!xAxis.isHorizontal()) { + throw new IllegalArgumentException("xAxis is not horizontal"); + } + xAxis.addPropertyChangeListener("dataMinimum", rebinListener); + xAxis.addPropertyChangeListener("dataMaximum", rebinListener); + xAxis.addPropertyChangeListener(DasAxis.PROPERTY_DATUMRANGE, rebinListener); + xAxis.addPropertyChangeListener("log", rebinListener); + xAxis.addPropertyChangeListener(DasAxis.PROPERTY_TICKS, ticksListener); + } + this.yAxis = yAxis; + if (yAxis != null) { + if (yAxis.isHorizontal()) { + throw new IllegalArgumentException("yAxis is not vertical"); + } + yAxis.addPropertyChangeListener("dataMinimum", rebinListener); + yAxis.addPropertyChangeListener("dataMaximum", rebinListener); + yAxis.addPropertyChangeListener(DasAxis.PROPERTY_DATUMRANGE, rebinListener); + yAxis.addPropertyChangeListener("log", rebinListener); + xAxis.addPropertyChangeListener(DasAxis.PROPERTY_TICKS, ticksListener); + } + + if (!"true".equals(DasApplication.getProperty("java.awt.headless", "false"))) { + addDefaultMouseModules(); + } + } + + /** + * returns the Renderer with the current focus. Clicking on a trace sets the focus. + */ + protected Renderer focusRenderer = null; + public static final String PROP_FOCUSRENDERER = "focusRenderer"; + + public Renderer getFocusRenderer() { + return focusRenderer; + } + + public void setFocusRenderer(Renderer focusRenderer) { + Renderer oldFocusRenderer = this.focusRenderer; + this.focusRenderer = focusRenderer; + firePropertyChange(PROP_FOCUSRENDERER, oldFocusRenderer, focusRenderer); + } + + private void drawLegend(Graphics2D graphics) { + + int em = (int) getEmSize(); + int msgx, msgy; + + Color backColor = GraphUtil.getRicePaperColor(); + Rectangle boundRect = null; + Rectangle mrect; + + em = (int) getEmSize(); + + msgx = xAxis.getColumn().getDMiddle() + em; + msgy = yAxis.getRow().getDMinimum() + em; + + int maxIconWidth = 0; + for (int i = 0; i < legendElements.size(); i++) { + LegendElement le = legendElements.get(i); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(graphics, String.valueOf(le.label)); // protect from nulls, which seems to happen + mrect = gtr.getBounds(); + mrect.translate(msgx, msgy); + maxIconWidth = Math.max(maxIconWidth, le.icon.getIconWidth()); + mrect.height = Math.max(mrect.height, le.icon.getIconHeight()); + if (boundRect == null) { + boundRect = mrect; + } else { + boundRect.add(mrect); + } + msgy += mrect.getHeight(); + } + + mrect = new Rectangle(boundRect); + int iconColumnWidth = maxIconWidth + em / 4; + mrect.width += iconColumnWidth; + mrect.x = xAxis.getColumn().getDMaximum() - em - mrect.width; + boundRect.x= mrect.x; + + graphics.setColor(backColor); + graphics.fillRoundRect( mrect.x - em / 4 , mrect.y, mrect.width + em / 2, mrect.height, 5, 5); + graphics.setColor(getForeground()); + graphics.drawRoundRect( mrect.x - em / 4 , mrect.y, mrect.width + em / 2, mrect.height, 5, 5); + + msgx = xAxis.getColumn().getDMaximum() - boundRect.width - em ; + msgy = yAxis.getRow().getDMinimum() + em; + + for (int i = 0; i < legendElements.size(); i++) { + LegendElement le = legendElements.get(i); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(graphics, String.valueOf(le.label)); // protect from nulls, which seems to happen + mrect = gtr.getBounds(); + mrect.translate(msgx, msgy); + gtr.draw(graphics, msgx, msgy); + Rectangle imgBounds= new Rectangle( msgx - (le.icon.getIconWidth() + em / 4), + msgy - (int) (mrect.getHeight() / 2 + le.icon.getIconHeight() / 2), + le.icon.getIconWidth(), le.icon.getIconHeight() ); + graphics.drawImage( le.icon.getImage(), imgBounds.x, imgBounds.y, null ); + msgy += mrect.getHeight(); + mrect.add( imgBounds ); + le.bounds= mrect; + } + + } + + private void drawMessages(Graphics2D graphics) { + Font font0 = graphics.getFont(); + int msgem = (int) Math.max(8, font0.getSize2D() / 2); + graphics.setFont(font0.deriveFont((float) msgem)); + int em = (int) getEmSize(); + + int msgx = xAxis.getColumn().getDMinimum() + em; + int msgy = yAxis.getRow().getDMinimum() + em; + + Color warnColor = new Color(255, 255, 100, 200); + Color errorColor = new Color(255, 140, 140, 200); + for (int i = 0; i < messages.size(); i++) { + MessageDescriptor message = (MessageDescriptor) messages.get(i); + + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(graphics, String.valueOf(message.text)); // protect from nulls, which seems to happen + Rectangle mrect = gtr.getBounds(); + mrect.translate(msgx, msgy); + Color backColor = GraphUtil.getRicePaperColor(); + if (message.messageType == DasPlot.WARNING) { + backColor = warnColor; + } else if (message.messageType == DasPlot.ERROR) { + backColor = errorColor; + } + graphics.setColor(backColor); + graphics.fillRoundRect(mrect.x - em / 4, mrect.y, mrect.width + em / 2, mrect.height, 5, 5); + graphics.setColor(getForeground()); + graphics.drawRoundRect(mrect.x - em / 4, mrect.y, mrect.width + em / 2, mrect.height, 5, 5); + gtr.draw(graphics, msgx, msgy); + message.bounds = mrect; + + msgy += gtr.getHeight() + msgem / 2; + } + } + + private void maybeDrawGrid(Graphics2D plotGraphics) { + Color gridColor = new Color(128, 128, 128, 70); + Color minorGridColor = new Color(128, 128, 128, 40); + + if (drawMinorGrid) { + DatumVector xticks = null; + DatumVector yticks = null; + if (getXAxis().isVisible()) { + xticks = getXAxis().getTickV().getMinorTicks(); + } + if (getYAxis().isVisible()) { + yticks = getYAxis().getTickV().getMinorTicks(); + } + plotGraphics.setColor(minorGridColor); + drawGrid(plotGraphics, xticks, yticks); + } + + if (drawGrid) { + DatumVector xticks = null; + DatumVector yticks = null; + if (getXAxis().isVisible()) { + xticks = getXAxis().getTickV().getMajorTicks(); + } + if (getYAxis().isVisible()) { + yticks = getYAxis().getTickV().getMajorTicks(); + } + plotGraphics.setColor(gridColor); + drawGrid(plotGraphics, xticks, yticks); + } + + } + + private void drawCacheImage(Graphics2D plotGraphics) { + + /* clear all the messages */ + messages = new ArrayList(); + legendElements = new ArrayList(); + + if (!gridOver) { + maybeDrawGrid(plotGraphics); + } + drawContent(plotGraphics); + + boolean noneActive = true; + for (int i = 0; i < renderers.size(); i++) { + Renderer rend = renderers.get(i); + if (rend.isActive()) { + logger.finest("rendering #" + i + ": " + rend); + rend.render(plotGraphics, xAxis, yAxis, new NullProgressMonitor()); + noneActive = false; + } + } + + if (gridOver) { + maybeDrawGrid(plotGraphics); + } + if (renderers.size() == 0) { + postMessage(null, "(no renderers)", DasPlot.INFO, null, null); + logger.fine("dasPlot has no renderers"); + } else if (noneActive) { + postMessage(null, "(no active renderers)", DasPlot.INFO, null, null); + } + } + + /** + * return the index of the renderer at canvas location (x,y), or -1 if + * no renderer is found at the position. + */ + public int findRendererAt(int x, int y) { + for (int i = 0; messages != null && i < messages.size(); i++) { + MessageDescriptor message = (MessageDescriptor) messages.get(i); + if (message.bounds.contains(x, y) && message.renderer != null) { + int result = this.renderers.indexOf(message.renderer); + if (result != -1) { + return result; + } + } + } + + for (int i = 0; legendElements != null && i < legendElements.size(); i++) { + LegendElement legendElement = legendElements.get(i); + if (legendElement.bounds.contains(x, y) && legendElement.renderer != null) { + int result = this.renderers.indexOf( legendElement.renderer ); + if (result != -1) { + return result; + } + } + } + + for (int i = renderers.size() - 1; i >= 0; i--) { + Renderer rend = renderers.get(i); + if (rend.isActive() && rend.acceptContext(x, y)) { + return i; + } + } + return -1; + } + + private Action getEditAction() { + return new AbstractAction("Renderer Properties") { + + public void actionPerformed(ActionEvent e) { + Point p = getDasMouseInputAdapter().getMousePressPosition(); + int i = findRendererAt(p.x + getX(), p.y + getY()); + if (i > -1) { + Renderer rend = getRenderer(i); + PropertyEditor editor = new PropertyEditor(rend); + editor.showDialog(DasPlot.this); + } + } + }; + } + + private void addDefaultMouseModules() { + + HorizontalRangeSelectorMouseModule hrs = + new HorizontalRangeSelectorMouseModule(this, xAxis); + mouseAdapter.addMouseModule(hrs); + hrs.addDataRangeSelectionListener(xAxis); + // TODO: support setYAxis, setXAxis + + VerticalRangeSelectorMouseModule vrs = + new VerticalRangeSelectorMouseModule(this, yAxis); + mouseAdapter.addMouseModule(vrs); + vrs.addDataRangeSelectionListener(yAxis); + // TODO: support setYAxis, setXAxis + + MouseModule x = CrossHairMouseModule.create(this); + mouseAdapter.addMouseModule(x); + + mouseAdapter.setSecondaryModule(new ZoomPanMouseModule(this, getXAxis(), getYAxis())); + + mouseAdapter.setPrimaryModule(x); + + mouseAdapter.addMouseModule(new BoxZoomMouseModule(this, null, getXAxis(), getYAxis())); + // TODO: support setYAxis, setXAxis. + + x = new MouseModule(this, new LengthDragRenderer(this, null, null), "Length"); + mouseAdapter.addMouseModule(x); + + x = new DisplayDataMouseModule(this); + mouseAdapter.addMouseModule(x); + + editRendererMenuItem = new JMenuItem(getEditAction()); + getDasMouseInputAdapter().addMenuItem(editRendererMenuItem); + + if (DasApplication.hasAllPermission()) { + JMenuItem dumpMenuItem = new JMenuItem(DUMP_TO_FILE_ACTION); + mouseAdapter.addMenuItem(dumpMenuItem); + } + + + } + public Action DUMP_TO_FILE_ACTION = new AbstractAction("Dump Data Set to File") { + + public void actionPerformed(ActionEvent e) { + if (renderers.isEmpty()) { + return; + } + Renderer renderer = renderers.get(0); + JFileChooser chooser = new JFileChooser(); + int result = chooser.showSaveDialog(DasPlot.this); + if (result == JFileChooser.APPROVE_OPTION) { + File selected = chooser.getSelectedFile(); + try { + FileChannel out = new FileOutputStream(selected).getChannel(); + DataSet ds = renderer.getDataSet(); + if (ds instanceof TableDataSet) { + TableUtil.dumpToAsciiStream((TableDataSet) ds, out); + } else if (ds instanceof VectorDataSet) { + VectorUtil.dumpToAsciiStream((VectorDataSet) ds, out); + } + } catch (IOException ioe) { + DasExceptionHandler.handle(ioe); + } + } + } + }; + + public DataSet getDataSet() { + // TODO: get rid of this!!! + return Data; + } + + public DataSet getConsumedDataSet() { + // TODO: get rid of this!!! + return Data; + } + + public DataSet getData() { + // TODO: get rid of this!!! + return Data; + } + + public void setXAxis(DasAxis xAxis) { + Object oldValue = this.xAxis; + Container parent = getParent(); + if (this.xAxis != null) { + DasProperties.getLogger().fine("setXAxis upsets the dmia"); + if (parent != null) { + parent.remove(this.xAxis); + } + xAxis.removePropertyChangeListener("dataMinimum", rebinListener); + xAxis.removePropertyChangeListener("dataMaximum", rebinListener); + xAxis.removePropertyChangeListener(DasAxis.PROPERTY_DATUMRANGE, rebinListener); + xAxis.removePropertyChangeListener("log", rebinListener); + xAxis.removePropertyChangeListener(DasAxis.PROPERTY_TICKS, ticksListener); + } + this.xAxis = xAxis; + if (xAxis != null) { + if (!xAxis.isHorizontal()) { + throw new IllegalArgumentException("xAxis is not horizontal"); + } + if (parent != null) { + parent.add(this.xAxis); + parent.validate(); + } + xAxis.addPropertyChangeListener("dataMinimum", rebinListener); + xAxis.addPropertyChangeListener("dataMaximum", rebinListener); + xAxis.addPropertyChangeListener(DasAxis.PROPERTY_DATUMRANGE, rebinListener); + xAxis.addPropertyChangeListener("log", rebinListener); + xAxis.addPropertyChangeListener(DasAxis.PROPERTY_TICKS, ticksListener); + } + if (xAxis != oldValue) { + firePropertyChange("xAxis", oldValue, xAxis); + } + } + + /** + * Attaches a new axis to the plot. The old axis is disconnected, and + * the plot will listen to the new axis. Also, the row and column for the + * axis is set, but this might change in the future. null appears to be + * a valid input as well. + * + * TODO: plot does not seem to be responding to changes in the axis. + * (goes grey because updatePlotImage is never done. + */ + public void setYAxis(DasAxis yAxis) { + Object oldValue = this.yAxis; + logger.fine("setYAxis(" + yAxis.getName() + "), removes " + this.yAxis); + Container parent = getParent(); + if (this.yAxis != null) { + DasProperties.getLogger().fine("setYAxis upsets the dmia"); + if (parent != null) { + parent.remove(this.yAxis); + } + this.yAxis.removePropertyChangeListener("dataMinimum", rebinListener); + this.yAxis.removePropertyChangeListener("dataMaximum", rebinListener); + this.yAxis.removePropertyChangeListener(DasAxis.PROPERTY_DATUMRANGE, rebinListener); + this.yAxis.removePropertyChangeListener("log", rebinListener); + this.yAxis.removePropertyChangeListener(DasAxis.PROPERTY_TICKS, ticksListener); + } + this.yAxis = yAxis; + if (yAxis != null) { + if (yAxis.isHorizontal()) { + throw new IllegalArgumentException("yAxis is not vertical"); + } + yAxis.setRow(getRow()); + yAxis.setColumn(getColumn()); + if (parent != null) { + parent.add(this.yAxis); + parent.validate(); + } + yAxis.addPropertyChangeListener("dataMinimum", rebinListener); + yAxis.addPropertyChangeListener("dataMaximum", rebinListener); + yAxis.addPropertyChangeListener(DasAxis.PROPERTY_DATUMRANGE, rebinListener); + yAxis.addPropertyChangeListener("log", rebinListener); + yAxis.addPropertyChangeListener(DasAxis.PROPERTY_TICKS, ticksListener); + } + if (yAxis != oldValue) { + firePropertyChange("yAxis", oldValue, yAxis); + } + } + + @Override + protected void updateImmediately() { + paintImmediately(0, 0, getWidth(), getHeight()); + logger.finer("DasPlot.updateImmediately"); + for (int i = 0; i < renderers.size(); i++) { + Renderer rend = renderers.get(i); + rend.update(); + } + } + + /* + * returns the AffineTransform to transform data from the last updatePlotImage call + * axes (if super.updatePlotImage was called), or null if the transform is not possible. + */ + protected AffineTransform getAffineTransform(DasAxis xAxis, DasAxis yAxis) { + if (xmemento == null) { + logger.fine("unable to calculate AT, because old transform is not defined."); + return null; + } else { + AffineTransform at = new AffineTransform(); + at = xAxis.getAffineTransform(xmemento, at); + at = yAxis.getAffineTransform(ymemento, at); + return at; + } + } + + /** + * at.isIdentity returns false if the at is not precisely identity, + * so this allows for some fuzz. + */ + private boolean isIdentity(AffineTransform at) { + return at.isIdentity() || + (Math.abs(at.getScaleX() - 1.00) < 0.001 && Math.abs(at.getScaleY() - 1.00) < 0.001 && Math.abs(at.getTranslateX()) < 0.001 && Math.abs(at.getTranslateY()) < 0.001); + } + + private void paintInvalidScreen(Graphics atGraphics, AffineTransform at) { + Color c = GraphUtil.getRicePaperColor(); + atGraphics.setColor(c); + int x = getColumn().getDMinimum(); + int y = getRow().getDMinimum(); + + atGraphics.fillRect(x - 1, y - 1, getWidth(), getHeight()); + final boolean debug = false; + if (debug) { + atGraphics.setColor(Color.DARK_GRAY); + + atGraphics.drawString("moment...", x + 10, y + 10); + String atstr = GraphUtil.getATScaleTranslateString(at); + + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(atGraphics, atstr); + gtr.draw(atGraphics, x + 10, y + 10 + atGraphics.getFontMetrics().getHeight()); + } + + logger.finest(" using cacheImage with ricepaper to invalidate"); + } + + protected void paintComponent(Graphics graphics1) { + + if (!getCanvas().isPrintingThread() && !EventQueue.isDispatchThread()) { + throw new RuntimeException("not event thread: " + Thread.currentThread().getName()); + } + //paintComponentCount++; + logger.finer("entering DasPlot.paintComponent"); + + if ( getCanvas().isPrintingThread() ) logger.fine("* printing thread *"); + + int x = getColumn().getDMinimum(); + int y = getRow().getDMinimum(); + + int xSize = getColumn().getDMaximum() - x; + int ySize = getRow().getDMaximum() - y; + + logger.fine("DasPlot clip=" + graphics1.getClip() + " @ "+getX()+","+getY() ); + + Rectangle clip = graphics1.getClipBounds(); + if (clip != null && (clip.y + getY()) >= (y + ySize)) { + logger.finer("returning because clip indicates nothing to be done."); + return; + } + + boolean disableImageCache= false; + + Graphics2D graphics = (Graphics2D) graphics1; + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics.translate(-getX(), -getY()); + if (cacheImageValid && !getCanvas().isPrintingThread() && !disableImageCache ) { + + Graphics2D atGraphics = (Graphics2D) graphics.create(); + + AffineTransform at = getAffineTransform(xAxis, yAxis); + if (at == null || (preview == false && !isIdentity(at))) { + atGraphics.drawImage(cacheImage, cacheImageBounds.x, cacheImageBounds.y, cacheImageBounds.width, cacheImageBounds.height, this); + paintInvalidScreen(atGraphics, at); + + } else { + String atDesc; + NumberFormat nf = new DecimalFormat("0.00"); + atDesc = GraphUtil.getATScaleTranslateString(at); + + if (!at.isIdentity()) { + logger.finest(" using cacheImage w/AT " + atDesc); + atGraphics.transform(at); + } else { + logger.finest(" using cacheImage " + cacheImageBounds + " " + xmemento + " " + ymemento ); + } + + //int reduceHeight= getRow().getDMinimum() - clip.y; + //if ( reduceHeight>0 ) { + // clip.y+= reduceHeight; + // clip.height-= reduceHeight; + //} + //clip.translate( getX(), getY() ); + //atGraphics.setClip(clip); + atGraphics.drawImage(cacheImage, cacheImageBounds.x, cacheImageBounds.y, cacheImageBounds.width, cacheImageBounds.height, this); + //atGraphics.setClip(null); + //return; + //graphics.drawString( "cacheImage "+atDesc, getWidth()/2, getHeight()/2 ); + + } + + atGraphics.dispose(); + + } else { + + synchronized (this) { + Graphics2D plotGraphics; + if ( getCanvas().isPrintingThread() || disableImageCache ) { + plotGraphics = (Graphics2D) graphics.create(x - 1, y - 1, xSize + 2, ySize + 2); + cacheImageBounds = new Rectangle(); + cacheImageBounds.width = getWidth(); + cacheImageBounds.height = getHeight(); + cacheImageBounds.x = x - 1; + cacheImageBounds.y = y - 1; + logger.finest(" printing thread, drawing"); + } else { + if (overSize) { + cacheImageBounds = new Rectangle(); + cacheImageBounds.width = 16 * getWidth() / 10; + cacheImageBounds.height = getHeight(); + cacheImage = new BufferedImage(cacheImageBounds.width, cacheImageBounds.height, BufferedImage.TYPE_4BYTE_ABGR); + cacheImageBounds.x = x - 3 * getWidth() / 10; + cacheImageBounds.y = y - 1; + + } else { + cacheImageBounds = new Rectangle(); + cacheImageBounds.width = getWidth(); + cacheImageBounds.height = getHeight(); + cacheImage = new BufferedImage(cacheImageBounds.width, cacheImageBounds.height, BufferedImage.TYPE_4BYTE_ABGR); + cacheImageBounds.x = x - 1; + cacheImageBounds.y = y - 1; + } + plotGraphics = (Graphics2D) cacheImage.getGraphics(); + plotGraphics.setBackground(getBackground()); + plotGraphics.setColor(getForeground()); + plotGraphics.setRenderingHints(org.das2.DasProperties.getRenderingHints()); + if (overSize) { + plotGraphics.translate(x - cacheImageBounds.x - 1, y - cacheImageBounds.y - 1); + } + + logger.finest(" rebuilding cacheImage"); + + } + + plotGraphics.translate(-x + 1, -y + 1); + + drawCacheImage(plotGraphics); + + //if (overSize) { + // postMessage(null, "Over size on", DasPlot.INFO, null, null); + //} + } + + + if ( !disableImageCache && !getCanvas().isPrintingThread() ) { + cacheImageValid = true; + //clip.y= Math.max( clip.y, getRow().getDMinimum() ); + //clip.translate( getX(), getY() ); + //graphics.setClip(clip); + graphics.drawImage(cacheImage, cacheImageBounds.x, cacheImageBounds.y, cacheImageBounds.width, cacheImageBounds.height, this); + //graphics.drawString( "new image", getWidth()/2, getHeight()/2 ); + //graphics.setClip(null); + + xmemento = xAxis.getMemento(); + ymemento = yAxis.getMemento(); + + logger.finest("recalc cacheImage, xmemento=" + xmemento + " ymemento=" + ymemento ); + } + } + + graphics.setColor(getForeground()); + graphics.drawRect(x - 1, y - 1, xSize + 1, ySize + 1); + + if (plotTitle != null && plotTitle.length() != 0) { + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setAlignment(GrannyTextRenderer.CENTER_ALIGNMENT); + gtr.setString(graphics, plotTitle); + int titleWidth = (int) gtr.getWidth(); + int titleX = x + (xSize - titleWidth) / 2; + int titleY = y - (int) gtr.getDescent() - (int) gtr.getAscent() / 2; + gtr.draw(graphics, (float) titleX, (float) titleY); + } + + Font font0= graphics.getFont(); + // --- draw messages --- + if (messages.size() > 0) { + drawMessages(graphics); + } + + if (legendElements.size() > 0) { + drawLegend(graphics); + } + + graphics.setFont(font0); + + graphics.translate(getX(), getY()); + + getDasMouseInputAdapter().paint(graphics); + + } + + private class MessageDescriptor { + + /** + * the renderer posting the text, or null if the plot owns the text + */ + Renderer renderer; + String text; + int messageType; + Datum x; + Datum y; + Rectangle bounds; // stores the drawn boundaries of the message for context menu. + + MessageDescriptor(Renderer renderer, String text, int messageType, Datum x, Datum y) { + this.renderer = renderer; + this.text = text; + this.messageType = messageType; + this.x = x; + this.y = y; + } + } + + private class LegendElement { + + ImageIcon icon; + Renderer renderer; + String label; + Rectangle bounds; + + LegendElement(ImageIcon icon, Renderer rend, String label) { + this.icon = icon; + this.renderer = rend; + this.label = label; + } + } + public static final int INFO = 0; + public static final int WARNING = 1; + public static final int ERROR = 2; + List messages; + List legendElements; + + /** + * Notify user of an exception, in the context of the plot. A position in + * the data space may be specified to locate the text within the data context. + * Note either or both x or y may be null. Messages must only be posted while the + * Renderer's render method is called. All messages are cleared before the render + * step. + * + * + * @param renderer identifies the renderer posting the exception + * @param text the text to be displayed, may contain granny text. + * @param messageType DasPlot.INFORMATION_MESSAGE, DasPlot.WARNING_MESSAGE, or DasPlot.ERROR_MESSAGE. + * @param x if non-null, the location on the x axis giving context for the text. + * @param y if non-null, the location on the y axis giving context for the text. + */ + public void postMessage(Renderer renderer, String message, int messageType, Datum x, Datum y) { + messages.add(new MessageDescriptor(renderer, message, messageType, x, y)); + } + + /** + * notify user of an exception, in the context of the plot. + */ + public void postException(Renderer renderer, Exception exception) { + String message = exception.getMessage(); + if (message == null) { + message = String.valueOf(exception); + } + int errorLevel = ERROR; + if (exception instanceof CancelledOperationException) { + errorLevel = INFO; + if (exception.getMessage() == null) { + message = "Operation Cancelled"; + } + } + postMessage(renderer, message, errorLevel, null, null); + } + + /** + * + * @param renderer identifies the renderer adding to the legend + * @param icon if non-null, an icon to use. If null, the renderer's icon is used. + * @param pos integer order parameter, and also identifies item. + * @param message String message to display. + */ + public void addToLegend(Renderer renderer, ImageIcon icon, int pos, String message) { + legendElements.add(new LegendElement( icon, renderer, message )); + } + + private void drawGrid(Graphics2D g, DatumVector xticks, DatumVector yticks) { + int xmin = this.cacheImageBounds.x; + int xmax = this.cacheImageBounds.x + this.cacheImageBounds.width; + int ymin = this.cacheImageBounds.y; + int ymax = this.cacheImageBounds.y + this.cacheImageBounds.height; + + if (yticks != null && yticks.getUnits().isConvertableTo(yAxis.getUnits())) { + for (int i = 0; i < yticks.getLength(); i++) { + int y = (int) yAxis.transform(yticks.get(i)); + g.drawLine(xmin, y, xmax, y); + } + } + if (xticks != null && xticks.getUnits().isConvertableTo(xAxis.getUnits())) { + for (int i = 0; i < xticks.getLength(); i++) { + int x = (int) xAxis.transform(xticks.get(i)); + g.drawLine(x, ymin, x, ymax); + } + } + } + + protected void drawContent(Graphics2D g) { + // override me to add to the axes. + } + + public void resize() { + logger.fine("resize DasPlot"); + if (isDisplayable()) { + Rectangle oldBounds= getBounds(); + Rectangle bounds = new Rectangle(); + bounds.x = getColumn().getDMinimum() - 1; + bounds.y = getRow().getDMinimum() - 1; + bounds.width = getColumn().getDMaximum() - bounds.x + 1; + bounds.height = getRow().getDMaximum() - bounds.y + 1; + if (!getTitle().equals("")) { + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(getFont(), getTitle()); + + int titleHeight = (int) gtr.getHeight() + (int) gtr.getAscent() / 2; + + bounds.y -= titleHeight; + bounds.height += titleHeight; + } + // TODO check bounds.height<10 + logger.fine("DasPlot setBounds "+bounds); + if ( !oldBounds.equals(bounds) ) { + invalidateCacheImage(); + } + setBounds(bounds); + } + } + + /** Sets the title which will be displayed above this plot. + * + * @param t The new title for this plot. + */ + public void setTitle(String t) { + Object oldValue = plotTitle; + plotTitle = t; + if (getCanvas() != null) { + FontMetrics fm = getFontMetrics(getCanvas().getFont()); + int titleHeight = fm.getHeight() + fm.getHeight() / 2; + resize(); + invalidateCacheImage(); + } + if (t != oldValue) { + firePropertyChange(PROP_TITLE, oldValue, t); + } + } + + /** Returns the title of this plot. + * + * @see #setTitle(String) + * + * @return The plot title + */ + public String getTitle() { + return plotTitle; + } + private List renderers = null; + + public DasAxis getXAxis() { + return this.xAxis; + } + + public DasAxis getYAxis() { + return this.yAxis; + } + + /** Getter for property dataSetDescriptor. + * @return Value of property dataSetDescriptor. + */ + public DataSetDescriptor getDataSetDescriptor() { + return dataSetDescriptor; + } + + /** Setter for property dataSetDescriptor. + * @param dataSetDescriptor New value of property dataSetDescriptor. + */ + public void setDataSetDescriptor(DataSetDescriptor dataSetDescriptor) { + this.dataSetDescriptor = dataSetDescriptor; + markDirty(); + } + + public void setData(DataSet ds) { + // TODO: get rid of this!!! + this.Data = ds; + markDirty(); + } + + protected class RebinListener implements java.beans.PropertyChangeListener { + + public void propertyChange(java.beans.PropertyChangeEvent e) { + // logger.fine("rebin listener got property change: "+e.getNewValue()); + markDirty(); + DasPlot.this.update(); + } + } + + protected void installComponent() { + super.installComponent(); + if (xAxis != null) { + getCanvas().add(xAxis, getRow(), getColumn()); + } + if (yAxis != null) { + getCanvas().add(yAxis, getRow(), getColumn()); + } + Renderer[] r = getRenderers(); + for (int index = 0; index < r.length; index++) { + r[index].installRenderer(); + } + if (!"true".equals(DasApplication.getProperty("java.awt.headless", "false"))) { + dndSupport = new PlotDnDSupport(getCanvas().dndSupport); + } + } + + protected void uninstallComponent() { + super.uninstallComponent(); + if (xAxis != null && xAxis.getCanvas() != null) { + xAxis.getCanvas().remove(xAxis); + } + if (yAxis != null && yAxis.getCanvas() != null) { + yAxis.getCanvas().remove(yAxis); + } + Renderer[] r = getRenderers(); + for (int index = 0; index < r.length; index++) { + r[index].uninstallRenderer(); + } + } + + public void addRenderer(Renderer rend) { + logger.fine("addRenderer(" + rend + ")"); + if (rend.parent != null) { + rend.parent.removeRenderer(rend); + } + renderers.add(rend); + rend.parent = this; + if (getCanvas() != null) { + rend.installRenderer(); + } + rend.update(); + invalidateCacheImage(); + } + + public void removeRenderer(Renderer rend) { + if (getCanvas() != null) { + rend.uninstallRenderer(); + } + renderers.remove(rend); + rend.parent = null; + invalidateCacheImage(); + + } + + public static DasPlot createDummyPlot() { + DasAxis xAxis = new DasAxis(Datum.create(-10), Datum.create(10), DasAxis.HORIZONTAL); + DasAxis yAxis = new DasAxis(Datum.create(-10), Datum.create(10), DasAxis.VERTICAL); + DasPlot result = new DasPlot(xAxis, yAxis); + return result; + } + + public static DasPlot createPlot(DatumRange xrange, DatumRange yrange) { + DasAxis xAxis = new DasAxis(xrange, DasAxis.HORIZONTAL); + DasAxis yAxis = new DasAxis(yrange, DasAxis.VERTICAL); + DasPlot result = new DasPlot(xAxis, yAxis); + return result; + } + + public Renderer getRenderer(int index) { + return renderers.get(index); + } + + public Renderer[] getRenderers() { + return (Renderer[]) renderers.toArray(new Renderer[0]); + } + + public static DasPlot processPlotElement(Element element, FormBase form) throws org.das2.DasPropertyException, org.das2.DasNameException, DasException, java.text.ParseException { + String name = element.getAttribute("name"); + + DasRow row = (DasRow) form.checkValue(element.getAttribute("row"), DasRow.class, ""); + DasColumn column = (DasColumn) form.checkValue(element.getAttribute("column"), DasColumn.class, ""); + + DasAxis xAxis = null; + DasAxis yAxis = null; + DasColorBar colorbar = null; + + //Get the axes + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + if (node instanceof Element) { + if (node.getNodeName().equals("xAxis")) { + xAxis = processXAxisElement((Element) node, row, column, form); + } else if (node.getNodeName().equals("yAxis")) { + yAxis = processYAxisElement((Element) node, row, column, form); + } else if (node.getNodeName().equals("zAxis")) { + colorbar = processZAxisElement((Element) node, row, column, form); + } + + } + } + + if (xAxis == null) { + xAxis = (DasAxis) form.checkValue(element.getAttribute("xAxis"), DasAxis.class, " or "); + } + if (yAxis == null) { + yAxis = (DasAxis) form.checkValue(element.getAttribute("yAxis"), DasAxis.class, " or "); + } + + DasPlot plot = new DasPlot(xAxis, yAxis); + + if (element.getNodeName().equals("spectrogram")) { + SpectrogramRenderer rend = new SpectrogramRenderer(null, colorbar); + plot.addRenderer(rend); + } + + plot.setTitle(element.getAttribute("title")); + plot.setDasName(name); + plot.setRow(row); + plot.setColumn(column); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, plot); + + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + if (node instanceof Element) { + if (node.getNodeName().equals("renderers")) { + processRenderersElement((Element) node, plot, form); + } + } + } + + return plot; + } + + private static DasAxis processXAxisElement(Element element, DasRow row, DasColumn column, FormBase form) throws org.das2.DasPropertyException, org.das2.DasNameException, DasException, java.text.ParseException { + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (node.getNodeName().equals("axis")) { + DasAxis axis = DasAxis.processAxisElement(e, form); + if (!axis.isHorizontal()) { + axis.setOrientation(DasAxis.HORIZONTAL); + } + return axis; + } else if (node.getNodeName().equals("timeaxis")) { + DasAxis axis = DasAxis.processTimeaxisElement(e, form); + if (!axis.isHorizontal()) { + axis.setOrientation(DasAxis.HORIZONTAL); + } + return axis; + } else if (node.getNodeName().equals("attachedaxis")) { + DasAxis axis = DasAxis.processAttachedaxisElement(e, form); + if (!axis.isHorizontal()) { + axis.setOrientation(DasAxis.HORIZONTAL); + } + return axis; + } + } + } + return null; + } + + private static DasAxis processYAxisElement(Element element, DasRow row, DasColumn column, FormBase form) throws org.das2.DasPropertyException, org.das2.DasNameException, org.das2.DasException, java.text.ParseException { + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + if (node instanceof Element) { + Element e = (Element) node; + if (node.getNodeName().equals("axis")) { + DasAxis axis = DasAxis.processAxisElement(e, form); + if (axis.isHorizontal()) { + axis.setOrientation(DasAxis.VERTICAL); + } + return axis; + } else if (node.getNodeName().equals("timeaxis")) { + DasAxis axis = DasAxis.processTimeaxisElement(e, form); + if (axis.isHorizontal()) { + axis.setOrientation(DasAxis.VERTICAL); + } + return axis; + } else if (node.getNodeName().equals("attachedaxis")) { + DasAxis axis = DasAxis.processAttachedaxisElement(e, form); + if (axis.isHorizontal()) { + axis.setOrientation(DasAxis.VERTICAL); + } + return axis; + } + } + } + return null; + } + + private static DasColorBar processZAxisElement(Element element, DasRow row, DasColumn column, FormBase form) throws DasPropertyException, DasNameException, DasException, java.text.ParseException { + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + if (node instanceof Element) { + if (node.getNodeName().equals("colorbar")) { + return DasColorBar.processColorbarElement((Element) node, form); + } + } + } + return null; + } + + private static void processRenderersElement(Element element, DasPlot parent, FormBase form) throws org.das2.DasPropertyException, org.das2.DasNameException, org.das2.DasException, java.text.ParseException { + NodeList children = element.getChildNodes(); + for (int index = 0; index < children.getLength(); index++) { + Node node = children.item(index); + if (node instanceof Element) { + if (node.getNodeName().equals("spectrogram")) { + parent.addRenderer(SpectrogramRenderer.processSpectrogramElement((Element) node, parent, form)); + } else if (node.getNodeName().equals("lineplot")) { + parent.addRenderer(SymbolLineRenderer.processLinePlotElement((Element) node, parent, form)); + } + } + } + } + + public Element getDOMElement(Document document) { + + Element element = document.createElement("plot"); + element.setAttribute("name", getDasName()); + element.setAttribute("row", getRow().getDasName()); + element.setAttribute("column", getColumn().getDasName()); + element.setAttribute("title", getTitle()); + + Element xAxisChild = document.createElement("xAxis"); + Element xAxisElement = getXAxis().getDOMElement(document); + xAxisElement.removeAttribute("orientation"); + if (xAxisElement.getAttribute("row").equals(getRow().getDasName())) { + xAxisElement.removeAttribute("row"); + } + if (xAxisElement.getAttribute("column").equals(getColumn().getDasName())) { + xAxisElement.removeAttribute("column"); + } + xAxisChild.appendChild(xAxisElement); + element.appendChild(xAxisChild); + + Element yAxisChild = document.createElement("yAxis"); + Element yAxisElement = getYAxis().getDOMElement(document); + yAxisElement.removeAttribute("orientation"); + if (yAxisElement.getAttribute("row").equals(getRow().getDasName())) { + yAxisElement.removeAttribute("row"); + } + if (yAxisElement.getAttribute("column").equals(getColumn().getDasName())) { + yAxisElement.removeAttribute("column"); + } + yAxisChild.appendChild(yAxisElement); + element.appendChild(yAxisChild); + + Renderer[] renderers = getRenderers(); + if (renderers.length > 0) { + Element renderersChild = document.createElement("renderers"); + for (int index = 0; index < renderers.length; index++) { + renderersChild.appendChild(renderers[index].getDOMElement(document)); + } + element.appendChild(renderersChild); + } + return element; + } + + public static DasPlot createNamedPlot(String name) { + DasAxis xAxis = DasAxis.createNamedAxis(null); + xAxis.setOrientation(DasAxis.BOTTOM); + DasAxis yAxis = DasAxis.createNamedAxis(null); + yAxis.setOrientation(DasAxis.LEFT); + DasPlot plot = new DasPlot(xAxis, yAxis); + if (name == null) { + name = "plot_" + Integer.toHexString(System.identityHashCode(plot)); + } + try { + plot.setDasName(name); + } catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + return plot; + } + + private class PlotDnDSupport extends org.das2.util.DnDSupport { + + PlotDnDSupport(org.das2.util.DnDSupport parent) { + super(DasPlot.this, DnDConstants.ACTION_COPY_OR_MOVE, parent); + } + + public void drop(DropTargetDropEvent dtde) { + } + + protected int canAccept(DataFlavor[] flavors, int x, int y, int action) { + for (int i = 0; i < flavors.length; i++) { + if (flavors[i].equals(TransferableRenderer.RENDERER_FLAVOR)) { + return action; + } + } + return -1; + } + + protected void done() { + } + + protected boolean importData(Transferable t, int x, int y, int action) { + boolean success = false; + try { + Renderer r = (Renderer) t.getTransferData(TransferableRenderer.RENDERER_FLAVOR); + addRenderer(r); + revalidate(); + success = true; + } catch (UnsupportedFlavorException ufe) { + } catch (IOException ioe) { + } + return success; + } + + protected Transferable getTransferable(int x, int y, int action) { + return null; + } + + protected void exportDone(Transferable t, int action) { + } + } + + public Shape getActiveRegion() { + return getBounds(); + } + + /** Potentially coalesce an event being posted with an existing + * event. This method is called by EventQueue.postEvent + * if an event with the same ID as the event to be posted is found in + * the queue (both events must have this component as their source). + * This method either returns a coalesced event which replaces + * the existing event (and the new event is then discarded), or + * null to indicate that no combining should be done + * (add the second event to the end of the queue). Either event + * parameter may be modified and returned, as the other one is discarded + * unless null is returned. + *

    + * This implementation of coalesceEvents coalesces + * DasUpdateEvents, returning the existingEvent parameter + * + * @param existingEvent the event already on the EventQueue + * @param newEvent the event being posted to the + * EventQueue + * @return a coalesced event, or null indicating that no + * coalescing was done + */ + protected AWTEvent coalesceEvents(AWTEvent existingEvent, AWTEvent newEvent) { + if (existingEvent instanceof DasRendererUpdateEvent && newEvent instanceof DasRendererUpdateEvent) { + DasRendererUpdateEvent e1 = (DasRendererUpdateEvent) existingEvent; + DasRendererUpdateEvent e2 = (DasRendererUpdateEvent) newEvent; + if (e1.getRenderer() == e2.getRenderer()) { + return existingEvent; + } else { + return null; + } + } + return super.coalesceEvents(existingEvent, newEvent); + } + + /** Processes events occurring on this component. By default this + * method calls the appropriate + * process<event type>Event + * method for the given class of event. + *

    Note that if the event parameter is null + * the behavior is unspecified and may result in an + * exception. + * + * @param e the event + * @see java.awt.Component#processComponentEvent + * @see java.awt.Component#processFocusEvent + * @see java.awt.Component#processKeyEvent + * @see java.awt.Component#processMouseEvent + * @see java.awt.Component#processMouseMotionEvent + * @see java.awt.Component#processInputMethodEvent + * @see java.awt.Component#processHierarchyEvent + * @see java.awt.Component#processMouseWheelEvent + * @see #processDasUpdateEvent + */ + protected void processEvent(AWTEvent e) { + if (e instanceof DasRendererUpdateEvent) { + DasRendererUpdateEvent drue = (DasRendererUpdateEvent) e; + drue.getRenderer().updateImmediately(); + cacheImageValid = false; + repaint(); + } else { + super.processEvent(e); + } + } + + public void repaint() { + super.repaint(); + repaintCount++; + } + + public synchronized void invalidateCacheImage() { + cacheImageValid = false; + repaint(); + } + + void markDirty() { + logger.finer("DasPlot.markDirty"); + super.markDirty(); + repaint(); + } + /** + * property drawGrid. If true, faint grey lines continue the axis major + * ticks across the plot. + */ + private boolean drawGrid = false; + + /** + * Getter for property drawGrid. If true, faint grey lines continue the axis major + * ticks across the plot. + * @return Value of property drawGrid. + */ + public boolean isDrawGrid() { + return this.drawGrid; + } + + /** + * Setter for property drawGrid. If true, faint grey lines continue the axis major + * ticks across the plot. + * @param drawGrid New value of property drawGrid. + */ + public void setDrawGrid(boolean drawGrid) { + boolean bOld = this.drawGrid; + this.drawGrid = drawGrid; + this.invalidateCacheImage(); + this.repaint(); + + if (bOld != drawGrid) { + firePropertyChange(PROP_DRAWGRID, bOld, drawGrid); + } + } + public static final String PROP_DRAWGRID = "drawGrid"; + private boolean drawMinorGrid; + public static final String PROP_DRAWMINORGRID = "drawMinorGrid"; + + /** + * Get the value of drawMinorGrid + * + * @return the value of drawMinorGrid + */ + public boolean isDrawMinorGrid() { + return this.drawMinorGrid; + } + + /** + * Set the value of drawMinorGrid + * + * @param newdrawMinorGrid new value of drawMinorGrid + */ + public void setDrawMinorGrid(boolean newdrawMinorGrid) { + boolean olddrawMinorGrid = drawMinorGrid; + this.drawMinorGrid = newdrawMinorGrid; + this.invalidateCacheImage(); + this.repaint(); + firePropertyChange(PROP_DRAWMINORGRID, olddrawMinorGrid, newdrawMinorGrid); + } + protected boolean gridOver = true; + public static final String PROP_GRIDOVER = "gridOver"; + + public boolean isGridOver() { + return gridOver; + } + + public void setGridOver(boolean gridOver) { + boolean oldGridOver = this.gridOver; + this.gridOver = gridOver; + this.invalidateCacheImage(); + this.repaint(); + firePropertyChange(PROP_GRIDOVER, oldGridOver, gridOver); + } + + public void setPreviewEnabled(boolean preview) { + this.preview = preview; + } + + public boolean isPreviewEnabled() { + return this.preview; + } + + public void setVisible(boolean visible) { + super.setVisible(visible); + xAxis.setVisible(visible); + yAxis.setVisible(visible); + } + protected boolean overSize = false; + public static final String PROP_OVERSIZE = "overSize"; + + public boolean isOverSize() { + return overSize; + } + + public void setOverSize(boolean overSize) { + boolean oldOverSize = this.overSize; + this.overSize = overSize; + invalidateCacheImage(); + firePropertyChange(PROP_OVERSIZE, oldOverSize, overSize); + } + + /** + * returns the rectange that renderers should paint so that when they + * are asked to render, they have everything pre-rendered. This is + * the same as the axis bounds them oversize is turned off. + * + * @return + */ + protected Rectangle getUpdateImageBounds() { + int x = getColumn().getDMinimum(); + int y = getRow().getDMinimum(); + cacheImageBounds = new Rectangle(); + cacheImageBounds.width = 16 * getWidth() / 10; + cacheImageBounds.height = getHeight(); + cacheImageBounds.x = x - 3 * getWidth() / 10; + cacheImageBounds.y = y - 1; + return cacheImageBounds; + } + + /** + * returns the position of the cacheImage in the canvas frame of reference. + * @return Rectangle + */ + protected Rectangle getCacheImageBounds() { + return cacheImageBounds; + } + + /** Reload and repaint the current data. + * Internally this tells the renderers to reload and repaint. + */ + @Override + public void reload(){ + for(Renderer rndr: renderers){ + rndr.reload(); + } + } +} diff --git a/dasCore/src/main/java/org/das2/graph/DasRendererUpdateEvent.java b/dasCore/src/main/java/org/das2/graph/DasRendererUpdateEvent.java new file mode 100644 index 000000000..3ee5150ab --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasRendererUpdateEvent.java @@ -0,0 +1,47 @@ +/* File: DasRendererUpdateEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on June 24, 2004, 4:48 PM + * by Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.event.DasUpdateEvent; + + +/** + * + * @author eew + */ +public class DasRendererUpdateEvent extends DasUpdateEvent { + + private Renderer renderer; + + /** Creates a new instance of DasRendererUpdateEvent */ + public DasRendererUpdateEvent(DasPlot parent, Renderer r) { + super(parent); + renderer = r; + } + + public Renderer getRenderer() { + return renderer; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/DasRow.java b/dasCore/src/main/java/org/das2/graph/DasRow.java new file mode 100755 index 000000000..453454636 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasRow.java @@ -0,0 +1,222 @@ +/* File: DasRow.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.dasml.FormBase; +import java.text.ParseException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** A DasRow defines a horizontal region of a DasCanvas. + * + * When placing {@link DasCanvasComponent}s, a DasRow and DasColumn are typically supplied + * to define the position of the component on the canvas. + * + * @see DasCanvas + * @see DasColumn + * @author jbf + */ +public class DasRow extends DasDevicePosition { + + /** Create a new row region on a {@link DasCanvas}, simplified constructor. + * + * @param parent The {@link DasCanvas} on which to define a horizontal region. + *

    + * @param top Sets the position the top of the Row, 0.0 = Top of the canvas, + * 1.0 = Bottom of the canvas. + *

    + * @param bottom Sets the position the bottom of the Row, 0.0 = Top of Parent object, + * 1.0 = Bottom of Parent object. + *

    + */ + public DasRow( DasCanvas parent, double top, double bottom) { + super(parent,top,bottom,false ); + } + + /** Create a new row region on a {@link DasCanvas}. + * + * @param canvas Set positions relative to a DasCanvas parent. Should be NULL + * if parent is non-null. + *

    + * @param parent Set positions relative to DasRow parent. Should be null if + * canvas is non-null. + *

    + * @param nMin Sets the position the top of the Row, 0.0 = Top of Parent object, + * 1.0 = Bottom of Parent object. + *

    + * @param nMax Sets the position the bottom of the Row, 0.0 = Top of Parent object, + * 1.0 = Bottom of Parent object. + *

    + * @param emMin Adjust the top boarder position in EM's (An EM is the size of a + * capital M in the current Canvas font.) Positive numbers move the + * top border down, Negative numbers move it up. + *

    + * @param emMax Adjust the bottom boarder position in EM's (An EM is the size of a + * capital M in the current Canvas font.) Positive numbers move the + * bottom border down, Negative numbers move it up. + *

    + * @param ptMin Similar to emMin, but move the top border in pixels. + * + *

    + * @param ptMax Similar to emMax, but move the bottom border in pixels. + */ + public DasRow( DasCanvas canvas, DasRow parent, double nMin, double nMax, + double emMin, double emMax, int ptMin, int ptMax ) { + super( canvas, false, parent, nMin, nMax, emMin, emMax, ptMin, ptMax ); + } + + /** + * makes a new DasRow by parsing a string like "100%-5em+3pt" to get the offsets. + * The three qualifiers are "%", "em", and "pt", but "px" is allowed as well + * as surely people will use that by mistake. If an offset or the normal position + * is not specified, then 0 is used. + * + * @param canvas the canvas for the layout, ignored when a parent DasRow is used. + * @param parent if non-null, this DasRow is specified with respect to parent. + * @param minStr a string like "0%+5em" + * @param maxStr a string like "100%-7em" + * @throws IllegalArgumentException if the strings cannot be parsed + */ + public static DasRow create( DasCanvas canvas, DasRow parent, String minStr, String maxStr ) { + double[] min, max; + try { + min= parseFormatStr( minStr ); + } catch ( ParseException e ) { + throw new IllegalArgumentException("unable to parse min: \""+minStr+"\""); + } + try { + max= parseFormatStr( maxStr ); + } catch ( ParseException e ) { + throw new IllegalArgumentException("unable to parse max: \""+maxStr+"\""); + } + return new DasRow( canvas, parent, min[0], max[0], min[1], max[1], (int)min[2], (int)max[2] ); + } + + public static final DasRow NULL= new DasRow(null,null,0,0,0,0,0,0); + + /** + * @deprecated This created a row that was not attached to anything, so + * it was simply a convenience method that didn't save much effort. + */ + public DasRow createSubRow(double ptop, double pbottom) { + double top= getMinimum(); + double bottom= getMaximum(); + double delta= top-bottom; + return new DasRow(getCanvas(),bottom+ptop*delta,bottom+pbottom*delta); + } + + public int getHeight() { + return getDMaximum()-getDMinimum(); + } + + public static DasRow create(DasCanvas parent) { + return new DasRow(parent,null,0.,1.0, 5, -5, 0,0 ); + } + + public static DasRow create( DasCanvas parent, int iplot, int nplot ) { + double min= 0.1 + iplot * ( 0.8 ) / nplot; + double max= 0.099 + ( iplot + 1 ) * ( 0.8 ) / nplot; + return new DasRow( parent, min, max ); + } + + /** Create a sub-row of a parent row. + * + * The canvas coordinates for objects in this row will be calculated as fractional + * offsets from the top and bottom of the parent row. + * + * @param ptop Fractional distance from the top of the parent row, to the top + * of the new sub-row. Here 0.0 would mean make the top of the sub-row + * coterminus with the top of the parent. + * + * @param pbottom Fractional distance from the top of the parent row, to the bottom + * of the new sub-row. Here 1.0 would mean make the bottom of the sub-row + * coterminus with bottom of the parent. + * + * @return a new DasRow object with top and bottom positions defined in relation to + * this row, instead of the Canvas. + */ + public DasRow createAttachedRow(double ptop, double pbottom) { + return new DasRow(null,this,ptop,pbottom,0,0,0,0); + } + + /** + * create a child by parsing spec strings like "50%+3em" + * @throws IllegalArgumentException when the string is malformed. + * @param smin + * @param smax + * @return + */ + public DasRow createChildRow( String smin, String smax ) { + try { + double[] min= DasDevicePosition.parseFormatStr(smin); + double[] max= DasDevicePosition.parseFormatStr(smax); + return new DasRow( null, this, min[0], max[0], min[1], max[1], (int)min[2], (int)max[2] ); + } catch (ParseException ex ) { + throw new IllegalArgumentException(ex); + } + } + + /** Process a <row> element. + * + * @param element The DOM tree node that represents the element + */ + static DasRow processRowElement(Element element, DasCanvas canvas, FormBase form) throws DasException { + String name = element.getAttribute("name"); + double minimum = Double.parseDouble(element.getAttribute("minimum")); + double maximum = Double.parseDouble(element.getAttribute("maximum")); + DasRow row = new DasRow(canvas, minimum, maximum); + row.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, row); + return row; + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("row"); + element.setAttribute("name", getDasName()); + element.setAttribute("minimum", Double.toString(getMinimum())); + element.setAttribute("maximum", Double.toString(getMaximum())); + return element; + } + + /** + * return the device location of the top of the row. + * @return + */ + public int top() { + return getDMinimum(); + } + + /** + * return the device location of the bottom (non-inclusive) of the row. + * @return + */ + public int bottom() { + return getDMaximum(); + } +} diff --git a/dasCore/src/main/java/org/das2/graph/DasSerializeable.java b/dasCore/src/main/java/org/das2/graph/DasSerializeable.java new file mode 100644 index 000000000..c2fc87995 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasSerializeable.java @@ -0,0 +1,23 @@ +/* + * DasSerializeable.java + * + * Created on November 4, 2004, 5:35 PM + */ + +package org.das2.graph; + +import org.das2.dasml.FormBase; +import org.das2.DasNameException; +import org.das2.DasPropertyException; +import java.text.*; +import org.w3c.dom.*; + +/** + * + * @author Jeremy + */ +public interface DasSerializeable { + + Element getDOMElement( Document document ); + Object processElement( Element element, DasPlot parent, FormBase form) throws DasPropertyException, DasNameException, ParseException; +} diff --git a/dasCore/src/main/java/org/das2/graph/DasZAxisPlot.java b/dasCore/src/main/java/org/das2/graph/DasZAxisPlot.java new file mode 100644 index 000000000..96dffb2a8 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DasZAxisPlot.java @@ -0,0 +1,44 @@ +/* File: DasZAxisPlot.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +/** + * + * @author jbf + */ +import org.das2.dataset.DataSet; +import org.das2.dataset.TableDataSetConsumer; + +public interface DasZAxisPlot extends org.das2.dataset.TableDataSetConsumer { + + /** Creates a new instance of DasZAxisPlot */ + public DasAxis getZAxis(); + public DasAxis getYAxis(); + public DasAxis getXAxis(); + + public org.das2.dataset.DataSet getDataSet(); + + public void setZAxis(DasAxis zAxis); + +} diff --git a/dasCore/src/main/java/org/das2/graph/DataLoader.java b/dasCore/src/main/java/org/das2/graph/DataLoader.java new file mode 100644 index 000000000..d346a1753 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DataLoader.java @@ -0,0 +1,124 @@ +/* + * DataLoader.java + * + * Created on September 13, 2005, 6:41 AM + * + * To change this template, choose Tools | Options and locate the template under + * the Source Creation and Management node. Right-click the template and choose + * Open. You can then make changes to the template in the Source Editor. + */ + +package org.das2.graph; +import org.das2.DasApplication; +import org.das2.dataset.RebinDescriptor; +import org.das2.util.monitor.ProgressMonitor; + +/** + * + * @author Jeremy + */ +public abstract class DataLoader { + + Renderer renderer; + + protected ProgressMonitor getMonitor( String description ) { + return DasApplication.getDefaultApplication().getMonitorFactory() + .getMonitor( renderer.getParent(), "Loading data set", description ); + } + + protected DataLoader( Renderer renderer ) { + this.renderer= renderer; + } + + /** + * an update message sent by the Renderer to indicate that something might have changed. + * Presently, the axis will send an update message to the plot, the plot will send an + * update message to the renderer, who will send an update message to the loader. + * THIS WILL PROBABLY CHANGE. + */ + abstract public void update(); + + private boolean fullResolution = false; + public boolean isFullResolution() { + return fullResolution; + } + + public void setFullResolution(boolean b) { + if (fullResolution == b) return; + fullResolution = b; + update(); + } + + // reloadDataSet is a dummy property that is Jeremy's way of telling the thing to + // reload through the property editor. calling setReloadDataSet(true) causes the + // dataset to reload and the image to be redrawn. + private boolean reloadDataSet; + public void setReloadDataSet( boolean reloadDataSet ) { + if ( reloadDataSet ) { + renderer.setDataSet( null ); + renderer.getParent().markDirty(); + renderer.getParent().update(); + + } + reloadDataSet= false; + } + + public boolean isReloadDataSet() { + return this.reloadDataSet; + } + + protected Renderer getRenderer() { + return this.renderer; + } + + public class Request { + public ProgressMonitor monitor; + public DasAxis.Memento xmem; + public DasAxis.Memento ymem; + public Request( ProgressMonitor mon, DasAxis.Memento xmem, DasAxis.Memento ymem ) { + this.monitor= mon; + this.xmem= xmem; + this.ymem= ymem; + } + public String toString() { + return xmem.toString(); + } + } + + /** + * convenient method for getting a rebindescriptor with one bin per pixel. -1 is + * returned by the rebinDescriptor when no bin holds the point. + */ + protected RebinDescriptor getRebinDescriptor( DasAxis axis ) { + int npix; + if ( axis.getOrientation()==DasAxis.VERTICAL ) { + npix= axis.getRow().getHeight(); + } else { + npix= axis.getColumn().getWidth(); + } + + RebinDescriptor rebinDescriptor; + rebinDescriptor = new RebinDescriptor( + axis.getDataMinimum(), axis.getDataMaximum(), + npix, + axis.isLog()); + rebinDescriptor.setOutOfBoundsAction( RebinDescriptor.MINUSONE ); + return rebinDescriptor; + } + + /** + * If active is false, then no data loading should occur. + */ + private boolean active= true; + + + public boolean isActive() { + return this.active && renderer.isActive(); + } + + public void setActive(boolean active) { + this.active = active; + update(); + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/DataRange.java b/dasCore/src/main/java/org/das2/graph/DataRange.java new file mode 100755 index 000000000..0d45c19a9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/DataRange.java @@ -0,0 +1,451 @@ + +/* File: DataRange.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.util.DasMath; +import org.das2.graph.event.DasUpdateListener; +import org.das2.graph.event.DasUpdateEvent; +import org.das2.system.DasLogger; + +import javax.swing.event.EventListenerList; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.util.*; +import java.util.Stack; +import java.util.logging.Logger; + +public class DataRange implements Cloneable { + + /** + * danger! axes share this object + */ + private DasAxis parent; + + private Units units; + + /** + * minimum, possibly with log applied + */ + private double minimum; + + /** + * maximum, possibly with log applied + */ + private double maximum; + + /** + * storage for values of temporary invalid states during state transition. + */ + private Datum pendingMin=null, pendingMax=null; + + /** range is the min and max, not in the log space. This is the + * range that controls the DataRange, as opposed to minimum and + * maximum, which are simply to implement it. + */ + private DatumRange range; + public static String PROPERTY_DATUMRANGE="datumRange"; + + private boolean log; + + private EventListenerList listenerList = new EventListenerList(); + + private Stack history; + + private Stack forwardHistory; + + private List favorites; + + private PropertyChangeSupport propertyChangeDelegate; + + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new Error("Assertion failure"); + } + } + + public DataRange( DasAxis parent, Datum min, Datum max, boolean log ) { + if (min.gt(max)) throw new IllegalArgumentException("data min on axis is greater than data max ("+min+">"+max+")"); + if (!min.isFinite()) throw new IllegalArgumentException("data_minimum on axis is not finite"); + if (!max.isFinite()) throw new IllegalArgumentException("data_maximum on axis is not finite"); + //if (min.getUnits()!=max.getUnits()) throw new IllegalArgumentException("units don't match on range"); + if (!min.getUnits().isConvertableTo(max.getUnits())) throw new IllegalArgumentException("units are not conversion compatible"); + this.parent= parent; + units= min.getUnits(); + if ( log ) { + minimum = DasMath.log10(min.doubleValue(units)); + maximum = DasMath.log10(max.doubleValue(units)); + } else { + minimum = min.doubleValue(units); + maximum = max.doubleValue(units); + } + this.range= new DatumRange( min, max ); + this.log = log; + history = new Stack(); + forwardHistory = new Stack(); + propertyChangeDelegate = new PropertyChangeSupport(this); + } + + public boolean isLog() { + return log; + } + + /* + * need some method for changing the axis units... + */ + public void resetRange( DatumRange range ) { + this.units= range.getUnits(); + this.range= range; + this.minimum= range.min().doubleValue(this.units); + this.maximum= range.max().doubleValue(this.units); + if ( isLog() ) { + this.minimum= DasMath.log10( this.minimum ); + this.maximum= DasMath.log10( this.maximum ); + } + fireUpdate(); + } + + /** + * set the log property. If log is true and max or min is zero or negative, + * then the values are reset to make them valid. + * @param log + */ + public void setLog(boolean log) { + /* + * propose new logic for going between lin/log axes: + * to log: pick first minor tick greater than zero. + * to lin: pick first minor tick in linear space less than min. + */ + + if (this.log==log) return; + boolean oldLog = this.log; + if (log) { + if (minimum<=0. || maximum <=0.) { + if ( maximum<=0 ) { + double oldMax= maximum; + maximum=100; + firePropertyChange("maximum", oldMax, maximum); + } + if ( minimum<=0 ) { + double oldMin= minimum; + minimum= maximum/1000; // three cycles, and typical the number of pixels. + firePropertyChange("minimum", oldMin, minimum); + } + this.range= new DatumRange( minimum, maximum, range.getUnits() ); + firePropertyChange("log", oldLog, log); + } + this.minimum= DasMath.log10(minimum); + this.maximum= DasMath.log10(maximum); + } else { + this.minimum= DasMath.exp10(minimum); + this.maximum= DasMath.exp10(maximum); + /* don't do this for now, we need to check that this does not mess things up when changing a bunch of properties at the same time. + if ( maximum / minimum > 999. ) { + minimum= 0; // minimum is close enough to zero. + this.range= new DatumRange( minimum, maximum, range.getUnits() ); + }*/ + } + clearHistory(); + this.log=log; + firePropertyChange("log", oldLog, log); + fireUpdate(); + } + + public DasAxis getCreator() { + return parent; + } + + public double getMinimum() { return minimum; } + + public double getMaximum() { return maximum; } + + /* @returns the floating point index within the range, where 0.0 indicates + * @param value is equal to minimum and 1.0 means it is equal to maximum, + * with log/lin/??? curvature considered. + */ + public final double findex( double value ) { + if ( log ) { + value= DasMath.log10(value); + } + return ( value-minimum ) / ( maximum - minimum ); + } + + public Units getUnits() { return units; } + + public DatumRange getDatumRange() { return range; } + + public void setUnits(Units newUnits) { + if (units.equals(newUnits)) { + return; + } + + minimum = units.convertDoubleTo(newUnits, minimum); + maximum = units.convertDoubleTo(newUnits, maximum); + units = newUnits; + + clearHistory(); + + } + + public void setMinimum( Datum min ) { + Datum max= pendingMax!=null ? pendingMax : this.range.max(); + if ( min.le( max ) ) { + setRange( new DatumRange( min, max ) ); + } else { + this.pendingMin= min; + } + } + + public void setMaximum( Datum max ) { + Datum min= pendingMin!=null ? pendingMin : this.range.min(); + if ( min.le( max ) ) { + setRange( new DatumRange( min, max ) ); + } else { + this.pendingMax= max; + } + } + + private void reportHistory() { + Logger log= DasLogger.getLogger( DasLogger.GUI_LOG ); + log.finest("history: "+history.size()); + for ( int i=0; i=0; i-=2) { + if (listeners[i]==DasUpdateListener.class) { + ((DasUpdateListener)listeners[i+1]).update(e); + } + } + } + + /** + * pop ipop items off the history list. This is used by the history menu + */ + protected void popHistory( int ipop ) { + for ( int i=0; i= eventMap.length ) { + setLabel(null); + } else { + int i= eventMap[ix]; + if ( i>=0 ) { + Datum sx= vds.getXTagDatum(i); + Datum sz= vds.getDatum(i); + VectorDataSet widthsDs= (VectorDataSet)vds.getPlanarView(widthPlaneId); + DatumRange dr= new DatumRange( sx, sx.add(widthsDs.getDatum(i)) ); + setLabel( textSpecifier.getText( dr, sz ) ); + } else { + setLabel(null); + } + } + return super.renderDrag( g, p1, p2 ); + } + + } + + private MouseModule getMouseModule() { + return new MouseModule( parent, new DragRenderer(parent), "event lookup" ); + } + + public void render(java.awt.Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + + VectorDataSet vds= (VectorDataSet)getDataSet(); + if (vds == null || vds.getXLength() == 0) { + DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("null data set"); + return; + } + + Graphics2D g= ( Graphics2D ) g1.create(); + + g.setColor(color); + + if ( vds==null && getLastException()!=null ) { + renderException( g, xAxis, yAxis, getLastException() ); + + } else { + VectorDataSet widthsDs= (VectorDataSet)vds.getPlanarView(widthPlaneId); + if ( widthsDs==null ) { + throw new IllegalArgumentException("no width plane named \""+widthPlaneId+"\" found"); + } + + DasColumn column= xAxis.getColumn(); + DasRow row= parent.getRow(); + + eventMap= new int[column.getWidth()]; + for ( int k=0; k0 ) { + UnitsConverter uc= UnitsConverter.getConverter( widthsDs.getYUnits(), xAxis.getUnits().getOffsetUnits() ); + + int ivds0= 0; + int ivds1= vds.getXLength(); + for ( int i=ivds0; i (ix) ) { + if ( iwidth==0 ) iwidth=1; + g.fill( new Rectangle( ix, row.getDMinimum(), iwidth, row.getHeight() ) ); + int im= ix-column.getDMinimum(); + int em0= im-1; + int em1= im+iwidth+1; + for ( int k=em0; k=0 && k Math.abs( tickSlope ) ) { // tick intersects the height of the label bounds. + if ( tickLine.getX2()>tickLine.getX1() ) { // e.g. 3 O'Clock + double rise= tickSlope * labelWidth / 2; + labelX= tickLine.getX2(); + labelY= tickLine.getY2() - (labelHeight)/2 + gtr.getAscent() + rise; + //g.setColor( Color.green ); + //((Graphics2D)g).draw( tickLine ); + } else { // e.g. 9 O'Clock + double rise= - tickSlope * labelWidth / 2; + labelX= tickLine.getX2() - labelWidth; + labelY= tickLine.getY2() - labelHeight/2 + gtr.getAscent() + rise; + //g.setColor( Color.red ); + //((Graphics2D)g).draw( tickLine ); + } + } else { // tick intersects the width of the label bounds + if ( tickLine.getY2() xSampleWidth) { + newPath.moveTo((float) i, (float) j); + skippedLast = false; + } else { + if (histogram) { + double i1 = (i0 + i) / 2; + newPath.lineTo((float) i1, (float) j0); + newPath.lineTo((float) i1, (float) j); + newPath.lineTo((float) i, (float) j); + } else { + newPath.lineTo((float) i, (float) j); + } + skippedLast = false; + } + t0 = t; + x0 = x; + y0 = y; + i0 = i; + j0 = j; + } + return newPath; + + } + + /** + * calculates the AffineTransform between two sets of x and y axes, if possible. + * @param xaxis0 the original reference frame x axis + * @param yaxis0 the original reference frame y axis + * @param xaxis1 the new reference frame x axis + * @param yaxis1 the new reference frame y axis + * @return an AffineTransform that transforms data positioned with xaxis0 and yaxis0 on xaxis1 and yaxis1, or null if no such transform exists. + */ + public static AffineTransform calculateAT(DasAxis xaxis0, DasAxis yaxis0, DasAxis xaxis1, DasAxis yaxis1) { + return calculateAT( xaxis0.getDatumRange(), yaxis0.getDatumRange(), xaxis1, yaxis1 ); + } + + public static AffineTransform calculateAT( + DatumRange xaxis0, DatumRange yaxis0, + DasAxis xaxis1, DasAxis yaxis1 ) { + + AffineTransform at = new AffineTransform(); + + double dmin0 = xaxis1.transform( xaxis0.min() ); // old axis in new axis space + double dmax0 = xaxis1.transform( xaxis0.max() ); + double dmin1 = xaxis1.transform( xaxis1.getDataMinimum() ); + double dmax1 = xaxis1.transform( xaxis1.getDataMaximum() ); + + double scalex = (dmin0 - dmax0) / (dmin1 - dmax1); + double transx = -1 * dmin1 * scalex + dmin0; + + at.translate(transx, 0); + at.scale(scalex, 1.); + + if (at.getDeterminant() == 0.000) { + return null; + } + + dmin0 = yaxis1.transform( yaxis0.min() ); // old axis in new axis space + dmax0 = yaxis1.transform( yaxis0.max() ); + dmin1 = yaxis1.transform(yaxis1.getDataMinimum()); + dmax1 = yaxis1.transform(yaxis1.getDataMaximum()); + + double scaley = (dmin0 - dmax0) / (dmin1 - dmax1); + double transy = -1 * dmin1 * scaley + dmin0; + + at.translate(0, transy); + at.scale(1., scaley); + + return at; + } + + public static DasAxis guessYAxis(DataSet dsz) { + boolean log = false; + + if (dsz.getProperty(DataSet.PROPERTY_Y_SCALETYPE) != null) { + if (dsz.getProperty(DataSet.PROPERTY_Y_SCALETYPE).equals("log")) { + log = true; + } + } + + DasAxis result; + + if (dsz instanceof TableDataSet) { + TableDataSet ds = (TableDataSet) dsz; + Units yunits = ds.getYUnits(); + Datum min, max; + + DatumRange yrange = DataSetUtil.yRange(dsz); + Datum dy = TableUtil.guessYTagWidth(ds); + if (UnitsUtil.isRatiometric(dy.getUnits())) { + log = true; + } + result = new DasAxis(yrange.min(), yrange.max(), DasAxis.LEFT, log); + + } else if (dsz instanceof VectorDataSet) { + VectorDataSet ds = (VectorDataSet) dsz; + Units yunits = ds.getYUnits(); + + DatumRange range = DataSetUtil.yRange(dsz); + if (range.width().doubleValue(yunits.getOffsetUnits()) == 0.) { + range = range.include(yunits.createDatum(0)); + if (range.width().doubleValue(yunits.getOffsetUnits()) == 0.) { + range = new DatumRange(0, 10, yunits); + } + } + result = new DasAxis(range.min(), range.max(), DasAxis.LEFT, log); + + } else { + throw new IllegalArgumentException("not supported: " + dsz); + } + + if (dsz.getProperty(DataSet.PROPERTY_Y_LABEL) != null) { + result.setLabel((String) dsz.getProperty(DataSet.PROPERTY_Y_LABEL)); + } + return result; + } + + public static DasAxis guessXAxis(DataSet ds) { + Datum min = ds.getXTagDatum(0); + Datum max = ds.getXTagDatum(ds.getXLength() - 1); + return new DasAxis(min, max, DasAxis.BOTTOM); + } + + public static DasAxis guessZAxis(DataSet dsz) { + if (!(dsz instanceof TableDataSet)) { + throw new IllegalArgumentException("only TableDataSet supported"); + } + TableDataSet ds = (TableDataSet) dsz; + Units zunits = ds.getZUnits(); + + DatumRange range = DataSetUtil.zRange(ds); + + boolean log = false; + if (dsz.getProperty(DataSet.PROPERTY_Z_SCALETYPE) != null) { + if (dsz.getProperty(DataSet.PROPERTY_Z_SCALETYPE).equals("log")) { + log = true; + if (range.min().doubleValue(range.getUnits()) <= 0) { // kludge for VALIDMIN + + double max = range.max().doubleValue(range.getUnits()); + range = new DatumRange(max / 1000, max, range.getUnits()); + } + } + } + + DasAxis result = new DasAxis(range.min(), range.max(), DasAxis.LEFT, log); + if (dsz.getProperty(DataSet.PROPERTY_Z_LABEL) != null) { + result.setLabel((String) dsz.getProperty(DataSet.PROPERTY_Z_LABEL)); + } + return result; + } + + public static Renderer guessRenderer(DataSet ds) { + Renderer rend = null; + if (ds instanceof VectorDataSet) {// TODO use SeriesRenderer + + if (ds.getXLength() > 10000) { + rend = new ImageVectorDataSetRenderer(new ConstantDataSetDescriptor(ds)); + } else { + rend = new SymbolLineRenderer(); + rend.setDataSet(ds); + ((SymbolLineRenderer) rend).setPsym(Psym.DOTS); + ((SymbolLineRenderer) rend).setSymSize(2.0); + } + + } else if (ds instanceof TableDataSet) { + Units zunits = ((TableDataSet) ds).getZUnits(); + DasAxis zaxis = guessZAxis(ds); + DasColorBar colorbar = new DasColorBar(zaxis.getDataMinimum(), zaxis.getDataMaximum(), zaxis.isLog()); + colorbar.setLabel(zaxis.getLabel()); + rend = new SpectrogramRenderer(new ConstantDataSetDescriptor(ds), colorbar); + } + return rend; + } + + public static DasPlot guessPlot(DataSet ds) { + DasAxis xaxis = guessXAxis(ds); + DasAxis yaxis = guessYAxis(ds); + DasPlot plot = new DasPlot(xaxis, yaxis); + plot.addRenderer(guessRenderer(ds)); + return plot; + } + + + public static DasPlot visualize( DataSet ds, String title, String xlabel, String ylabel ) { + DasPlot p= visualize(ds); + p.setTitle( title ); + p.getXAxis().setLabel(xlabel); + p.getYAxis().setLabel(ylabel); + return p; + } + + public static DasPlot visualize(DataSet ds) { + + JFrame jframe = new JFrame("DataSetUtil.visualize"); + DasCanvas canvas = new DasCanvas(400, 400); + jframe.getContentPane().add(canvas); + DasPlot result = guessPlot(ds); + canvas.add(result, DasRow.create(canvas), DasColumn.create(canvas,null,"5em","100%-10em") ); + + jframe.pack(); + jframe.setVisible(true); + jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + return result; + } + + public static DasPlot visualize(DataSet ds, boolean ylog) { + DatumRange xRange = DataSetUtil.xRange(ds); + DatumRange yRange = DataSetUtil.yRange(ds); + JFrame jframe = new JFrame("DataSetUtil.visualize"); + DasCanvas canvas = new DasCanvas(400, 400); + jframe.getContentPane().add(canvas); + DasPlot result = guessPlot(ds); + canvas.add(result, DasRow.create(canvas), DasColumn.create(canvas,null,"5em","100%-10em")); + Units xunits = result.getXAxis().getUnits(); + result.getXAxis().setDatumRange(xRange.zoomOut(1.1)); + Units yunits = result.getYAxis().getUnits(); + if (ylog) { + result.getYAxis().setDatumRange(yRange); + result.getYAxis().setLog(true); + } else { + result.getYAxis().setDatumRange(yRange.zoomOut(1.1)); + } + jframe.pack(); + jframe.setVisible(true); + jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + return result; + } + + /** + * Returns the input GeneralPath filled with new points which will be rendered identically to the input path, + * but contains a minimal number of points. Successive points occupying the same pixel are + * culled. + * TODO: bin average the points within a cell. The descretization messes up the label orientation in contour plotting. + * @return a new GeneralPath which will be rendered identically to the input path, + * but contains a minimal number of points. + * @param it A path iterator with minute details that will be lost when rendering. + * @param result A GeneralPath to put the result into. + */ + public static GeneralPath reducePath(PathIterator it, GeneralPath result) { + + float[] p = new float[6]; + + float x0 = Float.MAX_VALUE; + float y0 = Float.MAX_VALUE; + float sx0 = 0; + float sy0 = 0; + int nx0 = 0; + int ny0 = 0; + float ax0 = Float.NaN; + float ay0 = Float.NaN; // last averaged location + + int type0 = -999; + + float xres = 1; + float yres = 1; + + String[] types = new String[]{"M", "L", "QUAD", "CUBIC", "CLOSE"}; + + int points = 0; + int inCount = 0; + + while (!it.isDone()) { + inCount++; + + int type = it.currentSegment(p); + it.next(); + float dx = p[0] - x0; + float dy = p[1] - y0; + + //System.err.print("" + inCount + ": type: " + types[type] + " " + String.format("[ %f %f ] ", p[0], p[1])); + if ((type == PathIterator.SEG_MOVETO || type == type0) && Math.abs(dx) < xres && Math.abs(dy) < yres) { + sx0 += p[0]; + sy0 += p[1]; + nx0 += 1; + ny0 += 1; + //System.err.println(" accum"); + continue; + } else { + x0 = 0.5f + (int) Math.floor(p[0]); + y0 = 0.5f + (int) Math.floor(p[1]); + ax0 = nx0 > 0 ? sx0 / nx0 : p[0]; + ay0 = ny0 > 0 ? sy0 / ny0 : p[1]; + sx0 = p[0]; + sy0 = p[1]; + nx0 = 1; + ny0 = 1; + //System.err.print(" avg " + nx0 + " points (" + String.format("[ %f %f ]", ax0, ay0)); + } + + switch (type0) { + case PathIterator.SEG_LINETO: + result.lineTo(ax0, ay0); + points++; + //j System.err.println( ""+points+": " + GraphUtil.describe(result, false) ); + //System.err.println(" lineTo"+ String.format("[ %f %f ]", ax0, ay0)); + break; + case PathIterator.SEG_MOVETO: + result.moveTo(ax0, ay0); + //System.err.println(" moveTo"+ String.format("[ %f %f ]", ax0, ay0)); + break; + case -999: + //System.err.println(" ignore"+ String.format("[ %f %f ]", ax0, ay0)); + break; + default: + throw new IllegalArgumentException("not supported"); + } + + type0 = type; + } + + ax0 = nx0 > 0 ? sx0 / nx0 : p[0]; + ay0 = ny0 > 0 ? sy0 / ny0 : p[1]; + //System.err.print(" avg " + nx0 + " points " ); + + switch (type0) { + case PathIterator.SEG_LINETO: + result.lineTo(ax0, ay0); + points++; + //j System.err.println( ""+points+": " + GraphUtil.describe(result, false) ); + //System.err.println(" lineTo"+ String.format("[ %f %f ]", ax0, ay0) ); + break; + case PathIterator.SEG_MOVETO: + result.moveTo(ax0, ay0); + //System.err.println(" moveTo "+ String.format("[ %f %f ]", ax0, ay0) ); + break; + case -999: + //System.err.println(" ignore"); + break; + default: + throw new IllegalArgumentException("not supported"); + } + + return result; + } + + /** + * return the points along a curve. Used by ContourRenderer. The returned + * result is the remaining path length. Elements of pathlen that are beyond + * the total path length are not computed, and the result points will be null. + * @param pathlen monotonically increasing path lengths at which the position is to be located. May be null if only the total path length is desired. + * @param result the resultant points will be put into this array. This array should have the same number of elements as pathlen + * @param orientation the local orientation, in radians, of the point at will be put into this array. This array should have the same number of elements as pathlen + * @param it PathIterator first point is used to start the length. + * @param stopAtMoveTo treat SEG_MOVETO as the end of the path. The pathIterator will be left at this point. + * @return the remaining length. Note null may be used for pathlen, result, and orientation and this will simply return the total path length. + */ + public static double pointsAlongCurve(PathIterator it, double[] pathlen, Point2D.Double[] result, double[] orientation, boolean stopAtMoveTo) { + + float[] point = new float[6]; + float fx0 = Float.NaN, fy0 = Float.NaN; + + double slen = 0; + int pathlenIndex = 0; + int type; + + if (pathlen == null) { + pathlen = new double[0]; + } + while (!it.isDone()) { + type = it.currentSegment(point); + it.next(); + + if (!Float.isNaN(fx0) && type == PathIterator.SEG_MOVETO && stopAtMoveTo) { + break; + } + if (PathIterator.SEG_CUBICTO == type) { + throw new IllegalArgumentException("cubicto not supported"); + } else if (PathIterator.SEG_QUADTO == type) { + throw new IllegalArgumentException("quadto not supported"); + } else if (PathIterator.SEG_LINETO == type) { + } + + if (Float.isNaN(fx0)) { + fx0 = point[0]; + fy0 = point[1]; + continue; + } + + double thislen = (float) Point.distance(fx0, fy0, point[0], point[1]); + + if (thislen == 0) { + continue; + } else { + slen += thislen; + } + + while (pathlenIndex < pathlen.length && slen >= pathlen[pathlenIndex]) { + double alpha = 1 - (slen - pathlen[pathlenIndex]) / thislen; + double dx = point[0] - fx0; + double dy = point[1] - fy0; + if (result != null) { + result[pathlenIndex] = new Point2D.Double(fx0 + dx * alpha, fy0 + dy * alpha); + } + if (orientation != null) { + orientation[pathlenIndex] = Math.atan2(dy, dx); + } + pathlenIndex++; + } + + fx0 = point[0]; + fy0 = point[1]; + + } + + double remaining; + if (pathlenIndex > 0) { + remaining = slen - pathlen[pathlenIndex - 1]; + } else { + remaining = slen; + } + + if (result != null) { + for (; pathlenIndex < result.length; pathlenIndex++) { + result[pathlenIndex] = null; + } + } + + return remaining; + } + + /** + * @return a string representation of the affine transforms used in DasPlot for + * debugging. + */ + public static String getATScaleTranslateString(AffineTransform at) { + String atDesc; + NumberFormat nf = new DecimalFormat("0.00"); + + if (at == null) { + return "null"; + } else if (!at.isIdentity()) { + atDesc = "scaleX:" + nf.format(at.getScaleX()) + " translateX:" + nf.format(at.getTranslateX()); + atDesc += "!c" + "scaleY:" + nf.format(at.getScaleY()) + " translateY:" + nf.format(at.getTranslateY()); + return atDesc; + } else { + return "identity"; + } + } + + /** + * calculates the slope and intercept of a line going through two points. + * @return a double array with two elements [ slope, intercept ]. + */ + public static double[] getSlopeIntercept(double x0, double y0, double x1, double y1) { + double slope = (y1 - y0) / (x1 - x0); + double intercept = y0 - slope * x0; + return new double[]{slope, intercept}; + } + + public static Color getRicePaperColor() { + return new Color(255, 255, 255, 128); + } + + public static String describe(GeneralPath path, boolean enumeratePoints) { + PathIterator it = path.getPathIterator(null); + int count = 0; + int lineToCount = 0; + double[] coords = new double[6]; + while (!it.isDone()) { + int type = it.currentSegment(coords); + if (type == PathIterator.SEG_LINETO) { + lineToCount++; + } + if (enumeratePoints) { + //j System.err.println(" " + coords[0] + " " + coords[1]); + } + count++; + it.next(); + } + return "count: " + count + " lineToCount: " + lineToCount; + } + + static String toString(Line2D line) { + return ""+line.getX1()+","+line.getY1()+" "+line.getX2()+","+line.getY2(); + } + + //TODO: sun.awt.geom.Curve and sun.awt.geom.Crossings are GPL open-source, so + // these methods will provide reliable methods for getting rectangle, line + // intersections. + + /** + * returns the point where the two line segments intersect, or null. + * @param line1 + * @param line2 + * @param noBoundsCheck if true, then do not check the segment bounds. + * @return + */ + public static Point2D lineIntersection(Line2D line1, Line2D line2, boolean noBoundsCheck) { + Point2D result = null; + double a1, b1, c1, a2, b2, c2, denom; + a1 = line1.getY2() - line1.getY1(); + b1 = line1.getX1() - line1.getX2(); + c1 = line1.getX2() * line1.getY1() - line1.getX1() * line1.getY2(); + + a2 = line2.getY2() - line2.getY1(); + b2 = line2.getX1() - line2.getX2(); + c2 = line2.getX2() * line2.getY1() - line2.getX1() * line2.getY2(); + + denom = a1 * b2 - a2 * b1; + if (denom != 0) { + result = new Point2D.Double((b1 * c2 - b2 * c1) / denom, (a2 * c1 - + a1 * c2) / denom); + if (noBoundsCheck + ||(((result.getX() - line1.getX1()) * (line1.getX2() - result.getX()) >= 0) + && ((result.getY() - line1.getY1()) * (line1.getY2() - result.getY()) >= 0) + && ((result.getX() - line2.getX1()) * (line2.getX2() - result.getX()) >= 0) + && ((result.getY() - line2.getY1()) * (line2.getY2() - result.getY()) >= 0) ) ) { + return result; + } else { + return null; + } + } else { + return null; + } + } + + public static Point2D lineRectangleIntersection( Point2D p0, Point2D p1, Rectangle2D r0) { + + PathIterator it = r0.getPathIterator(null); + + Line2D line = new Line2D.Double( p0, p1 ); + + float[] c0 = new float[6]; + float[] c1 = new float[6]; + it.currentSegment(c0); + it.next(); + while ( !it.isDone() ) { + int type= it.currentSegment(c1); + if ( type==PathIterator.SEG_LINETO ) { + Line2D seg = new Line2D.Double(c0[0], c0[1], c1[0], c1[1]); + Point2D result = lineIntersection(line, seg, false); + if (result != null) { + return result; + } + } + it.next(); + c0[0]= c1[0]; + c0[1]= c1[1]; + } + return null; + } + + /** + * returns pixel range of the datum range, guarenteeing that the first + * element will be less than or equal to the second. + * @param axis + * @param range + * @return + */ + public static double[] transformRange( DasAxis axis, DatumRange range ) { + double x1= axis.transform(range.min()); + double x2= axis.transform(range.max()); + if ( x1>x2 ) { + double t= x2; + x2= x1; + x1= t; + } + return new double[] { x1, x2 }; + } + + public static DatumRange invTransformRange( DasAxis axis, double x1, double x2 ) { + Datum d1= axis.invTransform(x1); + Datum d2= axis.invTransform(x2); + if ( d1.gt(d2) ) { + Datum t= d2; + d2= d1; + d1= t; + } + return new DatumRange( d1, d2 ); + } + + /** + * return a block with the color and size. + * @param w + * @param h + * @return + */ + public static Icon colorIcon( Color iconColor, int w, int h ) { + BufferedImage image= new BufferedImage( w, h, BufferedImage.TYPE_INT_ARGB ); + Graphics g= image.getGraphics(); + Color save = g.getColor(); + if ( iconColor.getAlpha()!=255 ) { // draw checkerboard to indicate transparency + for ( int j=0; j<16/4; j++ ) { + for ( int i=0; i<16/4; i++ ) { + g.setColor( (i-j)%2 ==0 ? Color.GRAY : Color.WHITE ); + g.fillRect( 0+i*4,0+j*4,4,4); + } + } + } + g.setColor(iconColor); + g.fillRect( 0, 0, w, h ); + return new ImageIcon(image); + } +} diff --git a/dasCore/src/main/java/org/das2/graph/ImageVectorDataSetRenderer.java b/dasCore/src/main/java/org/das2/graph/ImageVectorDataSetRenderer.java new file mode 100644 index 000000000..0083a1953 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/ImageVectorDataSetRenderer.java @@ -0,0 +1,358 @@ +/* + * ImageVectorDataSetRenderer.java + * + * This renderer can handle vector data sets with tens of thousands of points + * by histogramming the points and then creating a greyscale spectrogram of + * the histogram. The property "saturationHitCount" defines the number of pixel + * hits that will make the pixel black. In the future, this may be modified to + * support color, alpha channel, and connected psyms. + * + * Created on April 14, 2005, 8:45 PM + */ +package org.das2.graph; + +import org.das2.dataset.WritableTableDataSet; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.DasException; +import org.das2.util.monitor.ProgressMonitor; +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.WritableRaster; +import org.das2.dataset.DataSet; +import org.das2.dataset.NoDataInIntervalException; + +/** + * + * @author Jeremy + */ +public class ImageVectorDataSetRenderer extends Renderer { + + GeneralPath path; + //SymbolLineRenderer highResRenderer; + Datum xTagWidth; + BufferedImage plotImage; + Rectangle plotImageBounds; + DatumRange imageXRange; + DatumRange imageYRange; + TableDataSet hist; + private Color color = Color.BLACK; + + /** Creates a new instance of LotsaPointsRenderer */ + public ImageVectorDataSetRenderer(DataSetDescriptor dsd) { + super(dsd); + } + + public synchronized void render(java.awt.Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + if ( ds==null ) { + parent.postMessage(this, "no data set", DasPlot.INFO, null, null); + return; + } + if (!xAxis.getUnits().isConvertableTo(ds.getXUnits())) { + parent.postMessage(this, "inconvertable xaxis units", DasPlot.INFO, null, null); + return; + } + + if (!yAxis.getUnits().isConvertableTo(ds.getYUnits())) { + parent.postMessage(this, "inconvertable yaxis units", DasPlot.INFO, null, null); + return; + } + Graphics2D g2 = (Graphics2D) g1; + if (plotImage == null) { + if (getLastException() != null) { + if (getLastException() instanceof NoDataInIntervalException) { + parent.postMessage(this, "no data in interval:!c" + getLastException().getMessage(), DasPlot.WARNING, null, null); + } else { + parent.postException(this, getLastException()); + } + } else { + if (getDataSet() == null) { + parent.postMessage(this, "no data set", DasPlot.INFO, null, null); + } else if (getDataSet().getXLength() == 0) { + parent.postMessage(this, "empty data set", DasPlot.INFO, null, null); + } + } + } else if (plotImage != null) { + Point2D p; + p = new Point2D.Float(plotImageBounds.x, plotImageBounds.y); + int x = (int) (p.getX() + 0.5); + int y = (int) (p.getY() + 0.5); + if (parent.getCanvas().isPrintingThread() && print300dpi) { + AffineTransformOp atop = new AffineTransformOp(AffineTransform.getScaleInstance(4, 4), AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + BufferedImage image300 = atop.filter((BufferedImage) plotImage, null); + AffineTransform atinv; + try { + atinv = atop.getTransform().createInverse(); + } catch (NoninvertibleTransformException ex) { + throw new RuntimeException(ex); + } + atinv.translate(x * 4, y * 4); + g2.drawImage(image300, atinv, getParent()); + } else { + g2.drawImage(plotImage, x, y, getParent()); + } + } + + //renderGhostly(g1, xAxis, yAxis); + } + + private void ghostlyImage2(DasAxis xAxis, DasAxis yAxis, VectorDataSet ds, Rectangle plotImageBounds2) { + int ny = plotImageBounds2.height; + int nx = plotImageBounds2.width; + + logger.fine("create Image"); + BufferedImage image = new BufferedImage(nx, ny, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D) image.getGraphics(); + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g.setColor(color); + g.setStroke(new BasicStroke(1.f / saturationHitCount)); + + g.translate( -plotImageBounds2.x, -plotImageBounds2.y); + + imageXRange= GraphUtil.invTransformRange( xAxis, plotImageBounds2.x, plotImageBounds2.x+plotImageBounds2.width ); + imageYRange= GraphUtil.invTransformRange( yAxis, plotImageBounds2.y, plotImageBounds2.y+plotImageBounds2.height ); + + DatumRange visibleRange = imageXRange; + + //if ( isOverloading() ) visibleRange= visibleRange.rescale(-1,2); + + boolean xmono = Boolean.TRUE == ds.getProperty(DataSet.PROPERTY_X_MONOTONIC); + + int firstIndex = xmono ? DataSetUtil.getPreviousColumn(ds, visibleRange.min()) : 0; + int lastIndex = xmono ? DataSetUtil.getNextColumn(ds, visibleRange.max()) : ds.getXLength(); + + final int STATE_LINETO = -991; + final int STATE_MOVETO = -992; + + int state = STATE_MOVETO; + + // TODO: data breaks + int ix0 = 0, iy0 = 0; + if (ds.getXLength() > 0) { + for (int i = firstIndex; i <= lastIndex; i++) { + if (ds.getDatum(i).isFill()) { + state = STATE_MOVETO; + } else { + int iy = (int) yAxis.transform(ds.getDatum(i)); + int ix = (int) xAxis.transform(ds.getXTagDatum(i)); + switch (state) { + case STATE_MOVETO: + g.fillRect(ix, iy, 1, 1); + ix0 = ix; + iy0 = iy; + break; + case STATE_LINETO: + g.draw(new Line2D.Float(ix0, iy0, ix, iy)); + g.fillRect(ix, iy, 1, 1); + ix0 = ix; + iy0 = iy; + break; + } + state = STATE_LINETO; + } + } + } + + logger.fine("done"); + plotImage = image; + + } + + private TableDataSet histogram(RebinDescriptor ddx, RebinDescriptor ddy, VectorDataSet ds) { + ddx.setOutOfBoundsAction(RebinDescriptor.MINUSONE); + ddy.setOutOfBoundsAction(RebinDescriptor.MINUSONE); + WritableTableDataSet tds = WritableTableDataSet.newSimple(ddx.numberOfBins(), ddx.getUnits(), + ddy.numberOfBins(), ddy.getUnits(), Units.dimensionless); + + if (ds.getXLength() > 0) { + Units xunits = ddx.getUnits(); + Units yunits = ddy.getUnits(); + Units zunits = Units.dimensionless; + + int i = DataSetUtil.getPreviousColumn(ds, ddx.binStart(0)); + int n = DataSetUtil.getNextColumn(ds, ddx.binStop(ddx.numberOfBins() - 1)); + for (; i <= n; i++) { + int ix = ddx.whichBin(ds.getXTagDouble(i, xunits), xunits); + int iy = ddy.whichBin(ds.getDouble(i, yunits), yunits); + if (ix != -1 && iy != -1) { + double d = tds.getDouble(ix, iy, zunits); + tds.setDouble(ix, iy, d + 1, zunits); + } + } + } + return tds; + } + + private void ghostlyImage(DasAxis xAxis, DasAxis yAxis, VectorDataSet ds, Rectangle plotImageBounds2) { + RebinDescriptor ddx; + + DatumRange xrange = new DatumRange(xAxis.invTransform(plotImageBounds2.x), + xAxis.invTransform(plotImageBounds2.x + plotImageBounds2.width)); + DatumRange yrange = new DatumRange(yAxis.invTransform(plotImageBounds2.y + plotImageBounds2.height), + yAxis.invTransform(plotImageBounds2.y)); + + ddx = new RebinDescriptor( + xrange.min(), + xrange.max(), + plotImageBounds2.width, + xAxis.isLog()); + + RebinDescriptor ddy = new RebinDescriptor( + yrange.min(), + yrange.max(), + plotImageBounds2.height, + yAxis.isLog()); + + + TableDataSet newHist = histogram(ddx, ddy, ds); + //WritableTableDataSet whist= (WritableTableDataSet)hist; + + /* double histMax= TableUtil.tableMax(hist, Units.dimensionless); + for ( int i=0; i 0 && d < histMax*floorFactor ) + whist.setDouble( i,j, histMax*floorFactor, Units.dimensionless ); + } + } */ + + + int h = ddy.numberOfBins(); + int w = ddx.numberOfBins(); + + int[] raster = new int[h * w]; + int colorInt = color.getRGB() & 0x00ffffff; + for (int i = 0; i < w; i++) { + for (int j = 0; j < h; j++) { + int index = (i - 0) + (h - j - 1) * w; + // alpha=0 for transparent, alpha=255 for opaque + int alpha = 255 * (int) newHist.getDouble(i, j, Units.dimensionless) / saturationHitCount; + + int icolor = (alpha << 24) | colorInt; + raster[index] = icolor; + } + } + + plotImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + WritableRaster r = plotImage.getRaster(); + r.setDataElements(0, 0, w, h, raster); + + imageXRange = xrange; + imageYRange = yrange; + } + + @Override + public synchronized void updatePlotImage(DasAxis xAxis, DasAxis yAxis, org.das2.util.monitor.ProgressMonitor monitor) throws DasException { + super.updatePlotImage(xAxis, yAxis, monitor); + + long t0 = System.currentTimeMillis(); + + VectorDataSet ds1 = (VectorDataSet) getDataSet(); + if (ds1 == null) { + return; + } + if (!xAxis.getUnits().isConvertableTo(ds1.getXUnits())) { + parent.postMessage(this, "inconvertable xaxis units", DasPlot.INFO, null, null); + return; + } + + if (!yAxis.getUnits().isConvertableTo(ds1.getYUnits())) { + parent.postMessage(this, "inconvertable yaxis units", DasPlot.INFO, null, null); + return; + } + + plotImageBounds = parent.getCacheImageBounds(); + if ( plotImageBounds==null ) { + //transient state in parent component. TODO: fix these + return; + } + + DatumRange visibleRange = xAxis.getDatumRange(); + + boolean xmono = Boolean.TRUE == ds1.getProperty(DataSet.PROPERTY_X_MONOTONIC); + + int firstIndex = xmono ? DataSetUtil.getPreviousColumn(ds1, visibleRange.min()) : 0; + int lastIndex = xmono ? DataSetUtil.getNextColumn(ds1, visibleRange.max()) : ds1.getXLength(); + + if ((lastIndex - firstIndex) > 20 * xAxis.getColumn().getWidth()) { + logger.fine("rendering with histogram"); + ghostlyImage(xAxis, yAxis, ds1, plotImageBounds); + } else { + logger.fine("rendinging with lines"); + ghostlyImage2(xAxis, yAxis, ds1, plotImageBounds); + } + logger.fine("done updatePlotImage"); + + } + int saturationHitCount = 5; + + public void setSaturationHitCount(int d) { + if (d > 10) { + d = 10; + } + this.saturationHitCount = d; + this.update(); + } + + public int getSaturationHitCount() { + return this.saturationHitCount; + } + + public void setColor(Color color) { + this.color = color; + refreshImage(); + } + + public Color getColor() { + return color; + } + + @Override + public boolean acceptContext(int x, int y) { + x -= parent.getCacheImageBounds().getX(); + y -= parent.getCacheImageBounds().getY(); + int i0 = Math.max(x - 2, 0); + int j0 = Math.max(y - 2, 0); + if (plotImage==null) return false; + int i1 = Math.min(x + 3, plotImage.getWidth()); + int j1 = Math.min(y + 3, plotImage.getHeight()); + for (int i = i0; i < i1; i++) { + for (int j = j0; j < j1; j++) { + if ( (this.plotImage.getRGB(i, j) & 0xff000000 ) > 0) { + return true; + } + } + } + return false; + } + /** + * Holds value of property print300dpi. + */ + private boolean print300dpi; + + /** + * Getter for property draw300dpi. + * @return Value of property draw300dpi. + */ + public boolean isPrint300dpi() { + return this.print300dpi; + } + + /** + * Setter for property draw300dpi. + * @param print300dpi New value of property draw300dpi. + */ + public void setPrint300dpi(boolean print300dpi) { + this.print300dpi = print300dpi; + } +} diff --git a/dasCore/src/main/java/org/das2/graph/Legend.java b/dasCore/src/main/java/org/das2/graph/Legend.java new file mode 100644 index 000000000..4b65743b4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/Legend.java @@ -0,0 +1,231 @@ +/* + * Legend.java + * + * Created on September 30, 2004, 5:01 PM + */ + +package org.das2.graph; + +import org.das2.system.DasLogger; +import org.das2.util.ObjectLocator; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.image.*; +import java.util.*; +import java.util.logging.Logger; +import javax.swing.*; +import org.das2.components.propertyeditor.Displayable; +import org.das2.components.propertyeditor.PropertyEditor; + +/** + * + * @author Jeremy + */ +public class Legend extends DasCanvasComponent { + + final static Logger logger= DasLogger.getLogger( DasLogger.GRAPHICS_LOG ); + + class LegendElement { + Icon icon; + Psym psym; + PsymConnector psymConnector; + Displayable rend; + Color color; + String label; + Legend parent; + + Icon getIcon() { + if ( rend!=null ) { + if ( rend instanceof Displayable ) { + return ((Displayable)rend).getListIcon(); + } else { + return null; + } + } else { + return icon; + } + } + + void update() { + if ( rend!=null ) { + if ( rend instanceof Displayable ) { + this.icon= ((Displayable)rend).getListIcon(); + } + } + } + + /** + * return the Displayable or null if no Displayable is associated with the + * legendElement. + */ + Displayable getDisplayable() { + return rend; + } + + private String getLabel() { + return label; + } + + private boolean isVisible() { + return ( rend==null || ! ( rend instanceof Renderer ) || ((Renderer)rend).isActive() ); + } + + LegendElement( Displayable rend, String label ) { + + this.icon= rend.getListIcon(); + this.label= label; + this.rend= rend; + } + + LegendElement( Icon icon, String label ) { + this.icon= icon; + this.label= label; + } + + + } + + ArrayList elements; // LegendElement + ObjectLocator locator; + + public Legend( ) { + elements= new ArrayList(); + getDasMouseInputAdapter().addMenuItem( new JMenuItem( getEditAction() ) ); + } + + private Action getEditAction() { + return new AbstractAction("Renderer Properties") { + public void actionPerformed(ActionEvent e) { + Point p= getDasMouseInputAdapter().getMousePressPosition(); + LegendElement item= (LegendElement)locator.closestObject(p); + if ( item==null ) return; + Displayable rend= item.getDisplayable(); + PropertyEditor editor= new PropertyEditor( rend ); + editor.showDialog(Legend.this); + } + }; + } + + public static Icon getIcon( Color color ) { + Image image= new BufferedImage(6,10,BufferedImage.TYPE_INT_RGB); + Graphics g2= image.getGraphics(); + g2.setColor(color); + g2.fillRect(0,0,6,10); + return new ImageIcon( image ); + } + + public void add( Displayable rend, String label ) { + LegendElement e= new LegendElement( rend, label ); + elements.add(e); + } + + public void remove( Displayable rend ) { + for ( int i=0; i icon.getIconHeight() ? fm.getHeight() : icon.getIconHeight() + border ); + int w1= itemWidth + x + 20; + if ( w1 > maxWidth ) maxWidth= w1; + y+= itemHeight; + } + + if ( !allVisible ) { + Font font0= g.getFont(); + g.setFont( font0.deriveFont(font0.getSize()*0.66f) ); + y+= g.getFontMetrics().getHeight()/2; + g.setFont(font0); + y+= g.getFontMetrics().getHeight()/2; + } + + g.setColor( new Color( 255, 255, 255, 240 ) ); + + g.fill( new Rectangle( 0, 0, maxWidth+10, y-1 ) ); + g.setColor( Color.DARK_GRAY ); + + g.draw( new Rectangle( 0,0, maxWidth+10, y-1 ) ); + + x= 5; + y= 5; + + for ( int i=0; i icon.getIconHeight() ? fm.getHeight() : icon.getIconHeight() + border ); + locator.addObject( new Rectangle( x, y-border/2, maxWidth, itemHeight ), e ); + y+= ( fm.getHeight() > icon.getIconHeight() ? fm.getHeight() : icon.getIconHeight() + border ); + } + + if ( !allVisible ) { + Font font0= g.getFont(); + g.setFont( font0.deriveFont(font0.getSize()*0.66f) ); + y+= g.getFontMetrics().getHeight()/2; + g.setColor( Color.DARK_GRAY ); + g.drawString( "\u00B9 not drawn", x+10, y ); + g.setFont(font0); + y+= g.getFontMetrics().getHeight()/2; + } + + g.setColor( color0 ); + int width= maxWidth+10+1; + int height= y; + + getDasMouseInputAdapter().paint(g1); + + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/Leveler.java b/dasCore/src/main/java/org/das2/graph/Leveler.java new file mode 100644 index 000000000..d2d832e15 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/Leveler.java @@ -0,0 +1,352 @@ +/* + * Leveler.java + * + * Created on April 22, 2003, 4:58 PM + */ + + +/** + * + * @author jbf + */ +// Manages a set of rows or columns, making sure they fill a space without +// overlapping. +package org.das2.graph; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Leveler { + + ArrayList rows; // DasDevicePositions + ArrayList weights; // doubles + double interMargin; + + DasCanvas parent; + + private static class LevelRow extends DasRow { + + Leveler lev; + + /** Creates a new instance of DasLevelRow */ + public LevelRow( DasCanvas parent, Leveler lev, double nposition, double weight ) { + super(parent,0,0); + this.lev= lev; + lev.insertAt(nposition,this,weight); + } + + public LevelRow(DasCanvas parent, Leveler lev, double nposition) { + this( parent, lev, nposition, 1.0 ); + } + + public double getMaximum() { + if (lev==null) { + return 0.; + } else { + return lev.getMaximum(this); + } + } + + public double getMinimum() { + if (lev==null) { + return 0.; + } else { + return lev.getMinimum(this); + } + } + + public int getDMinimum() { + return (int)( getMinimum()*getDeviceSize() ); + } + + public int getDMaximum() { + return (int)( getMaximum()*getDeviceSize() ); + } + + public void setDPosition( int minimum, int maximum) { + lev.setMaximum(this,maximum/(float)getDeviceSize()); + lev.setMinimum(this,minimum/(float)getDeviceSize()); + } + + public void setDMinimum(int minimum) { + super.setDMinimum(minimum); + } + + public void setDMaximum(int maximum) { + super.setDMaximum(maximum); + } + } + + DasRow row; // this row contains the Leveler + + public Leveler( DasCanvas parent ) { + this( parent, new DasRow( parent, 0.05, 0.9 ) ); + } + + public Leveler( DasCanvas parent, DasRow row ) { + this.parent= parent; + this.row= row; + rows= new ArrayList(); + weights= new ArrayList(); + + interMargin= 0.03; + } + + public double getWeight( DasRow row ) { + int index= rows.indexOf(row); + return ((Double)this.weights.get(index)).doubleValue(); + } + + public double getPosition( DasRow row ) { + return row.getMaximum(); + } + + public DasRow addRow( double nposition, double weight ) { + LevelRow r= new LevelRow( parent, this, nposition, weight ); + return r; + } + + public DasRow addRow( double nposition ) { + LevelRow r= new LevelRow( parent, this, nposition ); + return r; + } + + public DasRow addRow( ) { + LevelRow r= new LevelRow( parent, this, 1.0 ); + return r; + } + + public DasRow whichRow( int y ) { + int i= objectIndexAt( y / parent.getHeight() ); + if ( i>=0 && i= nposition ) { + break; + } + } + rows.add(i,row); + weights.add(i,new Double(weight)); + for ( int ii=0; ii + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.graph; + +import org.das2.components.propertyeditor.Displayable; +import org.das2.components.propertyeditor.Enumeration; +import org.das2.DasProperties; + +/** Type-safe enumeration class for the psym property of + * a DasSymbolPlot. + */ + +import javax.swing.*; +import java.awt.*; +import java.awt.geom.*; +import java.awt.geom.Line2D; +import java.awt.image.*; + +public class Psym implements Enumeration, Displayable { + + private static final String[] NAMES = { + "none", //0 + "dots", //1 + "circles", //2 + "triangles", //3 + "cross", //4 + + }; + + public static final Psym NONE = new Psym(0); + + public static final Psym DOTS = new Psym(1); + + public static final Psym CIRCLES = new Psym(2); + + public static final Psym TRIANGLES = new Psym(3); + + public static final Psym CROSS = new Psym(4); + + private int nameIndex; + + private Line2D line = new Line2D.Double(); + private Ellipse2D ellipse = new Ellipse2D.Double(); + + ImageIcon imageIcon; + + private Psym(int nameIndex) { + this.nameIndex = nameIndex; + this.line= new Line2D.Double(); + Image i= new BufferedImage(10,10,BufferedImage.TYPE_INT_RGB); + Graphics2D g= ( Graphics2D) i.getGraphics(); + g.setBackground( Color.white ); + g.setRenderingHints(DasProperties.getRenderingHints()); + g.setColor( Color.white ); + g.fillRect(0,0, 10,10); + g.setColor( Color.black ); + draw(g,5,5,2.f); + + this.imageIcon= new ImageIcon(i); + } + + public String toString() { + return NAMES[nameIndex]; + } + + public String getListLabel() { + return NAMES[nameIndex]; + } + + public Icon getListIcon() { + return imageIcon; + } + + /** Draw the psym at the given coordinates. + * if drawsLines() returns false, then the + * ix and iy parameters are ignored. + */ + public void draw(Graphics g, double x, double y, float size) { + //We are not guaranteed to get a Graphics2D. + Graphics2D g2 = (Graphics2D)(g instanceof Graphics2D ? g : null); + + switch (nameIndex) { + case 0: //LINES + break; + case 1: //DOTS + if ( size < 1f ) { + if (g instanceof Graphics2D) { + ellipse.setFrame(x, y, 1, 1); + g2.fill(ellipse); + } + else { + g.fillOval((int)x, (int)y, 1, 1); + } + } else { + if (g instanceof Graphics2D) { + ellipse.setFrame(x-size, y-size, size*2, size*2); + g2.fill(ellipse); + } + else { + g.fillOval((int)(x - size), (int)(x - size), (int)(size*2), (int)(size*2)); + } + } + break; + case 2: //CIRCLES + Color color0= g.getColor(); + ellipse.setFrame(x - size, y - size, size * 2, size * 2); + + Color backgroundColor= Color.white; + g.setColor(backgroundColor); + if (g instanceof Graphics2D) { + g2.fill(ellipse); + } + else { + g.fillOval((int)(x-size), (int)(y-size), (int)(size*2), (int)(size*2)); + } + g.setColor(color0); + if (g instanceof Graphics2D) { + g2.draw(ellipse); + } + else { + g.drawOval((int)(x-size), (int)(y-size), (int)(size*2), (int)(size*2)); + } + break; + case 3: //TRIANGLES + drawTriangle(g, x, y, size); + break; + case 4: //CROSS + if (g instanceof Graphics2D) { + line.setLine(x-size, y, x+size, y); + g2.draw(line); + line.setLine(x, y-size, x, y+size); + g2.draw(line); + } + else { + g.drawLine((int)(x-size), (int)y, (int)(x+size), (int)y); + g.drawLine((int)x, (int)(y-size), (int)x, (int)(y+size)); + } + break; + default: throw new IllegalArgumentException("Invalid nameIndex for psym"); + } + } + + public void drawTriangle(Graphics g, double x, double y, float size ) { + if (g instanceof Graphics2D) { + Graphics2D g2 = (Graphics2D)g; + line.setLine(x, y-size, x+size, y+size); + g2.draw(line); + line.setLine(x+size, y+size, x-size, y+size); + g2.draw(line); + line.setLine(x-size, y+size, x, y-size); + g2.draw(line); + } + else { + g.drawLine((int)x, (int)(y-size), (int)(x+size), (int)(y+size)); + g.drawLine((int)(x+size), (int)(y+size), (int)(x-size), (int)(y+size)); + g.drawLine((int)(x-size), (int)(y+size), (int)x, (int)(y-size)); + } + } + + public static Psym parsePsym(String str) { + if (str.equals("none")) { + return NONE; + } + else if (str.equals("dots")) { + return DOTS; + } + else if (str.equals("circles")) { + return CIRCLES; + } + else if (str.equals("triangles")) { + return TRIANGLES; + } + else if (str.equals("cross")) { + return CROSS; + } + else { + throw new IllegalArgumentException(str); + } + } + +} + diff --git a/dasCore/src/main/java/org/das2/graph/PsymConnector.java b/dasCore/src/main/java/org/das2/graph/PsymConnector.java new file mode 100644 index 000000000..869d9bc34 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/PsymConnector.java @@ -0,0 +1,141 @@ +/* File: PsymConnector.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 29, 2003, 10:42 AM by __FULLNAME__ <__EMAIL__> + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +package org.das2.graph; + +import org.das2.components.propertyeditor.Displayable; +import org.das2.components.propertyeditor.Enumeration; +import org.das2.DasProperties; +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.*; +import javax.swing.*; + +/** + * + * @author jbf + */ +public class PsymConnector implements Enumeration, Displayable { + + String name; + Icon imageIcon; + BasicStroke stroke; + BasicStroke cacheStroke; + float cacheWidth; + Line2D line; + + public static final PsymConnector NONE= new PsymConnector( "None", null ); + public static final PsymConnector SOLID= new PsymConnector( "Solid", new BasicStroke( 1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ); + public static final PsymConnector DOTFINE= new PsymConnector( "DotFine", + new BasicStroke( 1.0f, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, 1.0f, new float[] {1.5f,2.0f}, 0.f ) ); + public static final PsymConnector DASHFINE= new PsymConnector( "DashFine", + new BasicStroke( 1.0f, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, 1.0f, new float[] {3.0f,2.0f}, 0.f ) ); + + private PsymConnector( String name, BasicStroke stroke ) { + line= new Line2D.Double(); + + this.name= name; + this.stroke= stroke; + this.cacheStroke= stroke; + if ( stroke!=null ) this.cacheWidth= cacheStroke.getLineWidth(); + + Image i= new BufferedImage(15,10,BufferedImage.TYPE_INT_RGB); + Graphics2D g= (Graphics2D)i.getGraphics(); + g.setRenderingHints(DasProperties.getRenderingHints()); + g.setColor(Color.LIGHT_GRAY); + g.fillRect(0,0,15,10); + g.setColor(Color.black); + drawLine(g,2,3,13,7,2.f); + this.imageIcon= new ImageIcon(i); + } + + protected Stroke getStroke( float width ) { + if ( width!=cacheWidth ) { + float[] dashArray= stroke.getDashArray(); + float[] dashArrayWidth=null; + if ( dashArray!=null ) { + dashArrayWidth= new float[dashArray.length]; + for ( int i=0; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.graph; + +import org.das2.dataset.NoDataInIntervalException; +import org.das2.dataset.DataSetConsumer; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.VectorUtil; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.util.DasExceptionHandler; +import java.beans.PropertyChangeListener; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.components.propertyeditor.Editable; +import org.das2.system.DasLogger; +import java.awt.geom.*; + +import javax.swing.*; +import java.awt.*; +import java.io.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import java.util.logging.Logger; +import org.w3c.dom.*; + +public abstract class Renderer implements DataSetConsumer, Editable { + + /** + * identifies the dataset (in the DataSetDescriptor sense) being plotted + * by the Renderer. May be null if no such identifier exists. See + * DataSetDescriptor.create( String id ). + */ + String dataSetId; + /** + * The dataset that is being plotted by the Renderer. + */ + protected DataSet ds; + /** + * Memento for x axis state last time updatePlotImage was called. + */ + private DasAxis.Memento xmemento; + /** + * Memento for y axis state last time updatePlotImage was called. + */ + private DasAxis.Memento ymemento; + /** + * plot containing this renderer + */ + DasPlot parent; + //DasPlot2 parent2; + /** + * the responsibility of keeping a relevant dataset loaded. Can be null + * if a loading mechanism is not used. The DataLoader will be calling + * setDataSet and setException. + */ + DataLoader loader; + + /** DataSet properties to override during the rendering process */ + HashMap override; + + /** + * When a dataset cannot be loaded, the exception causing the failure + * will be rendered instead. + */ + private Exception lastException; + + protected static Logger logger = DasLogger.getLogger(DasLogger.GRAPHICS_LOG); + private String PROPERTY_ACTIVE = "active"; + private String PROPERTY_DATASET = "dataSet"; + + protected Renderer(DataSetDescriptor dsd) { + override = new HashMap<>(); + this.loader = new XAxisDataLoader(this, dsd); + } + + protected Renderer(DataSet ds) { + override = new HashMap<>(); + this.ds = ds; + this.loader = null; + } + + protected Renderer() { + this((DataSetDescriptor) null); + } + + public DasPlot getParent() { + return this.parent; + } + + protected void invalidateParentCacheImage() { + if (parent != null) parent.invalidateCacheImage(); + } + + /** + * returns the current dataset being displayed. + */ + public DataSet getDataSet() { + return this.ds; + } + + /** + * return the data for DataSetConsumer, which might be rebinned. + */ + public DataSet getConsumedDataSet() { + return this.ds; + } + private boolean dumpDataSet; + + /** Getter for property dumpDataSet. + * @return Value of property dumpDataSet. + * + */ + public boolean isDumpDataSet() { + return this.dumpDataSet; + } + + /** Setter for property dumpDataSet setting this to + * true causes the dataSet to be dumped. + * @param dumpDataSet New value of property dumpDataSet. + * + */ + public void setDumpDataSet(boolean dumpDataSet) { + this.dumpDataSet = dumpDataSet; + if (dumpDataSet == true) { + try { + if (ds == null) { + setDumpDataSet(false); + throw new DasException("data set is null"); + } else { + JFileChooser chooser = new JFileChooser(); + int xx = chooser.showSaveDialog(this.getParent()); + if (xx == JFileChooser.APPROVE_OPTION) { + File file = chooser.getSelectedFile(); + if (ds instanceof TableDataSet) { + TableUtil.dumpToAsciiStream((TableDataSet) ds, new FileOutputStream(file)); + } else if (ds instanceof VectorDataSet) { + VectorUtil.dumpToAsciiStream((VectorDataSet) ds, new FileOutputStream(file)); + } else { + throw new DasException("don't know how to serialize data set: " + ds); + } + } + setDumpDataSet(false); + } + } catch (Exception e) { + DasExceptionHandler.handle(e); + } + this.dumpDataSet = dumpDataSet; + } + } + + /** Get a copy of this renderer's display override's. + * + * @return The internal property override map wrapped as an unmodifiable map. + */ + public Map getOverrideMap(){ + return Collections.unmodifiableMap(override); + } + + /** Copy in a set of display overrides for this renderer + * + * @param m An object,object map. The keys should be property strings from + * DataSet + */ + public void setOverrideMap(Map m){ + override = new HashMap<>(m); + } + + /** Get and override property value without removing it + * + * @param name a well know dataset property name. These are provided + * in the DataSet class. + * @return The value for the override, see the DataSet class to cast the returned + * value to the appropriate type. + */ + public Object getOverride(String name){ + return override.get(name); + } + + /** Set a dataset property to override during the rendering process + * + * @param name a well know dataset property name. These are provided + * in the DataSet class. + * + * @return The value object for the property, or null if that property has not + * been overridden. See docs for the DataSet class to cast the return type + * appropriately. + */ + public void setOverride(String name, Object o){ + override.put(name, o); + } + + /** Determine if a particular DataSet property will be overridden during display + * processing + * @param name A DataSet property string + * @return true if overridden, false if the key name is not in the renderers internal + * override map + */ + public boolean hasOverride(String name){ + return override.get(name) == null; + } + + /** Remove an override + * + * @param name A DataSet property string + * @return The previous value held for the property override, or null the property in + * question did not have an override value + */ + public Object removeOverride(String name){ + return override.remove(name); + } + + public void setLastException(Exception e) { + this.lastException = e; + } + + public Exception getLastException() { + return this.lastException; + } + + public void setDataSet(DataSet ds) { + logger.finer("Renderer.setDataSet: " + ds); + DataSet oldDs = this.ds; + + if (oldDs != ds) { + this.ds = ds; + refresh(); + invalidateParentCacheImage(); + propertyChangeSupport.firePropertyChange(PROPERTY_DATASET, oldDs, ds); + } + } + + public void setException(Exception e) { + logger.finer("Renderer.setException: " + e); + Exception oldException = this.lastException; + this.lastException = e; + if (parent != null && oldException != e) { + //parent.markDirty(); + //parent.update(); + refresh(); + invalidateParentCacheImage(); + } + //refresh(); + } + + public void setDataSetID(String id) throws org.das2.DasException { + if (id == null) throw new NullPointerException("Null dataPath not allowed"); + if (id.equals("")) { + setDataSetDescriptor(null); + return; + } + try { + DataSetDescriptor dsd = DataSetDescriptor.create(id); + setDataSetDescriptor(dsd); + } catch (DasException ex) { + ex.printStackTrace(); + throw ex; + } + } + + public String getDataSetID() { + if (getDataSetDescriptor() == null) { + return ""; + } else { + return getDataSetDescriptor().getDataSetID(); + } + } + + /* + * returns the AffineTransform to transform data from the last updatePlotImage call + * axes (if super.updatePlotImage was called), or null if the transform is not possible. + * + * @depricated DasPlot handles the affine transform and previews now. + */ + protected AffineTransform getAffineTransform(DasAxis xAxis, DasAxis yAxis) { + if (xmemento == null) { + logger.fine("unable to calculate AT, because old transform is not defined."); + return null; + } else { + AffineTransform at = new AffineTransform(); + at = xAxis.getAffineTransform(xmemento, at); + at = yAxis.getAffineTransform(ymemento, at); + return at; + } + } + + /** Render is called whenever the image needs to be refreshed or the content + * has changed. This operation should occur with an animation-interactive + * time scale, and an image should be cached when this is not possible. The graphics + * object will have its origin at the upper-left corner of the screen. + */ + public abstract void render(Graphics g, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon); + + /** + * Returns true if the render thinks it can provide the context for a point. That is, + * the renderer affected that point, or nearby points. For example, this is used currently to provide + * a way for the operator to click on a plot and directly edit the renderer who drew the pixel. + * + * @param x the x coordinate in the canvas coordinate system. + * @param y the y coordinate in the canvas coordinate system. + */ + public boolean acceptContext(int x, int y) { + return false; + } + + protected void renderException(Graphics g, DasAxis xAxis, DasAxis yAxis, Exception e) { + + String s; + String message; + FontMetrics fm = g.getFontMetrics(); + + if (e instanceof NoDataInIntervalException) { + s = "no data in interval"; + message = e.getMessage(); + } else { + s = e.getMessage(); + message = ""; + if (s == null || s.length() < 10) { + s = e.toString(); + } + } + + if (!message.equals("")) { + s += ":!c" + message; + } + + parent.postMessage(this, s, DasPlot.ERROR, null, null); + + } + + /** updatePlotImage is called once the expensive operation of loading + * the data is completed. This operation should occur on an interactive + * time scale. This is an opportunity to create a cache + * image of the data with the current axis state, when the render + * operation cannot operate on an animation interactive time scale. + * Codes can no longer assume that the xAxis sent to render will be in + * the same state as it was when updatePlotImage was called, so use + * the getAffineTransform method. Only Renderer should call this method! + */ + public void updatePlotImage(DasAxis xAxis, DasAxis yAxis, ProgressMonitor monitor) throws DasException { + } + + protected void refreshImage() { + if (getParent() != null) { + refresh(); + } + } + + /** + * Something has changed with the Render, and the plot should come back + * to allow this render to repaint. Its cacheImage is invalidated and a + * repaint is posted on the event thread. + */ + public void update() { + if (getParent() != null) getParent().repaint(); + logger.fine("Renderer.update"); + if (parent != null) { + java.awt.EventQueue eventQueue = + Toolkit.getDefaultToolkit().getSystemEventQueue(); + DasRendererUpdateEvent drue = new DasRendererUpdateEvent(parent, this); + eventQueue.postEvent(drue); + } else { + logger.fine("update but parent was null"); + } + } + + /** + * updateImmediately is called from DasPlot when it gets an update event from the + * AWT Event thread. This should trigger a data load and eventually a refresh to + * render the dataset. + */ + protected void updateImmediately() { + logger.finer("entering Renderer.updateImmediately"); + if (parent == null || !parent.isDisplayable()) { + return; + } + + // If there's a loader, then tell him he might want to load new data. + if (loader != null) { + loader.update(); + } + + // The parent has already used an AffineTransform to preview the image, but + // we might as well re-render using the dataset we have. + refresh(); + } + + /** + * recalculate the plot image and repaint. The dataset or exception have + * been updated, or the axes have changed, so we need to perform updatePlotImage + * to do the expensive parts of rendering. + */ + protected void refresh() { + if (!isActive()) return; + + logger.fine("entering Renderer.refresh"); + if (parent == null) { + logger.fine("null parent in refresh"); + return; + } + if (!parent.isDisplayable()) { + logger.fine("parent not displayable"); + return; + } + + Runnable run = new Runnable() { + + public void run() { + logger.fine("update plot image"); + try { + if (parent != null) { // TODO: make synchronized, but this is non-trivial since deadlock. + final ProgressMonitor progressPanel = DasApplication.getDefaultApplication().getMonitorFactory().getMonitor(parent, "Rebinning data set", "updatePlotImage"); + updatePlotImage(parent.getXAxis(), parent.getYAxis(), progressPanel); + xmemento = parent.getXAxis().getMemento(); + ymemento = parent.getYAxis().getMemento(); + lastException = null; + } else { + return; + } + } catch (DasException de) { + // TODO: there's a problem here, that the Renderer can set its own exception and dataset. This needs to be addressed, or handled as an invalid state. + logger.warning("exception: " + de); + ds = null; + } catch (RuntimeException re) { + logger.warning("exception: " + re); + re.printStackTrace(); + parent.invalidateCacheImage(); + parent.repaint(); + throw re; + } finally { + // this code used to call finished() on the progressPanel + } + + logger.fine("invalidate parent cacheImage and repaint"); + + parent.invalidateCacheImage(); + parent.repaint(); + } + }; + + boolean async = false; // updating was done on the event thread... + if (EventQueue.isDispatchThread()) { + if (async) { + new Thread(run, "updatePlotImage").start(); + } else { + run.run(); + } + } else { + run.run(); + } + } + + public void setDataSetDescriptor(DataSetDescriptor dsd) { + if (loader == null) { + logger.warning("installing loader--danger!"); + loader = new XAxisDataLoader(this, dsd); + } + if (loader instanceof XAxisDataLoader) { + ((XAxisDataLoader) loader).setDataSetDescriptor(dsd); + if (parent != null) { + parent.markDirty(); + parent.update(); + } + this.ds = null; + } else { + throw new RuntimeException("loader is not based on DataSetDescriptor"); + } + + } + + public DataLoader getDataLoader() { + return this.loader; + } + + public void setDataSetLoader(DataLoader loader) { + this.loader = loader; + if (loader != null) loader.update(); + } + + public DataSetDescriptor getDataSetDescriptor() { + if (loader == null) { + return null; + } else { + if (this.loader instanceof XAxisDataLoader) { + return ((XAxisDataLoader) loader).getDataSetDescriptor(); + } else { + return null; + } + } + } + + protected void installRenderer() { + // override me + } + + protected void uninstallRenderer() { + // override me + } + + protected Element getDOMElement(Document document) { + // override me + return null; + } + private boolean overloading = false; + + public boolean isOverloading() { + return this.overloading; + } + + public void setOverloading(boolean overloading) { + this.overloading = overloading; + update(); + } + /** + * Holds value of property active. + */ + private boolean active = true; + + /** + * Getter for property active. + * @return Value of property active. + */ + public boolean isActive() { + return this.active; + } + + /** + * Setter for property active. + * @param active New value of property active. + */ + public void setActive(boolean active) { + boolean oldValue = this.active; + this.active = active; + propertyChangeSupport.firePropertyChange(PROPERTY_ACTIVE, oldValue, active); + update(); + } + /** + * Utility field used by bound properties. + */ + protected java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); + + /** + * Adds a PropertyChangeListener to the listener list. + * @param l The listener to add. + */ + public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.addPropertyChangeListener(l); + } + + /** + * Removes a PropertyChangeListener from the listener list. + * @param l The listener to remove. + */ + public void removePropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.removePropertyChangeListener(l); + } + + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + propertyChangeSupport.removePropertyChangeListener(propertyName, listener); + } + + public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + propertyChangeSupport.addPropertyChangeListener(propertyName, listener); + } + + /** Reload the current dataset */ + void reload(){ + + // If we have a data set factory, have it remake the data set, otherwise, just + // repaint. + if( getDataSetDescriptor() != null){ + loader.setReloadDataSet(true); + loader.update(); + } + + refresh(); + } +} diff --git a/dasCore/src/main/java/org/das2/graph/RowRowConnector.java b/dasCore/src/main/java/org/das2/graph/RowRowConnector.java new file mode 100644 index 000000000..9b47f8610 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/RowRowConnector.java @@ -0,0 +1,89 @@ +package org.das2.graph; + +import java.awt.*; + +public class RowRowConnector extends DasCanvasComponent implements java.beans.PropertyChangeListener { + + private DasCanvas parent; + + private DasRow leftRow; + private DasRow rightRow; + + private DasColumn leftColumn; + private DasColumn rightColumn; + + private boolean centerRightRow= false; // this causes a funny bug + + public RowRowConnector( DasCanvas parent, DasRow leftRow, DasRow rightRow, DasColumn leftColumn, DasColumn rightColumn ) { + this.leftRow= leftRow; + this.rightRow= rightRow; + this.leftColumn= leftColumn; + this.rightColumn= rightColumn; + this.parent= parent; + leftRow.addPropertyChangeListener(this); + rightRow.addPropertyChangeListener(this); + rightColumn.addPropertyChangeListener(this); + leftColumn.addPropertyChangeListener(this); + } + + private Rectangle getMyBounds() { + if ( centerRightRow ) { + int rightHeight= rightRow.getHeight(); + int leftCenter= leftRow.getDMiddle(); + if ( leftCenter - rightHeight/2 < 0 ) leftCenter= rightHeight / 2; + if ( leftCenter + rightHeight/2 > parent.getHeight() ) leftCenter= parent.getHeight() - rightHeight / 2; + rightRow.setDPosition( leftCenter-rightHeight/2, leftCenter+rightHeight/2 ); + } + + int xleft= leftColumn.getDMaximum(); + int xright= rightColumn.getDMaximum(); + int ylow= Math.max( leftRow.getDMaximum(), rightRow.getDMaximum() ); + int yhigh= Math.min( leftRow.getDMinimum(), rightRow.getDMinimum() ); + + Rectangle result= new Rectangle( xleft, yhigh, (xright-xleft), (ylow-yhigh+2) ); + return result; + } + + public void setLeftRow( DasRow row ) { + this.leftRow= row; + update(); + } + + public void resize() { + setBounds(getMyBounds()); + } + + protected void paintComponent(Graphics g1) { + Graphics2D g= (Graphics2D)g1.create(); + g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); + g.translate(-getX(), -getY()); + + int hlen=3; + + int x1= leftColumn.getDMaximum()+hlen; + int x2= rightColumn.getDMaximum()-hlen; + int ylow1= leftRow.getDMaximum(); + int ylow2= rightRow.getDMaximum(); + int yhigh1= leftRow.getDMinimum(); + int yhigh2= rightRow.getDMinimum(); + + g.setColor(Color.lightGray); + g.draw(new java.awt.geom.Line2D.Double(x1-hlen,ylow1,x1,ylow1)); + g.draw(new java.awt.geom.Line2D.Double(x2,ylow2,x2+hlen,ylow2)); + g.draw(new java.awt.geom.Line2D.Double(x1,ylow1,x2,ylow2)); + g.draw(new java.awt.geom.Line2D.Double(x1-hlen,yhigh1,x1,yhigh1)); + g.draw(new java.awt.geom.Line2D.Double(x2,yhigh2,x2+hlen,yhigh2)); + g.draw(new java.awt.geom.Line2D.Double(x1,yhigh1,x2,yhigh2)); + + g.dispose(); + + getDasMouseInputAdapter().paint(g1); + } + + public void propertyChange(java.beans.PropertyChangeEvent propertyChangeEvent) { + markDirty(); + update(); + } + +} + diff --git a/dasCore/src/main/java/org/das2/graph/SeriesRenderer.java b/dasCore/src/main/java/org/das2/graph/SeriesRenderer.java new file mode 100644 index 000000000..4c6464678 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/SeriesRenderer.java @@ -0,0 +1,1586 @@ +/* File: SeriesRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.graph; + +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.DasProperties; +import org.das2.components.propertyeditor.Displayable; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.event.DasMouseInputAdapter; +import org.das2.event.LengthDragRenderer; +import org.das2.event.MouseModule; +import org.das2.system.DasLogger; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; + +import java.awt.image.BufferedImage; +import java.awt.image.IndexColorModel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Logger; +import javax.swing.ImageIcon; +import org.das2.datum.UnitsUtil; +import org.das2.util.monitor.ProgressMonitor; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * SeriesRender is a high-performance replacement for the SymbolLineRenderer. + * The SymbolLineRenderer is limited to about 30,000 points, beyond which + * contracts for speed start breaking degrading usability. The goal of the + * SeriesRenderer is to plot 1,000,000 points without breaking the contracts. + * + * @author jbf + */ +public class SeriesRenderer extends Renderer implements Displayable { + + private DefaultPlotSymbol psym = DefaultPlotSymbol.CIRCLES; + private double symSize = 3.0; // radius in pixels + + private double lineWidth = 1.0; // width in pixels + + private boolean histogram = false; + private PsymConnector psymConnector = PsymConnector.SOLID; + private FillStyle fillStyle = FillStyle.STYLE_FILL; + private int renderCount = 0; + private int updateImageCount = 0; + private Color color = Color.BLACK; + private long lastUpdateMillis; + private boolean antiAliased = "on".equals(DasProperties.getInstance().get("antiAlias")); + private int firstIndex;/* the index of the first point drawn, nonzero when X is monotonic and we can clip. */ + + private int lastIndex;/* the non-inclusive index of the last point drawn. */ + + boolean updating = false; + private Logger log = DasLogger.getLogger(DasLogger.GRAPHICS_LOG); + /** + * indicates the dataset was clipped by dataSetSizeLimit + */ + private boolean dataSetClipped; + + public SeriesRenderer() { + super(); + updatePsym(); + } + Image psymImage; + Image[] coloredPsyms; + int cmx, cmy; + FillRenderElement fillElement = new FillRenderElement(); + ErrorBarRenderElement errorElement = new ErrorBarRenderElement(); + PsymConnectorRenderElement psymConnectorElement = new PsymConnectorRenderElement(); + PsymConnectorRenderElement[] extraConnectorElements; + PsymRenderElement psymsElement = new PsymRenderElement(); + public static final String PROPERTY_X_DELTA_PLUS = "X_DELTA_PLUS"; + public static final String PROPERTY_X_DELTA_MINUS = "X_DELTA_MINUS"; + public static final String PROPERTY_Y_DELTA_PLUS = "Y_DELTA_PLUS"; + public static final String PROPERTY_Y_DELTA_MINUS = "Y_DELTA_MINUS"; + + interface RenderElement { + + int render(Graphics2D g, DasAxis xAxis, DasAxis yAxis, VectorDataSet vds, ProgressMonitor mon); + + void update(DasAxis xAxis, DasAxis yAxis, VectorDataSet vds, ProgressMonitor mon); + + boolean acceptContext(Point2D.Double dp); + } + + class PsymRenderElement implements RenderElement { + + protected GeneralPath psymsPath; // store the location of the psyms here. + + int[] colors; // store the color index of each psym + + int[] ipsymsPath; // store the location of the psyms here, evens=x, odds=y + + int count; // the number of points to plot + + + /** + * render the psyms by stamping an image at the psym location. The intent is to + * provide fast rendering by reducing fidelity. + * On 20080206, this was measured to run at 320pts/millisecond for FillStyle.FILL + * On 20080206, this was measured to run at 300pts/millisecond in FillStyle.OUTLINE + */ + private int renderStamp(Graphics2D g, DasAxis xAxis, DasAxis yAxis, VectorDataSet vds, ProgressMonitor mon) { + + VectorDataSet colorByDataSet = null; + if (colorByDataSetId != null && !colorByDataSetId.equals("")) { + colorByDataSet = (VectorDataSet) vds.getPlanarView(colorByDataSetId); + } + + if (colorByDataSet != null) { + for (int i = 0; i < count; i++) { + int icolor = colors[i]; + g.drawImage(coloredPsyms[icolor], ipsymsPath[i * 2] - cmx, ipsymsPath[i * 2 + 1] - cmy, parent); + } + } else { + for (int i = 0; i < count; i++) { + g.drawImage(psymImage, ipsymsPath[i * 2] - cmx, ipsymsPath[i * 2 + 1] - cmy, parent); + } + } + + return count; + + } + + /** + * Render the psyms individually. This is the highest fidelity rendering, and + * should be used in printing. + * On 20080206, this was measured to run at 45pts/millisecond in FillStyle.FILL + * On 20080206, this was measured to run at 9pts/millisecond in FillStyle.OUTLINE + */ + private int renderDraw(Graphics2D graphics, DasAxis xAxis, DasAxis yAxis, VectorDataSet dataSet, ProgressMonitor mon) { + + float fsymSize = (float) symSize; + + if (colorByDataSetId != null && !colorByDataSetId.equals("")) { + colorByDataSet = (VectorDataSet) dataSet.getPlanarView(colorByDataSetId); + } + + graphics.setStroke(new BasicStroke((float) lineWidth)); + + Color[] ccolors = null; + if (colorByDataSet != null) { + IndexColorModel icm = colorBar.getIndexColorModel(); + ccolors = new Color[icm.getMapSize()]; + for (int j = 0; j < icm.getMapSize(); j++) { + ccolors[j] = new Color(icm.getRGB(j)); + } + } + + if (colorByDataSet != null) { + for (int i = 0; i < count; i++) { + graphics.setColor(ccolors[colors[i]]); + psym.draw(graphics, ipsymsPath[i * 2], ipsymsPath[i * 2 + 1], fsymSize, fillStyle); + } + + } else { + for (int i = 0; i < count; i++) { + psym.draw(graphics, ipsymsPath[i * 2], ipsymsPath[i * 2 + 1], fsymSize, fillStyle); + } + } + + return count; + + } + + public synchronized int render(Graphics2D graphics, DasAxis xAxis, DasAxis yAxis, VectorDataSet vds, ProgressMonitor mon) { + int i; + if ( parent==null ) return 0; + if (stampPsyms && !parent.getCanvas().isPrintingThread()) { + i = renderStamp(graphics, xAxis, yAxis, vds, mon); + } else { + i = renderDraw(graphics, xAxis, yAxis, vds, mon); + } + return i; + } + + public synchronized void update(DasAxis xAxis, DasAxis yAxis, VectorDataSet dataSet, ProgressMonitor mon) { + + VectorDataSet colorByDataSet = null; + Units cunits = null; + if (colorByDataSetId != null && !colorByDataSetId.equals("")) { + colorByDataSet = (VectorDataSet) dataSet.getPlanarView(colorByDataSetId); + if (colorByDataSet != null) { + cunits = colorByDataSet.getYUnits(); + } + } + + + Units xUnits = xAxis.getUnits(); + Units yUnits = yAxis.getUnits(); + + double x, y; + int fx, fy; + + psymsPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, 110 * (lastIndex - firstIndex) / 100); + ipsymsPath = new int[(lastIndex - firstIndex) * 2]; + colors = new int[lastIndex - firstIndex + 2]; + + int index = firstIndex; + + x = dataSet.getXTagDouble(index, xUnits); + y = dataSet.getDouble(index, yUnits); + fx = (int) xAxis.transform(x, xUnits); + fy = (int) yAxis.transform(y, yUnits); + + int i = 0; + for (; index < lastIndex; index++) { + x = dataSet.getXTagDouble(index, xUnits); + y = dataSet.getDouble(index, yUnits); + + final boolean isValid = yUnits.isValid(y) && xUnits.isValid(x); + + fx = (int) xAxis.transform(x, xUnits); + fy = (int) yAxis.transform(y, yUnits); + + if (isValid) { + ipsymsPath[i * 2] = fx; + ipsymsPath[i * 2 + 1] = fy; + if (colorByDataSet != null) { + colors[i] = colorBar.indexColorTransform(colorByDataSet.getDouble(index, cunits), cunits); + } + i++; + } + } + + count = i; + + } + + public boolean acceptContext(Point2D.Double dp) { + if (ipsymsPath == null) { + return false; + } + double rad = Math.max(symSize, 5); + + for (int index = firstIndex; index < lastIndex; index++) { + int i = index - firstIndex; + if (dp.distance(ipsymsPath[i * 2], ipsymsPath[i * 2 + 1]) < rad) { + return true; + } + } + return false; + } + } + + class ErrorBarRenderElement implements RenderElement { + + GeneralPath p; + + public int render(Graphics2D g, DasAxis xAxis, DasAxis yAxis, VectorDataSet vds, ProgressMonitor mon) { + if (p == null) { + return 0; + } + g.draw(p); + return lastIndex - firstIndex; + } + + public synchronized void update(DasAxis xAxis, DasAxis yAxis, VectorDataSet vds, ProgressMonitor mon) { + VectorDataSet deltaPlusY = (VectorDataSet) vds.getPlanarView(PROPERTY_Y_DELTA_PLUS); + VectorDataSet deltaMinusY = (VectorDataSet) vds.getPlanarView(PROPERTY_Y_DELTA_MINUS); + + p = null; + + if (deltaMinusY == null) { + return; + } + if (deltaMinusY == null) { + return; + } + Units xunits = vds.getXUnits(); + Units yunits = vds.getYUnits(); + Units yoffsetUnits = yunits.getOffsetUnits(); + + p = new GeneralPath(); + for (int i = firstIndex; i < lastIndex; i++) { + float ix = (float) xAxis.transform(vds.getXTagDouble(i, xunits), xunits); + double dp= deltaPlusY.getDouble(i, yoffsetUnits); + double dm= deltaMinusY.getDouble(i, yoffsetUnits); + if ( yoffsetUnits.isValid(dp) && yoffsetUnits.isValid(dm) ) { + float iym = (float) yAxis.transform(vds.getDouble(i, yunits) - dm, yunits); + float iyp = (float) yAxis.transform(vds.getDouble(i, yunits) + dp, yunits); + p.moveTo(ix, iym); + p.lineTo(ix, iyp); + } + } + + } + + public boolean acceptContext(Point2D.Double dp) { + return p != null && p.contains(dp.x - 2, dp.y - 2, 5, 5); + + } + } + + class PsymConnectorRenderElement implements RenderElement { + + private GeneralPath path1; + private Color color; // override default color + + + public int render(Graphics2D g, DasAxis xAxis, DasAxis yAxis, VectorDataSet vds, ProgressMonitor mon) { + if (path1 == null) { + return 0; + } + if (color != null) { + g.setColor(color); + } + psymConnector.draw(g, path1, (float) lineWidth); + return 0; + } + + public synchronized void update(DasAxis xAxis, DasAxis yAxis, VectorDataSet dataSet, ProgressMonitor mon) { + Units xUnits = xAxis.getUnits(); + Units yUnits = yAxis.getUnits(); + + if ( lastIndex-firstIndex==0 ) { + this.path1= null; + return; + } + + GeneralPath newPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, 110 * (lastIndex - firstIndex) / 100); + + Datum sw = DataSetUtil.guessXTagWidth(dataSet); + double xSampleWidth; + boolean logStep; + if ( UnitsUtil.isRatiometric(sw.getUnits())) { + xSampleWidth = sw.doubleValue(Units.logERatio); + logStep= true; + } else { + xSampleWidth = sw.doubleValue(xUnits.getOffsetUnits()); + logStep= false; + } + + + /* fuzz the xSampleWidth */ + xSampleWidth = xSampleWidth * 1.10; + + double x = Double.NaN; + double y = Double.NaN; + + double x0 = Double.NaN; /* the last plottable point */ + double y0 = Double.NaN; /* the last plottable point */ + + float fx = Float.NaN; + float fy = Float.NaN; + float fx0 = Float.NaN; + float fy0 = Float.NaN; + + int index; + + index = firstIndex; + x = (double) dataSet.getXTagDouble(index, xUnits); + y = (double) dataSet.getDouble(index, yUnits); + + // first point // + logger.fine("firstPoint moveTo,LineTo= " + x + "," + y); + fx = (float) xAxis.transform(x, xUnits); + fy = (float) yAxis.transform(y, yUnits); + if (histogram) { + float fx1 = midPoint( xAxis, x, xUnits, xSampleWidth, sw.getUnits(), -0.5 ); + newPath.moveTo(fx1, fy); + newPath.lineTo(fx, fy); + } else { + newPath.moveTo(fx, fy); + newPath.lineTo(fx, fy); + } + + x0 = x; + y0 = y; + fx0 = fx; + fy0 = fy; + + index++; + + // now loop through all of them. // + boolean ignoreCadence= ! cadenceCheck; + for (; index < lastIndex; index++) { + + x = dataSet.getXTagDouble(index, xUnits); + y = dataSet.getDouble(index, yUnits); + + final boolean isValid = yUnits.isValid(y) && xUnits.isValid(x); + + fx = (float) xAxis.transform(x, xUnits); + fy = (float) yAxis.transform(y, yUnits); + + //double tx= xAxis.transformFast( x, xUnits ); + + //System.err.println( ""+(float)tx+ " " + fx ); + + if (isValid) { + double step= logStep ? Math.log(x/x0) : x-x0; + if ( ( ignoreCadence && step < xSampleWidth*20 ) || step < xSampleWidth) { + // draw connect-a-dot between last valid and here + if (histogram) { + float fx1 = (fx0 + fx) / 2; + newPath.lineTo(fx1, fy0); + newPath.lineTo(fx1, fy); + newPath.lineTo(fx, fy); + } else { + newPath.lineTo(fx, fy); // this is the typical path + + } + + } else { + // introduce break in line + if (histogram) { + float fx1 = (float) xAxis.transform(x0 + xSampleWidth / 2, xUnits); + newPath.lineTo(fx1, fy0); + + fx1 = (float) xAxis.transform(x - xSampleWidth / 2, xUnits); + newPath.moveTo(fx1, fy); + newPath.lineTo(fx, fy); + + } else { + newPath.moveTo(fx, fy); + newPath.lineTo(fx, fy); + } + + } // else introduce break in line + + x0 = x; + y0 = y; + fx0 = fx; + fy0 = fy; + + } else { + newPath.moveTo(fx0, fy0); // place holder + + } + + } // for ( ; index < ixmax && lastIndex; index++ ) + + + if (!histogram && simplifyPaths && colorByDataSet == null) { + //j System.err.println( "input: " ); + //j System.err.println( GraphUtil.describe( newPath, true) ); + this.path1 = GraphUtil.reducePath(newPath.getPathIterator(null), new GeneralPath(GeneralPath.WIND_NON_ZERO, lastIndex - firstIndex)); + } else { + this.path1 = newPath; + } + + } + + public boolean acceptContext(Point2D.Double dp) { + return this.path1 != null && path1.intersects(dp.x - 5, dp.y - 5, 10, 10); + } + } + + private float midPoint(DasAxis axis, double d1, Units units, double delta, Units offsetUnits, double alpha ) { + float fx1; + if (axis.isLog() && offsetUnits==Units.logERatio ) { + fx1 = (float) axis.transform( Math.exp( Math.log(d1) + delta * alpha ), units); + } else { + fx1 = (float) axis.transform( d1 + delta * alpha, units); + } + return fx1; + } + + class FillRenderElement implements RenderElement { + + private GeneralPath fillToRefPath1; + + public int render(Graphics2D g, DasAxis xAxis, DasAxis yAxis, VectorDataSet vds, ProgressMonitor mon) { + if ( fillToRefPath1==null ) { + return 0; + } + g.setColor(fillColor); + g.fill(fillToRefPath1); + return 0; + } + + public void update(DasAxis xAxis, DasAxis yAxis, VectorDataSet dataSet, ProgressMonitor mon) { + Units xUnits = xAxis.getUnits(); + Units yUnits = yAxis.getUnits(); + + GeneralPath fillPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, 110 * (lastIndex - firstIndex) / 100); + + Datum sw = DataSetUtil.guessXTagWidth(dataSet); + double xSampleWidth; + if ( UnitsUtil.isRatiometric(sw.getUnits())) { + xSampleWidth = sw.doubleValue(Units.logERatio); + } else { + xSampleWidth = sw.doubleValue(xUnits.getOffsetUnits()); + } + + /* fuzz the xSampleWidth */ + xSampleWidth = xSampleWidth * 1.10; + + if (reference != null && reference.getUnits() != yAxis.getUnits()) { + // switch the units to the axis units. + reference = yAxis.getUnits().createDatum(reference.doubleValue(reference.getUnits())); + } + + if (reference == null) { + reference = yUnits.createDatum(yAxis.isLog() ? 1.0 : 0.0); + } + + double yref = (double) reference.doubleValue(yUnits); + + double x = Double.NaN; + double y = Double.NaN; + + double x0 = Double.NaN; /* the last plottable point */ + double y0 = Double.NaN; /* the last plottable point */ + + float fyref = (float) yAxis.transform(yref, yUnits); + float fx = Float.NaN; + float fy = Float.NaN; + float fx0 = Float.NaN; + float fy0 = Float.NaN; + + int index; + + index = firstIndex; + x = (double) dataSet.getXTagDouble(index, xUnits); + y = (double) dataSet.getDouble(index, yUnits); + + // first point // + fx = (float) xAxis.transform(x, xUnits); + fy = (float) yAxis.transform(y, yUnits); + if (histogram) { + float fx1; + fx1= midPoint( xAxis, x, xUnits, xSampleWidth, sw.getUnits(), -0.5 ); + fillPath.moveTo(fx1, fyref); + fillPath.lineTo(fx1, fy); + fillPath.lineTo(fx, fy); + + } else { + fillPath.moveTo(fx, fyref); + fillPath.lineTo(fx, fy); + + } + + x0 = x; + y0 = y; + fx0 = fx; + fy0 = fy; + + if (psymConnector != PsymConnector.NONE || fillToReference) { + // now loop through all of them. // + + for (; index < lastIndex; index++) { + + x = dataSet.getXTagDouble(index, xUnits); + y = dataSet.getDouble(index, yUnits); + + final boolean isValid = yUnits.isValid(y) && xUnits.isValid(x); + + fx = (float) xAxis.transform(x, xUnits); + fy = (float) yAxis.transform(y, yUnits); + + if (isValid) { + if ((x - x0) < xSampleWidth) { + // draw connect-a-dot between last valid and here + if (histogram) { + float fx1 = (fx0 + fx) / 2; //sloppy with ratiometric spacing + fillPath.lineTo(fx1, fy0); + fillPath.lineTo(fx1, fy); + fillPath.lineTo(fx, fy); + } else { + fillPath.lineTo(fx, fy); + } + + } else { + // introduce break in line + if (histogram) { + float fx1 = midPoint( xAxis, x0, xUnits, xSampleWidth, sw.getUnits(), 0.5 ); + fillPath.lineTo(fx1, fy0); + fillPath.lineTo(fx1, fyref); + fx1 = midPoint( xAxis, x, xUnits, xSampleWidth, sw.getUnits(), -0.5 ); + fillPath.moveTo(fx1, fyref); + fillPath.lineTo(fx1, fy); + fillPath.lineTo(fx, fy); + + } else { + fillPath.lineTo(fx0, fyref); + fillPath.moveTo(fx, fyref); + fillPath.lineTo(fx, fy); + } + + } // else introduce break in line + + x0 = x; + y0 = y; + fx0 = fx; + fy0 = fy; + + } + + } // for ( ; index < ixmax && lastIndex; index++ ) + + } + + fillPath.lineTo(fx0, fyref); + this.fillToRefPath1 = fillPath; + + if (simplifyPaths) { + fillToRefPath1 = GraphUtil.reducePath(fillToRefPath1.getPathIterator(null), new GeneralPath(GeneralPath.WIND_NON_ZERO, lastIndex - firstIndex)); + } + + } + + public boolean acceptContext(Point2D.Double dp) { + return fillToRefPath1 != null && fillToRefPath1.contains(dp); + } + } + + /** + * updates the image of a psym that is stamped + */ + private void updatePsym() { + int sx = 6+(int) Math.ceil(symSize + 2 * lineWidth); + int sy = 6+(int) Math.ceil(symSize + 2 * lineWidth); + double dcmx, dcmy; + dcmx = (lineWidth + (int) Math.ceil( ( symSize ) / 2)) +2 ; + dcmy = (lineWidth + (int) Math.ceil( ( symSize ) / 2)) +2 ; + BufferedImage image = new BufferedImage(sx, sy, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D) image.getGraphics(); + + Object rendering = antiAliased ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF; + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, rendering); + g.setColor(color); + if (parent != null) { + g.setBackground(parent.getBackground()); + } + + g.setStroke(new BasicStroke((float) lineWidth)); + + psym.draw(g, dcmx, dcmy, (float) symSize, fillStyle); + psymImage = image; + + if (colorBar != null) { + IndexColorModel model = colorBar.getIndexColorModel(); + coloredPsyms = new Image[model.getMapSize()]; + for (int i = 0; i < model.getMapSize(); i++) { + Color c = new Color(model.getRGB(i)); + image = new BufferedImage(sx, sy, BufferedImage.TYPE_INT_ARGB); + g = (Graphics2D) image.getGraphics(); + if (parent != null) { + g.setBackground(parent.getBackground()); + } + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, rendering); + g.setColor(c); + g.setStroke(new BasicStroke((float) lineWidth)); + + psym.draw(g, dcmx, dcmy, (float) symSize, this.fillStyle); + coloredPsyms[i] = image; + } + + } + + cmx = (int) dcmx; + cmy = (int) dcmy; + + update(); + } + + private void reportCount() { + //if ( renderCount % 100 ==0 ) { + //System.err.println(" updates: "+updateImageCount+" renders: "+renderCount ); + //new Throwable("").printStackTrace(); + //} + } + + public synchronized void render(Graphics g, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + + logger.fine( "ds: "+this.ds+", drawing indeces "+this.firstIndex+" to "+this.lastIndex ); + if ( parent==null ) return; + if ( this.ds == null && getLastException() != null) { + parent.postException(this, getLastException()); + return; + } + + if ( legendLabel!=null && legendLabel.length()>0 ) { + parent.addToLegend( this, (ImageIcon)this.getListIcon(), 0, legendLabel ); + } + + renderCount++; + reportCount(); + + long timer0 = System.currentTimeMillis(); + + DataSet dataSet = getDataSet(); + + if (dataSet == null) { + DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("null data set"); + parent.postMessage(this, "no data set", DasPlot.INFO, null, null); + return; + } + + if (dataSet.getXLength() == 0) { + DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("empty data set"); + parent.postMessage(this, "empty data set", DasPlot.INFO, null, null); + return; + } + + TableDataSet tds = null; + VectorDataSet vds = null; + boolean plottable = false; + + if (dataSet instanceof VectorDataSet) { + vds = (VectorDataSet) dataSet; + plottable = dataSet.getYUnits().isConvertableTo(yAxis.getUnits()); + } else if (dataSet instanceof TableDataSet) { + tds = (TableDataSet) dataSet; + plottable = tds.getZUnits().isConvertableTo(yAxis.getUnits()); + } + plottable = plottable && dataSet.getXUnits().isConvertableTo(xAxis.getUnits()); + + if (!plottable) { + parent.postMessage( this, "data set units cannot convert to axis units", DasPlot.WARNING, null, null ); + return; + } + + logger.fine("rendering points: " + lastIndex + " " + firstIndex); + if (lastIndex == firstIndex) { + parent.postMessage(SeriesRenderer.this, "dataset contains no valid data", DasPlot.INFO, null, null); + return; + } + + logger.fine("render data set " + dataSet); + + Graphics2D graphics = (Graphics2D) g.create(); + + if (antiAliased) { + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } else { + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + } + + if (tds != null) { + if (extraConnectorElements == null) { + return; + } + if (tds.getYLength(0) != extraConnectorElements.length) { + return; + } else { + int maxWidth = 0; + for (int j = 0; j < tds.getYLength(0); j++) { + String label = String.valueOf(tds.getYTagDatum(0, j)); + maxWidth = Math.max(maxWidth, g.getFontMetrics().stringWidth(label)); + } + for (int j = 0; j < tds.getYLength(0); j++) { + vds = tds.getYSlice(j, 0); + + graphics.setColor(color); + if (extraConnectorElements[j] != null) { // thread race + + extraConnectorElements[j].render(graphics, xAxis, yAxis, vds, mon); + + String label = String.valueOf(tds.getYTagDatum(0, j)).trim(); + + parent.addToLegend( this, (ImageIcon)GraphUtil.colorIcon( extraConnectorElements[j].color, 5, 5 ), j, label ); + } + } + } + return; + } + + if (this.fillToReference) { + fillElement.render(graphics, xAxis, yAxis, vds, mon); + } + + + graphics.setColor(color); + log.finest("drawing psymConnector in " + color); + + int count=0; + + count= Math.max( count, psymConnectorElement.render(graphics, xAxis, yAxis, vds, mon) ); + + count= Math.max( count, errorElement.render(graphics, xAxis, yAxis, vds, mon) ); + + if (psym != DefaultPlotSymbol.NONE) { + + count= Math.max( count, psymsElement.render(graphics, xAxis, yAxis, vds, mon) ); + +//double simplifyFactor = (double) ( i - firstIndex ) / (lastIndex - firstIndex); + if ( count==0 ) { + parent.postMessage( this, "no valid points", DasPlot.INFO, null, null ); + } + mon.finished(); + } + +//g.drawString( "renderCount="+renderCount+" updateCount="+updateImageCount,xAxis.getColumn().getDMinimum()+5, yAxis.getRow().getDMinimum()+20 ); + long milli = System.currentTimeMillis(); + long renderTime = (milli - timer0); + double dppms = (lastIndex - firstIndex) / (double) renderTime; + + setRenderPointsPerMillisecond(dppms); + + logger.finer("render: " + renderTime + " total:" + (milli - lastUpdateMillis) + " fps:" + (1000. / (milli - lastUpdateMillis)) + " pts/ms:" + dppms); + lastUpdateMillis = milli; + + if (dataSetClipped) { + parent.postMessage(this, "dataset clipped at " + dataSetSizeLimit + " points", DasPlot.WARNING, null, null); + } + + if ( count>0 && lastIndex - firstIndex < 2 ) { + parent.postMessage(this, "less than two points visible", DasPlot.INFO, null, null); + } + + } + + /** + * updates firstIndex and lastIndex that point to the part of + * the data that is plottable. The plottable part is the part that + * might be visible while limiting the number of plotted points. + */ + private synchronized void updateFirstLast(DasAxis xAxis, DasAxis yAxis, VectorDataSet dataSet) { + + Units xUnits = xAxis.getUnits(); + Units yUnits = yAxis.getUnits(); + + int ixmax; + int ixmin; + + Boolean xMono = (Boolean) dataSet.getProperty(DataSet.PROPERTY_X_MONOTONIC); + if (xMono != null && xMono.booleanValue()) { + DatumRange visibleRange = xAxis.getDatumRange(); + if (parent.isOverSize()) { + Rectangle plotBounds = parent.getCacheImageBounds(); + if ( plotBounds!=null ) { + visibleRange = new DatumRange(xAxis.invTransform(plotBounds.x), xAxis.invTransform(plotBounds.x + plotBounds.width)); + } + + } + ixmin = DataSetUtil.getPreviousColumn(dataSet, visibleRange.min()); + ixmax = DataSetUtil.getNextColumn(dataSet, visibleRange.max()) + 1; // +1 is for exclusive. + + } else { + ixmin = 0; + ixmax = dataSet.getXLength(); + } + + Datum sw = DataSetUtil.guessXTagWidth(dataSet); + double xSampleWidth = sw.doubleValue(xUnits.getOffsetUnits()); + + /* fuzz the xSampleWidth */ + xSampleWidth = xSampleWidth * 1.10; + + double x = Double.NaN; + double y = Double.NaN; + + int index; + + // find the first valid point, set x0, y0 // + for (index = ixmin; index < ixmax; index++) { + x = (double) dataSet.getXTagDouble(index, xUnits); + y = (double) dataSet.getDouble(index, yUnits); + + final boolean isValid = yUnits.isValid(y) && xUnits.isValid(x); + if (isValid) { + firstIndex = index; // TODO: what if no valid points? + + index++; + break; + } + } + + // find the last valid point, minding the dataSetSizeLimit + int pointsPlotted = 0; + for (index = firstIndex; index < ixmax && pointsPlotted < dataSetSizeLimit; index++) { + y = dataSet.getDouble(index, yUnits); + + final boolean isValid = yUnits.isValid(y) && xUnits.isValid(x); + + if (isValid) { + pointsPlotted++; + } + } + + if (index < ixmax && pointsPlotted == dataSetSizeLimit) { + dataSetClipped = true; + } + + lastIndex = index; + + } + + /** + * do the same as updatePlotImage, but use AffineTransform to implement axis transform. + */ + @Override + public synchronized void updatePlotImage(DasAxis xAxis, DasAxis yAxis, ProgressMonitor monitor) { + logger.fine("enter updatePlotImage"); + + updating = true; + + updateImageCount++; + + reportCount(); + + try { + super.updatePlotImage(xAxis, yAxis, monitor); + } catch (DasException e) { + // it doesn't throw DasException, but interface requires exception, jbf 5/26/2005 + throw new RuntimeException(e); + } + + + DataSet dataSet = getDataSet(); + + if (dataSet == null ) { + logger.fine("dataset was null"); + return; + } + + if ( dataSet.getXLength() == 0) { + logger.fine("dataset was empty"); + return; + } + + TableDataSet tds = null; + VectorDataSet vds = null; + boolean plottable = false; + + if (dataSet instanceof VectorDataSet) { + vds = (VectorDataSet) dataSet; + plottable = dataSet.getYUnits().isConvertableTo(yAxis.getUnits()); + } else if (dataSet instanceof TableDataSet) { + tds = (TableDataSet) dataSet; + plottable = tds.getZUnits().isConvertableTo(yAxis.getUnits()); + } + plottable = plottable && dataSet.getXUnits().isConvertableTo(xAxis.getUnits()); + + if (!plottable) { + return; + } + + + logger.fine("entering updatePlotImage"); + long t0 = System.currentTimeMillis(); + + dataSetClipped = false; + + + if (vds != null) { + updateFirstLast(xAxis, yAxis, vds); + + if (fillToReference) { + fillElement.update(xAxis, yAxis, vds, monitor); + } + if (psymConnector != PsymConnector.NONE) { + psymConnectorElement.update(xAxis, yAxis, vds, monitor); + } + + errorElement.update(xAxis, yAxis, vds, monitor); + psymsElement.update(xAxis, yAxis, vds, monitor); + + } else if (tds != null) { + extraConnectorElements = new PsymConnectorRenderElement[tds.getYLength(0)]; + for (int i = 0; i < tds.getYLength(0); i++) { + extraConnectorElements[i] = new PsymConnectorRenderElement(); + + float[] colorHSV = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null); + if (colorHSV[2] < 0.7f) { + colorHSV[2] = 0.7f; + } + if (colorHSV[1] < 0.7f) { + colorHSV[1] = 0.7f; + } + extraConnectorElements[i].color = Color.getHSBColor(i / 6.f, colorHSV[1], colorHSV[2]); + vds = tds.getYSlice(i, 0); + + if (i == 0) { + updateFirstLast(xAxis, yAxis, vds); // minimal support assumes vert slice data is all valid or all invalid. + + } + extraConnectorElements[i].update(xAxis, yAxis, vds, monitor); + } + } + + if (getParent() != null) { + getParent().repaint(); + } + + logger.fine("done updatePlotImage in " + (System.currentTimeMillis() - t0) + " ms"); + updating = false; + long milli = System.currentTimeMillis(); + long renderTime = (milli - t0); + double dppms = (lastIndex - firstIndex) / (double) renderTime; + + setUpdatesPointsPerMillisecond(dppms); + } + + protected void installRenderer() { + if (!DasApplication.getDefaultApplication().isHeadless()) { + DasMouseInputAdapter mouseAdapter = parent.mouseAdapter; + DasPlot p = parent; + mouseAdapter.addMouseModule(new MouseModule(p, new LengthDragRenderer(p, p.getXAxis(), p.getYAxis()), "Length")); + } + + updatePsym(); + } + + protected void uninstallRenderer() { + } + + public Element getDOMElement(Document document) { + return null; + } + + /** + * get an Icon representing the trace. This will be an ImageIcon. + * TODO: cache the result to support use in legend. + * @return + */ + public javax.swing.Icon getListIcon() { + Image i = new BufferedImage(15, 10, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D) i.getGraphics(); + g.setRenderingHints(DasProperties.getRenderingHints()); + if ( parent!=null ) g.setBackground(parent.getBackground()); + + // leave transparent if not white + if (color.equals(Color.white)) { + g.setColor(Color.GRAY); + } else { + g.setColor(new Color(0, 0, 0, 0)); + } + + g.fillRect(0, 0, 15, 10); + + if (fillToReference) { + g.setColor(fillColor); + Polygon p = new Polygon(new int[]{2, 13, 13, 2}, new int[]{3, 7, 10, 10}, 4); + g.fillPolygon(p); + } + + g.setColor(color); + Stroke stroke0 = g.getStroke(); + getPsymConnector().drawLine(g, 2, 3, 13, 7, 1.5f); + g.setStroke(stroke0); + psym.draw(g, 7, 5, 3.f, fillStyle); + return new ImageIcon(i); + } + + public String getListLabel() { + return String.valueOf(this.getDataSetDescriptor()); + } + +// ------- Begin Properties --------------------------------------------- // + public PsymConnector getPsymConnector() { + return psymConnector; + } + + public void setPsymConnector(PsymConnector p) { + PsymConnector old = this.psymConnector; + if (!p.equals(psymConnector)) { + psymConnector = p; + refreshImage(); + propertyChangeSupport.firePropertyChange("psymConnector", old, p); + } + + } + + /** Getter for property psym. + * @return Value of property psym. + */ + public PlotSymbol getPsym() { + return this.psym; + } + + /** Setter for property psym. + * @param psym New value of property psym. + */ + public void setPsym(PlotSymbol psym) { + if (psym == null) { + throw new NullPointerException("psym cannot be null"); + } + + if (psym != this.psym) { + Object oldValue = this.psym; + this.psym = (DefaultPlotSymbol) psym; + updatePsym(); + update(); + propertyChangeSupport.firePropertyChange("psym", oldValue, psym); + } + + } + + /** Getter for property symsize. + * @return Value of property symsize. + */ + public double getSymSize() { + return this.symSize; + } + + /** Setter for property symsize. + * @param symSize New value of property symsize. + */ + public void setSymSize(double symSize) { + double old = this.symSize; + if (this.symSize != symSize) { + this.symSize = symSize; + setPsym(this.psym); + updatePsym(); + update(); + propertyChangeSupport.firePropertyChange("symSize", new Double(old), new Double(symSize)); + } + + } + + /** Getter for property color. + * @return Value of property color. + */ + public Color getColor() { + return color; + } + + /** Setter for property color. + * @param color New value of property color. + */ + public void setColor(Color color) { + if (color == null) { + throw new IllegalArgumentException("null color"); + } + Color old = this.color; + if (!this.color.equals(color)) { + this.color = color; + updatePsym(); + refreshImage(); + propertyChangeSupport.firePropertyChange("color", old, color); + updatePsym(); + } + + } + + public double getLineWidth() { + return lineWidth; + } + + public void setLineWidth(double f) { + double old = this.lineWidth; + if (this.lineWidth != f) { + lineWidth = f; + updatePsym(); + refreshImage(); + propertyChangeSupport.firePropertyChange("lineWidth", new Double(old), new Double(f)); + } + + } + + /** Getter for property antiAliased. + * @return Value of property antiAliased. + * + */ + public boolean isAntiAliased() { + return this.antiAliased; + } + + /** Setter for property antiAliased. + * @param antiAliased New value of property antiAliased. + * + */ + public void setAntiAliased(boolean antiAliased) { + boolean old = this.antiAliased; + this.antiAliased = antiAliased; + updatePsym(); + refreshImage(); + propertyChangeSupport.firePropertyChange("antiAliased", old, antiAliased); + } + + public boolean isHistogram() { + return histogram; + } + + public void setHistogram(final boolean b) { + boolean old = b; + if (b != histogram) { + histogram = b; + refreshImage(); + propertyChangeSupport.firePropertyChange("histogram", old, antiAliased); + } + + } + /** + * Holds value of property selected. + */ + private boolean selected; + + /** + * Getter for property selected. + * @return Value of property selected. + */ + public boolean isSelected() { + return this.selected; + } + + /** + * Setter for property selected. + * @param selected New value of property selected. + */ + public void setSelected(boolean selected) { + this.selected = selected; + } + /** + * Holds value of property fillColor. + */ + private Color fillColor = Color.lightGray; + + /** + * Getter for property fillReference. + * @return Value of property fillReference. + */ + public Color getFillColor() { + return this.fillColor; + } + + /** + * Setter for property fillReference. + * @param fillReference New value of property fillReference. + */ + public void setFillColor(Color color) { + Color old = this.fillColor; + if (!this.fillColor.equals(color)) { + this.fillColor = color; + update(); + propertyChangeSupport.firePropertyChange("fillColor", old, color); + } + + } + /** + * Holds value of property colorByDataSetId. + */ + private String colorByDataSetId = null; + + /** + * Getter for property colorByDataSetId. + * @return Value of property colorByDataSetId. + */ + public String getColorByDataSetId() { + return this.colorByDataSetId; + } + + /** + * The dataset plane to use to get colors. If this is null or "", then + * no coloring is done. (Note the default plane cannot be used to color.) + * @param colorByDataSetId New value of property colorByDataSetId. + */ + public void setColorByDataSetId(String colorByDataSetId) { + String oldVal = this.colorByDataSetId; + this.colorByDataSetId = colorByDataSetId; + update(); + propertyChangeSupport.firePropertyChange("colorByDataSetId", oldVal, colorByDataSetId); + } + /** + * Holds value of property colorBar. + */ + private DasColorBar colorBar; + + /** + * Getter for property colorBar. + * @return Value of property colorBar. + */ + public DasColorBar getColorBar() { + return this.colorBar; + } + + /** + * Setter for property colorBar. + * @param colorBar New value of property colorBar. + */ + public void setColorBar(DasColorBar colorBar) { + this.colorBar = colorBar; + colorBar.addPropertyChangeListener(new PropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent evt) { + if (colorByDataSetId != null && !colorByDataSetId.equals("")) { + update(); + } + } + }); + refreshImage(); + updatePsym(); + } + /** + * Holds value of property fillToReference. + */ + private boolean fillToReference; + + /** + * Getter for property fillToReference. + * @return Value of property fillToReference. + */ + public boolean isFillToReference() { + return this.fillToReference; + } + + /** + * Setter for property fillToReference. + * @param fillToReference New value of property fillToReference. + */ + public void setFillToReference(boolean fillToReference) { + boolean old = this.fillToReference; + if (this.fillToReference != fillToReference) { + this.fillToReference = fillToReference; + update(); + propertyChangeSupport.firePropertyChange("fillToReference", old, fillToReference); + } + + } + /** + * Holds value of property reference. + */ + private Datum reference = Units.dimensionless.createDatum(0); + + /** + * Getter for property reference. + * @return Value of property reference. + */ + public Datum getReference() { + return this.reference; + } + + /** + * Setter for property reference. + * @param reference New value of property reference. + */ + public void setReference(Datum reference) { + Datum old = this.reference; + if (!this.reference.equals(reference)) { + this.reference = reference; + refreshImage(); + propertyChangeSupport.firePropertyChange("reference", old, reference); + } + + } + /** + * Holds value of property colorByDataSet. + */ + private org.das2.dataset.VectorDataSet colorByDataSet; + + /** + * Getter for property colorByDataSet. + * @return Value of property colorByDataSet. + */ + public org.das2.dataset.VectorDataSet getColorByDataSet() { + return this.colorByDataSet; + } + + /** + * Setter for property colorByDataSet. + * @param colorByDataSet New value of property colorByDataSet. + */ + public void setColorByDataSet(org.das2.dataset.VectorDataSet colorByDataSet) { + this.colorByDataSet = colorByDataSet; + refreshImage(); + } + /** + * Holds value of property resetDebugCounters. + */ + private boolean resetDebugCounters; + + /** + * Getter for property resetDebugCounters. + * @return Value of property resetDebugCounters. + */ + public boolean isResetDebugCounters() { + return this.resetDebugCounters; + } + + /** + * Setter for property resetDebugCounters. + * @param resetDebugCounters New value of property resetDebugCounters. + */ + public void setResetDebugCounters(boolean resetDebugCounters) { + if (resetDebugCounters) { + renderCount = 0; + updateImageCount = 0; + update(); + } + + } + /** + * Holds value of property simplifyPaths. + */ + private boolean simplifyPaths = true; + + /** + * Getter for property simplifyPaths. + * @return Value of property simplifyPaths. + */ + public boolean isSimplifyPaths() { + return this.simplifyPaths; + } + + /** + * Setter for property simplifyPaths. + * @param simplifyPaths New value of property simplifyPaths. + */ + public void setSimplifyPaths(boolean simplifyPaths) { + this.simplifyPaths = simplifyPaths; + refreshImage(); + } + private boolean stampPsyms = true; + public static final String PROP_STAMPPSYMS = "stampPsyms"; + + public boolean isStampPsyms() { + return this.stampPsyms; + } + + public void setStampPsyms(boolean newstampPsyms) { + boolean oldstampPsyms = stampPsyms; + this.stampPsyms = newstampPsyms; + propertyChangeSupport.firePropertyChange(PROP_STAMPPSYMS, oldstampPsyms, newstampPsyms); + refreshImage(); + } + + public FillStyle getFillStyle() { + return fillStyle; + } + + public void setFillStyle(FillStyle fillStyle) { + this.fillStyle = fillStyle; + updatePsym(); + refreshImage(); + } + + /** + * If non-null and non-zero-length, use this label to describe the renderer + * in the plot's legend. + */ + public static final String PROP_LEGENDLABEL = "legendLabel"; + + protected String legendLabel = ""; + + public String getLegendLabel() { + return legendLabel; + } + + public void setLegendLabel(String legendLabel) { + String oldLegendLabel = this.legendLabel; + this.legendLabel = legendLabel; + propertyChangeSupport.firePropertyChange(PROP_LEGENDLABEL, oldLegendLabel, legendLabel); + } + + + @Override + public boolean acceptContext(int x, int y) { + boolean accept = false; + + Point2D.Double dp = new Point2D.Double(x, y); + + if (this.fillToReference && fillElement.acceptContext(dp)) { + accept = true; + } + + if ((!accept) && psymConnectorElement.acceptContext(dp)) { + accept = true; + } + + if ((!accept) && extraConnectorElements != null) { + for (int j = 0; j < extraConnectorElements.length; j++) { + if (!accept && extraConnectorElements[j] != null && extraConnectorElements[j].acceptContext(dp)) { + accept = true; + } + } + } + + if ((!accept) && psymsElement.acceptContext(dp)) { + accept = true; + } + + if ((!accept) && errorElement.acceptContext(dp)) { + accept = true; + } + + return accept; + } + /** + * property dataSetSizeLimit is the maximum number of points that will be rendered. + * This is introduced because large datasets cause java2D plotting to fail. + * When the size limit is reached, the data is clipped and a message is displayed. + */ + private int dataSetSizeLimit = 200000; + + /** + * Getter for property dataSetSizeLimit. + * @return Value of property dataSetSizeLimit. + */ + public int getDataSetSizeLimit() { + return this.dataSetSizeLimit; + } + + /** + * Setter for property dataSetSizeLimit. + * @param dataSetSizeLimit New value of property dataSetSizeLimit. + */ + public void setDataSetSizeLimit(int dataSetSizeLimit) { + int oldDataSetSizeLimit = this.dataSetSizeLimit; + this.dataSetSizeLimit = dataSetSizeLimit; + refreshImage(); + propertyChangeSupport.firePropertyChange("dataSetSizeLimit", new Integer(oldDataSetSizeLimit), new Integer(dataSetSizeLimit)); + } + private double updatesPointsPerMillisecond; + public static final String PROP_UPDATESPOINTSPERMILLISECOND = "updatesPointsPerMillisecond"; + + public double getUpdatesPointsPerMillisecond() { + return this.updatesPointsPerMillisecond; + } + + public void setUpdatesPointsPerMillisecond(double newupdatesPointsPerMillisecond) { + //double oldupdatesPointsPerMillisecond = updatesPointsPerMillisecond; + this.updatesPointsPerMillisecond = newupdatesPointsPerMillisecond; + //propertyChangeSupport.firePropertyChange(PROP_UPDATESPOINTSPERMILLISECOND, oldupdatesPointsPerMillisecond, newupdatesPointsPerMillisecond); + } + private double renderPointsPerMillisecond; + public static final String PROP_RENDERPOINTSPERMILLISECOND = "renderPointsPerMillisecond"; + + public double getRenderPointsPerMillisecond() { + return this.renderPointsPerMillisecond; + } + + public void setRenderPointsPerMillisecond(double newrenderPointsPerMillisecond) { + //double oldrenderPointsPerMillisecond = renderPointsPerMillisecond; + this.renderPointsPerMillisecond = newrenderPointsPerMillisecond; + //propertyChangeSupport.firePropertyChange(PROP_RENDERPOINTSPERMILLISECOND, oldrenderPointsPerMillisecond, newrenderPointsPerMillisecond); + } + + public int getFirstIndex() { + return this.firstIndex; + } + + public int getLastIndex() { + return this.lastIndex; + } + + protected boolean cadenceCheck = true; + public static final String PROP_CADENCECHECK = "cadenceCheck"; + + public boolean isCadenceCheck() { + return cadenceCheck; + } + + /** + * If true, then use a cadence estimate to determine and indicate data gaps. + * @param cadenceCheck + */ + public void setCadenceCheck(boolean cadenceCheck) { + boolean oldCadenceCheck = this.cadenceCheck; + this.cadenceCheck = cadenceCheck; + refreshImage(); + propertyChangeSupport.firePropertyChange(PROP_CADENCECHECK, oldCadenceCheck, cadenceCheck); + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/SpectrogramRenderer.java b/dasCore/src/main/java/org/das2/graph/SpectrogramRenderer.java new file mode 100755 index 000000000..0584b8457 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/SpectrogramRenderer.java @@ -0,0 +1,680 @@ +/* File: SpectrogramRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.graph; + +import org.das2.event.DasMouseInputAdapter; +import org.das2.event.MouseModule; +import org.das2.event.HorizontalSlicerMouseModule; +import org.das2.event.HorizontalDragRangeSelectorMouseModule; +import org.das2.event.VerticalSlicerMouseModule; +import org.das2.event.CrossHairMouseModule; +import org.das2.components.propertyeditor.Enumeration; +import org.das2.dataset.DataSetRebinner; +import org.das2.dataset.NoDataInIntervalException; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.AverageTableRebinner; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.DataSet; +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.DasNameException; +import org.das2.DasPropertyException; +import org.das2.components.HorizontalSpectrogramSlicer; +import org.das2.components.VerticalSpectrogramAverager; +import org.das2.components.VerticalSpectrogramSlicer; +import org.das2.dasml.FormBase; +import org.das2.datum.DatumRange; +import org.das2.datum.InconvertibleUnitsException; +import org.das2.datum.Units; +import org.das2.system.DasLogger; +import org.das2.util.DasExceptionHandler; +import org.das2.util.monitor.ProgressMonitor; +import java.awt.*; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.geom.*; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.net.URL; +import java.text.ParseException; +import java.util.Arrays; +import java.util.logging.Logger; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import org.w3c.dom.*; + +/** + * + * @author jbf + */ +public class SpectrogramRenderer extends Renderer implements TableDataSetConsumer, org.das2.components.propertyeditor.Displayable { + + private Object lockObject = new Object(); + private DasColorBar colorBar; + private Image plotImage; + private Rectangle plotImageBounds; + + private byte[] raster; + private int rasterWidth, rasterHeight; + DatumRange imageXRange; + DatumRange imageYRange; + DasAxis.Memento xmemento, ymemento, cmemento; + int updateImageCount = 0, renderCount = 0; + private TableDataSet rebinDataSet; // simpleTableDataSet at pixel resolution + + protected class RebinListener implements PropertyChangeListener { + + public void propertyChange(PropertyChangeEvent e) { + update(); + refreshImage(); + } + } + RebinListener rebinListener = new RebinListener(); + private static Logger logger = DasLogger.getLogger(DasLogger.GRAPHICS_LOG); + /** Holds value of property rebinner. */ + private RebinnerEnum rebinnerEnum; + + public static class RebinnerEnum implements Enumeration { + + DataSetRebinner rebinner; + String label; + + public RebinnerEnum(DataSetRebinner rebinner, String label) { + this.rebinner = rebinner; + this.label = label; + } + public static final RebinnerEnum binAverage = new RebinnerEnum(new AverageTableRebinner(), "binAverage"); + //public static final RebinnerEnum nearestNeighbor = new RebinnerEnum(new NearestNeighborTableRebinner(), "nearestNeighbor"); + public static final RebinnerEnum nearestNeighbor; + public static final RebinnerEnum binAverageNoInterpolate; + public static final RebinnerEnum binAverageNoInterpolateNoEnlarge; + + + static { + AverageTableRebinner rebinner = new AverageTableRebinner(); + rebinner.setInterpolate(false); + binAverageNoInterpolate = new RebinnerEnum(rebinner, "noInterpolate"); + + rebinner = new AverageTableRebinner(); + rebinner.setInterpolate(false); + rebinner.setEnlargePixels(false); + binAverageNoInterpolateNoEnlarge = new RebinnerEnum(rebinner, "noInterpolateNoEnlarge"); + + rebinner = new AverageTableRebinner(); + rebinner.setInterpolateType( AverageTableRebinner.Interpolate.NearestNeighbor ); + nearestNeighbor = new RebinnerEnum(rebinner, "nearestNeighbor"); + } + + /*public static final RebinnerEnum binAverage= new RebinnerEnum(new AverageTableRebinner(),"binAverage"); + public static final RebinnerEnum nearestNeighbor; + public static final RebinnerEnum binAverageNoInterpolate= new RebinnerEnum(new AverageNoInterpolateTableRebinner(),"noInterpolate"); + public static final RebinnerEnum binAverageNoInterpolateNoEnlarge; + static { + AverageNoInterpolateTableRebinner rebin= new AverageNoInterpolateTableRebinner(); + rebin.setNearestNeighbor(true); + nearestNeighbor= new RebinnerEnum(rebin, "nearestNeighbor"); + AverageTableRebinner rebinner; + rebinner = new AverageTableRebinner(); + rebinner.setInterpolate(false); + rebinner.setEnlargePixels(false); + binAverageNoInterpolateNoEnlarge = new RebinnerEnum(rebinner, "noInterpolateNoEnlarge"); + }*/ + public Icon getListIcon() { + URL resource = SpectrogramRenderer.class.getResource("/images/icons/rebin." + label + ".png"); + if (resource == null) { + return null; + } + return new ImageIcon(resource); + } + + public String toString() { + return this.label; + } + + DataSetRebinner getRebinner() { + return this.rebinner; + } + } + + /** Creates a new instance of SpectrogramRenderer. + * @param colorBar A colorBar object I presume? + */ + public SpectrogramRenderer(DasColorBar colorBar) { + this(null, colorBar); + } + + /** Creates a new instance of SpectrogramRenderer. + * + * @param dsd And object used to provide data to the renderer based on an + * index range. Note: This may be null. + * @param colorBar A colorBar object I presume? + */ + public SpectrogramRenderer(DataSetDescriptor dsd, DasColorBar colorBar) { + super(dsd); + this.colorBar = colorBar; + if (this.colorBar != null) { + colorBar.addPropertyChangeListener("dataMinimum", rebinListener); + colorBar.addPropertyChangeListener("dataMaximum", rebinListener); + colorBar.addPropertyChangeListener("log", rebinListener); + colorBar.addPropertyChangeListener(DasColorBar.PROPERTY_TYPE, rebinListener); + colorBar.addPropertyChangeListener(DasColorBar.PROPERTY_FILL_COLOR, rebinListener); + } + setRebinner(SpectrogramRenderer.RebinnerEnum.binAverage); + } + + /** Creates a new instance of SpectrogramRenderer + * @deprecated use {link + * #SpectrogramRenderer(org.das2.dataset.DataSetDescriptor, + * org.das2.graph.DasColorBar)} + */ + public SpectrogramRenderer(DasPlot parent, DataSetDescriptor dsd, DasColorBar colorBar) { + this(dsd, colorBar); + this.parent = parent; + } + + public DasAxis getZAxis() { + return colorBar; //.getAxis(); + } + + public DasColorBar getColorBar() { + return colorBar; + } + + public void setColorBar(DasColorBar cb) { + if (colorBar == cb) { + return; + } + if (colorBar != null) { + colorBar.removePropertyChangeListener("dataMinimum", rebinListener); + colorBar.removePropertyChangeListener("dataMaximum", rebinListener); + colorBar.removePropertyChangeListener("log", rebinListener); + colorBar.removePropertyChangeListener(DasColorBar.PROPERTY_TYPE, rebinListener); + colorBar.removePropertyChangeListener(DasColorBar.PROPERTY_FILL_COLOR, rebinListener); + if (parent != null && parent.getCanvas() != null) { + parent.getCanvas().remove(colorBar); + } + } + colorBar = cb; + if (colorBar != null) { + colorBar.addPropertyChangeListener("dataMinimum", rebinListener); + colorBar.addPropertyChangeListener("dataMaximum", rebinListener); + colorBar.addPropertyChangeListener("log", rebinListener); + colorBar.addPropertyChangeListener(DasColorBar.PROPERTY_TYPE, rebinListener); + colorBar.addPropertyChangeListener(DasColorBar.PROPERTY_FILL_COLOR, rebinListener); + if (parent != null && parent.getCanvas() != null) { + parent.getCanvas().add(colorBar); + } + } + } + + public void render(Graphics g, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + logger.finer("entering SpectrogramRenderer.render"); + Graphics2D g2 = (Graphics2D) g; + + renderCount++; + reportCount(); + synchronized (lockObject) { + if (plotImage == null) { + if (getLastException() != null) { + if (getLastException() instanceof NoDataInIntervalException) { + parent.postMessage(this, "no data in interval:!c" + getLastException().getMessage(), DasPlot.WARNING, null, null); + } else { + parent.postException(this, getLastException()); + } + } else { + if (getDataSet() == null) { + parent.postMessage(this, "no data set", DasPlot.INFO, null, null); + } else if (getDataSet().getXLength() == 0) { + parent.postMessage(this, "empty data set", DasPlot.INFO, null, null); + } + } + } else if (plotImage != null) { + Point2D p; + p = new Point2D.Float( plotImageBounds.x, plotImageBounds.y ); + int x = (int) (p.getX() + 0.5); + int y = (int) (p.getY() + 0.5); + if (parent.getCanvas().isPrintingThread() && print300dpi) { + AffineTransformOp atop = new AffineTransformOp(AffineTransform.getScaleInstance(4, 4), AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + BufferedImage image300 = atop.filter((BufferedImage) plotImage, null); + AffineTransform atinv; + try { + atinv = atop.getTransform().createInverse(); + } catch (NoninvertibleTransformException ex) { + throw new RuntimeException(ex); + } + atinv.translate(x * 4, y * 4); + g2.drawImage(image300, atinv, getParent()); + } else { + g2.drawImage(plotImage, x, y, getParent()); + } + } + } + } + int count = 0; + private boolean sliceRebinnedData = true; + + /** + * transforms the simpleTableDataSet into a Raster byte array. The rows of + * the table are adjacent in the output byte array. + */ + private static byte[] transformSimpleTableDataSet(TableDataSet rebinData, DasColorBar cb, boolean flipY) { + + if (rebinData.tableCount() > 1) { + throw new IllegalArgumentException("TableDataSet contains more than one table"); + } + logger.fine("converting to pixel map"); + //TableDataSet weights= (TableDataSet)rebinData.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); + int itable = 0; + int ny = rebinData.getYLength(itable); + int h = ny; + int nx = rebinData.tableEnd(itable) - rebinData.tableStart(itable); + int w = nx; + int icolor; + + Units units = cb.getUnits(); + int ncolor = cb.getType().getColorCount(); + + TableDataSet weights = (TableDataSet) rebinData.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); + + byte[] pix = new byte[nx * ny]; + Arrays.fill(pix, (byte) cb.getFillColorIndex()); + + + for (int i = rebinData.tableStart(itable); i < rebinData.tableEnd(itable); i++) { + for (int j = 0; j < ny; j++) { + if (weights == null || weights.getDouble(i, j, Units.dimensionless) > 0.) { + int index; + index = (i - 0) + (ny - j - 1) * nx; + icolor = cb.indexColorTransform(rebinData.getDouble(i, j, units), units); + pix[index] = (byte) icolor; + } + } + } + + return pix; + } + + private void reportCount() { + if (updateImageCount % 10 == 0) { + //System.err.println(" updates: "+updateImageCount+" renders: "+renderCount ); + } + + } + +// private String getDsId() { +// if ( this.ds==null ) { +// return "null"; +// } else if ( this.ds.getXLength()==0 ) { +// return "empty"+this.ds.hashCode(); +// } else { +// return this.ds.getXTagDatum(0).toString(); +// } +// } + public void updatePlotImage( DasAxis xAxis, DasAxis yAxis, ProgressMonitor monitor ) throws DasException { + logger.finer("entering SpectrogramRenderer.updatePlotImage"); + updateImageCount++; + reportCount(); + try { + try { + + BufferedImage plotImage2; // index color model + + synchronized (lockObject) { + + Rectangle plotImageBounds2= parent.getUpdateImageBounds(); + + if ( raster != null + && xmemento != null && ymemento != null + && xAxis.getMemento().equals(xmemento) + && yAxis.getMemento().equals(ymemento) + && colorBar.getMemento().equals(cmemento) + && plotImageBounds2.width==rasterWidth // TODO: figure out how plotImageBounds2 and xmemento get out of sync. + && plotImageBounds2.height==rasterHeight ) { + logger.fine("same xaxis, yaxis, reusing raster"); + + } else { + + if (getParent() == null || plotImageBounds2==null || plotImageBounds2.width <= 1 || plotImageBounds2.height <= 1) { + logger.finest("canvas not useable!!!"); + return; + } + + if (this.ds == null) { + logger.fine("got null dataset, setting image to null"); + plotImage = null; + plotImageBounds= null; + rebinDataSet = null; + imageXRange = null; + imageYRange = null; + getParent().repaint(); + return; + + } + + if (this.ds.getXLength() == 0) { + logger.fine("got empty dataset, setting image to null"); + plotImage = null; + plotImageBounds= null; + rebinDataSet = null; + imageXRange = null; + imageYRange = null; + getParent().repaint(); + return; + } + + if (!this.ds.getXUnits().isConvertableTo(xAxis.getUnits())) { + logger.fine("dataset units are incompatable with x axis."); + return; + } + + if (!this.ds.getYUnits().isConvertableTo(yAxis.getUnits())) { + logger.fine("dataset units are incompatable with y axis."); + return; + } + + if (!((TableDataSet)this.ds).getZUnits().isConvertableTo(colorBar.getUnits()) ) { + logger.fine("dataset units are incompatable with colorbar."); + return; + } + + RebinDescriptor xRebinDescriptor; + xRebinDescriptor = new RebinDescriptor( + xAxis.invTransform(plotImageBounds2.x), + xAxis.invTransform(plotImageBounds2.x+plotImageBounds2.width), + plotImageBounds2.width, + xAxis.isLog()); + + RebinDescriptor yRebinDescriptor = new RebinDescriptor( + yAxis.invTransform(plotImageBounds2.y+plotImageBounds2.height), + yAxis.invTransform(plotImageBounds2.y), + plotImageBounds2.height, + yAxis.isLog()); + + imageXRange = xAxis.getDatumRange(); + imageYRange = yAxis.getDatumRange(); + + logger.fine("rebinning to pixel resolution: "+ xRebinDescriptor + " " + yRebinDescriptor ); + logger.fine("rebinning to pixel resolution: "+ plotImageBounds2 ); + + DataSetRebinner rebinner = this.rebinnerEnum.getRebinner(); + + long t0; + + t0 = System.currentTimeMillis(); + + rebinDataSet = (TableDataSet) + rebinner.rebin(this.ds, xRebinDescriptor, yRebinDescriptor, null); + + xmemento = xAxis.getMemento(); + ymemento = yAxis.getMemento(); + cmemento = colorBar.getMemento(); + + logger.fine("rebinning to pixel resolution: "+ xmemento + " " + ymemento ); + + raster = transformSimpleTableDataSet(rebinDataSet, colorBar, yAxis.isFlipped()); + rasterWidth = plotImageBounds2.width; + rasterHeight = plotImageBounds2.height; + + } + + IndexColorModel model = colorBar.getIndexColorModel(); + plotImage2 = new BufferedImage(plotImageBounds2.width, plotImageBounds2.height, BufferedImage.TYPE_BYTE_INDEXED, model); + + WritableRaster r = plotImage2.getRaster(); + + try { + if ( plotImageBounds2.width==rasterWidth && plotImageBounds2.height==rasterHeight ) { + r.setDataElements(0, 0, rasterWidth, rasterHeight, raster); + } else { + System.err.println("avoided raster ArrayIndex... track this down sometime..."); + } + } catch (ArrayIndexOutOfBoundsException ex) { + throw ex; + } + plotImage = plotImage2; + plotImageBounds= plotImageBounds2; + + } + } catch (InconvertibleUnitsException ex) { + logger.fine("inconvertable units, setting image to null"); + ex.printStackTrace(); + plotImage = null; + plotImageBounds= null; + rebinDataSet = null; + imageXRange = null; + imageYRange = null; + if (this.getLastException() == null) setException(ex); + getParent().repaint(); + return; + } + + } catch (NullPointerException ex) { + ex.printStackTrace(); + } catch (NoDataInIntervalException e) { + setLastException(e); + plotImage = null; + } finally { + getParent().repaint(); + } + } + + protected void installRenderer() { + if (parent != null && parent.getCanvas() != null) { + if (colorBar != null) { + if (colorBar.getColumn() == DasColumn.NULL) { + DasColumn column = parent.getColumn(); + colorBar.setColumn(new DasColumn(null, column, 1.0, 1.0, 1, 2, 0, 0)); + } + parent.getCanvas().add(colorBar, parent.getRow(), colorBar.getColumn()); + if (!"true".equals(DasApplication.getProperty("java.awt.headless", "false"))) { + DasMouseInputAdapter mouseAdapter = parent.mouseAdapter; + VerticalSpectrogramSlicer vSlicer = + VerticalSpectrogramSlicer.createSlicer(parent, this); + VerticalSlicerMouseModule vsl = VerticalSlicerMouseModule.create(this); + vsl.addDataPointSelectionListener(vSlicer); + mouseAdapter.addMouseModule(vsl); + + HorizontalSpectrogramSlicer hSlicer = HorizontalSpectrogramSlicer.createSlicer(parent, this); + HorizontalSlicerMouseModule hsl = HorizontalSlicerMouseModule.create(this); + hsl.addDataPointSelectionListener(hSlicer); + mouseAdapter.addMouseModule(hsl); + + VerticalSpectrogramAverager vAverager = VerticalSpectrogramAverager.createAverager(parent, this); + HorizontalDragRangeSelectorMouseModule vrl = new HorizontalDragRangeSelectorMouseModule(parent, this, parent.getXAxis()); + //vrl.setLabel("Vertical Averager"); + vrl.addDataRangeSelectionListener(vAverager); + mouseAdapter.addMouseModule(vrl); + + MouseModule ch = new CrossHairMouseModule(parent, this, parent.getXAxis(), parent.getYAxis()); + mouseAdapter.addMouseModule(ch); + + } + } + } + } + + protected void uninstallRenderer() { + if (colorBar != null && colorBar.getCanvas() != null) { + colorBar.getCanvas().remove(colorBar); + } + } + + public static SpectrogramRenderer processSpectrogramElement( + Element element, DasPlot parent, FormBase form) throws DasPropertyException, DasNameException, ParseException { + String dataSetID = element.getAttribute("dataSetID"); + DasColorBar colorbar = null; + + NodeList children = element.getChildNodes(); + for (int index = 0; index < children.getLength(); index++) { + Node node = children.item(index); + if (node instanceof Element && node.getNodeName().equals("zAxis")) { + colorbar = processZAxisElement((Element) node, form); + } + } + + if (colorbar == null) { + try { + colorbar = (DasColorBar) form.checkValue(element.getAttribute("colorbar"), DasColorBar.class, ""); + } catch (DasPropertyException dpe) { + dpe.setPropertyName("colorbar"); + throw dpe; + } + } + + SpectrogramRenderer renderer = new SpectrogramRenderer(parent, null, colorbar); + try { + renderer.setDataSetID(dataSetID); + } catch (DasException de) { + DasExceptionHandler.handle(de); + } + return renderer; + } + + private static DasColorBar processZAxisElement(Element element, FormBase form) throws DasPropertyException, DasNameException, ParseException { + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + if (node instanceof Element) { + if (node.getNodeName().equals("colorbar")) { + return DasColorBar.processColorbarElement((Element) node, form); + } + } + } + return null; + } + + public Element getDOMElement(Document document) { + + Element element = document.createElement("spectrogram"); + element.setAttribute("dataSetID", getDataSetID()); + + Element zAxisChild = document.createElement("zAxis"); + Element zAxisElement = getColorBar().getDOMElement(document); + if (zAxisElement.getAttribute("row").equals(getParent().getRow().getDasName())) { + zAxisElement.removeAttribute("row"); + } + if (zAxisElement.getAttribute("column").equals(getParent().getColumn().getDasName())) { + zAxisElement.removeAttribute("column"); + } + zAxisChild.appendChild(zAxisElement); + element.appendChild(zAxisChild); + + return element; + } + + /** Getter for property rebinner. + * @return Value of property rebinner. + * + */ + public RebinnerEnum getRebinner() { + return this.rebinnerEnum; + } + + /** Setter for property rebinner. + * @param rebinnerEnum New value of property rebinner. + * + */ + public void setRebinner(RebinnerEnum rebinnerEnum) { + RebinnerEnum old = this.rebinnerEnum; + if (old != rebinnerEnum) { + this.rebinnerEnum = rebinnerEnum; + this.raster = null; + this.plotImage = null; + refreshImage(); + propertyChangeSupport.firePropertyChange("rebinner", old, rebinnerEnum); + } + } + + /** Getter for property sliceRebinnedData. + * @return Value of property sliceRebinnedData. + * + */ + public boolean isSliceRebinnedData() { + return this.sliceRebinnedData; + } + + /** Setter for property sliceRebinnedData. + * @param sliceRebinnedData New value of property sliceRebinnedData. + * + */ + public void setSliceRebinnedData(boolean sliceRebinnedData) { + this.sliceRebinnedData = sliceRebinnedData; + } + + public String getListLabel() { + return "spectrogram"; + } + + public Icon getListIcon() { + return rebinnerEnum.getListIcon(); + } + + public DataSet getConsumedDataSet() { + if (sliceRebinnedData) { + return rebinDataSet; + } else { + return this.ds; + } + } + + public void setDataSet(DataSet ds) { + DataSet oldDs = this.ds; + if (parent != null && oldDs != ds) { + this.raster = null; + // TODO: preserve plotImage until updatePlotImage is done + this.plotImage = null; + } + super.setDataSet(ds); + } + /** + * Holds value of property print300dpi. + */ + private boolean print300dpi; + + /** + * Getter for property draw300dpi. + * @return Value of property draw300dpi. + */ + public boolean isPrint300dpi() { + return this.print300dpi; + } + + /** + * Setter for property draw300dpi. + * @param print300dpi New value of property draw300dpi. + */ + public void setPrint300dpi(boolean print300dpi) { + this.print300dpi = print300dpi; + } + + public boolean acceptContext(int x, int y) { + return true; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/StackedHistogramRenderer.java b/dasCore/src/main/java/org/das2/graph/StackedHistogramRenderer.java new file mode 100644 index 000000000..971035cd7 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/StackedHistogramRenderer.java @@ -0,0 +1,630 @@ +/* File: DasStackedHistogramPlot.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.event.DasMouseInputAdapter; +import org.das2.event.MouseModule; +import org.das2.event.LengthDragRenderer; +import org.das2.event.CrossHairMouseModule; +import org.das2.components.propertyeditor.Displayable; +import org.das2.dataset.DataSetRebinner; +import org.das2.dataset.NearestNeighborTableRebinner; +import org.das2.dataset.AveragePeakTableRebinner; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.datum.LocationUnits; +import org.das2.datum.DatumRange; +import org.das2.dasml.FormBase; +import org.das2.DasNameException; +import org.das2.DasPropertyException; +import org.das2.util.DasExceptionHandler; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.DasException; +import org.das2.components.HorizontalSpectrogramSlicer; +import org.das2.components.VerticalSpectrogramSlicer; +import org.das2.event.HorizontalSlicerMouseModule; +import org.das2.event.VerticalSlicerMouseModule; +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.components.propertyeditor.Enumeration; + +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.BufferedImage; +import java.beans.*; +import java.text.*; +import java.util.Map; +import javax.swing.*; +import static org.das2.graph.Renderer.logger; +import org.w3c.dom.*; + +/** + * + * @author jbf + */ +public class StackedHistogramRenderer extends org.das2.graph.Renderer + implements TableDataSetConsumer, PropertyChangeListener, Displayable +{ + + private DasLabelAxis yAxis= null; + private DasAxis zAxis= null; + private RowRowConnector zAxisConnector= null; + private DasRow littleRow=null; + + private RebinDescriptor xBins= null; + + private PeaksIndicator peaksIndicator; + + /** Holds value of property sliceRebinnedData. */ + private boolean sliceRebinnedData; + + Image plotImage; + DatumRange imageXRange, imageYRange; + + public static class PeaksIndicator implements Enumeration, Displayable { + + String id; + + PeaksIndicator(String id) { + this.id= id; + } + + @Override + public String toString() { + return this.id; + } + + public static final PeaksIndicator NoPeaks= new PeaksIndicator("None"); + /** + * draw grey bar up to max observed. + */ + public static final PeaksIndicator GrayPeaks= new PeaksIndicator("Gray Peaks"); + /** + * draw red bar up to max observed. + */ + public static final PeaksIndicator RedPeaks= new PeaksIndicator("Red Peaks"); + /** + * draw blue bar up to max observed. + */ + public static final PeaksIndicator BluePeaks= new PeaksIndicator("Blue Peaks"); + /** + * draw green bar up to max observed. + */ + public static final PeaksIndicator GreenPeaks= new PeaksIndicator("Green Peaks"); + /** + * draw a connect-a-dot line from peak to peak. + */ + public static final PeaksIndicator LinePeaks= new PeaksIndicator("Line Peaks"); + /** + * draw black bar up to max observed (only peaks are visible then). + */ + public static final PeaksIndicator BlackPeaks= new PeaksIndicator("Black Peaks"); + /** + * draw a point at the maximum observed. + */ + public static final PeaksIndicator MaxLines= new PeaksIndicator("Lines"); + + /** Get a peaks indicator object given a string. + * + * @param s + * @return null if the string doesn't match one of the pre-defined items + */ + public static PeaksIndicator fromString(String s){ + if(NoPeaks.toString().equals(s)) return NoPeaks; + if(GrayPeaks.toString().equals(s)) return GrayPeaks; + if(RedPeaks.toString().equals(s)) return RedPeaks; + if(BluePeaks.toString().equals(s)) return BluePeaks; + if(GreenPeaks.toString().equals(s)) return GreenPeaks; + if(LinePeaks.toString().equals(s)) return LinePeaks; + if(BlackPeaks.toString().equals(s)) return BlackPeaks; + if(MaxLines.toString().equals(s)) return MaxLines; + return null; + } + + @Override + public String getListLabel() { + return this.id; + } + + @Override + public javax.swing.Icon getListIcon() { + return null; + } + + } + + protected class RebinListener implements PropertyChangeListener { + @Override + public void propertyChange(PropertyChangeEvent e) { + update(); + } + } + + RebinListener rebinListener= new RebinListener(); + + public StackedHistogramRenderer( DasPlot parent, DataSetDescriptor dsd, DasAxis zAxis, DasLabelAxis yAxis ) { + super(); + + this.yAxis= yAxis; + this.zAxis= zAxis; + + zAxis.addPropertyChangeListener(rebinListener); + + setDataSetDescriptor( dsd ); + } + + + @Override + public void render(Graphics g, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + + Graphics2D g2= (Graphics2D)g.create(); + + Point2D p; + if (getDataSet()==null && getLastException()!=null ) { + renderException(g2,xAxis,yAxis,getLastException()); + } else if (plotImage!=null) { + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR ); + p= new Point2D.Float( xAxis.getColumn().getDMinimum(), yAxis.getRow().getDMinimum() ); + + g2.drawImage( plotImage,(int)(p.getX()+0.5),(int)(p.getY()+0.5), getParent() ); + + } + g2.dispose(); + } + + @Override + protected void installRenderer() { + DasCanvas canvas= parent.getCanvas(); + littleRow= new DasRow( canvas, 0.5,0.6 ); + zAxisConnector= new RowRowConnector( canvas, littleRow, zAxis.getRow(), parent.getColumn(), zAxis.getColumn() ); + zAxisConnector.setVisible(false); + canvas.add(zAxisConnector); + + yAxis.setFloppyItemSpacing(true); + yAxis.setOutsidePadding(1); + + this.peaksIndicator= PeaksIndicator.MaxLines; + + DasMouseInputAdapter mouseAdapter = parent.getDasMouseInputAdapter(); + + //TODO: consider delaying construction of slicers until first event + VerticalSpectrogramSlicer vSlicer = VerticalSpectrogramSlicer.createSlicer( parent, this ); + VerticalSlicerMouseModule vsl = VerticalSlicerMouseModule.create(this); + vsl.addDataPointSelectionListener(vSlicer); + mouseAdapter.addMouseModule(vsl); + + HorizontalSpectrogramSlicer hSlicer = HorizontalSpectrogramSlicer.createSlicer( parent, this); + HorizontalSlicerMouseModule hsl = HorizontalSlicerMouseModule.create(this); + hsl.addDataPointSelectionListener(hSlicer); + mouseAdapter.addMouseModule(hsl); + + MouseModule ch= new CrossHairMouseModule(parent,this,parent.getXAxis(), parent.getYAxis()); + mouseAdapter.addMouseModule(ch); + + DasPlot p= parent; + mouseAdapter.addMouseModule( new MouseModule( p, new LengthDragRenderer(p,p.getXAxis(),p.getYAxis()), "Length" ) ); + } + + @Override + protected void uninstallRenderer() { + } + + + public void setZAxis(DasAxis zAxis) { + this.zAxis= zAxis; + throw new IllegalStateException("not supported"); + } + + @Override + public void propertyChange(java.beans.PropertyChangeEvent e) { + // this code was intended to make it so the zaxis component would move up and down + // with the labelAxis. + /* DasLabelAxis axis= (DasLabelAxis)getYAxis(); + + if ( axis!=null ) { + if ( getRow()!=DasRow.NULL ) { + if ( axis.getInterItemSpace() > getRow().getHeight()/3.5 ) { + System.out.println("axis spacing exceeds zAxis spacing"); + int[] labelPositions= axis.getLabelPositions(); + zAxisComponent.getAxis().getRow().setDPosition( labelPositions[0], labelPositions[1] ); + } else { + int xx2= getRow().getDMaximum(); + int xx1= getRow().getDMinimum(); + zAxisComponent.getAxis().getRow().setDPosition( xx1, xx2 ); + } + } + } */ + } + + /** + * @throws IllegalArgumentException if the yAxis is not an instanceof DasLabelAxis + */ + public void setYAxis(DasAxis yAxis) { + if (yAxis instanceof DasLabelAxis) { + this.yAxis= (DasLabelAxis)yAxis; + yAxis.addPropertyChangeListener(this); + } else { + throw new IllegalArgumentException("You can't call setYAxis for stackedHistogramPlot"); + } + } + + protected String getPrimaryPeaksId(){ + String sPrimary = getDataSet().getPlaneIds()[0]; + + if(sPrimary.equals("")) // Handle un-named primary planes + return "peaks"; + else + return sPrimary + ".peaks"; // Handle named primary planes + } + + //////////////////////////////////////////////////////////////////////////////////// + // updatePlotImage helper + private int getInterpDistance(DasAxis axis){ + // Get peaks dataset in data coordinates, not screen coordinates + TableDataSet ds = (TableDataSet) getDataSet().getPlanarView(getPrimaryPeaksId()); + + Datum dInterp; + if(override.containsKey(DataSet.PROPERTY_X_TAG_WIDTH)) + dInterp = (Datum)override.get(DataSet.PROPERTY_X_TAG_WIDTH); + else + dInterp = (Datum)ds.getProperty(DataSet.PROPERTY_X_TAG_WIDTH); + + // Use the start of the axis a the basis point for finding the interpolation + // distance in screen coordinates + Datum dBeg = axis.getDataMaximum(); + + // Too bad can't do this in java: Datum dEnd = dBeg + dInterp; + Datum dEnd = dBeg.add(dInterp); + // The factor of 1.5 is to match the check used in + // AverageTableRebinner.fillInterpolateX -> line 508. + int iMaxInterp = (int) ((axis.transform(dEnd) - axis.transform(dBeg))*1.5); + return iMaxInterp; + } + + @Override + synchronized public void updatePlotImage( DasAxis xAxis, DasAxis yAxis_1, ProgressMonitor monitor ) throws DasException { + super.updatePlotImage( xAxis, yAxis_1, monitor ); + final Color BAR_COLOR= getParent().getForeground(); + final Color GREY_PEAKS_COLOR= Color.GRAY; + + Component parent= getParent(); + Cursor cursor0= parent.getCursor(); + parent.setCursor(new Cursor(Cursor.WAIT_CURSOR)); + + DasColumn column= xAxis.getColumn(); + DasRow row= yAxis.getRow(); + + int w= column.getWidth(); + int h= row.getHeight(); + + if ( w==0 ) return; + + //plotImage = new java.awt.image.BufferedImage(w, h, java.awt.image.BufferedImage.TYPE_INT_ARGB); + BufferedImage plotImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + + Graphics2D g = plotImage.createGraphics(); + g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); + if ( ! isTransparentBackground() ) { + g.setColor( getParent().getBackground() ); + g.fillRect(0, 0, plotImage.getWidth(), plotImage.getHeight()); + } + g.translate(-column.getDMinimum(),-row.getDMinimum()); + + Dimension d; + + double iMin= column.getDMinimum(); + double jMin= row.getDMinimum(); + + RebinDescriptor xbins= new RebinDescriptor(xAxis.getDataMinimum(), xAxis.getDataMaximum(), (int)(Math.abs(column.getWidth())/1)+1, (xAxis.isLog())); + + imageXRange= xAxis.getDatumRange(); + imageYRange= yAxis.getDatumRange(); + + int xDMax= column.getDMaximum(); + int xDMin= column.getDMinimum(); + + TableDataSet xtysData= (TableDataSet)getDataSet(); + + if ( xtysData==null ) { + this.plotImage= null; + if ( getLastException()==null ) this.setLastException( new DasException("null data set" ) ); + return; + } + + if ( xtysData.tableCount()==0 ) { + this.setLastException( new DasException("empty data set") ); + this.ds= null; + return; + } + + DataSetRebinner rebinner = new Rebinner(); + + TableDataSet data= (TableDataSet)rebinner.rebin(xtysData, xbins, null, override); + TableDataSet peaks= (TableDataSet)data.getPlanarView(getPrimaryPeaksId()); + + DasLabelAxis yAxis= (DasLabelAxis)yAxis_1; + + int zmid= zAxis.getRow().getDMiddle(); + boolean haveLittleRow= false; + + for (int j = 0; j < data.getYLength(0); j++) { + + int yBase; + + if ( j==(data.getYLength(0)-1) ) { /* Draw top grey line */ + yBase= yAxis.getItemMin(data.getYTagDatum(0, j)); + g.setColor(GREY_PEAKS_COLOR); + g.drawLine(xDMin, yBase, xDMax, yBase ); + g.setColor(BAR_COLOR); + } + + yBase= yAxis.getItemMax(data.getYTagDatum(0, j)); + g.setColor(Color.lightGray); + g.drawLine(xDMin, yBase, xDMax, yBase ); + g.setColor(BAR_COLOR); + + int yBase1= yAxis.getItemMin(data.getYTagDatum(0, j)); + + if ( !haveLittleRow && yBase1 <= zmid ) { + littleRow.setDPosition(yBase1,yBase); + haveLittleRow= true; + this.zAxisConnector.setVisible(true); + this.zAxisConnector.repaint(); + } + + double [] binStarts= xbins.binStarts(); + double [] binStops= xbins.binStops(); + + int y0= yBase; + + int littleRowHeight= yBase - yBase1; + double zAxisMax= zAxis.getDataMaximum().doubleValue(xtysData.getZUnits()); + double zAxisMin= zAxis.getDataMinimum().doubleValue(xtysData.getZUnits()); + + if ( yBase1 >= row.getDMinimum() && yBase <= row.getDMaximum() ) { + if ( peaksIndicator==PeaksIndicator.LinePeaks && peaks!=null ) { + GeneralPath p= new GeneralPath(); + + boolean bHasBeg = false; + int nMaxInterpPx = getInterpDistance(xAxis); + int xLast = 0; + int yLast = 0; + for (int ibin=0; ibin < data.getXLength(); ibin++) { + + double zz= peaks.getDouble( ibin, j, data.getZUnits() ); + if( data.getZUnits().isFill(zz) || Double.isNaN(zz) ) + continue; + + int x0= (int)xAxis.transform(binStarts[ibin],xbins.getUnits()); + int yMax= (int)zAxis.transform( zz, data.getZUnits(), yBase, yBase1 ); + + // If there is no previous point, set one and move on. + if(!bHasBeg){ + p.moveTo( x0, Math.min( yMax, y0 ) ); + xLast = x0; + yLast = Math.min( yMax, y0 ); + bHasBeg = true; + continue; + } + + // Have data, if previous exists and is close enough, connect + // with line. If not, just put a small line at previous point + // and reset begin to here. + if((x0 - xLast) <= nMaxInterpPx){ + p.lineTo( x0, Math.min( yMax, y0 ) ); + } + else{ + p.lineTo( xLast, yLast); //Can a single point be a line? + p.moveTo( x0, Math.min(yMax, y0)); + } + xLast = x0; + yLast = Math.min( yMax, y0 ); + } + + g.draw(p); + } + for (int ibin=0; ibin < data.getXLength(); ibin++) { + int x0= (int)xAxis.transform(binStarts[ibin],xbins.getUnits()); + double zz= data.getDouble( ibin, j, data.getZUnits() ); + if ( !( data.getZUnits().isFill(zz) || Double.isNaN(zz) ) ) { + int yAvg= (int)zAxis.transform( zz, data.getZUnits(), yBase, yBase1 ); + yAvg= yAvg > ( y0 - littleRowHeight ) ? yAvg : ( y0 - littleRowHeight ); + int yHeight= (y0-yAvg)>(0) ? (y0-yAvg) : 0; + //yHeight= yHeight < littleRowHeight ? yHeight : littleRowHeight; + if ( peaks!=null ) { + double peakValue = peaks.getDouble(ibin, j, peaks.getZUnits()); + if (peakValue > zAxisMin) { + int yMax= (int)zAxis.transform( peakValue, data.getZUnits(), yBase, yBase1 ); + yMax= Math.min( yMax, y0 ); + yMax= yMax > ( y0 - littleRowHeight ) ? yMax : ( y0 - littleRowHeight ); + + if (peaksIndicator==PeaksIndicator.MaxLines) { + g.drawLine(x0,yMax,x0,yMax); + } else if ( peaksIndicator==PeaksIndicator.GrayPeaks ) { + g.setColor(Color.lightGray); + g.drawLine(x0,yMax,x0,y0); + g.setColor(BAR_COLOR); + } else if ( peaksIndicator==PeaksIndicator.RedPeaks ) { + g.setColor(Color.red); + g.drawLine(x0,yMax,x0,y0); + g.setColor(BAR_COLOR); + } else if ( peaksIndicator==PeaksIndicator.GreenPeaks ) { + g.setColor(Color.GREEN); + g.drawLine(x0,yMax,x0,y0); + g.setColor(BAR_COLOR); + } else if ( peaksIndicator==PeaksIndicator.BluePeaks ) { + g.setColor(Color.CYAN); + g.drawLine(x0,yMax,x0,y0); + g.setColor(BAR_COLOR); + } else if ( peaksIndicator==PeaksIndicator.LinePeaks ) { + //nothing here + } else if ( peaksIndicator==PeaksIndicator.BlackPeaks ) { + g.setColor(BAR_COLOR); + g.drawLine(x0,yMax,x0,y0); + } + } + } + if ( zz>=zAxisMin ) g.drawLine(x0, yAvg, x0, yAvg+yHeight ); + } + } + } + } + + g.dispose(); + this.plotImage = plotImage; + parent.setCursor(cursor0); + getParent().repaint(); + + if ( sliceRebinnedData ) super.ds= data; + + } + + @Override + public DasAxis getZAxis() { + return zAxis; + } + + public void setZTitle(String title) { + getZAxis().setLabel(title); + } + + public class Rebinner implements DataSetRebinner { + DataSetRebinner highResRebinner; + DataSetRebinner lowResRebinner; + Rebinner() { + highResRebinner= new NearestNeighborTableRebinner(); + //highResRebinner= new AveragePeakTableRebinner(); + lowResRebinner= new AveragePeakTableRebinner(); + } + + @Override + public DataSet rebin( + DataSet ds, RebinDescriptor x, RebinDescriptor y, Map override + ) throws IllegalArgumentException, DasException { + Datum xwidth= (Datum)ds.getProperty( "xTagWidth" ); + if ( xwidth==null ) xwidth= DataSetUtil.guessXTagWidth((TableDataSet)ds); + Units rdUnits= x.getUnits(); + if ( rdUnits instanceof LocationUnits ) { + rdUnits= ((LocationUnits)rdUnits).getOffsetUnits(); + } + + try { + DataSet result; + if ( x.binWidth() < xwidth.doubleValue(rdUnits) ) { + logger.fine("using rebinner "+highResRebinner); + result= highResRebinner.rebin( ds, x, y, override ); + } else { + logger.fine("using rebinner "+lowResRebinner); + result= lowResRebinner.rebin( ds, x, y, override ); + } + return result; + } catch ( IllegalArgumentException | DasException e ) { + DasExceptionHandler.handle(e); + return null; + } + } + + } + + /** Getter for property peaksIndicator. + * @return Value of property peaksIndicator. + */ + public PeaksIndicator getPeaksIndicator() { + return this.peaksIndicator; + } + + /** Setter for property peaksIndicator. + * @param peaksIndicator New value of property peaksIndicator. + */ + public void setPeaksIndicator(PeaksIndicator peaksIndicator) { + this.peaksIndicator= peaksIndicator; + refreshImage(); + } + + /** Getter for property sliceRebinnedData. + * @return Value of property sliceRebinnedData. + * + */ + public boolean isSliceRebinnedData() { + return this.sliceRebinnedData; + } + + /** Setter for property sliceRebinnedData. + * @param sliceRebinnedData New value of property sliceRebinnedData. + * + */ + public void setSliceRebinnedData(boolean sliceRebinnedData) { + this.sliceRebinnedData = sliceRebinnedData; + } + protected boolean transparentBackground = false; + public static final String PROP_TRANSPARENTBACKGROUND = "transparentBackground"; + + public boolean isTransparentBackground() { + return transparentBackground; + } + + public void setTransparentBackground(boolean transparentBackground) { + boolean oldTransparentBackground = this.transparentBackground; + this.transparentBackground = transparentBackground; + propertyChangeSupport.firePropertyChange(PROP_TRANSPARENTBACKGROUND, oldTransparentBackground, transparentBackground); + } + + @Override + public Element getDOMElement(Document document) { + + Element element = document.createElement("stackedHistogram"); + element.setAttribute("zAxis", zAxis.getDasName() ); + element.setAttribute("dataSetID", getDataSetID() ); + return element; + } + + public static Renderer processStackedHistogramElement(Element element, DasPlot parent, FormBase form) + throws DasPropertyException, DasNameException, ParseException + { + String dataSetID = element.getAttribute("dataSetID"); + + Renderer renderer = new StackedHistogramRenderer( parent, (DataSetDescriptor)null, (DasAxis)null, (DasLabelAxis)parent.getYAxis() ); + try { + renderer.setDataSetID(dataSetID); + } catch (DasException de) { + DasExceptionHandler.handle(de); + } + return renderer; + } + + @Override + public String getListLabel() { + return "stacked histogram"; + } + + @Override + public Icon getListIcon() { + return null; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/StackedHistogramRenderer_rework.java.save b/dasCore/src/main/java/org/das2/graph/StackedHistogramRenderer_rework.java.save new file mode 100644 index 000000000..477668d85 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/StackedHistogramRenderer_rework.java.save @@ -0,0 +1,690 @@ +/* File: DasStackedHistogramPlot.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.event.DasMouseInputAdapter; +import org.das2.event.MouseModule; +import org.das2.event.LengthDragRenderer; +import org.das2.event.CrossHairMouseModule; +import org.das2.components.propertyeditor.Displayable; +import org.das2.dataset.DataSetRebinner; +import org.das2.dataset.NearestNeighborTableRebinner; +import org.das2.dataset.AveragePeakTableRebinner; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.datum.LocationUnits; +import org.das2.datum.DatumRange; +import org.das2.dasml.FormBase; +import org.das2.DasNameException; +import org.das2.DasPropertyException; +import org.das2.util.DasExceptionHandler; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.DasException; +import org.das2.components.HorizontalSpectrogramSlicer; +import org.das2.components.VerticalSpectrogramSlicer; +import org.das2.event.HorizontalSlicerMouseModule; +import org.das2.event.VerticalSlicerMouseModule; +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.components.propertyeditor.Enumeration; + +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.BufferedImage; +import java.beans.*; +import java.text.*; +import java.util.Map; +import javax.swing.*; +import org.w3c.dom.*; + +/** + * + * @author jbf + */ +public class StackedHistogramRenderer extends org.das2.graph.Renderer + implements TableDataSetConsumer, PropertyChangeListener, Displayable +{ + + private DasLabelAxis yAxis= null; + private DasAxis zAxis= null; + private RowRowConnector zAxisConnector= null; + private DasRow littleRow=null; + + private RebinDescriptor xBins= null; + + private PeaksIndicator peaksIndicator; + + /** Holds value of property sliceRebinnedData. */ + private boolean sliceRebinnedData; + + Image plotImage; + DatumRange imageXRange, imageYRange; + + /** If set this will override the xTagWidth property during display */ + Datum m_rMaxXInterp = null; + + public static class PeaksIndicator implements Enumeration, Displayable { + + String id; + + PeaksIndicator(String id) { + this.id= id; + } + + @Override + public String toString() { + return this.id; + } + + public static final PeaksIndicator NoPeaks= new PeaksIndicator("None"); + /** + * draw grey bar up to max observed. + */ + public static final PeaksIndicator GrayPeaks= new PeaksIndicator("Gray Peaks"); + /** + * draw red bar up to max observed. + */ + public static final PeaksIndicator RedPeaks= new PeaksIndicator("Red Peaks"); + /** + * draw blue bar up to max observed. + */ + public static final PeaksIndicator BluePeaks= new PeaksIndicator("Blue Peaks"); + /** + * draw green bar up to max observed. + */ + public static final PeaksIndicator GreenPeaks= new PeaksIndicator("Green Peaks"); + /** + * draw a connect-a-dot line from peak to peak. + */ + public static final PeaksIndicator LinePeaks= new PeaksIndicator("Line Peaks"); + /** + * draw black bar up to max observed (only peaks are visible then). + */ + public static final PeaksIndicator BlackPeaks= new PeaksIndicator("Black Peaks"); + /** + * draw a point at the maximum observed. + */ + public static final PeaksIndicator MaxLines= new PeaksIndicator("Lines"); + + /** Get a peaks indicator object given a string. + * + * @param s + * @return null if the string doesn't match one of the pre-defined items + */ + public static PeaksIndicator fromString(String s){ + if(NoPeaks.toString().equals(s)) return NoPeaks; + if(GrayPeaks.toString().equals(s)) return GrayPeaks; + if(RedPeaks.toString().equals(s)) return RedPeaks; + if(BluePeaks.toString().equals(s)) return BluePeaks; + if(GreenPeaks.toString().equals(s)) return GreenPeaks; + if(LinePeaks.toString().equals(s)) return LinePeaks; + if(BlackPeaks.toString().equals(s)) return BlackPeaks; + if(MaxLines.toString().equals(s)) return MaxLines; + return null; + } + + @Override + public String getListLabel() { + return this.id; + } + + @Override + public javax.swing.Icon getListIcon() { + return null; + } + + } + + protected class RebinListener implements PropertyChangeListener { + @Override + public void propertyChange(PropertyChangeEvent e) { + update(); + } + } + + RebinListener rebinListener= new RebinListener(); + + public StackedHistogramRenderer( DasPlot parent, DataSetDescriptor dsd, DasAxis zAxis, DasLabelAxis yAxis ) { + super(); + + this.yAxis= yAxis; + this.zAxis= zAxis; + + zAxis.addPropertyChangeListener(rebinListener); + + setDataSetDescriptor( dsd ); + } + + + @Override + public void render(Graphics g, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + + Graphics2D g2= (Graphics2D)g.create(); + + Point2D p; + if (getDataSet()==null && getLastException()!=null ) { + renderException(g2,xAxis,yAxis,getLastException()); + } else if (plotImage!=null) { + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR ); + p= new Point2D.Float( xAxis.getColumn().getDMinimum(), yAxis.getRow().getDMinimum() ); + + g2.drawImage( plotImage,(int)(p.getX()+0.5),(int)(p.getY()+0.5), getParent() ); + + } + g2.dispose(); + } + + @Override + protected void installRenderer() { + DasCanvas canvas= parent.getCanvas(); + littleRow= new DasRow( canvas, 0.5,0.6 ); + zAxisConnector= new RowRowConnector( canvas, littleRow, zAxis.getRow(), parent.getColumn(), zAxis.getColumn() ); + zAxisConnector.setVisible(false); + canvas.add(zAxisConnector); + + yAxis.setFloppyItemSpacing(true); + yAxis.setOutsidePadding(1); + + this.peaksIndicator= PeaksIndicator.MaxLines; + + DasMouseInputAdapter mouseAdapter = parent.getDasMouseInputAdapter(); + + //TODO: consider delaying construction of slicers until first event + VerticalSpectrogramSlicer vSlicer = VerticalSpectrogramSlicer.createSlicer( parent, this ); + VerticalSlicerMouseModule vsl = VerticalSlicerMouseModule.create(this); + vsl.addDataPointSelectionListener(vSlicer); + mouseAdapter.addMouseModule(vsl); + + HorizontalSpectrogramSlicer hSlicer = HorizontalSpectrogramSlicer.createSlicer( parent, this); + HorizontalSlicerMouseModule hsl = HorizontalSlicerMouseModule.create(this); + hsl.addDataPointSelectionListener(hSlicer); + mouseAdapter.addMouseModule(hsl); + + MouseModule ch= new CrossHairMouseModule(parent,this,parent.getXAxis(), parent.getYAxis()); + mouseAdapter.addMouseModule(ch); + + DasPlot p= parent; + mouseAdapter.addMouseModule( new MouseModule( p, new LengthDragRenderer(p,p.getXAxis(),p.getYAxis()), "Length" ) ); + } + + @Override + protected void uninstallRenderer() { + } + + + public void setZAxis(DasAxis zAxis) { + this.zAxis= zAxis; + throw new IllegalStateException("not supported"); + } + + @Override + public void propertyChange(java.beans.PropertyChangeEvent e) { + // this code was intended to make it so the zaxis component would move up and down + // with the labelAxis. + /* DasLabelAxis axis= (DasLabelAxis)getYAxis(); + + if ( axis!=null ) { + if ( getRow()!=DasRow.NULL ) { + if ( axis.getInterItemSpace() > getRow().getHeight()/3.5 ) { + System.out.println("axis spacing exceeds zAxis spacing"); + int[] labelPositions= axis.getLabelPositions(); + zAxisComponent.getAxis().getRow().setDPosition( labelPositions[0], labelPositions[1] ); + } else { + int xx2= getRow().getDMaximum(); + int xx1= getRow().getDMinimum(); + zAxisComponent.getAxis().getRow().setDPosition( xx1, xx2 ); + } + } + } */ + } + + /** + * @throws IllegalArgumentException if the yAxis is not an instance of DasLabelAxis + */ + public void setYAxis(DasAxis yAxis) { + if (yAxis instanceof DasLabelAxis) { + this.yAxis= (DasLabelAxis)yAxis; + yAxis.addPropertyChangeListener(this); + } else { + throw new IllegalArgumentException("You can't call setYAxis for stackedHistogramPlot"); + } + } + + protected String getPrimaryPeaksId(){ + String sPrimary = getDataSet().getPlaneIds()[0]; + + if(sPrimary.equals("")) // Handle un-named primary planes + return "peaks"; + else + return sPrimary + ".peaks"; // Handle named primary planes + } + + //////////////////////////////////////////////////////////////////////////////////// + // updatePlotImage helper + private void drawAxisLines(Graphics2D g){ + + Color BAR_COLOR = getParent().getForeground(); + Color GREY_PEAKS_COLOR = Color.GRAY; + + if(j == (data.getYLength(0) - 1)){ /* Draw grey line */ + + yMin = yAxis.getItemMin(data.getYTagDatum(0, j)); + g.setColor(GREY_PEAKS_COLOR); + g.drawLine(xDMin, yMin, xDMax, yMin); + g.setColor(BAR_COLOR); + } + + yMax = yAxis.getItemMax(data.getYTagDatum(0, j)); + g.setColor(Color.lightGray); + g.drawLine(xDMin, yMax, xDMax, yMax); + g.setColor(BAR_COLOR); + + yMin = yAxis.getItemMin(data.getYTagDatum(0, j)); + + if(!haveLittleRow && yMin <= zmid){ + littleRow.setDPosition(yMin, yMax); + haveLittleRow = true; + this.zAxisConnector.setVisible(true); + this.zAxisConnector.repaint(); + + } + } + + //////////////////////////////////////////////////////////////////////////////////// + // updatePlotImage helper + private int getInterpDistance(DasAxis axis){ + // Get peaks dataset in data coordinates, not screen coordinates + TableDataSet ds = (TableDataSet) getDataSet().getPlanarView(getPrimaryPeaksId()); + + Datum dInterp; + if(override.containsKey(DataSet.PROPERTY_X_TAG_WIDTH)) + dInterp = (Datum)override.get(DataSet.PROPERTY_X_TAG_WIDTH); + else + dInterp = (Datum)ds.getProperty(DataSet.PROPERTY_X_TAG_WIDTH); + + // Use the start of the axis a the basis point for finding the interpolation + // distance in screen coordinates + Datum dBeg = axis.getDataMaximum(); + + // Too bad can't do this in java: Datum dEnd = dBeg + dInterp; + Datum dEnd = dBeg.add(dInterp); + int iMaxInterp = (int) (axis.transform(dEnd) - axis.transform(dBeg)); + return iMaxInterp; + } + + //////////////////////////////////////////////////////////////////////////////////// + // updatePlotImage helper + private void drawLinePeaks(Graphics2D g, DasAxis xAxis){ + + int iMaxInterp = getInterpDistance(xAxis); + + GeneralPath p = new GeneralPath(); + + double x0data; + int x0, x1, y0, y1; + boolean bHasBeg = false; + + for(int ibin = 0; ibin < data.getXLength(); ibin++){ + // Do nothing until you encounter data + double zTmp = peaks.getDouble(ibin, j, data.getZUnits()); + if(data.getZUnits().isFill(zTmp) || Double.isNaN(zTmp)){ + continue; + } + + x1 = (int) xAxis.transform(binStarts[ibin], xbins.getUnits()); + y1 = (int) zAxis.transform(zTmp, data.getZUnits(), yMin, yMax); + + if(!bHasBeg){ + x0 = x1; + y0 = y1; + x0data = binStarts[ibin]; + p.moveTo(x0, Math.min(yMax, y0)); + bHasBeg = true; + } + else{ + //Check to see if last value is too far away, if so just draw a tiny + //2 pixel straight line + if((binStarts[ibin] - x0Data) > xInterp){ + + } + } + + double zz = peaks.getDouble(ibin, j, data.getZUnits()); + if(!(data.getZUnits().isFill(zz) || Double.isNaN(zz))){ + int yMax = (int) zAxis.transform(zz, data.getZUnits(), yMax, yBase1); + if(lastWasFill){ + p.moveTo(x0, Math.min(yMax, y0)); + } + p.lineTo(x0, Math.min(yMax, y0)); + + } + } + g.draw(p); + } + + //////////////////////////////////////////////////////////////////////////////////// + // updatePlotImage helper + private void drawStdPeaks(Graphics2D g, DasAxis xAxis){ + + Color BAR_COLOR = getParent().getForeground(); + + for(int ibin = 0; ibin < data.getXLength(); ibin++){ + int x0 = (int) xAxis.transform(binStarts[ibin], xbins.getUnits()); + double zz = data.getDouble(ibin, j, data.getZUnits()); + if(!(data.getZUnits().isFill(zz) || Double.isNaN(zz))){ + int yAvg = (int) zAxis.transform(zz, data.getZUnits(), yMin, yMin); + yAvg = yAvg > (y0 - littleRowHeight) ? yAvg : (y0 - littleRowHeight); + int yHeight = (y0 - yAvg) > (0) ? (y0 - yAvg) : 0; + //yHeight= yHeight < littleRowHeight ? yHeight : littleRowHeight; + if(peaks != null){ + double peakValue = peaks.getDouble(ibin, j, peaks.getZUnits()); + if(peakValue > zAxisMin){ + int yMax = (int) zAxis.transform(peakValue, data.getZUnits(), yMax, yBase1); + yMax = Math.min(yMax, y0); + yMax = yMax > (y0 - littleRowHeight) ? yMax : (y0 - littleRowHeight); + + if(peaksIndicator == PeaksIndicator.MaxLines){ + g.drawLine(x0, yMax, x0, yMax); + } + else if(peaksIndicator == PeaksIndicator.GrayPeaks){ + g.setColor(Color.lightGray); + g.drawLine(x0, yMax, x0, y0); + g.setColor(BAR_COLOR); + } + else if(peaksIndicator == PeaksIndicator.RedPeaks){ + g.setColor(Color.red); + g.drawLine(x0, yMax, x0, y0); + g.setColor(BAR_COLOR); + } + else if(peaksIndicator == PeaksIndicator.GreenPeaks){ + g.setColor(Color.GREEN); + g.drawLine(x0, yMax, x0, y0); + g.setColor(BAR_COLOR); + } + else if(peaksIndicator == PeaksIndicator.BluePeaks){ + g.setColor(Color.CYAN); + g.drawLine(x0, yMax, x0, y0); + g.setColor(BAR_COLOR); + } + else if(peaksIndicator == PeaksIndicator.LinePeaks){ + //nothing here + } + else if(peaksIndicator == PeaksIndicator.BlackPeaks){ + g.setColor(BAR_COLOR); + g.drawLine(x0, yMax, x0, y0); + } + } + } + if(zz >= zAxisMin){ + g.drawLine(x0, yAvg, x0, yAvg + yHeight); + } + } + } + } + + //////////////////////////////////////////////////////////////////////////////////// + // updatePlotImage helper + private void drawAverages(Graphics2D g, DasAxis xAxis){ + + } + + + @Override + synchronized public void updatePlotImage( + DasAxis xAxis, DasAxis yAxis_1, ProgressMonitor monitor + ) throws DasException{ + + super.updatePlotImage(xAxis, yAxis_1, monitor); + + Component parent = getParent(); + Cursor cursor0 = parent.getCursor(); + parent.setCursor(new Cursor(Cursor.WAIT_CURSOR)); + + DasColumn column = xAxis.getColumn(); + DasRow row = yAxis.getRow(); + + int w = column.getWidth(); + int h = row.getHeight(); + + if(w == 0) return; + + // Set up graphics context + BufferedImage plotImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = plotImage.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + if(!isTransparentBackground()){ + g.setColor(getParent().getBackground()); + g.fillRect(0, 0, plotImage.getWidth(), plotImage.getHeight()); + } + g.translate(-column.getDMinimum(), -row.getDMinimum()); + + imageXRange = xAxis.getDatumRange(); + imageYRange = yAxis.getDatumRange(); + + int xDMax = column.getDMaximum(); + int xDMin = column.getDMinimum(); + + // Handle missing/empty datasets + TableDataSet xtysData = (TableDataSet) getDataSet(); + + if(xtysData == null){ + this.plotImage = null; + if(getLastException() == null){ + this.setLastException(new DasException("null data set")); + } + g.dispose(); + return; + } + + if(xtysData.tableCount() == 0){ + this.setLastException(new DasException("empty data set")); + this.ds = null; + g.dispose(); + return; + } + + // Get the data into pixel space + DataSetRebinner rebinner = new Rebinner(); + + RebinDescriptor xbins = new RebinDescriptor( + xAxis.getDataMinimum(), xAxis.getDataMaximum(), + (int) (Math.abs(column.getWidth()) / 1) + 1, + (xAxis.isLog()) + ); + + TableDataSet data = (TableDataSet) rebinner.rebin(xtysData, xbins, null, override); + TableDataSet peaks = (TableDataSet) data.getPlanarView(getPrimaryPeaksId()); + + DasLabelAxis yAxis = (DasLabelAxis) yAxis_1; + + int zmid = zAxis.getRow().getDMiddle(); + boolean haveLittleRow = false; + + // Loop over Y dimension generating individual histograms + for(int j = 0; j < data.getYLength(0); j++){ + + int yMax, yMin; + + // Axis lines + drawAxisLines(g); + + // Data rendering code + double[] binStarts = xbins.binStarts(); + double[] binStops = xbins.binStops(); + + int littleRowHeight = yMax - yMin; + double zAxisMax = zAxis.getDataMaximum().doubleValue(xtysData.getZUnits()); + double zAxisMin = zAxis.getDataMinimum().doubleValue(xtysData.getZUnits()); + + if(yMin >= row.getDMinimum() && yMax <= row.getDMaximum()){ + + if(peaks != null){ + if(peaksIndicator == PeaksIndicator.LinePeaks) + drawLinePeaks(g, xAxis); + else + drawStdPeaks(g, xAxis); + } + + drawAverages(g, xAxis); + } + } + + g.dispose(); + this.plotImage = plotImage; + parent.setCursor(cursor0); + getParent().repaint(); + + if(sliceRebinnedData){ + super.ds = data; + } + } + + @Override + public DasAxis getZAxis() { + return zAxis; + } + + public void setZTitle(String title) { + getZAxis().setLabel(title); + } + + public class Rebinner implements DataSetRebinner { + DataSetRebinner highResRebinner; + DataSetRebinner lowResRebinner; + Rebinner() { + highResRebinner= new NearestNeighborTableRebinner(); + //highResRebinner= new AveragePeakTableRebinner(); + lowResRebinner= new AveragePeakTableRebinner(); + } + + @Override + public DataSet rebin( + DataSet ds, RebinDescriptor x, RebinDescriptor y, Map override + ) throws IllegalArgumentException, DasException { + Datum xwidth= (Datum)ds.getProperty( "xTagWidth" ); + if ( xwidth==null ) xwidth= DataSetUtil.guessXTagWidth((TableDataSet)ds); + Units rdUnits= x.getUnits(); + if ( rdUnits instanceof LocationUnits ) { + rdUnits= ((LocationUnits)rdUnits).getOffsetUnits(); + } + + try { + DataSet result; + if ( x.binWidth() < xwidth.doubleValue(rdUnits) ) { + logger.fine("using rebinner "+highResRebinner); + result= highResRebinner.rebin( ds, x, y, override ); + } else { + logger.fine("using rebinner "+lowResRebinner); + result= lowResRebinner.rebin( ds, x, y, override ); + } + return result; + } catch ( IllegalArgumentException | DasException e ) { + DasExceptionHandler.handle(e); + return null; + } + } + + } + + /** Getter for property peaksIndicator. + * @return Value of property peaksIndicator. + */ + public PeaksIndicator getPeaksIndicator() { + return this.peaksIndicator; + } + + /** Setter for property peaksIndicator. + * @param peaksIndicator New value of property peaksIndicator. + */ + public void setPeaksIndicator(PeaksIndicator peaksIndicator) { + this.peaksIndicator= peaksIndicator; + refreshImage(); + } + + /** Getter for property sliceRebinnedData. + * @return Value of property sliceRebinnedData. + * + */ + public boolean isSliceRebinnedData() { + return this.sliceRebinnedData; + } + + /** Setter for property sliceRebinnedData. + * @param sliceRebinnedData New value of property sliceRebinnedData. + * + */ + public void setSliceRebinnedData(boolean sliceRebinnedData) { + this.sliceRebinnedData = sliceRebinnedData; + } + protected boolean transparentBackground = false; + public static final String PROP_TRANSPARENTBACKGROUND = "transparentBackground"; + + public boolean isTransparentBackground() { + return transparentBackground; + } + + public void setTransparentBackground(boolean transparentBackground) { + boolean oldTransparentBackground = this.transparentBackground; + this.transparentBackground = transparentBackground; + propertyChangeSupport.firePropertyChange(PROP_TRANSPARENTBACKGROUND, oldTransparentBackground, transparentBackground); + } + + @Override + public Element getDOMElement(Document document) { + + Element element = document.createElement("stackedHistogram"); + element.setAttribute("zAxis", zAxis.getDasName() ); + element.setAttribute("dataSetID", getDataSetID() ); + return element; + } + + public static Renderer processStackedHistogramElement(Element element, DasPlot parent, FormBase form) + throws DasPropertyException, DasNameException, ParseException + { + String dataSetID = element.getAttribute("dataSetID"); + + Renderer renderer = new StackedHistogramRenderer( parent, (DataSetDescriptor)null, (DasAxis)null, (DasLabelAxis)parent.getYAxis() ); + try { + renderer.setDataSetID(dataSetID); + } catch (DasException de) { + DasExceptionHandler.handle(de); + } + return renderer; + } + + @Override + public String getListLabel() { + return "stacked histogram"; + } + + @Override + public Icon getListIcon() { + return null; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/StippledTableRenderer.java b/dasCore/src/main/java/org/das2/graph/StippledTableRenderer.java new file mode 100644 index 000000000..1b92c81e1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/StippledTableRenderer.java @@ -0,0 +1,270 @@ +/* File: SpectrogramRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.dataset.DataSetRebinner; +import org.das2.dataset.AverageTableRebinner; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DefaultTableDataSet; +import org.das2.dataset.RebinDescriptor; +import org.das2.util.DasMath; +import org.das2.DasException; +import org.das2.DasNameException; +import org.das2.DasPropertyException; +import org.das2.dasml.FormBase; +import org.das2.datum.Units; +import org.das2.util.DasDie; +import org.das2.util.DasExceptionHandler; +import org.das2.util.monitor.ProgressMonitor; +import java.awt.*; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.geom.*; +import java.awt.image.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.ParseException; +import java.util.Collections; +import org.w3c.dom.*; + +/** + * + * @author jbf + */ +public class StippledTableRenderer extends Renderer { + + Image plotImage; + + Units zUnits= Units.dimensionless; + + protected class RebinListener implements PropertyChangeListener { + public void propertyChange(PropertyChangeEvent e) { + update(); + } + } + + RebinListener rebinListener= new RebinListener(); + + public StippledTableRenderer(DataSetDescriptor dsd ) { + super( dsd ); + } + + + /** Creates a new instance of SpectrogramRenderer + * @deprecated use {link + * #SpectrogramRenderer(org.das2.dataset.DataSetDescriptor, + * org.das2.graph.DasColorBar)} + */ + public StippledTableRenderer(DasPlot parent, DataSetDescriptor dsd ) { + this( dsd ); + this.parent = parent; + } + + public void render(Graphics g, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + Graphics2D g2= (Graphics2D)g.create(); + + if (getDataSet()==null && getLastException()!=null ) { + renderException(g2,xAxis,yAxis,getLastException()); + } else if (plotImage!=null) { + Point2D p; + p= new Point2D.Float( xAxis.getColumn().getDMinimum(), yAxis.getRow().getDMinimum() ); + int x= (int)(p.getX()+0.5); + int y= (int)(p.getY()+0.5); + + g2.drawImage( plotImage,x,y, getParent() ); + } + g2.dispose(); + } + + int count = 0; + + private boolean sliceRebinnedData= true; + + public void updatePlotImage( DasAxis xAxis, DasAxis yAxis, ProgressMonitor monitor ) throws DasException { + super.updatePlotImage( xAxis, yAxis, monitor ); + try { + TableDataSet rebinData; + + if (monitor != null) { + if (monitor.isCancelled()) { + return; + } else { + monitor.setTaskSize(-1); + monitor.started(); + } + } + + int w = xAxis.getColumn().getDMaximum() - xAxis.getColumn().getDMinimum(); + int h = yAxis.getRow().getDMaximum() - yAxis.getRow().getDMinimum(); + + if (getParent()==null || w<=1 || h<=1 ) { + DasDie.println("canvas not useable!!!"); + return; + } + + if ( getDataSet() == null) { + Units xUnits = getParent().getXAxis().getUnits(); + Units yUnits = getParent().getYAxis().getUnits(); + + double[] xTags, yTags; + xTags = yTags = new double[0]; + double[][] zValues = {yTags}; + rebinData = new DefaultTableDataSet(xTags, xUnits, yTags, yUnits, zValues, zUnits, Collections.EMPTY_MAP); + plotImage= null; + rebinData= null; + getParent().repaint(); + return; + } else { + RebinDescriptor xRebinDescriptor; + xRebinDescriptor = new RebinDescriptor( + xAxis.getDataMinimum(), xAxis.getDataMaximum(), + w, + xAxis.isLog()); + + RebinDescriptor yRebinDescriptor = new RebinDescriptor( + yAxis.getDataMinimum(), yAxis.getDataMaximum(), + h, + yAxis.isLog()); + + DataSetRebinner rebinner= new AverageTableRebinner(); + + rebinData = (TableDataSet)rebinner.rebin( + getDataSet(),xRebinDescriptor, yRebinDescriptor, null + ); + + //TableDataSet weights= (TableDataSet)rebinData.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); + int itable=0; + int ny= rebinData.getYLength(itable); + int nx= rebinData.tableEnd(itable)-rebinData.tableStart(itable); + + + BufferedImage image= new BufferedImage( w, h, BufferedImage.TYPE_4BYTE_ABGR ); + Graphics2D g= (Graphics2D)image.getGraphics(); + g.setColor( Color.BLACK ); + + Units zunits= rebinData.getZUnits(); + + double maxn=0; + for (int i=rebinData.tableStart(itable); imaxn ) maxn=n; + if ( Math.random() < n ) { + g.fillRect( i,ny-j,1,1); + } + } + } + plotImage= image; + } + + } finally { + if (monitor != null) { + if (monitor.isCancelled()) { + return; + } else { + monitor.finished(); + } + } + + getParent().repaint(); + } + } + + protected void installRenderer() { + } + + protected void uninstallRenderer() { + } + + public static SpectrogramRenderer processSpectrogramElement(Element element, DasPlot parent, FormBase form) throws DasPropertyException, DasNameException, ParseException { + String dataSetID = element.getAttribute("dataSetID"); + DasColorBar colorbar = null; + + NodeList children = element.getChildNodes(); + for (int index = 0; index < children.getLength(); index++) { + Node node = children.item(index); + if (node instanceof Element && node.getNodeName().equals("zAxis")) { + colorbar = processZAxisElement((Element)node, form); + } + } + + if (colorbar == null) { + try { + colorbar = (DasColorBar)form.checkValue(element.getAttribute("colorbar"), DasColorBar.class, ""); + } catch (DasPropertyException dpe) { + dpe.setPropertyName("colorbar"); + throw dpe; + } + } + + SpectrogramRenderer renderer = new SpectrogramRenderer(null, colorbar); + try { + renderer.setDataSetID(dataSetID); + } catch (DasException de) { + DasExceptionHandler.handle(de); + } + return renderer; + } + + private static DasColorBar processZAxisElement(Element element, FormBase form) throws DasPropertyException, DasNameException, ParseException { + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + if (node instanceof Element) { + } + } + return null; + } + + public Element getDOMElement(Document document) { + + Element element = document.createElement("stippledTable"); + element.setAttribute("dataSetID", getDataSetID()); + + return element; + } + + /** Getter for property sliceRebinnedData. + * @return Value of property sliceRebinnedData. + * + */ + public boolean isSliceRebinnedData() { + return this.sliceRebinnedData; + } + + /** Setter for property sliceRebinnedData. + * @param sliceRebinnedData New value of property sliceRebinnedData. + * + */ + public void setSliceRebinnedData(boolean sliceRebinnedData) { + this.sliceRebinnedData = sliceRebinnedData; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/SymColor.java b/dasCore/src/main/java/org/das2/graph/SymColor.java new file mode 100644 index 000000000..6558cd649 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/SymColor.java @@ -0,0 +1,113 @@ +/* File: SymColor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.components.propertyeditor.Displayable; +import org.das2.components.propertyeditor.Enumeration; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; + +/** + * + * @author jbf + */ + +public final class SymColor extends Color implements Enumeration, Displayable { + + private String name; + private ImageIcon imageIcon; + + public static final SymColor black= new SymColor( "black",Color.black ); + public static final SymColor blue= new SymColor( "blue",Color.blue ); + public static final SymColor lightRed= new SymColor( "lightRed", new Color( 255, 128, 128 ) ); + public static final SymColor red= new SymColor( "red",Color.red ); + public static final SymColor darkRed= new SymColor( "darkRed",Color.red.darker() ); + public static final SymColor green= new SymColor( "green",Color.green ); + public static final SymColor darkGreen= new SymColor( "darkGreen",Color.green.darker() ); + public static final SymColor white= new SymColor( "white",Color.white ); + public static final SymColor gray= new SymColor( "gray",Color.gray ); + public static final SymColor magenta = new SymColor( "magenta",Color.magenta); + + /** Creates a new instance of SymColor */ + private SymColor(String name, Color color) { + this(name, color.getRGB()); + } + + /** Creates a new instance of SymColor */ + private SymColor(String name, int rgb) { + super(rgb); + this.name= name; + Image i= new BufferedImage(10,10,BufferedImage.TYPE_INT_RGB); + Graphics g= i.getGraphics(); + g.setColor(this); + g.fillRect(0,0,10,10); + this.imageIcon= new ImageIcon(i); + } + + /** An icon can be provided that will be shown in a list + * along with the textual description of the element. + * This method should return null if there + * is no icon available. + */ + public Icon getListIcon() { + return imageIcon; + } + + public String getListLabel() { + return name; + } + + public String toString() { + return name; + } + + public Color toColor() { + return this; + } + + public static SymColor parseSymColor(String str) { + if (str.equals("black")) { + return black; + } + else if (str.equals("blue")) { + return blue; + } + else if (str.equals("red")) { + return red; + } + else if (str.equals("green")) { + return green; + } + else if (str.equals("white")) { + return white; + } + else if (str.equals("gray")) { + return gray; + } + else { + throw new IllegalArgumentException(str); + } + } +} diff --git a/dasCore/src/main/java/org/das2/graph/SymbolLineRenderer.java b/dasCore/src/main/java/org/das2/graph/SymbolLineRenderer.java new file mode 100755 index 000000000..cd9c87a74 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/SymbolLineRenderer.java @@ -0,0 +1,527 @@ +/* File: SymbolLineRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.components.propertyeditor.Displayable; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.VectorUtil; +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.DatumRange; +import org.das2.datum.Datum; +import org.das2.system.DasLogger; +import org.das2.DasApplication; +import org.das2.DasProperties; +import org.das2.DasException; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.dasml.FormBase; +import org.das2.event.DasMouseInputAdapter; +import org.das2.event.LengthDragRenderer; +import org.das2.event.MouseModule; +import java.awt.image.*; +import javax.swing.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.awt.*; +import java.awt.geom.*; +import java.util.logging.Logger; +import org.das2.datum.Units; + + +/** + * + * @author jbf + */ +public class SymbolLineRenderer extends Renderer implements Displayable { + + private Psym psym = Psym.NONE; + private double symSize = 3.0; // radius in pixels + private float lineWidth = 1.0f; // width in pixels + private boolean histogram = false; + //private Stroke stroke; + private PsymConnector psymConnector = PsymConnector.SOLID; + + int renderCount=0, updateImageCount=0; + + /** Holds value of property color. */ + private Color color= Color.BLACK; + + private long lastUpdateMillis; + + /** Holds value of property antiAliased. */ + private boolean antiAliased= ("on".equals(DasProperties.getInstance().get("antiAlias"))); + + /** The 'image' of the data */ + private GeneralPath path; + + private Logger log= DasLogger.getLogger(DasLogger.GRAPHICS_LOG); + + public SymbolLineRenderer() { + super(); + } + + /** + * @deprecated use SymbolLineRenderer() and setDataSet() instead. Note that + * behavior may be slightly different since a DataLoader is created. + */ + public SymbolLineRenderer(DataSet ds) { + super(ds); + } + + /** + * @deprecated use SymbolLineRenderer() and setDataSetDescriptor() instead. + */ + public SymbolLineRenderer(DataSetDescriptor dsd) { + super(dsd); + } + + private void reportCount() { + //if ( renderCount % 100 ==0 ) { + // System.err.println(" updates: "+updateImageCount+" renders: "+renderCount ); + //} + } + + + @Override + public void render(Graphics g, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + renderCount++; + // reportCount(); + + long timer0= System.currentTimeMillis(); + + VectorDataSet dataSet= (VectorDataSet)getDataSet(); + + if ( this.ds==null && getLastException()!=null ) { + renderException(g,xAxis,yAxis,getLastException()); + return; + } + + if (dataSet == null || dataSet.getXLength() == 0) { + DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("null data set"); + parent.postMessage(this, "null data set", DasPlot.INFO, null, null ); + return; + } + + DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("render data set "+dataSet); + + g.setColor(color); + + Graphics2D graphics= (Graphics2D) g.create(); + + RenderingHints hints0= graphics.getRenderingHints(); + if ( antiAliased ) { + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } else { + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + } + + log.finest("drawing psym in "+color); + + // draw the stored path that we calculated in updatePlotImage + if (path != null) { + psymConnector.draw(graphics, path, lineWidth); + } + + Dimension d; + + double xmin, xmax, ymin, ymax; + + org.das2.datum.Units xUnits= xAxis.getUnits(); + org.das2.datum.Units yUnits= yAxis.getUnits(); + + Rectangle r= g.getClipBounds(); + + if ( r==null ) { + xmax= xAxis.getDataMaximum().doubleValue(xUnits); + xmin= xAxis.getDataMinimum().doubleValue(xUnits); + ymax= yAxis.getDataMaximum().doubleValue(yUnits); + ymin= yAxis.getDataMinimum().doubleValue(yUnits); + } else { + xmin= xAxis.invTransform((int)r.getX()).doubleValue(xUnits); + xmax= xAxis.invTransform((int)(r.getX()+r.getWidth())).doubleValue(xUnits); + ymin= yAxis.invTransform((int)r.getY()).doubleValue(yUnits); + ymax= yAxis.invTransform((int)(r.getY()+r.getHeight())).doubleValue(yUnits); + } + + //Support flipped axes + if (xmax < xmin) { + double tmp = xmax; + xmax = xmin; + xmin = tmp; + } + if (ymax < ymin) { + double tmp = ymax; + ymax = ymin; + ymin = tmp; + } + + if ( psym!=Psym.NONE ) { // optimize for common case + int ixmax, ixmin; + + ixmin= VectorUtil.closestXTag(dataSet,xmin,xUnits); + if ( ixmin>0 ) ixmin--; + ixmax= VectorUtil.closestXTag(dataSet,xmax,xUnits); + if ( ixmax=dataSet.getXLength() ) { + ixmax= dataSet.getXLength()-1; + } + + for (int index = ixmin; index <= ixmax; index++) { + double x = dataSet.getXTagDouble(index, xUnits); + double y = dataSet.getDouble(index, yUnits); + double i = xAxis.transform(x, xUnits); + double j = yAxis.transform(y, yUnits); + if ( yUnits.isFill( dataSet.getDouble(index,yUnits) ) || Double.isNaN(y) ) { + skippedLast = true; + } else if (skippedLast) { + newPath.moveTo((float)i, (float)j); + skippedLast = false; + } else if (Math.abs(x - x0) > xSampleWidth) { + //This should put a point on an isolated data value + newPath.lineTo((float)i0, (float)j0); + newPath.moveTo((float)i, (float)j); + skippedLast = false; + } else { + if (histogram) { + double i1 = (i0 + i)/2; + newPath.lineTo((float)i1, (float)j0); + newPath.lineTo((float)i1, (float)j); + newPath.lineTo((float)i, (float)j); + } else { + newPath.lineTo((float)i, (float)j); + } + skippedLast = false; + } + x0= x; + y0= y; + i0= i; + j0= j; + } + path = newPath; + + } else { + path= null; + } + if (getParent() != null) { + getParent().repaint(); + } + DasLogger.getLogger( DasLogger.GRAPHICS_LOG ).fine( "done updatePlotImage" ); + updating=false; + } + + + public PsymConnector getPsymConnector() { + return psymConnector; + } + + public void setPsymConnector(PsymConnector p) { + psymConnector = p; + refreshImage(); + } + + /** Getter for property psym. + * @return Value of property psym. + */ + public Psym getPsym() { + return this.psym; + } + + + /** Setter for property psym. + * @param psym New value of property psym. + */ + public void setPsym(Psym psym) { + if (psym == null) throw new NullPointerException("psym cannot be null"); + Object oldValue = this.psym; + this.psym = psym; + refreshImage(); + } + + /** Getter for property symsize. + * @return Value of property symsize. + */ + public double getSymSize() { + return this.symSize; + } + + /** Setter for property symsize. + * @param symSize New value of property symsize. + */ + public void setSymSize(double symSize) { + this.symSize= symSize; + setPsym(this.psym); + refreshImage(); + } + + /** Getter for property color. + * @return Value of property color. + */ + public Color getColor() { + return color; + } + + /** Setter for property color. + * @param color New value of property color. + */ + public void setColor(Color color) { + this.color= color; + refreshImage(); + } + + public float getLineWidth() { + return lineWidth; + } + + public void setLineWidth(float f) { + lineWidth = f; + refreshImage(); + } + + @Override + protected void installRenderer() { + if ( ! DasApplication.getDefaultApplication().isHeadless() ) { + DasMouseInputAdapter mouseAdapter = parent.mouseAdapter; + DasPlot p= parent; + mouseAdapter.addMouseModule( new MouseModule( p, new LengthDragRenderer( p,p.getXAxis(),p.getYAxis()), "Length" ) ); + } + } + + @Override + protected void uninstallRenderer() { + } + + public static SymbolLineRenderer processLinePlotElement(Element element, DasPlot parent, FormBase form) { + String dataSetID = element.getAttribute("dataSetID"); + Psym psym = Psym.parsePsym(element.getAttribute("psym")); + SymColor color = SymColor.parseSymColor(element.getAttribute("color")); + SymbolLineRenderer renderer = new SymbolLineRenderer( (VectorDataSet)null ); + parent.addRenderer(renderer); + float lineWidth = Float.parseFloat(element.getAttribute("lineWidth")); + try { + renderer.setDataSetID(dataSetID); + } catch (org.das2.DasException de) { + org.das2.util.DasExceptionHandler.handle(de); + } + renderer.setPsym(psym); + renderer.setColor(color); + renderer.setLineWidth(lineWidth); + return renderer; + } + + @Override + public Element getDOMElement(Document document) { + + Element element = document.createElement("lineplot"); + element.setAttribute("dataSetID", getDataSetID()); + element.setAttribute("psym", getPsym().toString()); + element.setAttribute("color", getColor().toString()); + + return element; + } + + /** Getter for property antiAliased. + * @return Value of property antiAliased. + * + */ + public boolean isAntiAliased() { + return this.antiAliased; + } + + /** Setter for property antiAliased. + * @param antiAliased New value of property antiAliased. + * + */ + public void setAntiAliased(boolean antiAliased) { + this.antiAliased = antiAliased; + refreshImage(); + } + + public boolean isHistogram() { + return histogram; + } + + public void setHistogram(final boolean b) { + if (b != histogram) { + histogram = b; + refreshImage(); + } + } + + @Override + public String getListLabel() { + return String.valueOf( this.getDataSetDescriptor() ); + } + + @Override + public javax.swing.Icon getListIcon() { + Image i= new BufferedImage(15,10,BufferedImage.TYPE_INT_ARGB); + Graphics2D g= (Graphics2D)i.getGraphics(); + g.setRenderingHints(DasProperties.getRenderingHints()); + + // leave transparent if not white + if ( color.equals( Color.white ) ) { + g.setColor( Color.GRAY ); + } else { + g.setColor( new Color( 0,0,0,0 ) ); + } + g.fillRect(0,0,15,10); + g.setColor(color); + Stroke stroke0= g.getStroke(); + getPsymConnector().drawLine( g, 2, 3, 13, 7, 1.5f ); + g.setStroke(stroke0); + psym.draw( g, 7, 5, 3.f ); + return new ImageIcon(i); + } + + @Override + public boolean acceptContext(int x, int y) { + return path!=null && path.intersects( x-5, y-5, 10, 10 ); + } + + private int findFirstVisibleSegment( + VectorDataSet dataSet, + Rectangle2D visibleRectangle, + Units xUnits, Units yUnits, + boolean inReverse) + { + int n = dataSet.getXLength(); + for (int i = 0; i < n-1; i++) { + int index0 = inReverse ? n-1-i : i; + int index1 = inReverse ? index0-1 : index0+1; + double x0 = dataSet.getXTagDouble(index0, xUnits); + double x1 = dataSet.getXTagDouble(index1, xUnits); + double y0 = dataSet.getDouble(index0, yUnits); + double y1 = dataSet.getDouble(index1, yUnits); + if (visibleRectangle.intersectsLine(x0, y0, x1, y1)) { + return index0; + } + } + return -1; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/TickCurveRenderer.java b/dasCore/src/main/java/org/das2/graph/TickCurveRenderer.java new file mode 100755 index 000000000..4d9a39b7f --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/TickCurveRenderer.java @@ -0,0 +1,332 @@ +/* File: TickCurveRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on November 3, 2003, 11:43 AM by __FULLNAME__ <__EMAIL__> + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.dataset.VectorUtil; +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.Units; +import org.das2.util.DasMath; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.components.propertyeditor.Enumeration; +import java.awt.*; +import java.awt.geom.*; + +/** + * + * @author jbf + */ +public class TickCurveRenderer extends Renderer { + + private Stroke stroke; + + TickVDescriptor tickv; + private String xplane; + private String yplane; + + private VectorDataSet xds; + private VectorDataSet yds; + private Units xunits; // xUnits of the axis + private Units yunits; // yUnits of the axis + private double[][] idata; // data transformed to pixel space + + TickLabeller tickLabeller; + + /** Holds value of property tickStyle. */ + private TickStyle tickStyle; + + /** Holds value of property lineWidth. */ + private double lineWidth; + + /** Holds value of property tickLength. */ + private float tickLength; + + public static class TickStyle implements Enumeration { + private String name; + public static TickStyle outer= new TickStyle("Outer"); + public static TickStyle both= new TickStyle("Both"); + private TickStyle(String name) { + this.name= name; + } + public String toString() { + return this.name; + } + public javax.swing.Icon getListIcon() { + return null; + } + + } + + /** The dataset be a Vector data set with planes identified + * by xplane and yplane. + */ + public TickCurveRenderer( DataSet ds, String xplane, String yplane, TickVDescriptor tickv) { + super(ds); + + setTickStyle( TickCurveRenderer.TickStyle.outer ); + setLineWidth( 1.0f ); + setTickLength( 8.0f ); + this.xplane= xplane; + this.yplane= yplane; + this.tickv= tickv; + } + + protected void uninstallRenderer() { + } + + protected void installRenderer() { + } + + private static double length( Line2D line ) { + double dx= line.getX2()-line.getX1(); + double dy= line.getY2()-line.getY1(); + double dist= Math.sqrt( dx*dx + dy*dy ); + return dist; + } + + private static Line2D normalize(Line2D line, double len) { + // make line segment length len, starting at line.getP1() + Point2D p1= line.getP1(); + double dx= line.getX2()-line.getX1(); + double dy= line.getY2()-line.getY1(); + double dist= Math.sqrt( dx*dx + dy*dy ); + Line2D result= (Line2D) line.clone(); + result.setLine( p1.getX(), p1.getY(), p1.getX() + dx/dist * len, p1.getY() + dy/dist*len ); + return result; + } + + private double turnDir( double x1, double y1, double x2, double y2, double x3, double y3 ) { + // returns positive double if turning clockwise, negative is ccw. Number is + // based on the cross product of the two difference vectors. + double dx1= x2-x1; + double dx2= x3-x2; + double dy1= y2-y1; + double dy2= y3-y2; + return dx1*dy2 - dx2*dy1; + } + + private double turnDirAt( double findex ) { + int nvert= xds.getXLength(); + int index0, index1, index2; + if ( findex<1 ) { + index0= 0; + } else if ( findex>nvert-2 ) { + index0= nvert-3; + } else { + index0= (int)Math.floor(findex)-1; + } + index1= index0+1; + index2= index1+1; + + return turnDir( xds.getDouble(index0,xunits), yds.getDouble(index0,yunits), + xds.getDouble(index1,xunits), yds.getDouble(index1,yunits), + xds.getDouble(index2,xunits), yds.getDouble(index2,yunits) ); + } + + private Line2D outsideNormalAt( double findex ) { + int nvert= xds.getXLength(); + int index0= (int)Math.floor(findex); + if ( index0==nvert-1 ) index0--; + double x1= idata[0][index0]; + double x2= idata[0][index0+1]; + double y1= idata[1][index0]; + double y2= idata[1][index0+1]; + + double xinterp= DasMath.interpolate( idata[0], findex ); + double yinterp= DasMath.interpolate( idata[1], findex ); + + double dx= x2-x1; + double dy= y2-y1; + + double turnDir= turnDirAt(findex); + // we want the turnDir of the tick to be opposite turnDir of the curve + + double dxNorm= dy; + double dyNorm= -dx; + + double turnDirTick= -1*(dx*dyNorm-dxNorm*dy); + + if ( turnDir*turnDirTick > 0 ) { // same sign, use the other perp direction. + dxNorm= -dy; + dyNorm= dx; + } + + Line2D normal; + + return normalize( new Line2D.Double(xinterp, yinterp, xinterp+dxNorm,yinterp+dyNorm ), 1. ) ; + + } + + private void drawTick( Graphics2D g, double findex ) { + float tl= getTickLength()*2/3; + Line2D tick= normalize( outsideNormalAt( findex ), tl ); + if ( tickStyle==TickStyle.both ) { + Line2D flipTick= normalize( tick, -tl ); + Line2D bothTick= new Line2D.Double( flipTick.getP2(), tick.getP2() ); + g.draw( bothTick ); + } else { + g.draw( tick ); + } + } + + private double slope( Line2D line ) { + return ( line.getY2()-line.getY1() ) / ( line.getX2()-line.getX1() ); + } + + private void drawLabelTick( Graphics2D g, double findex, int tickNumber ) { + float tl= getTickLength(); + Line2D tick= normalize( outsideNormalAt( findex ), tl ); + if ( tickStyle==TickStyle.both ) { + Line2D flipTick= normalize( tick, -tl ); + Line2D bothTick= new Line2D.Double( flipTick.getP2(), tick.getP2() ); + g.draw( bothTick ); + } else { + g.draw( tick ); + } + + tickLabeller.labelMajorTick( g, tickNumber, tick ); + + } + + public void render(java.awt.Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + + if ( ds==null ) { + return; + } + + Graphics2D g= (Graphics2D)g1; + g.setStroke( stroke ); + + g.setColor( Color.black ); + + DataSet ds= getDataSet(); + xds= (VectorDataSet) ds.getPlanarView(xplane); + yds= (VectorDataSet) ds.getPlanarView(yplane); + xunits= xds.getYUnits(); + yunits= yds.getYUnits(); + + idata= new double[2][xds.getXLength()]; + for ( int i=0; i=0 && findex[i]=0 && findex[i] 0) ? (Datum) majorTicks.get(0) : (Datum) minorTicks.get(0); + Units u = d.getUnits(); + double[] major = new double[majorTicks.size()]; + for (int i = 0; i < major.length; i++) { + major[i] = ((Datum) majorTicks.get(i)).doubleValue(u); + } + double[] minor = new double[minorTicks.size()]; + for (int i = 0; i < minor.length; i++) { + minor[i] = ((Datum) minorTicks.get(i)).doubleValue(u); + } + return new TickVDescriptor(minor, major, u); + } + + public DatumVector getMajorTicks() { + return tickV; + } + + public DatumVector getMinorTicks() { + return minorTickV; + } + + public DatumFormatter getFormatter() { + return this.datumFormatter; + } + + /** + * Locates the next or previous tick starting at xDatum. + * + * @param xDatum find the tick closest to this. + * @param direction -1 previous, 1 next, 0 closest + * @param minor find closest minor tick, major if false. + * @return the closest tick. If there is no tick in the given direction, then + * the behavior is undefined. + */ + public Datum findTick(Datum xDatum, double direction, boolean minor) { + int majorLen; + int minorLen; + double[] ticks; + + // direction<0 nearest left, direction>0 nearest right, direction=0 nearest. + if (tickV == null) { + return xDatum; + } + majorLen = tickV.getLength(); + minorLen = minorTickV.getLength(); + ticks = new double[majorLen + minorLen]; + for (int i = 0; i < majorLen; i++) { + ticks[i] = tickV.doubleValue(i, units); + } + for (int i = 0; i < minorLen; i++) { + ticks[i + majorLen] = minorTickV.doubleValue(i, units); + } + + int iclose = 0; + double close = Double.MAX_VALUE; + + double x = xDatum.doubleValue(units); + + for (int i = 0; i < ticks.length; i++) { + if (direction < 0 && ticks[i] < x && x - ticks[i] < close) { + iclose = i; + close = x - ticks[i]; + } else if (direction > 0 && x < ticks[i] && ticks[i] - x < close) { + iclose = i; + close = ticks[i] - x; + } + if (direction == 0 && Math.abs(ticks[i] - x) < close) { + iclose = i; + close = Math.abs(ticks[i] - x); + } + } + + return Datum.create(ticks[iclose], units); + + } + + /** + * Defining method for getting the range close to the given range, + * but containing at least one minor(or major) tick interval. + * + * @param minor find the range from the minor ticks. + */ + public DatumRange enclosingRange(DatumRange dr, boolean minor) { + Datum s1 = findTick(dr.min(), 0, minor); + Datum s2 = findTick(dr.max(), 0, minor); + DatumRange result; + if (s1.equals(s2)) { + s1 = findTick(dr.min(), -1, true); + s2 = findTick(dr.max(), 1, true); + } + return new DatumRange(s1, s2); + } + + public void setFormatter(DatumFormatter datumFormatter) { + this.datumFormatter = datumFormatter; + } + + /** Returns a String representation of the TickVDescriptor. + * @return a String representation of the TickVDescriptor. + * + */ + public String toString() { + String s = "tickV=" + getMajorTicks(); + s += ",minor=" + getMinorTicks(); + return s; + } + + /** + * return a set of linear ticks, within the given constraints. + */ + public static TickVDescriptor bestTickVLinear(Datum min, Datum max, int nTicksMin, int nTicksMax, boolean fin) { + + TickVDescriptor res = new TickVDescriptor(); + + res.units = min.getUnits(); + double minimum = min.doubleValue(res.units); + double maximum = max.doubleValue(res.units); + + int targetTicks = Math.max(Math.min(6, nTicksMax), nTicksMin); + + double maj = (maximum - minimum) / (targetTicks - 1); + double mag = DasMath.exp10(Math.floor(DasMath.log10(maj))); + double absissa = maj / mag; + + if (absissa < 1.666) { + absissa = 1.0; + } else if (absissa < 3.333) { + absissa = 2.0; + } else if (absissa < 9.0) { + absissa = 5.0; + } else { + absissa = 1.; + mag *= 10; + } + + double axisLengthData = maximum - minimum; + + int minorPerMajor; + + if (absissa == 5.) { + minorPerMajor = 5; + } else if (absissa == 2.) { + minorPerMajor = 2; + } else { + minorPerMajor = 10; + } + + double minorTickSize = absissa * mag / minorPerMajor; + double majorTickSize = minorTickSize * minorPerMajor; + double firstTick = majorTickSize * Math.ceil((minimum - axisLengthData) / majorTickSize - 0.01); + double lastTick = majorTickSize * Math.floor((maximum + axisLengthData) / majorTickSize + 0.01); + + int nTicks = 1 + (int) Math.round((lastTick - firstTick) / majorTickSize); + + double[] result = new double[nTicks]; + for (int i = 0; i < nTicks; i++) { + result[i] = firstTick + (i * minorPerMajor * minorTickSize); + } + res.tickV = DatumVector.newDatumVector(result, res.units); + + int ifirst = nTicks / 3; + int ilast = 2 * nTicks / 3; + + res.datumFormatter = DatumUtil.bestFormatter(res.units.createDatum(result[ifirst]), + res.units.createDatum(result[ilast]), ilast - ifirst); + + double firstMinor = firstTick; + double lastMinor = lastTick; + int nMinor = (int) ((lastMinor - firstMinor) / minorTickSize + 0.5); + double[] minorTickV = new double[nMinor]; + for (int i = 0; i < nMinor; i++) { + minorTickV[i] = firstMinor + i * minorTickSize; + } + res.minorTickV = DatumVector.newDatumVector(minorTickV, res.units); + + return res; + + } + private static final DatumFormatter DEFAULT_LOG_FORMATTER; + + + static { + try { + DatumFormatterFactory factory = DefaultDatumFormatterFactory.getInstance(); + DEFAULT_LOG_FORMATTER = factory.newFormatter("0E0"); + } catch (ParseException pe) { + throw new RuntimeException(pe); + } + } + + /** + * return a set of log ticks, within the given constraints. + */ + public static TickVDescriptor bestTickVLogNew(Datum minD, Datum maxD, int nTicksMin, int nTicksMax, boolean fin) { + + TickVDescriptor ticks = new TickVDescriptor(); + ticks.units = minD.getUnits(); + double min = minD.doubleValue(ticks.units); + double max = maxD.doubleValue(ticks.units); + + if (max <= 0) { + max = 100.; + } + if (min <= 0) { + min = max / 1000.; + } + double logMin = DasMath.log10(min); + double logMax = DasMath.log10(max); + int ntick0 = (int) (Math.floor(logMax * 0.999) - Math.ceil(logMin * 1.001) + 1); + + if (ntick0 < 2) { + TickVDescriptor result = bestTickVLinear(minD, maxD, nTicksMin, nTicksMax, fin); + int ii = 0; + + DatumVector majortics = result.getMajorTicks(); + Units u = majortics.getUnits(); + + while (ii < majortics.getLength() && majortics.get(ii).doubleValue(u) <= 0) { + ii++; + } + majortics = majortics.getSubVector(ii, majortics.getLength()); + + DatumVector minortics = result.getMinorTicks(); + while (ii < minortics.getLength() && minortics.get(ii).doubleValue(u) <= 0) { + ii++; + } + minortics = minortics.getSubVector(ii, minortics.getLength()); + + DatumFormatter df = result.datumFormatter; + result = TickVDescriptor.newTickVDescriptor(majortics, minortics); + result.datumFormatter = df; + + return result; + + } + + if (ntick0 > nTicksMax) { + Units units = minD.getUnits(); + Datum logMinD = units.createDatum(DasMath.log10(min)); + Datum logMaxD = units.createDatum(DasMath.log10(max)); + TickVDescriptor linTicks = bestTickVLinear(logMinD, logMaxD, nTicksMin, nTicksMax, fin); + double[] tickV = linTicks.tickV.toDoubleArray(linTicks.units); + + // copy over the ticks into the linear space, but cull the fractional ones + int i2 = 0; + for (int i = 0; i < tickV.length; i++) { + if (tickV[i] % 1. == 0.) { + tickV[i2++] = DasMath.exp10(tickV[i]); + } + } + double[] t = tickV; + tickV = new double[i2]; + for (int i = 0; i < i2; i++) { + tickV[i] = t[i]; + } + + // now fill in the minor ticks, if there's room + int idx = 0; + double[] minorTickV; + if ((tickV[1] / tickV[0]) <= 10.00001) { + minorTickV = new double[(tickV.length + 1) * 9]; + for (int j = 2; j < 10; j++) { + minorTickV[idx++] = j * (tickV[0] / 10); + } + for (int i = 0; i < tickV.length; i++) { + for (int j = 2; j < 10; j++) { + minorTickV[idx++] = j * tickV[i]; + } + } + } else { + minorTickV = linTicks.minorTickV.toDoubleArray(linTicks.units); + for (int i = 0; i < minorTickV.length; i++) { + minorTickV[i] = DasMath.exp10(minorTickV[i]); + } + } + + linTicks.tickV = DatumVector.newDatumVector(tickV, linTicks.units); + linTicks.minorTickV = DatumVector.newDatumVector(minorTickV, linTicks.units); + linTicks.datumFormatter = DEFAULT_LOG_FORMATTER; + return linTicks; + } + + double min3 = min / (max / min); + double max3 = max * (max / min); + + double dMinTick = DasMath.roundNFractionalDigits(DasMath.log10(min3), 4); + int minTick = (int) Math.ceil(dMinTick); + double dMaxTick = DasMath.roundNFractionalDigits(DasMath.log10(max3), 4); + int maxTick = (int) Math.floor(dMaxTick); + + int nTicks = (maxTick - minTick) + 1; + + double[] major; // major ticks labels + double[] minors; // minor ticks to label -- {}, or { 2,3,4,5,6,7,8,9 }! !! + + major = new double[nTicks]; + for (int i = 0; i < nTicks; i++) { + major[i] = i + minTick; + } + minors = new double[]{2, 3, 4, 5, 6, 7, 8, 9}; + + ticks.datumFormatter = DEFAULT_LOG_FORMATTER; + + int firstMinorTickCycle = (int) Math.floor(DasMath.log10(min3)); + int lastMinorTickCycle = (int) Math.floor(DasMath.log10(max3)); + + double[] minorTickV = null; + int idx = 0; + minorTickV = new double[(lastMinorTickCycle - firstMinorTickCycle + 1) * minors.length]; + for (int i = firstMinorTickCycle; i <= lastMinorTickCycle; i++) { + for (int j = 0; j < minors.length; j++) { + minorTickV[idx++] = DasMath.exp10(i) * minors[j]; + } + } + ticks.minorTickV = DatumVector.newDatumVector(minorTickV, ticks.units); + + for (int i = 0; i < major.length; i++) { + major[i] = DasMath.exp10(major[i]); + } + ticks.tickV = DatumVector.newDatumVector(major, ticks.units); + + return ticks; + + } + + /** + * find a divider that gives the biggest divisions for unitsPerDecade. For example, we want to + * divide a pizza evenly. If the pizza has 12 pieces, then we can return 1,2,3,4 or 6. + * If the pizza has 10 pieces, then we can return 1,2,or 5. + * A minute has 60 seconds, so we can return 1,2,5,10,20,30. (why not 6 or 12? exclude param introduced) + * A day had 24 hours, so we can return 1,2,4,6,or 12. + * A circle had 360 degrees, so we can return 1,2,5,10,15,30,60,45,90, + * + * Find the biggest of tt that divides into unitsPerDecade and is less than sizeLimit. + * + * @param sizeLimit max number of pieces + * @param factors number of divisions allowed. + * @param exclude don't allow this one. + */ + private static int getMantissa(int sizeLimit, int unitsPerDecade, int exclude) { + int[] tt = new int[]{1, 2, 3, 5, 6, 10, 12, 15, 20, 25, 30, 45, 60, 90, 100, 200, 500, 1000, 2000, 5000, 10000}; + int biggest = 1; + for (int i = 0; i < tt.length && tt[i] <= sizeLimit; i++) { + if (unitsPerDecade % tt[i] == 0 && (exclude == 0 || tt[i] % exclude != 0)) { + biggest = tt[i]; + } + } + return biggest; + } + + /** + * return list of mantissas as described in getMantissa. The goal here is to + * return a list of mantissas that divide the pie into integer number of pieces + * and in a number of divisions humans like. THIS IS THE GENERAL CODE!!! + * + * If the pizza has 10 pieces, then we can return 1,2,or 5. + * A minute has 60 seconds, so we can return 1,2,5,10,20,30. (why not 6 or 12? exclude param introduced) + * A day had 24 hours, so we can return 1,2,4,6,or 12. + * A circle had 360 degrees, so we can return 1,2,5,10,15,30,60,45,90, + */ + private static List/**/ getMantissas(int divisionsPerDecade, int exclude, int include) { + int[] tt = new int[]{1, 2, 3, 5, 6, 10, 12, 15, 20, 25, 30, 45, 60, 90, 100, 200, 500, 1000, 2000, 5000, 10000}; + int biggest = 1; + ArrayList result = new ArrayList(); + for (int i = 0; i < tt.length && tt[i] < divisionsPerDecade; i++) { + boolean incl = include != 0 && tt[i] % include == 0; + boolean excl = exclude != 0 && tt[i] % exclude == 0; + if (excl && !incl) { + continue; + } + if (divisionsPerDecade % tt[i] == 0) { + result.add(new Integer(tt[i])); + } + } + return result; + } + + /** + * + * @param minD + * @param maxD + * @param units + * @param biggerUnits + * @param biggerUnitsCount kludge for when units==YEAR and biggerUnits==YEAR, so we can get "10 years" + * @param unitLengthNanos + * @param mantissa + * @param fin true when this is called for the last time, for debugging. + * @return + */ + private static TickVDescriptor countOffTicks2(Datum minD, Datum maxD, + TimeUtil.TimeDigit units, TimeUtil.TimeDigit biggerUnits, int biggerUnitsCount, + long unitLengthNanos, int mantissa, boolean fin) { + DatumRange range = new DatumRange(minD, maxD); + + Datum majorTickLength = Units.nanoseconds.createDatum(unitLengthNanos * mantissa); + + Datum first; + + /** + * next is the next major tick + */ + Datum next; + + if (units == TimeUtil.TD_YEAR) { + int iyear = TimeUtil.toTimeArray(minD)[0]; + iyear = (iyear / biggerUnitsCount) * biggerUnitsCount; // round to mantissa + first = TimeUtil.createTimeDatum(iyear, 1, 1, 0, 0, 0, 0); + } else { + int[] digits = TimeUtil.toTimeArray(minD); + first = TimeUtil.prev(CalendarTime.Step.HigerStep(units.getOrdinal()), minD); + } + + + next = TimeUtil.next(biggerUnits.getOrdinal(), first); + for (int i = 1; i < biggerUnitsCount; i++) { + next = TimeUtil.next(biggerUnits.getOrdinal(), next); + } + next = next.subtract(majorTickLength.divide(2)); // don't bump right up to it. + + ArrayList majorTicks = new ArrayList(); + ArrayList minorTicks = new ArrayList(); + Datum d = first; + + TimeUtil.TimeDigit minorUnits = units; + int minorMantissa = 1; + + TimeUtil.TimeDigit majorUnits = units; + int majorMantissa = mantissa; + + if (majorMantissa == 1) { + minorUnits = TimeUtil.TimeDigit.fromOrdinal(CalendarTime.Step.LowerStep(majorUnits.getOrdinal())); + minorMantissa = majorUnits==TimeUtil.TD_MONTH ? 10 : majorUnits.divisions() / 4; + } + + Datum nextMajorTick= TimeUtil.next( majorUnits, majorMantissa, d ); + + if ( minorMantissa==0 ) { + throw new RuntimeException("minorMantissa==0"); + } + while (d.le(maxD)) { + + while (d.le(next)) { + if (DatumRangeUtil.sloppyContains(range, d)) { + majorTicks.add(d); + } + nextMajorTick= TimeUtil.next(majorUnits, majorMantissa, d); + while ( d.lt(nextMajorTick) ) { + if (DatumRangeUtil.sloppyContains(range, d)) { + minorTicks.add(d); + } + d = TimeUtil.next(minorUnits, minorMantissa, d); + } + } + next = next.add(majorTickLength.divide(2)); // this is all to avoid March-30, April-1. + while (d.le(next)) { + while ( d.lt(nextMajorTick) ) { + if (DatumRangeUtil.sloppyContains(range, d)) { + if (d.lt(next)) { + minorTicks.add(d); // so it doesn't get added twice + } + } + d = TimeUtil.next(minorUnits, minorMantissa, d); + } + nextMajorTick= TimeUtil.next(majorUnits, majorMantissa, d); + } + d = next; + next = TimeUtil.next(majorUnits, majorMantissa, next); + next = next.subtract(majorTickLength.divide(2)); + } + + return TickVDescriptor.newTickVDescriptor(majorTicks, minorTicks); + + } + + private static boolean checkMono(DatumVector ticks) { + Datum d = ticks.get(0); + for (int i = 1; i < ticks.getLength(); i++) { + if (ticks.get(i).lt(d)) { + // System.err.println("TickVDescriptor: not mono at " + i + ": " + d + " > " + ticks.get(i)); + return false; + } + d = ticks.get(i); + } + return true; + } + + /** + * return a set of ticks counting off ordinal time ranges, such as months, years, days, etc. + */ + public static TickVDescriptor bestTickVTimeOrdinal(Datum minD, Datum maxD, int nTicksMin, int nTicksMax, boolean fin) { + + //System.err.println( "bestTimeOrdinal: "+ new DatumRange(minD,maxD) + " "+nTicksMin+" "+nTicksMax ); + Datum lengthMin = maxD.subtract(minD).divide(nTicksMax + 1); // this is the approximation--you can't simply divide + Datum lengthMax = maxD.subtract(minD).divide(Math.max(1, nTicksMin - 1)); + + long lengthNanosMax = (long) lengthMax.doubleValue(Units.nanoseconds); + double lengthDaysMax = lengthMax.doubleValue(Units.days); + long lengthNanosMin = (long) lengthMin.doubleValue(Units.nanoseconds); + double lengthDaysMin = lengthMin.doubleValue(Units.days); + + TimeUtil.TimeDigit[] units = new TimeUtil.TimeDigit[]{TimeUtil.TD_NANO, TimeUtil.TD_SECOND, + TimeUtil.TD_MINUTE, TimeUtil.TD_HOUR, TimeUtil.TD_DAY, TimeUtil.TD_MONTH, TimeUtil.TD_YEAR + }; + long[] lengths = new long[]{1, (long) 1e9, (long) 60e9, (long) 3600e9, (long) 86400e9, 30 * (long) 86400e9, 365 * (long) 86400e9}; + int[] excludeFactors = new int[]{0, 6, 6, 0, 3, 0, 0}; + int[] includeFactors = new int[]{0, 30, 30, 0, 15, 0, 0}; + + // find the range of units to try + int biggestUnitIndex, smallestUnitIndex; + int lessThanIndex = 0; + while (lessThanIndex < units.length && lengths[lessThanIndex] < lengthNanosMax) { + lessThanIndex++; + } + lessThanIndex--; + biggestUnitIndex = lessThanIndex; + + lessThanIndex = 0; + while (lessThanIndex < units.length && lengths[lessThanIndex] < lengthNanosMin) { + lessThanIndex++; + } + lessThanIndex--; + + smallestUnitIndex = lessThanIndex; + + TickVDescriptor bestTickV = null; + TickVDescriptor secondBestTickV = null; // fallback + TimeUtil.TimeDigit bestUnit = null; + TimeUtil.TimeDigit secondBestUnit = null; + + // loop over units and mantissas for each unit + for (int iunit = smallestUnitIndex; bestTickV == null && iunit <= biggestUnitIndex; iunit++) { + + int mantissa; + + TimeDigit biggerUnits; + if (units[lessThanIndex] == TimeUtil.TD_YEAR) { + biggerUnits = TimeUtil.TD_YEAR; + } else { + biggerUnits = units[lessThanIndex + 1]; + } + + List/**/ mantissas; + + if (units[iunit] != TimeUtil.TD_YEAR) { + int factors; + factors = (int) (lengths[iunit + 1] / lengths[iunit]); + mantissas = getMantissas(factors, excludeFactors[lessThanIndex], includeFactors[lessThanIndex]); + } else { + int factors = 10; + mantissas = getMantissas(factors, excludeFactors[lessThanIndex], includeFactors[lessThanIndex]); + } + + TickVDescriptor test; + + for (int imant = 0; imant < mantissas.size(); imant++) { + mantissa = ((Integer) mantissas.get(imant)).intValue(); + + int biggerUnitsCount = units[iunit] == biggerUnits ? mantissa : 1; + + DatumRange visibleRange= new DatumRange( minD, maxD ); + DatumRange ticksRange= fin ? DatumRangeUtil.rescale( visibleRange, -1.0, 2.0 ) : visibleRange; + + test = countOffTicks2( ticksRange.min(), ticksRange.max(), units[iunit], biggerUnits, biggerUnitsCount, lengths[lessThanIndex], mantissa, fin); + // // for debugging + //if (fin && test.tickV.getLength() <= nTicksMax) { + // test = countOffTicks2(ticksRange.min(), ticksRange.max(), units[iunit], biggerUnits, biggerUnitsCount, lengths[lessThanIndex], mantissa, fin); + //} + //if (!checkMono(test.getMinorTicks())) { + // test = countOffTicks2(minD, maxD, units[iunit], biggerUnits, biggerUnitsCount, lengths[lessThanIndex], mantissa, fin); + //} + int nticks= fin ? test.tickV.getLength() / 3 : test.tickV.getLength(); + if ( nticks <= nTicksMax && nticks >= nTicksMin) { + bestTickV = test; + bestUnit = units[iunit]; + break; + } + + if ( nticks >= nTicksMin) { // this is our back up. + secondBestTickV = test; + secondBestUnit = units[iunit]; + } + + } + + } + + if (bestTickV == null) { + bestUnit = secondBestUnit; + bestTickV = secondBestTickV; + } + + TickVDescriptor ticks = bestTickV; + + if (bestUnit == null) { + throw new IllegalArgumentException("failed to find best unit"); + } + ticks.datumFormatter = TimeDatumFormatter.formatterForScale(bestUnit.getOrdinal(), new DatumRange(minD, maxD)); + + return ticks; + + } + + public static TickVDescriptor bestTickVTime(Datum minD, Datum maxD, int nTicksMin, int nTicksMax, boolean fin) { + + Datum length = maxD.subtract(minD); + + Datum minute = Datum.create(60.0, Units.seconds); + if (maxD.subtract(minD).lt(minute)) { + Datum base = TimeUtil.prevMidnight(minD); + + Units offUnits = Units.seconds; + Datum offMin = minD.subtract(base).convertTo(offUnits); + Datum offMax = maxD.subtract(base).convertTo(offUnits); + TickVDescriptor offTicks = bestTickVLinear(offMin, offMax, nTicksMin, nTicksMax, fin); + + DatumVector minorTicks = offTicks.getMinorTicks().add(base); + DatumVector majorTicks = offTicks.getMajorTicks().add(base); + + TickVDescriptor result = TickVDescriptor.newTickVDescriptor(majorTicks, minorTicks); + result.datumFormatter = DatumUtil.bestFormatter(majorTicks); + return result; + } + + if (maxD.subtract(minD).gt(Datum.create(10 * 365, Units.days))) { + int yearMin = new CalendarTime(minD).year(); + int yearMax = new CalendarTime(maxD).year(); + TickVDescriptor yearTicks = bestTickVLinear(Units.dimensionless.createDatum(yearMin), + Units.dimensionless.createDatum(yearMax), nTicksMin, nTicksMax, fin); + yearTicks.units = minD.getUnits(); + double[] tickV = yearTicks.tickV.toDoubleArray(Units.dimensionless); + for (int i = 0; i < tickV.length; i++) { + int iyear = (int) tickV[i]; + tickV[i] = TimeUtil.convert(iyear, 1, 1, 0, 0, 0, (TimeLocationUnits) yearTicks.units); + } + yearTicks.tickV = DatumVector.newDatumVector(tickV, yearTicks.units); + double[] minorTickV = yearTicks.minorTickV.toDoubleArray(Units.dimensionless); + for (int i = 0; i < minorTickV.length; i++) { + int iyear = (int) minorTickV[i]; + minorTickV[i] = TimeUtil.convert(iyear, 1, 1, 0, 0, 0, (TimeLocationUnits) yearTicks.units); + } + yearTicks.minorTickV = DatumVector.newDatumVector(minorTickV, yearTicks.units); + Datum t1 = yearTicks.getMajorTicks().get(0); + int nticks = yearTicks.getMajorTicks().getLength(); + Datum t2 = yearTicks.getMajorTicks().get(nticks - 1); + yearTicks.datumFormatter = DatumUtil.bestTimeFormatter(t1, t2, nticks); + return yearTicks; + } else { + return bestTickVTimeOrdinal(minD, maxD, nTicksMin, nTicksMax, fin); + + } + } + +} + diff --git a/dasCore/src/main/java/org/das2/graph/TimeRangeLabel.java b/dasCore/src/main/java/org/das2/graph/TimeRangeLabel.java new file mode 100755 index 000000000..341b61982 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/TimeRangeLabel.java @@ -0,0 +1,298 @@ +/* File: TimeRangeLabel.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.Units; +import org.das2.datum.format.TimeDatumFormatter; +import org.das2.datum.Datum; +import org.das2.datum.UnitsConverter; +import org.das2.datum.TimeUtil; +import org.das2.DasProperties; +import org.das2.event.MouseModule; +import org.das2.util.DasMath; +import org.das2.util.DasExceptionHandler; + +import java.awt.*; +import java.awt.geom.*; +import java.beans.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.DecimalFormat; +import javax.swing.JFrame; +import javax.swing.JPanel; +import org.das2.datum.DatumRange; + +/** + * + * @author jbf + */ +public class TimeRangeLabel extends DasCanvasComponent { + + private static final DatumFormatter MINUTES; + private static final DatumFormatter SECONDS; + private static final DatumFormatter MILLISECONDS; + + private boolean rangeLabel; + + static { + try { + MINUTES = new TimeDatumFormatter("yyyy-MM-dd '('DDD')' HH:mm"); + SECONDS = new TimeDatumFormatter("yyyy-MM-dd '('DDD')' HH:mm:ss"); + MILLISECONDS = new TimeDatumFormatter("yyyy-MM-dd '('DDD')' HH:mm:ss.SSS"); + } + catch (java.text.ParseException pe) { + //If this is happening, then there is a major problem. + throw new RuntimeException(pe); + } + } + + private DataRange dataRange; + + private Datum min = TimeUtil.createTimeDatum(2000, 1, 1, 0, 0, 0, 0); + + private Datum max = TimeUtil.createTimeDatum(2000, 1, 2, 0, 0, 0, 0); + + private DatumFormatter df; + + double emOffset = 2.0; + + private class DataRangePropertyChangeListener implements PropertyChangeListener { + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("minimum")) { + setMin(Datum.create(dataRange.getMinimum(), dataRange.getUnits())); + } + else if (evt.getPropertyName().equals("maximum")) { + setMax(Datum.create(dataRange.getMaximum(), dataRange.getUnits())); + } + } + } + + private boolean startOnly = false; + + /** Creates a new instance of TimeRangeLabel */ + public TimeRangeLabel(DataRange dataRange) { + this.dataRange= dataRange; + this.min = Datum.create(dataRange.getMinimum(), dataRange.getUnits()); + this.max = Datum.create(dataRange.getMaximum(), dataRange.getUnits()); + DataRangePropertyChangeListener listener = new DataRangePropertyChangeListener(); + dataRange.addPropertyChangeListener("minimum", listener); + dataRange.addPropertyChangeListener("maximum", listener); + updateFormatter(); + } + + public TimeRangeLabel() { + updateFormatter(); + } + + public DatumRange getRange() { + return new DatumRange(min, max); + } + + public void setRange(DatumRange range) { + if (range == null) { + min = max = null; + } + else { + min = range.min(); + max = range.max(); + } + repaint(); + } + + public Datum getMax() { + return max; + } + + public void setMax(Datum max) { + this.max = max; + repaint(); + } + + public Datum getMin() { + return min; + } + + public void setMin(Datum min) { + this.min = min; + repaint(); + } + + protected void paintComponent(Graphics graphics) { + Graphics2D g= (Graphics2D) graphics; + g.setRenderingHints(DasProperties.getRenderingHints()); + + FontMetrics fm= g.getFontMetrics(); + + int y = getRow().getDMinimum(); + int x = getColumn().getDMinimum(); + + g.translate(-getX(),-getY()); + + int yLevel= y - (int)(getFont().getSize()*emOffset + 0.5); + + if ( this.rangeLabel ) { + String label= getRange().toString(); + g.drawString( label, x, yLevel ); + return; + } else { + g.drawString(df.format(min), x, yLevel ); + } + + if (!startOnly) { + String label= df.format(max); + x += getColumn().getWidth() - fm.stringWidth(label); + g.drawString(label, x, yLevel ); + } + } + public void resize() { + Rectangle bounds= new Rectangle( + getColumn().getDMinimum()-30, + getRow().getDMinimum()-(int)(getFont().getSize()*(emOffset+1)+0.5), + getColumn().getWidth()+60, + getFont().getSize()*3 ); + this.setBounds( bounds ); + } + + private void updateFormatter() { + //UnitsConverter converter = Units.getConverter(dataRange.getUnits(), Units.t2000); + double min = this.min.doubleValue(Units.t2000); + double max = this.max.doubleValue(Units.t2000); + min = secondsSinceMidnight(min); + max = secondsSinceMidnight(max); + int minMS = (int)(min * 1000.); + int maxMS = (int)(max * 1000.); + if ((minMS % 1000) != 0 || (maxMS % 1000) != 0) { + df = MILLISECONDS; + } + else if ((minMS % 60000) != 0 || (maxMS % 60000) != 0) { + df = SECONDS; + } + else { + df = MINUTES; + } + } + + private double secondsSinceMidnight(double t2000) { + if (t2000 < 0) { + t2000 = t2000 % 86400; + if (t2000 == 0) { + return 0; + } else { + return 86400 + t2000; + } + } else { + return t2000 % 86400; + } + } + + public PropertyChangeListener createDataRangePropertyListener() { + return new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + String propertyName = e.getPropertyName(); + Object oldValue = e.getOldValue(); + Object newValue = e.getNewValue(); + if (propertyName.equals("log")) { + update(); + firePropertyChange("log", oldValue, newValue); + } + else if (propertyName.equals("minimum")) { + update(); + updateFormatter(); + firePropertyChange("dataMinimum", oldValue, newValue); + } + else if (propertyName.equals("maximum")) { + update(); + updateFormatter(); + firePropertyChange("dataMaximum", oldValue, newValue); + } + markDirty(); + } + }; + } + + public boolean isStartOnly() { + return startOnly; + } + + public void setStartOnly(boolean b) { + this.startOnly = b; + if (isDisplayable()) { + repaint(); + } + } + + /** + * Use strings like "2004-01-01 00:00 to 00:20" to identify times. + */ + public void setRangeLabel( boolean b ) { + this.rangeLabel= b; + repaint(); + } + + public boolean isRangeLabel( ) { + return this.rangeLabel; + } + + public double getEmOffset() { + return emOffset; + } + + public void setEmOffset(double emOffset) { + this.emOffset = emOffset; + if (getCanvas() != null) { + resize(); + repaint(); + } + } + + public static void main( String[] args ) { + JFrame jframe= new JFrame(); + jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JPanel panel= new JPanel(); + DasCanvas canvas= new DasCanvas(300,300); + + DasRow row1= new DasRow(canvas,0.1,0.2); + DasRow row2 = new DasRow(canvas, 0.3, 0.4); + DasRow row3 = new DasRow(canvas, 0.5, 0.6); + DasRow row4 = new DasRow(canvas, 0.7, 0.8); + DasColumn column= new DasColumn(canvas,0.1,0.9); + + DataRange dataRange1= new DataRange(null,TimeUtil.createValid("1998-01-01 12:20"),TimeUtil.createValid("1999-01-01"),false); + DataRange dataRange2 = new DataRange(null, TimeUtil.createValid("1998-01-02 12:30:02"), TimeUtil.createValid("1999-01-01"),false); + DataRange dataRange3 = new DataRange(null, TimeUtil.createValid("1998-01-03 12:40:02.244"), TimeUtil.createValid("1999-01-01"),false); + + canvas.add(new TimeRangeLabel(dataRange1),row1,column); + canvas.add(new TimeRangeLabel(dataRange2),row2,column); + canvas.add(new TimeRangeLabel(dataRange3),row3,column); + + panel.setLayout(new BorderLayout()); + panel.add(canvas,BorderLayout.CENTER); + jframe.setContentPane(panel); + jframe.pack(); + jframe.setVisible(true); + + canvas.repaint(); + } +} diff --git a/dasCore/src/main/java/org/das2/graph/XAxisDataLoader.java b/dasCore/src/main/java/org/das2/graph/XAxisDataLoader.java new file mode 100644 index 000000000..052614f64 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/XAxisDataLoader.java @@ -0,0 +1,282 @@ +/* + * DataLoader.java + * + * Created on September 10, 2005, 5:28 AM + * + * Remove the data loading responsibilities from the Renderer, and introduce + * pluggable strategies for data loading. + * + */ + +package org.das2.graph; + +import org.das2.CancelledOperationException; +import org.das2.dataset.CacheTag; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.DataSetUpdateEvent; +import org.das2.dataset.DataSetUpdateListener; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.NoDataInIntervalException; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.graph.DataLoader.Request; +import org.das2.datum.DatumUtil; +import org.das2.stream.StreamException; +import org.das2.system.DasLogger; +import org.das2.util.DasExceptionHandler; +import org.das2.util.monitor.ProgressMonitor; +import java.io.InterruptedIOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + * + * @author Jeremy + */ +public class XAxisDataLoader extends DataLoader implements DataSetUpdateListener { + + DasAxis xaxis; + DataSetDescriptor dsd; + ProgressMonitor progressMonitor; + Logger logger= DasLogger.getLogger( DasLogger.GRAPHICS_LOG, "XAxisDataLoader" ); + + Request currentRequest; + List unsolicitedRequests; + + Object lockObject= new Object(); + + /** Creates a new instance of DataLoader */ + public XAxisDataLoader( Renderer r, DataSetDescriptor dsd ) { + super(r); + this.dsd= dsd; + this.logger= logger; + if ( dsd!=null ) dsd.addDataSetUpdateListener( this ); + unsolicitedRequests= new ArrayList(); + } + + public void update() { + if ( isActive() ) { + logger.finer("enter XAxisDataLoader.update"); + DasPlot p= getRenderer().getParent(); + if ( p==null ) { + logger.fine("plot is null, no need to load"); + } else { + DasAxis xAxis = p.getXAxis(); + DasAxis yAxis = p.getYAxis(); + if ( xAxis.valueIsAdjusting()==false && yAxis.valueIsAdjusting()==false ) { + loadDataSet( xAxis, yAxis ); + } + } + } else { + logger.finer("enter XAxisDataLoader.update, ignored not active"); + } + } + + /* requests a reload of data, indicating its current data set in case it's + * suitable. + */ + private void loadDataSet( DasAxis xAxis, DasAxis yAxis ) { + + logger.fine( "render requests dataset for x:"+xAxis.getMemento() + " y:"+yAxis.getMemento()); + + if ( xaxis==null ) this.xaxis= xAxis; + + if ( xaxis.getColumn()==DasColumn.NULL ) { + logger.fine("column not set yet"); + return; + } + + if ( dsd==null ) { + logger.fine("dsd is null, nothing to do"); + return; + } + + synchronized (lockObject) { + if ( currentRequest!=null ) { + synchronized (currentRequest) { + if ( ! xAxis.getMemento().equals( currentRequest.xmem ) ) { + logger.fine( "cancel old request: "+currentRequest ); + ProgressMonitor monitor= currentRequest.monitor; + currentRequest= null; + monitor.cancel(); + } else { + logger.fine( "ignore repeat request" ); + return; // ignore the repeated request + } + } + } + + Datum resolution; + Datum dataRange1 = xAxis.getDataMaximum().subtract(xAxis.getDataMinimum()); + + double deviceRange = Math.floor(xAxis.getColumn().getDMaximum() + 0.5) - Math.floor(xAxis.getColumn().getDMinimum() + 0.5); + if ( isFullResolution() ) { + resolution = null; + } else { + resolution = dataRange1.divide(deviceRange); + } + + if ( deviceRange==0.0 ) { + // this condition occurs sometimes at startup, it's not known why + return; + } + + DasPlot parent= renderer.getParent(); + + DatumRange loadRange= xAxis.getDatumRange(); + + CacheTag cacheTag= new CacheTag( loadRange, resolution ); + if ( dsd.getDataSetCache().haveStored(dsd, cacheTag) ) { + renderer.setDataSet( dsd.getDataSetCache().retrieve( dsd, cacheTag ) ); + currentRequest= null; + + } else { + + progressMonitor = getMonitor( "dsd.requestDataSet "+dsd+":"+loadRange+" @ "+ + ( resolution==null ? "intrinsic" : ""+DatumUtil.asOrderOneUnits(resolution) ) ); + + parent.repaint( 0, 0, parent.getWidth(), parent.getHeight() ); + + //if ( renderer.isOverloading() ) loadRange= loadRange.rescale(-1,2); + logger.fine("request data from dsd: "+loadRange+" @ "+resolution); + + currentRequest= new Request( progressMonitor, xAxis.getMemento(), yAxis.getMemento() ); + + dsd.requestDataSet( loadRange.min(), loadRange.max(), resolution, progressMonitor, parent.getCanvas() ); + // the request will come back with a DataSetUpdated event + } + } + } + + /* + * If an exception is handled by the Renderer putting the exception in place of the data, + * then return true here. If the exception is more exceptional and we really need to get + * user's attention, return false. + */ + private boolean rendererHandlesException( Exception e ) { + boolean result= + e instanceof InterruptedIOException || + e instanceof NoDataInIntervalException || + e instanceof StreamException || + e instanceof CancelledOperationException ; + if ( result==false ) { + result= e.getCause() instanceof InterruptedIOException; + } + return result; + } + + public void dataSetUpdated( DataSetUpdateEvent e ) { + + synchronized ( lockObject ) { + if ( renderer.getDataLoader()!=this ) return; // see bug 233 + + logger.fine("got dataset update:"+e); + // TODO make sure Exception is cleared--what if data set is non-null but Exception is as well? + if ( e.getException()!=null && e.getDataSet()!=null ) { + throw new IllegalStateException("both exception and data set"); + } else if (e.getException() != null) { + logger.fine("got dataset update exception: "+e.getException()); + Exception exception = e.getException(); + if ( !rendererHandlesException(exception) ) { + DasExceptionHandler.handle(exception); + } + + ProgressMonitor mon= e.getMonitor(); + if ( currentRequest!=null ) { + if ( mon==null || mon==currentRequest.monitor ) { + renderer.setException( exception ); + renderer.setDataSet(null); + logger.fine("current request completed w/exception: " + currentRequest ); + currentRequest=null; + } else { + logger.fine("got exception but not for currentRequest " ); + } + } else { + logger.fine("got exception but currentRequest " ); + } + + + if ( !rendererHandlesException(exception) ) { + DasExceptionHandler.handle(exception); + } + + } else if ( e.getDataSet()==null ) { + // this indicates that the DataSetDescriptor has changed, and that the + // renderer needs to reread the data. Cause this by invalidating the + // component. + logger.fine("got dataset update notification (no dataset)."); + loadDataSet( renderer.getParent().getXAxis(), renderer.getParent().getYAxis() ); + return; + } else { + if ( currentRequest==null ) { + logger.fine( "ignore update w/dataset, currentRequest=null" ); + // note this is hiding a bug. Why did the dataset continue to load after we + // cancelled it? --jbf + } else { + DataSet ds= e.getDataSet(); + ProgressMonitor mon= e.getMonitor(); + if ( mon==null || currentRequest.monitor==mon ) { + logger.fine("got dataset update w/dataset: "+ds); + if ( ds!=null ) { + if ( ds.getXLength()>0 ) { + logger.fine(" ds range: "+DataSetUtil.xRange(ds) ); + } else { + logger.fine(" ds range: (empty)" ); + } + } + renderer.setDataSet( ds ); + + logger.fine("current request completed w/dataset: " + currentRequest.xmem ); + currentRequest=null; + } else { + logger.fine("got dataset update w/dataset but not my monitor: "+ds); + } + } + } + } + } + + + public void setDataSetDescriptor( DataSetDescriptor dsd ) { + logger.fine("set dsd: "+dsd); + if ( this.dsd!=null ) this.dsd.removeDataSetUpdateListener(this); + this.dsd = dsd; + if ( dsd!=null ) dsd.addDataSetUpdateListener(this); + update(); + } + + public DataSetDescriptor getDataSetDescriptor() { + return this.dsd; + } + + public void setReloadDataSet(boolean reloadDataSet) { + super.setReloadDataSet(reloadDataSet); + this.dsd.reset(); + } + + //TODO: this shadows the same property of the super class. This should be cleaned up. + private boolean fullResolution = false; + public boolean isFullResolution() { + return fullResolution; + } + + public void setFullResolution(boolean b) { + if (fullResolution == b) return; + fullResolution = b; + } + + public Request getCurrentRequest() { + return this.currentRequest; + } + + public Request[] getUnsolicitedRequests() { + return (Request[])this.unsolicitedRequests.toArray( new Request[unsolicitedRequests.size()] ); + } + + public Request getUnsolicitedRequests( int i ) { + return (Request)unsolicitedRequests.get(i); + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/ZDeformRenderer.java b/dasCore/src/main/java/org/das2/graph/ZDeformRenderer.java new file mode 100644 index 000000000..1e680df32 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/ZDeformRenderer.java @@ -0,0 +1,73 @@ +/* + * ZDeformRenderer.java + * + * Created on November 14, 2003, 8:18 PM + */ + +package org.das2.graph; + +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.TableUtil; +import org.das2.datum.Datum; +import org.das2.util.monitor.ProgressMonitor; +import java.awt.*; +import java.awt.geom.*; + +/** + * + * @author Owner + */ +public class ZDeformRenderer extends Renderer { + + int dx= 20; + int dy= 0; // deform direction + + /** Creates a new instance of ZDeformRenderer */ + public ZDeformRenderer( DataSetDescriptor dsd ) { + super(dsd); + } + + protected void installRenderer() { + } + + public void render(java.awt.Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + Graphics2D g= (Graphics2D) g1; + TableDataSet tds= (TableDataSet) getDataSet(); + double zmax= TableUtil.tableMax(tds,tds.getZUnits()); + for ( int itable=0; itable(-1000) && ix<1000 ) { + double z0= tds.getDouble(i,0,tds.getZUnits()); + Line2D.Double line= new Line2D.Double(); + for ( int j=1; j-1e30 && z0>-1e30 ) { + line.setLine(ix+z0/zmax*dx, iys[j-1]+z0/zmax*dy, ix+z1/zmax*dx, iys[j]+z0/zmax*dy ); + g.draw(line); + } + z0= z1; + } + } + } + } + } + + protected void uninstallRenderer() { + } + + protected org.w3c.dom.Element getDOMElement(org.w3c.dom.Document document) { + return null; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/dnd/TransferableCanvas.java b/dasCore/src/main/java/org/das2/graph/dnd/TransferableCanvas.java new file mode 100644 index 000000000..937c3fec5 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/dnd/TransferableCanvas.java @@ -0,0 +1,134 @@ +/* File: TransferableCanvas.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph.dnd; + +import org.das2.graph.DasCanvas; +//import org.apache.xml.serialize.Method; +//import org.apache.xml.serialize.OutputFormat; +//import org.apache.xml.serialize.XMLSerializer; +import org.w3c.dom.Document; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.io.StringWriter; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; + +/** + * + * @author eew + */ +public class TransferableCanvas implements Transferable { + + public final static DataFlavor CANVAS_FLAVOR; + static { + try { + String typeStr = DataFlavor.javaJVMLocalObjectMimeType + + ";class=org.das2.graph.DasCanvas"; + CANVAS_FLAVOR = new DataFlavor(typeStr,null,DasCanvas.class.getClassLoader()); + } + catch (ClassNotFoundException cnfe) { + throw new RuntimeException(cnfe); + } + } + + private DasCanvas canvas; + + /** Creates a new instance of TransferableCanvas */ + public TransferableCanvas(DasCanvas canvas) { + this.canvas = canvas; + } + + /** Returns an object which represents the data to be transferred. The class + * of the object returned is defined by the representation class of the flavor. + * + * @param flavor the requested flavor for the data + * @see DataFlavor#getRepresentationClass + * @exception IOException if the data is no longer available + * in the requested flavor. + * @exception UnsupportedFlavorException if the requested data flavor is + * not supported. + */ + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + if (flavor.equals(CANVAS_FLAVOR)) { + return canvas; + } + else if (flavor.equals(DataFlavor.stringFlavor)) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.newDocument(); + document.appendChild(canvas.getDOMElement(document)); + StringWriter writer = new StringWriter(); + + DOMImplementationLS ls = (DOMImplementationLS) + document.getImplementation().getFeature("LS", "3.0"); + LSOutput output = ls.createLSOutput(); + output.setEncoding("UTF-8"); + output.setCharacterStream(writer); + LSSerializer serializer = ls.createLSSerializer(); + serializer.write(document, output); + + /* + OutputFormat format = new OutputFormat(Method.XML, "UTF-8", true); + format.setOmitXMLDeclaration(true); + format.setOmitDocumentType(true); + XMLSerializer serializer = new XMLSerializer(writer, format); + serializer.serialize(document); + */ + + return writer.toString(); + } + catch (ParserConfigurationException pce) { + throw new RuntimeException(pce); + } + } + throw new UnsupportedFlavorException(flavor); } + + /** Returns an array of DataFlavor objects indicating the flavors the data + * can be provided in. The array should be ordered according to preference + * for providing the data (from most richly descriptive to least descriptive). + * @return an array of data flavors in which this data can be transferred + */ + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] {CANVAS_FLAVOR, DataFlavor.stringFlavor}; + } + + /** Returns whether or not the specified data flavor is supported for + * this object. + * @param flavor the requested flavor for the data + * @return boolean indicating whether or not the data flavor is supported + */ + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(CANVAS_FLAVOR) || flavor.equals(DataFlavor.stringFlavor); + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/dnd/TransferableCanvasComponent.java b/dasCore/src/main/java/org/das2/graph/dnd/TransferableCanvasComponent.java new file mode 100644 index 000000000..9a4b724a9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/dnd/TransferableCanvasComponent.java @@ -0,0 +1,119 @@ +/* File: TransferableCanvasComponent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph.dnd; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasColorBar; +import org.das2.graph.DasPlot; + +/** + * + * @author eew + */ +public class TransferableCanvasComponent implements Transferable { + + public static final DataFlavor CANVAS_COMPONENT_FLAVOR = localJVMFlavor(org.das2.graph.DasCanvasComponent.class); + public static final DataFlavor AXIS_FLAVOR = localJVMFlavor(org.das2.graph.DasAxis.class); + public static final DataFlavor PLOT_FLAVOR = localJVMFlavor(org.das2.graph.DasPlot.class); + public static final DataFlavor COLORBAR_FLAVOR = localJVMFlavor(org.das2.graph.DasColorBar.class); + + private List flavorList; + private DasCanvasComponent component; + + private static DataFlavor localJVMFlavor(Class cl) { + try { + String className = cl.getName(); + String x = DataFlavor.javaJVMLocalObjectMimeType; + return new DataFlavor(x + ";class=" + className,null,cl.getClassLoader()); + } + catch (ClassNotFoundException cnfe) { + throw new RuntimeException(cnfe); + } + } + + public TransferableCanvasComponent(DasAxis axis) { + flavorList = Arrays.asList(new DataFlavor[]{AXIS_FLAVOR, CANVAS_COMPONENT_FLAVOR, DataFlavor.stringFlavor}); + component = axis; + } + + public TransferableCanvasComponent(DasPlot plot) { + flavorList = Arrays.asList(new DataFlavor[]{PLOT_FLAVOR, CANVAS_COMPONENT_FLAVOR, DataFlavor.stringFlavor}); + component = plot; + } + + public TransferableCanvasComponent(DasColorBar cb) { + flavorList = Arrays.asList(new DataFlavor[]{PLOT_FLAVOR, CANVAS_COMPONENT_FLAVOR, DataFlavor.stringFlavor}); + component = cb; + } + + /** Returns an object which represents the data to be transferred. The class + * of the object returned is defined by the representation class of the flavor. + * + * @param flavor the requested flavor for the data + * @see DataFlavor#getRepresentationClass + * @exception IOException if the data is no longer available + * in the requested flavor. + * @exception UnsupportedFlavorException if the requested data flavor is + * not supported. + */ + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + if (isDataFlavorSupported(flavor)) { + if (flavor.equals(DataFlavor.stringFlavor)) { + throw new UnsupportedFlavorException(flavor); + } + else { + return component; + } + } + else { + throw new UnsupportedFlavorException(flavor); + } + } + + /** Returns an array of DataFlavor objects indicating the flavors the data + * can be provided in. The array should be ordered according to preference + * for providing the data (from most richly descriptive to least descriptive). + * @return an array of data flavors in which this data can be transferred + */ + public DataFlavor[] getTransferDataFlavors() { + return (DataFlavor[])flavorList.toArray(new DataFlavor[flavorList.size()]); + } + + /** Returns whether or not the specified data flavor is supported for + * this object. + * @param flavor the requested flavor for the data + * @return boolean indicating whether or not the data flavor is supported + */ + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavorList.contains(flavor); + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/dnd/TransferableRenderer.java b/dasCore/src/main/java/org/das2/graph/dnd/TransferableRenderer.java new file mode 100644 index 000000000..64f0eb12f --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/dnd/TransferableRenderer.java @@ -0,0 +1,97 @@ +/* File: TransferableRenderer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph.dnd; + +import org.das2.graph.Renderer; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; + +/** + * + * @author eew + */ +public class TransferableRenderer implements Transferable { + + public static final DataFlavor RENDERER_FLAVOR = localJVMFlavor(org.das2.graph.Renderer.class); + + private static DataFlavor localJVMFlavor(Class cl) { + try { + String className = cl.getName(); + String x = DataFlavor.javaJVMLocalObjectMimeType; + return new DataFlavor(x + ";class=" + className,null,cl.getClassLoader()); + } + catch (ClassNotFoundException cnfe) { + throw new RuntimeException(cnfe); + } + } + + /** The Renderer that this tranferable encapsulates. */ + private Renderer renderer; + + /** Creates a new instance of TransferableRenderer */ + public TransferableRenderer(Renderer renderer) { + this.renderer = renderer; + } + + /** Returns an object which represents the data to be transferred. The class + * of the object returned is defined by the representation class of the flavor. + * + * @param flavor the requested flavor for the data + * @see DataFlavor#getRepresentationClass + * @exception IOException if the data is no longer available + * in the requested flavor. + * @exception UnsupportedFlavorException if the requested data flavor is + * not supported. + */ + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + if (isDataFlavorSupported(flavor)) { + if (flavor.equals(RENDERER_FLAVOR)) { + return renderer; + } + } + throw new UnsupportedFlavorException(flavor); + } + + /** Returns an array of DataFlavor objects indicating the flavors the data + * can be provided in. The array should be ordered according to preference + * for providing the data (from most richly descriptive to least descriptive). + * @return an array of data flavors in which this data can be transferred + */ + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { RENDERER_FLAVOR }; + } + + /** Returns whether or not the specified data flavor is supported for + * this object. + * @param flavor the requested flavor for the data + * @return boolean indicating whether or not the data flavor is supported + */ + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(RENDERER_FLAVOR); + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/dnd/package.html b/dasCore/src/main/java/org/das2/graph/dnd/package.html new file mode 100644 index 000000000..33418b1d9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/dnd/package.html @@ -0,0 +1,4 @@ + + Contains Transferable classes for implementing the drop and drop in the + IDE. This is under review and may be removed in a future release. + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/graph/event/DasAxisEvent.java b/dasCore/src/main/java/org/das2/graph/event/DasAxisEvent.java new file mode 100644 index 000000000..f1bd15325 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/event/DasAxisEvent.java @@ -0,0 +1,56 @@ +/* File: DasAxisEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph.event; + +import java.util.EventObject; + +/** + * + * @author eew + */ +public class DasAxisEvent extends EventObject +{ + + private double minimum; + private double maximum; + + /** Creates a new instance of DasAxisEvent */ + public DasAxisEvent(Object source, double minimum, double maximum) + { + super(source); + this.minimum = minimum; + this.maximum = maximum; + } + + public double getDataMinimum() + { + return minimum; + } + + public double getDataMaximum() + { + return maximum; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/event/DasAxisListener.java b/dasCore/src/main/java/org/das2/graph/event/DasAxisListener.java new file mode 100644 index 000000000..28b7ebf5f --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/event/DasAxisListener.java @@ -0,0 +1,35 @@ +/* File: DasAxisListener.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph.event; + +import java.util.EventListener; + +/** + * + * @author eew + */ +public interface DasAxisListener extends EventListener +{ + void dataRangeChanged(DasAxisEvent e); +} diff --git a/dasCore/src/main/java/org/das2/graph/event/DasDevicePositionEvent.java b/dasCore/src/main/java/org/das2/graph/event/DasDevicePositionEvent.java new file mode 100644 index 000000000..b2ab5cb96 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/event/DasDevicePositionEvent.java @@ -0,0 +1,53 @@ +/* File: DasDevicePositionEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph.event; + +import java.util.EventObject; + +/** + * + * @author eew + */ +public class DasDevicePositionEvent extends EventObject +{ + + private double min, max; + + /** Creates a new instance of DasDevicePositionEvent */ + public DasDevicePositionEvent(Object source, double min, double max) + { + super(source); + } + + public double getMinimum() + { + return min; + } + + public double getMaximum() + { + return max; + } + +} diff --git a/dasCore/src/main/java/org/das2/graph/event/DasDevicePositionListener.java b/dasCore/src/main/java/org/das2/graph/event/DasDevicePositionListener.java new file mode 100644 index 000000000..b5e7c7593 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/event/DasDevicePositionListener.java @@ -0,0 +1,34 @@ +/* File: DasDevicePositionListener.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph.event; + +/** + * + * @author eew + */ +public interface DasDevicePositionListener extends java.util.EventListener +{ + void devicePositionChanged(DasDevicePositionEvent e); + void deviceRangeChanged(DasDevicePositionEvent e); +} diff --git a/dasCore/src/main/java/org/das2/graph/event/DasUpdateEvent.java b/dasCore/src/main/java/org/das2/graph/event/DasUpdateEvent.java new file mode 100644 index 000000000..f4a7a4acd --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/event/DasUpdateEvent.java @@ -0,0 +1,37 @@ +/* File: DasUpdateEvent.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph.event; + +/** + * + * @author eew + */ +public class DasUpdateEvent extends java.util.EventObject +{ + /** Creates a new instance of DasUpdateEvent */ + public DasUpdateEvent(Object source) + { + super(source); + } +} diff --git a/dasCore/src/main/java/org/das2/graph/event/DasUpdateListener.java b/dasCore/src/main/java/org/das2/graph/event/DasUpdateListener.java new file mode 100644 index 000000000..2815ece08 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/event/DasUpdateListener.java @@ -0,0 +1,33 @@ +/* File: DasUpdateListener.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph.event; + +/** + * + * @author eew + */ +public interface DasUpdateListener extends java.util.EventListener +{ + void update(DasUpdateEvent e); +} diff --git a/dasCore/src/main/java/org/das2/graph/event/package.html b/dasCore/src/main/java/org/das2/graph/event/package.html new file mode 100644 index 000000000..b14df9541 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/event/package.html @@ -0,0 +1,4 @@ + +Contains classes for implementing internal update event model of +CanvasComponents. + diff --git a/dasCore/src/main/java/org/das2/graph/package.html b/dasCore/src/main/java/org/das2/graph/package.html new file mode 100644 index 000000000..82f92c257 --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/package.html @@ -0,0 +1,18 @@ + +

    Mostly contains DasCanvasComponents, which are components that live within +the DasCanvas, and Renderers which draw on DasPlots. DasCanvas, a subclass of +JComponent, is the container for drawing graphics in das2. DasCanvasComponents +are added to the DasCanvas to set up the graphic. DasCanvasComponents include +objects like DasAxis, DasColorBar, and DasPlot, but are simply objects that +occupy a region of the canvas specified with a DasRow (vertical position) and +DasColumn (horizontal position). Note too the CanvasComponents can be +heirarchitical, for example a DasPlot contains two DasAxes. Each CanvasComponent +has a DasMouseInputAdapter that handles mouse and keyboard events for the +component. +

    +

    To add new 2-D plotting capabilities, Renderers are introduced to paint +the space defined by an x and y axis. Examples of Renderers are SymbolLineRenderer +(line plots) and SpectrogramRenderer (spectrograms). +

    + + diff --git a/dasCore/src/main/java/org/das2/graph/scratchPad.txt b/dasCore/src/main/java/org/das2/graph/scratchPad.txt new file mode 100644 index 000000000..4e719763e --- /dev/null +++ b/dasCore/src/main/java/org/das2/graph/scratchPad.txt @@ -0,0 +1,6 @@ +2006-10-4 jbf + For purposes of consistency, we define an "em" as being the contextual font's size, as in + Graphics2D g; + int em = g.getFont().getSize(); + + diff --git a/dasCore/src/main/java/org/das2/math/Contour.java b/dasCore/src/main/java/org/das2/math/Contour.java new file mode 100644 index 000000000..6c93289c9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/Contour.java @@ -0,0 +1,519 @@ +/* + * Contour.java + * See http://www.mactech.com/articles/mactech/Vol.13/13.09/ContourPlottinginJava/index.html + * Created on May 20, 2004, 7:49 PM + */ + +package org.das2.math; + +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import java.awt.*; +import java.io.*; + +/** + * Contouring based on code published in Javatech, Volume 13 Issue 9, "Contour Plotting In Java" + * by David Rand. This code is based on Fortran code implementing 1978 article by W. V. Snyder. + * W. V. Snyder, "Algorithm 531, Contour plotting [J6]," ACM Trans. Math. Softw. 4, 3 (Sept. 1978), 290-294. + * @author Owner + */ +public class Contour { + + public static final String PLANE_X="x"; + public static final String PLANE_Y="y"; + + final static public class ContourPlot { + + TableDataSet zz; + Units zunits; + long idx; /* monotonically increasing X Value */ + + public ContourPlot( TableDataSet tds, DatumVector contourValues ) { + super(); + this.zz= tds; + + xSteps = tds.getXLength(); + ySteps = tds.getYLength(0); + + zunits= tds.getZUnits(); + + ncv= contourValues.getLength(); + cv= new float[ncv]; + for ( int i=0; i0 ) { + double y1= zz.getYTagDouble(0, indx+1, zz.getYUnits() ); + return y0 + fp * ( y1 - y0 ); + } else { + return y0; + } + } + + final double getXValue( double findex ) { + findex--; /* one is first index */ + int indx= (int)findex; + if ( indx==zz.getXLength() ) indx--; + double fp= findex - indx; + double x0= zz.getXTagDouble( indx, zz.getXUnits() ); + if ( fp>0 ) { + double x1= zz.getXTagDouble( indx+1, zz.getXUnits() ); + return x0 + fp * ( x1 - x0 ); + } else { + return x0; + } + } + + //------------------------------------------------------- + // "DrawKernel" is the guts of drawing and is called + // directly or indirectly by "ContourPlotKernel" in order + // to draw a segment of a contour or to set the pen + // position "prevXY". Its action depends on "iflag": + // + // iflag == 1 means Continue a contour + // iflag == 2 means Start a contour at a boundary + // iflag == 3 means Start a contour not at a boundary + // iflag == 4 means Finish contour at a boundary + // iflag == 5 means Finish closed contour not at boundary + // iflag == 6 means Set pen position + // + // If the constant "SHOW_NUMBERS" is true then when + // completing a contour ("iflag" == 4 or 5) the contour + // index is drawn adjacent to where the contour ends. + //------------------------------------------------------- + void DrawKernel(VectorDataSetBuilder dsbuilder) { + int prevU,prevV,u,v; + + if ((iflag == 1) || (iflag == 4) || (iflag == 5)) { + + } else { + idx++ ; + } + + + dsbuilder.insertY(idx,cval); + + dsbuilder.insertY(idx,getXValue(xy[0]),PLANE_X); + dsbuilder.insertY(idx,getYValue(xy[1]),PLANE_Y); + + idx++; + + } + + //------------------------------------------------------- + // "DetectBoundary" + //------------------------------------------------------- + final void DetectBoundary() { + ix = 1; + if (ij[1-elle] != 1) { + ii = ij[0] - i1[1-elle]; + jj = ij[1] - i1[elle]; + if ( !zunits.isFill(zz.getDouble(ii-1,jj-1,zunits)) ) { + ii = ij[0] + i2[elle]; + jj = ij[1] + i2[1-elle]; + if ( !zunits.isFill(zz.getDouble(ii-1,jj-1,zunits)) ) ix = 0; + } + if (ij[1-elle] >= l1[1-elle]) { + ix = ix + 2; + return; + } + } + ii = ij[0] + i1[1-elle]; + jj = ij[1] + i1[elle]; + if ( zunits.isFill( zz.getDouble(ii-1,jj-1,zunits) ) ) { + ix = ix + 2; + return; + } + if ( zunits.isFill( zz.getDouble(ij[0],ij[1],zunits) ) ) ix = ix + 2; + } + + //------------------------------------------------------- + // "Routine_label_020" corresponds to a block of code + // starting at label 20 in Synder's subroutine "GCONTR". + //------------------------------------------------------- + boolean Routine_label_020() { + l2[0] = ij[0]; + l2[1] = ij[1]; + l2[2] = -ij[0]; + l2[3] = -ij[1]; + idir = 0; + nxidir = 1; + k = 1; + ij[0] = Math.abs(ij[0]); + ij[1] = Math.abs(ij[1]); + if ( zunits.isFill( zz.getDouble(ij[0]-1,ij[1]-1,zunits) ) ) { + elle = idir % 2; + ij[elle] = sign(ij[elle],l1[k-1]); + return true; + } + elle = 0; + return false; + } + + //------------------------------------------------------- + // "Routine_label_050" corresponds to a block of code + // starting at label 50 in Synder's subroutine "GCONTR". + //------------------------------------------------------- + boolean Routine_label_050() { + while (true) { + if (ij[elle] >= l1[elle]) { + if (++elle <= 1) continue; + elle = idir % 2; + ij[elle] = sign(ij[elle],l1[k-1]); + if (Routine_label_150()) return true; + continue; + } + ii = ij[0] + i1[elle]; + jj = ij[1] + i1[1-elle]; + if ( zunits.isFill( zz.getDouble(ii-1,jj-1,zunits) ) ) { + if (++elle <= 1) continue; + elle = idir % 2; + ij[elle] = sign(ij[elle],l1[k-1]); + if (Routine_label_150()) return true; + continue; + } + break; + } + jump = false; + return false; + } + + //------------------------------------------------------- + // "Routine_label_150" corresponds to a block of code + // starting at label 150 in Synder's subroutine "GCONTR". + //------------------------------------------------------- + boolean Routine_label_150() { + while (true) { + //------------------------------------------------ + // Lines from z[ij[0]-1][ij[1]-1] + // to z[ij[0] ][ij[1]-1] + // and z[ij[0]-1][ij[1]] + // are not satisfactory. Continue the spiral. + //------------------------------------------------ + if (ij[elle] < l1[k-1]) { + ij[elle]++; + if (ij[elle] > l2[k-1]) { + l2[k-1] = ij[elle]; + idir = nxidir; + nxidir = idir + 1; + k = nxidir; + if (nxidir > 3) nxidir = 0; + } + ij[0] = Math.abs(ij[0]); + ij[1] = Math.abs(ij[1]); + if ( zunits.isFill( zz.getDouble(ij[0]-1,ij[1]-1,zunits) ) ) { + elle = idir % 2; + ij[elle] = sign(ij[elle],l1[k-1]); + continue; + } + elle = 0; + return false; + } + if (idir != nxidir) { + nxidir++; + ij[elle] = l1[k-1]; + k = nxidir; + elle = 1 - elle; + ij[elle] = l2[k-1]; + if (nxidir > 3) nxidir = 0; + continue; + } + + if (ibkey != 0) return true; + ibkey = 1; + ij[0] = icur; + ij[1] = jcur; + if (Routine_label_020()) continue; + return false; + } + } + + //------------------------------------------------------- + // "Routine_label_200" corresponds to a block of code + // starting at label 200 in Synder's subroutine "GCONTR". + // It has return values 0, 1 or 2. + //------------------------------------------------------- + short Routine_label_200(VectorDataSetBuilder dsbuilder, boolean workSpace[]) { + while (true) { + xy[elle] = 1.0*ij[elle] + intersect[iedge-1]; + xy[1-elle] = 1.0*ij[1-elle]; + workSpace[2*(xSteps*(ySteps*cntrIndex+ij[1]-1) + +ij[0]-1) + elle] = true; + DrawKernel(dsbuilder); + if (iflag >= 4) { + icur = ij[0]; + jcur = ij[1]; + return 1; + } + ContinueContour(); + if (!workSpace[2*(xSteps*(ySteps*cntrIndex + +ij[1]-1)+ij[0]-1)+elle]) return 2; + iflag = 5; // 5. Finish a closed contour + iedge = ks + 2; + if (iedge > 4) iedge = iedge - 4; + intersect[iedge-1] = intersect[ks-1]; + } + } + + //------------------------------------------------------- + // "CrossedByContour" is true iff the current segment in + // the grid is crossed by one of the contour values and + // has not already been processed for that value. + //------------------------------------------------------- + boolean CrossedByContour(boolean workSpace[]) { + ii = ij[0] + i1[elle]; + jj = ij[1] + i1[1-elle]; + z1 = zz.getDouble(ij[0]-1,ij[1]-1,zunits); + z2 = zz.getDouble(ii-1,jj-1,zunits); + for (cntrIndex = 0; cntrIndex < ncv; cntrIndex++) { + int i = 2*(xSteps*(ySteps*cntrIndex+ij[1]-1) + ij[0]-1) + elle; + + if (!workSpace[i]) { + float x = cv[cntrIndex]; + if ((x>Math.min(z1,z2)) && (x<=Math.max(z1,z2))) { + workSpace[i] = true; + return true; + } + } + } + return false; + } + + //------------------------------------------------------- + // "ContinueContour" continues tracing a contour. Edges + // are numbered clockwise, the bottom edge being # 1. + //------------------------------------------------------- + void ContinueContour() { + short local_k; + + ni = 1; + if (iedge >= 3) { + ij[0] = ij[0] - i3[iedge-1]; + ij[1] = ij[1] - i3[iedge+1]; + } + for (local_k = 1; local_k < 5; local_k++) + if (local_k != iedge) { + ii = ij[0] + i3[local_k-1]; + jj = ij[1] + i3[local_k]; + z1 = zz.getDouble(ii-1,jj-1,zunits); + ii = ij[0] + i3[local_k]; + jj = ij[1] + i3[local_k+1]; + z2 = zz.getDouble(ii-1,jj-1,zunits); + if ((cval > Math.min(z1,z2) && (cval <= Math.max(z1,z2)))) { + if ((local_k == 1) || (local_k == 4)) { + double zz = z2; + + z2 = z1; + z1 = zz; + } + intersect[local_k-1] = (cval - z1)/(z2 - z1); + ni++; + ks = local_k; + } + } + if (ni != 2) { + //------------------------------------------------- + // The contour crosses all 4 edges of cell being + // examined. Choose lines top-to-left & bottom-to- + // right if interpolation point on top edge is + // less than interpolation point on bottom edge. + // Otherwise, choose the other pair. This method + // produces the same results if axes are reversed. + // The contour may close at any edge, but must not + // cross itself inside any cell. + //------------------------------------------------- + ks = 5 - iedge; + if (intersect[2] >= intersect[0]) { + ks = 3 - iedge; + if (ks <= 0) ks = ks + 4; + } + } + //---------------------------------------------------- + // Determine whether the contour will close or run + // into a boundary at edge ks of the current cell. + //---------------------------------------------------- + elle = ks - 1; + iflag = 1; // 1. Continue a contour + jump = true; + if (ks >= 3) { + ij[0] = ij[0] + i3[ks-1]; + ij[1] = ij[1] + i3[ks+1]; + elle = ks - 3; + } + } + + //------------------------------------------------------- + // "ContourPlotKernel" is the guts of this class and + // corresponds to Synder's subroutine "GCONTR". + //------------------------------------------------------- + void ContourPlotKernel(VectorDataSetBuilder dsbuilder, boolean workSpace[]) { + short val_label_200; + + l1[0] = xSteps; l1[1] = ySteps; + l1[2] = -1;l1[3] = -1; + i1[0] = 1; i1[1] = 0; + i2[0] = 1; i2[1] = -1; + i3[0] = 1; i3[1] = 0; i3[2] = 0; + i3[3] = 1; i3[4] = 1; i3[5] = 0; + prevXY[0] = 0.0; prevXY[1] = 0.0; + xy[0] = 1.0; xy[1] = 1.0; + cntrIndex = 0; + prevIndex = -1; + iflag = 6; + DrawKernel(dsbuilder); + icur = Math.max(1, Math.min((int)Math.floor(xy[0]), xSteps)); + jcur = Math.max(1, Math.min((int)Math.floor(xy[1]), ySteps)); + ibkey = 0; + ij[0] = icur; + ij[1] = jcur; + if (Routine_label_020() && + Routine_label_150()) return; + if (Routine_label_050()) return; + while (true) { + DetectBoundary(); + if (jump) { + if (ix != 0) iflag = 4; // Finish contour at boundary + iedge = ks + 2; + if (iedge > 4) iedge = iedge - 4; + intersect[iedge-1] = intersect[ks-1]; + val_label_200 = Routine_label_200(dsbuilder,workSpace); + if (val_label_200 == 1) { + if (Routine_label_020() && Routine_label_150()) return; + if (Routine_label_050()) return; + continue; + } + if (val_label_200 == 2) continue; + return; + } + if ((ix != 3) && (ix+ibkey != 0) && CrossedByContour(workSpace)) { + // + // An acceptable line segment has been found. + // Follow contour until it hits a + // boundary or closes. + // + iedge = elle + 1; + cval = cv[cntrIndex]; + if (ix != 1) iedge = iedge + 2; + iflag = 2 + ibkey; + intersect[iedge-1] = (cval - z1) / (z2 - z1); + val_label_200 = Routine_label_200(dsbuilder,workSpace); + if (val_label_200 == 1) { + if (Routine_label_020() && Routine_label_150()) return; + if (Routine_label_050()) return; + continue; + } + if (val_label_200 == 2) continue; + return; + } + if (++elle > 1) { + elle = idir % 2; + ij[elle] = sign(ij[elle],l1[k-1]); + if (Routine_label_150()) return; + } + if (Routine_label_050()) return; + } + } + } + + /** + * returns a rank 1 dataset, a vector dataset, listing the points + * of the contour paths. The data set will have three planes: + * X, Y and the default plane is the Z. + */ + public static VectorDataSet contour( TableDataSet tds, DatumVector levels ) { + if ( tds.tableCount()>1 ) { + throw new IllegalArgumentException("TableDataSet can only have one table"); + } + Contour.ContourPlot cp= new ContourPlot( tds, levels ); + return cp.performContour(); + } + + public static VectorDataSet contour( TableDataSet tds, Datum level ) { + if ( tds.tableCount()>1 ) { + throw new IllegalArgumentException("TableDataSet can only have one table"); + } + Units units= level.getUnits(); + double value= level.doubleValue(units); + Contour.ContourPlot cp= new ContourPlot( tds, DatumVector.newDatumVector( new double[] { value }, units ) ); + return cp.performContour(); + } + + +} diff --git a/dasCore/src/main/java/org/das2/math/Interpolate.java b/dasCore/src/main/java/org/das2/math/Interpolate.java new file mode 100644 index 000000000..a6adf6ab9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/Interpolate.java @@ -0,0 +1,169 @@ +/* + * Interpolate.java + * + * Created on March 15, 2005, 9:00 AM + */ + +package org.das2.math; + +/** + * + * @author Jeremy + */ +public class Interpolate { + + public interface DDoubleArray { + double get( int i, int j ); + void put( int i, int j, double value ); + int rows(); + int columns(); + } + + public interface FDoubleArray { + float get( int i, int j ); + void put( int i, int j, float value ); + int rows(); + int columns(); + } + + public static DDoubleArray newDDoubleArray( final double[][] array ) { + return new DDoubleArray() { + public double get( int i, int j ) { return array[i][j]; } + public void put( int i, int j, double value ) { array[i][j]= value; }; + public int rows() { return array.length; } + public int columns() { return array[0].length; } + }; + } + + public static DDoubleArray newDDoubleArray( int columns, int rows ) { + return new DDoubleArrayImpl( columns, rows ); + } + + private static final class DDoubleArrayImpl implements DDoubleArray { + // return DDoubleArray backed by single array, storing the double array column major. + double[] back; + int rows; // index by i + int columns; + static final boolean boundsCheck= false; + DDoubleArrayImpl( int columns, int rows ) { + back= new double[ rows * columns ]; + this.columns= columns; + this.rows= rows; + } + public double get( int i, int j ) { + if ( boundsCheck ) { + if ( i>=rows ) throw new ArrayIndexOutOfBoundsException("index i="+i+" out of bounds"); + if ( i<0 ) throw new ArrayIndexOutOfBoundsException("index i="+i+" out of bounds"); + if ( j>=columns ) throw new ArrayIndexOutOfBoundsException("index j="+j+" out of bounds"); + } + return back[ j*rows + i ]; + } + public void put( int i, int j, double value ) { + if ( boundsCheck ) { + if ( i>=rows ) throw new ArrayIndexOutOfBoundsException("index i="+i+" out of bounds"); + if ( i<0 ) throw new ArrayIndexOutOfBoundsException("index i="+i+" out of bounds"); + if ( j>=columns ) throw new ArrayIndexOutOfBoundsException("index j="+j+" out of bounds"); + } + back[ j*rows + i ]= value; + }; + public int rows() { return rows; } + public int columns() { return columns; } + } + + public static DDoubleArray interpolate2( DDoubleArray source, float[] xFindex, float[] yFindex ) { + + DDoubleArray result= newDDoubleArray( xFindex.length, yFindex.length ); + double[] xfp0= new double[xFindex.length]; + double[] xfp1= new double[xFindex.length]; + int[] xip0= new int[xFindex.length]; + int[] xip1= new int[xFindex.length]; + for ( int i=0; i 80. + * + * The value of bound must be adjusted to the maximal value of L. + */ + private int PoissonInver(double L, Random random ) { + + final int bound = 130; // safety bound. Must be > L + 8*sqrt(L). + double r; // uniform random number + double f; // function value + int x; // return value + + if (L != p_L_last) { // set up + p_L_last = L; + p_f0 = Math.exp(-L); + } // f(0) = probability of x=0 + + while (true) { + r = random.nextDouble(); x = 0; f = p_f0; + do { // recursive calculation: f(x) = f(x-1) * L / x + r -= f; + if (r <= 0) return x; + x++; + f *= L; + r *= x;} // instead of f /= x + while (x <= bound); + } + } + + } + + static PoissonRatioUniforms poissonRatioUniforms= new PoissonRatioUniforms(); + + /** + * This subfunction generates a random variate with the poisson + * distribution using the ratio-of-uniforms rejection method (PRUAt). + * + * Execution time does not depend on L, except that it matters whether L + * is within the range where ln(n!) is tabulated. + * + * Reference: E. Stadlober: "The ratio of uniforms approach for generating + * discrete random variates". Journal of Computational and Applied Mathematics, + * vol. 31, no. 1, 1990, pp. 181-189. + */ + static class PoissonRatioUniforms { + final double SHAT1 = 2.943035529371538573; // 8/e + final double SHAT2 = 0.8989161620588987408; // 3-sqrt(12/e) + + double p_L_last = -1.0; // previous L cache tag + double p_a; // hat center + double p_h; // hat width + double p_g; // ln(L) + double p_q; // value at mode + int p_bound; // upper bound + int mode; // mode + + private int PoissonRatioUniforms(double L, Random random ) { + double u; // uniform random + double lf; // ln(f(x)) + double x; // real sample + int k; // integer sample + + if (p_L_last != L) { + p_L_last = L; // Set-up + p_a = L + 0.5; // hat center + mode = (int)L; // mode + p_g = Math.log(L); + p_q = mode * p_g - fac.lnFac(mode); // value at mode + p_h = Math.sqrt(SHAT1 * (L+0.5)) + SHAT2; // hat width + p_bound = (int)(p_a + 6.0 * p_h); + } // safety-bound + + while(true) { + u = random.nextDouble(); + if (u == 0) continue; // avoid division by 0 + x = p_a + p_h * (random.nextDouble() - 0.5) / u; + if (x < 0 || x >= p_bound) continue; // reject if outside valid range + k = (int)(x); + lf = k * p_g - fac.lnFac(k) - p_q; + if (lf >= u * (4.0 - u) - 3.0) break; // quick acceptance + if (u * (u - lf) > 1.0) continue; // quick rejection + if (2.0 * Math.log(u) <= lf) break;} // final acceptance + return(k); + } + } + + /** + * This function generates a random variate with the poisson distribution. + * + * Uses inversion by chop-down method for L < 17, and ratio-of-uniforms + * method for L >= 17. + * + * For L < 1.E-6 numerical inaccuracy is avoided by direct calculation. + * For L > 2E9 too big--throws IllegalArgumentException + */ + public static int poisson( double L,Random random ) { + + + //------------------------------------------------------------------ + // choose method + //------------------------------------------------------------------ + if (L < 17) { + if (L < 1.E-6) { + if (L == 0) return 0; + if (L < 0) throw new IllegalArgumentException("Parameter negative in poisson function"); + + //-------------------------------------------------------------- + // calculate probabilities + //-------------------------------------------------------------- + // For extremely small L we calculate the probabilities of x = 1 + // and x = 2 (ignoring higher x). The reason for using this + // method is to prevent numerical inaccuracies in other methods. + //-------------------------------------------------------------- + return PoissonLow(L,random); + } else { + + //-------------------------------------------------------------- + // inversion method + //-------------------------------------------------------------- + // The computation time for this method grows with L. + // Gives overflow for L > 80 + //-------------------------------------------------------------- + return poissonInver.PoissonInver(L,random); + } + } + + else { + if (L > 2.E9) throw new IllegalArgumentException("Parameter too big in poisson function"); + + //---------------------------------------------------------------- + // ratio-of-uniforms method + //---------------------------------------------------------------- + // The computation time for this method does not depend on L. + // Use where other methods would be slower. + //---------------------------------------------------------------- + return poissonRatioUniforms.PoissonRatioUniforms(L,random); + } + } + + /** + * This subfunction generates a random variate with the poisson + * distribution for extremely low values of L. + * + * The method is a simple calculation of the probabilities of x = 1 + * and x = 2. Higher values are ignored. + * + * The reason for using this method is to avoid the numerical inaccuracies + * in other methods. + */ + private static int PoissonLow( double L, Random random ) { + double d, r; + d = Math.sqrt(L); + if ( random.nextDouble() >= d ) return 0; + r = random.nextDouble() * d; + if (r > L * (1.-L)) return 0; + if (r > 0.5 * L*L * (1.-L)) return 1; + return 2;} + + + +} diff --git a/dasCore/src/main/java/org/das2/math/QuadFitUtil.java b/dasCore/src/main/java/org/das2/math/QuadFitUtil.java new file mode 100644 index 000000000..4a9c0686a --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/QuadFitUtil.java @@ -0,0 +1,107 @@ +/* + * QuadFitUtil.java + * + * Created on March 8, 2005, 5:24 PM + */ + +package org.das2.math; + +import org.das2.math.matrix.ArrayMatrix; +import org.das2.math.matrix.Matrix; +import org.das2.math.matrix.MatrixUtil; +import java.util.Arrays; + +/** + * + * @author eew + */ +public class QuadFitUtil { + + private QuadFitUtil() {} + + public static double[] quadfit(double[] x, double[] y, double[] w) { + return polyfitw(x, y, w, 2); + } + + public static double[] polyfitw(double[] x, double[] y, double[] w, int degree) { + int n = x.length; + int m = degree + 1; + + Matrix a = new ArrayMatrix(m, m); + double[] b = new double[m]; + double[] z = new double[n]; + Arrays.fill(z, 1.0); + + a.set(0, 0, total(w)); + b[0] = totalMult(w, y); + + for (int p = 1; p <= 2*degree; p++) { + for (int iz = 0; iz < z.length; iz++) { + z[iz] *= x[iz]; + } + if (p < m ) { + b[p] = totalMult(w, y, z); + } + double sum = totalMult(w, z); + int degreeLTp = Math.min(degree, p); + for (int j = Math.max(0, p - degree); j <= degreeLTp; j++) { + a.set(j, p - j, sum); + } + } + + a = MatrixUtil.inverse(a); + + double[] c = new double[m]; + + MatrixUtil.multiply(new ArrayMatrix(b, 1, m), a, new ArrayMatrix(c, 1, m)); + + return c; + } + + private static double total(double[] a) { + double total = 0.0; + for (int i = 0; i < a.length; i++) { + total += a[i]; + } + return total; + } + + private static double totalMult(double[] a, double[] b) { + double total = 0.0; + for (int i = 0; i < a.length; i++) { + total += (a[i] * b[i]); + } + return total; + } + + private static double totalMult(double[] a, double[] b, double[] c) { + double total = 0.0; + for (int i = 0; i < a.length; i++) { + total += (a[i] * b[i] * c[i]); + } + return total; + } + + /** + * The peak of the quadradic. + * y = c0 + c1*x + c2x^2 + * dy/dx = c1 + 2*c2*x + * for dy/dx == 0, x = -c1/(2*c2) + */ + public static double quadPeak(double[] c) { + if (c.length != 3) { + throw new IllegalArgumentException("c must have a length of 3"); + } + return -0.5 * c[1] / c[2]; + } + + /* + * The width only depends on the x^2 coefficient. + * dy is the delta y at which the half width is measured. + * w = -((-c2*dy)^(1/2)) / c2 + */ + public static double quadHalfWidth(double[] c, double dy) { + return Math.sqrt(-c[2]*dy) / -c[2]; + } + +} diff --git a/dasCore/src/main/java/org/das2/math/Triangulator.java b/dasCore/src/main/java/org/das2/math/Triangulator.java new file mode 100644 index 000000000..47f1424fb --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/Triangulator.java @@ -0,0 +1,1600 @@ +/* + * The Triangulator - a java applet which animates some brute force + * Delaunay triangulation algorithms of varying computational + * complexity. + * + * Author: Geoff Leach. gl@cs.rmit.edu.au + * Date: 29/3/96 + * + * License to copy and use this software is granted provided that + * appropriate credit is given to both RMIT and the author. + * + * The point of the applet is educational: + * + * (a) To illustrate the importance of computational complexity. + * The three Delaunay triangulation algorithms implemented have + * computational complexities of O(n^2), O(n^3) and O(n^4). The + * user can see for themselves the improvement in speed going + * from O(n^4) to O(n^2). + * + * (b) To illustrate what Delaunay triangulations are. The Delaunay + * triangulation, and the related Voronoi diagram, is a + * particularly useful data structure for a number of problems. + * Without going into the applications the applet attempts to + * show what Delaunay triangulation algorithms are. + * + * (c) To provide a useful context for me to initially learn Java. + * + * The code still needs polishing ... + * + */ + +package org.das2.math; + +import java.applet.*; +import java.awt.*; +import java.io.PrintStream; + +/* + * Sometimes we just have to die. + */ +class Panic { + static public void panic(String m) { + System.err.println(m); + System.exit(1); + } +} + +/* + * Wrapper class for basic int type for pass by reference. + */ +class Int { + int i; + + public void Int() { + i = 0; + } + + public void Int(int i) { + this.i = i; + } + + public void setValue(int i) { + this.i = i; + } + + public int getValue() { + return i; + } +} + +/* + * Point class. RealPoint to avoid clash with java.awt.Point. + */ +class RealPoint { + float x, y; + + RealPoint() { x = y = 0.0f; } + RealPoint(float x, float y) { this.x = x; this.y = y; } + RealPoint(RealPoint p) { x = p.x; y = p.y; } + public float x() { return this.x; } + public float y() { return this.y; } + public void set(float x, float y) { this.x = x; this.y = y; } + + public float distance(RealPoint p) { + float dx, dy; + + dx = p.x - x; + dy = p.y - y; + return (float)Math.sqrt((double)(dx * dx + dy * dy)); + } + + public float distanceSq(RealPoint p) { + float dx, dy; + + dx = p.x - x; + dy = p.y - y; + return (float)(dx * dx + dy * dy); + } +} + +/* + * Edge class. Edges have two vertices, s and t, and two faces, + * l (left) and r (right). The triangulation representation and + * the Delaunay triangulation algorithms require edges. + */ +class Edge { + int s, t; + int l, r; + + Edge() { s = t = 0; } + Edge(int s, int t) { this.s =s; this.t = t; } + int s() { return this.s; } + int t() { return this.t; } + int l() { return this.l; } + int r() { return this.r; } +} + + +/* + * Vector class. A few elementary vector operations. + */ +class Vector { + float u, v; + + Vector() { u = v = 0.0f; } + Vector(RealPoint p1, RealPoint p2) { + u = p2.x() - p1.x(); + v = p2.y() - p1.y(); + } + Vector(float u, float v) { this.u = u; this.v = v; } + + float dotProduct(Vector v) { return u * v.u + this.v * v.v; } + + static float dotProduct(RealPoint p1, RealPoint p2, RealPoint p3) { + float u1, v1, u2, v2; + + u1 = p2.x() - p1.x(); + v1 = p2.y() - p1.y(); + u2 = p3.x() - p1.x(); + v2 = p3.y() - p1.y(); + + return u1 * u2 + v1 * v2; + } + + float crossProduct(Vector v) { return u * v.v - this.v * v.u; } + + static float crossProduct(RealPoint p1, RealPoint p2, RealPoint p3) { + float u1, v1, u2, v2; + + u1 = p2.x() - p1.x(); + v1 = p2.y() - p1.y(); + u2 = p3.x() - p1.x(); + v2 = p3.y() - p1.y(); + + return u1 * v2 - v1 * u2; + } + + void setRealPoints(RealPoint p1, RealPoint p2) { + u = p2.x() - p1.x(); + v = p2.y() - p1.y(); + } +} + +/* + * Circle class. Circles are fundamental to computation of Delaunay + * triangulations. In particular, an operation which computes a + * circle defined by three points is required. + */ +class Circle { + RealPoint c; + float r; + + Circle() { c = new RealPoint(); r = 0.0f; } + Circle(RealPoint c, float r) { this.c = c; this.r = r; } + public RealPoint center() { return c; } + public float radius() { return r; } + public void set(RealPoint c, float r) { this.c = c; this.r = r; } + + /* + * Tests if a point lies inside the circle instance. + */ + public boolean inside(RealPoint p) { + if (c.distanceSq(p) < r * r) + return true; + else + return false; + } + + /* + * Compute the circle defined by three points (circumcircle). + */ + public void circumCircle(RealPoint p1, RealPoint p2, RealPoint p3) { + float cp; + + cp = Vector.crossProduct(p1, p2, p3); + if (cp != 0.0) + { + float p1Sq, p2Sq, p3Sq; + float num, den; + float cx, cy; + + p1Sq = p1.x() * p1.x() + p1.y() * p1.y(); + p2Sq = p2.x() * p2.x() + p2.y() * p2.y(); + p3Sq = p3.x() * p3.x() + p3.y() * p3.y(); + num = p1Sq*(p2.y() - p3.y()) + p2Sq*(p3.y() - p1.y()) + p3Sq*(p1.y() - p2.y()); + cx = num / (2.0f * cp); + num = p1Sq*(p3.x() - p2.x()) + p2Sq*(p1.x() - p3.x()) + p3Sq*(p2.x() - p1.x()); + cy = num / (2.0f * cp); + + c.set(cx, cy); + } + + // Radius + r = c.distance(p1); + } +} + +/* + * Triangulation class. A triangulation is represented as a set of + * points and the edges which form the triangulation. + */ +class Triangulation { + static final int Undefined = -1; + static final int Universe = 0; + int nPoints; + RealPoint point[]; + int nEdges; + int maxEdges; + Edge edge[]; + + Triangulation(int nPoints) { + + // Allocate points. + this.nPoints = nPoints; + this.point = new RealPoint[nPoints]; + for (int i = 0; i < nPoints; i++) + point[i] = new RealPoint(); + + // Allocate edges. + maxEdges = 3 * nPoints - 6; // Max number of edges. + edge = new Edge[maxEdges]; + for (int i = 0; i < maxEdges; i++) + edge[i] = new Edge(); + nEdges = 0; + } + + /* + * Sets the number of points in the triangulation. Reuses already + * allocated points and edges. + */ + public void setNPoints(int nPoints) { + // Fix edge array. + Edge tmpEdge[] = edge; + int tmpMaxEdges = maxEdges; + maxEdges = 3 * nPoints - 6; // Max number of edges. + edge = new Edge[maxEdges]; + + // Which is smaller? + int minMaxEdges; + if (tmpMaxEdges < maxEdges) + minMaxEdges = tmpMaxEdges; + else + minMaxEdges = maxEdges; + + // Reuse allocated edges. + for (int i = 0; i < minMaxEdges; i++) + this.edge[i] = tmpEdge[i]; + + // Get new edges. + for (int i = minMaxEdges; i < maxEdges; i++) + this.edge[i] = new Edge(); + + // Fix point array. + RealPoint tmpPoint[] = point; + point = new RealPoint[nPoints]; + + // Which is smaller? + int minPoints; + if (nPoints < this.nPoints) + minPoints = nPoints; + else + minPoints = this.nPoints; + + // Reuse allocated points. + for (int i = 0; i < minPoints; i++) + this.point[i] = tmpPoint[i]; + + // Get new points. + for (int i = minPoints; i < nPoints; i++) + this.point[i] = new RealPoint(); + + this.nPoints = nPoints; + } + + /* + * Generates a set of random points to triangulate. + */ + public void randomPoints(RealWindow w) { + for (int i = 0; i < nPoints; i++) + { + point[i].x = (float)Math.random() * w.xMax(); + point[i].y = (float)Math.random() * w.yMax(); + } + nEdges = 0; + } + + /* + * Copies a set of points. + */ + public void copyPoints(Triangulation t) { + int n; + + if (t.nPoints < nPoints) + n = t.nPoints; + else + n = nPoints; + + for (int i = 0; i < n; i++) { + point[i].x = t.point[i].x; + point[i].y = t.point[i].y; + } + + nEdges = 0; + } + + void addTriangle(int s, int t, int u) { + addEdge(s, t); + addEdge(t, u); + addEdge(u, s); + } + + public int addEdge(int s, int t) { + return addEdge(s, t, Undefined, Undefined); + } + + /* + * Adds an edge to the triangulation. Store edges with lowest + * vertex first (easier to debug and makes no other difference). + */ + public int addEdge(int s, int t, int l, int r) { + int e; + + // Add edge if not already in the triangulation. + e = findEdge(s, t); + if (e == Undefined) + if (s < t) + { + edge[nEdges].s = s; + edge[nEdges].t = t; + edge[nEdges].l = l; + edge[nEdges].r = r; + return nEdges++; + } + else + { + edge[nEdges].s = t; + edge[nEdges].t = s; + edge[nEdges].l = r; + edge[nEdges].r = l; + return nEdges++; + } + else + return Undefined; + } + + public int findEdge(int s, int t) { + boolean edgeExists = false; + int i; + + for (i = 0; i < nEdges; i++) + if (edge[i].s == s && edge[i].t == t || + edge[i].s == t && edge[i].t == s) { + edgeExists = true; + break; + } + + if (edgeExists) + return i; + else + return Undefined; + } + + /* + * Update the left face of an edge. + */ + public void updateLeftFace(int eI, int s, int t, int f) { + if (!((edge[eI].s == s && edge[eI].t == t) || + (edge[eI].s == t && edge[eI].t == s))) + Panic.panic("updateLeftFace: adj. matrix and edge table mismatch"); + if (edge[eI].s == s && edge[eI].l == Triangulation.Undefined) + edge[eI].l = f; + else if (edge[eI].t == s && edge[eI].r == Triangulation.Undefined) + edge[eI].r = f; + else + Panic.panic("updateLeftFace: attempt to overwrite edge info"); + } + + public void draw(RealWindowGraphics rWG, Color pC, Color eC) { + drawPoints(rWG, pC); + drawEdges(rWG, eC); + } + + public void drawPoints(RealWindowGraphics rWG, Color c) { + for (int i = 0; i < nPoints; i++) + rWG.drawPoint(point[i], c); + } + + public void drawEdges(RealWindowGraphics rWG, Color c) { + for (int i = 0; i < nEdges; i++) + drawEdge(rWG, edge[i], c); + } + + public void drawEdge(RealWindowGraphics rWG, Edge e, Color c) { + rWG.drawLine(point[e.s], point[e.t], c); + } + + public void print(PrintStream p) { + printPoints(p); + printEdges(p); + } + + public void printPoints(PrintStream p) { + for (int i = 0; i < nPoints; i++) + p.println(String.valueOf(point[i].x) + " " + String.valueOf(point[i].y)); + } + + public void printEdges(PrintStream p) { + for (int i = 0; i < nEdges; i++) + p.println(String.valueOf(edge[i].s) + " " + String.valueOf(edge[i].t)); + } +} + +/* + * Rectangle class. Need rectangles for window to viewport mapping. + */ +class RealRectangle { + RealPoint ll; + RealPoint ur; + + RealRectangle() { } + + RealRectangle (RealRectangle r) { + this.ll = new RealPoint(r.ll); + this.ur = new RealPoint(r.ur); + } + + RealRectangle (RealPoint ll, RealPoint ur) { + this.ll = new RealPoint(ll); + this.ur = new RealPoint(ur); + } + + RealRectangle(float xMin, float yMin, float xMax, float yMax) { + this.ll = new RealPoint(xMin, yMin); + this.ur = new RealPoint(xMax, yMax); + } + + public float width() { return ur.x() - ll.x(); } + public float height() { return ur.y() - ll.y(); } + + public RealPoint ll() { return ll; } + public RealPoint ur() { return ur; } + + public float xMin() { return ll.x; } + public float yMin() { return ll.y; } + + public float xMax() { return ur.x; } + public float yMax() { return ur.y; } +} + +/* + * A window is essentially a rectangle. + */ +class RealWindow extends RealRectangle { + RealWindow() {} + RealWindow(float xMin, float yMin, float xMax, float yMax) { + super(xMin, yMin, xMax, yMax); + } + RealWindow(RealWindow w) { super(w.ll(), w.ur()); } +} + +/* + * RealWindowGraphics class. Has a window, a viewport and a + * graphics context into which to draw. The graphics context + * is only set after calls to repaint result in calls to update. + * Contains drawing operations, drawTriangle for example, needed + * elsewhere. + */ +class RealWindowGraphics { + RealWindow w = null; // window + Dimension v = null; // viewport + Graphics g = null; + float scale = 1.0f; + + static final float realPointRadius = 0.04f; + static final int pixelPointRadius = 4; + static final int halfPixelPointRadius = 2; + + RealWindowGraphics(RealWindow w) { + this.w = new RealWindow(w); + } + + RealWindowGraphics(RealWindow w, Dimension d, Graphics g) { + this.w = new RealWindow(w); + this.v = new Dimension(d.width, d.height); + this.g = g; + calculateScale(); + } + + public void setWindow(RealWindow w) { + this.w = new RealWindow(w); + calculateScale(); + } + + public void setViewport(Dimension d) { + this.v = new Dimension(d.width, d.height); + calculateScale(); + } + + public void setGraphics(Graphics g) { + this.g = g; + } + + public Graphics getGraphics(Graphics g) { + return g; + } + + public void calculateScale() { + float sx, sy; + + sx = v.width / w.width(); + sy = v.height / w.height(); + + if (sx < sy) + scale = sx; + else + scale = sy; + } + + public void drawTriangle(RealPoint p1, + RealPoint p2, + RealPoint p3, + Color c) { + drawLine(p1, p2, c); + drawLine(p2, p3, c); + drawLine(p3, p1, c); + } + + public void drawLine(RealPoint p1, RealPoint p2, Color c) { + int x1, y1, x2, y2; + + g.setColor(c); + x1 = (int)(p1.x() * scale); + y1 = (int)(p1.y() * scale); + x2 = (int)(p2.x() * scale); + y2 = (int)(p2.y() * scale); + g.drawLine(x1, y1, x2, y2); + } + + public void drawPoint(RealPoint p, Color c) { + g.setColor(c); + + g.fillOval((int)(scale * p.x()) - halfPixelPointRadius, + (int)(scale * p.y()) - halfPixelPointRadius, + pixelPointRadius, pixelPointRadius); + } + + public void drawCircle(Circle circle, Color c) { + drawCircle(circle.center().x(), circle.center().y(), circle.radius(), c); + } + + public void drawCircle(RealPoint p, float r, Color c) { + drawCircle(p.x(), p.y(), r, c); + } + + public void drawCircle(float x, float y, float r, Color c) { + g.setColor(c); + + g.drawOval((int)(scale * (x - r)), (int)(scale * (y - r)), + (int)(2.0f * r * scale), (int)(2.0f * r * scale)); + } + + public void fillCircle(float x, float y, float r, Color c) { + g.setColor(c); + + g.fillOval((int)(scale * (x - r)), (int)(scale * (y - r)), + (int)(2.0f * r * scale), (int)(2.0f * r * scale)); + } +} + +/* + * AlgorithmUIHeading class. Provides a heading for part of the user + * interface. + */ +class AlgorithmUIHeading extends Panel { + + public AlgorithmUIHeading() { + // Headings. + setLayout(new GridLayout(0,7)); + add(new Label("Algorithm", Label.LEFT)); + add(new Label("Run", Label.LEFT)); + add(new Label("Points", Label.LEFT)); + add(new Label("Triangles", Label.LEFT)); + add(new Label("Circles", Label.LEFT)); + add(new Label("Points", Label.LEFT)); + add(new Label("Pause (mS)", Label.LEFT)); + } +} + +/* + * TriangulationCanvas class. Each of the triangulation algorithms + * needs a canvas to draw into. + */ +@SuppressWarnings("deprecation") +class TriangulationCanvas extends Canvas { + Triangulation t; + RealWindowGraphics rWG; // Does the actual drawing. + boolean needToClear = false; + boolean newPoints = false; + TriangulationAlgorithm alg; // The algorithm which uses this canvas. + + TriangulationCanvas(Triangulation t, + RealWindow w, + TriangulationAlgorithm alg) { + this.t = t; + rWG = new RealWindowGraphics(w); + this.alg = alg; + } + + public Insets insets() { + return new Insets(2,10,2,15); + } + + public void paint(Graphics g) { + if (needToClear) { + g.clearRect(0, 0, size().width, size().height); + + needToClear = false; + } + g.drawRect(0, 0, size().width-1, size().height-1); + rWG.setGraphics(g); + rWG.setViewport(size()); + alg.draw(rWG, t); + } + + public void update(Graphics g) { + paint(g); + } +} + +/* + * AlgorithmUI class. Each algorithm has a set of user interface + * controls. This class provides them. + */ +@SuppressWarnings("deprecation") +class AlgorithmUI extends Panel { + TextField nPointsTextField; + Checkbox animateCheckBox[]; + Checkbox runCheckBox; + TextField pauseTextField; + TriangulationAlgorithm algorithm; // Algorithm which uses this UI. + + public AlgorithmUI(TriangulationAlgorithm algorithm, + String label, int nPoints, int pause) { + + this.algorithm = algorithm; + + // One set of controls per algorithm. + setLayout(new GridLayout(0,7)); + add(new Label(label, Label.LEFT)); + add(runCheckBox = new Checkbox(null, null, true)); + add(nPointsTextField = new TextField(String.valueOf(nPoints), 5)); + animateCheckBox = new Checkbox[AnimateControl.nEntities]; + animateCheckBox[AnimateControl.triangles] = new Checkbox(null, null, true); + add(animateCheckBox[AnimateControl.triangles]); + animateCheckBox[AnimateControl.circles] = new Checkbox(null, null, true); + add(animateCheckBox[AnimateControl.circles]); + animateCheckBox[AnimateControl.points] = new Checkbox(null, null, true); + add(animateCheckBox[AnimateControl.points]); + pauseTextField = new TextField(String.valueOf(pause), 5); + add(pauseTextField); + } + + public void setAlgorithm(TriangulationAlgorithm algorithm) { + this.algorithm = algorithm; + } + + // Gets the current value in a text field. + int getValue(TextField tF) { + int i; + try { + i = Integer.valueOf(tF.getText()).intValue(); + } catch (java.lang.NumberFormatException e) { + i = 0; + } + return i; + } + + public boolean handleEvent(Event evt) { + if (evt.id == Event.ACTION_EVENT) { + if (evt.target == runCheckBox) { + algorithm.control().setRun(((Boolean)evt.arg).booleanValue()); + return true; + } else if (evt.target == animateCheckBox[AnimateControl.triangles]) { + algorithm.control().setAnimate(AnimateControl.triangles, + ((Boolean)evt.arg).booleanValue()); + return true; + } else if (evt.target == animateCheckBox[AnimateControl.circles]) { + algorithm.control().setAnimate(AnimateControl.circles, + ((Boolean)evt.arg).booleanValue()); + return true; + } else if (evt.target == animateCheckBox[AnimateControl.points]) { + algorithm.control().setAnimate(AnimateControl.points, + ((Boolean)evt.arg).booleanValue()); + return true; + } else if (evt.target == pauseTextField) { + algorithm.control().setPause(getValue(pauseTextField)); + return true; + } else if (evt.target == nPointsTextField) { + algorithm.control().nPoints = getValue(nPointsTextField); + return true; + } + } + + return false; + } +} + +/* + * AnimateControl class. Each algorithm animation has various entities + * which can be displayed. This class provides the state which controls + * what is being displayed. It is manipulated by AlgorithmUI and + * accessed by the animation routines in each algorithm. + */ +class AnimateControl { + TriangulationAlgorithm triAlg; + static final int automatic = 0; + static final int manual = 1; + int animateMode = automatic; + int pause = 10; + static final int algorithm = 0; + static final int triangles = 1; + static final int points = 2; + static final int circles = 3; + static final int nEntities = 4; + boolean run; + boolean animate[]; + int nPoints; + + AnimateControl(TriangulationAlgorithm algorithm) { + triAlg = algorithm; + animate = new boolean[nEntities]; + for (int i = 0; i < nEntities; i++) + animate[i] = true; + run = true; + } + + AnimateControl(TriangulationAlgorithm algorithm, int nPoints) { + this(algorithm); + this.nPoints = nPoints; + } + + public void setAnimate(int entity, boolean v) { + animate[entity] = v; + if (!v) + triAlg.canvas().needToClear = true; + } + + public boolean animate(int entity) { + return animate[entity]; + } + + public int mode() { + return animateMode; + } + + public void setManualAnimateMode() { + animateMode = manual; + } + + public void setAutomaticAnimateMode() { + animateMode = automatic; + } + + public int getPause() { + return pause; + } + + public void setPause(int p) { + pause = p; + } + + public int getNPoints() { + return nPoints; + } + + public void setNPoints(int n) { + nPoints = n; + } + + public void setRun(boolean v) { + run = v; + } + + public boolean getRun() { + return run; + } +} + +/* + * TriangulationAlgorithm class. Absract. Superclass for + * actual algorithms. Has several abstract function members - + * including the triangulation member which actually computes + * the triangulation. + */ +abstract class TriangulationAlgorithm { + String algName; + TriangulationCanvas triCanvas; + AnimateControl aniControl; + AlgorithmUI algorithmUI; + RealWindow w; + RealWindowGraphics rWG; + + // Variables and constants for animation state. + final int nStates = 5; + boolean state[] = new boolean[nStates]; + static final int triangulationState = 0; + static final int pointState = 1; + static final int triangleState = 2; + static final int insideState = 4; + static final int edgeState = 5; + + public TriangulationAlgorithm(Triangulation t, RealWindow w, + String name, int nPoints) { + algName = name; + aniControl = new AnimateControl(this, nPoints); + algorithmUI = new AlgorithmUI(this, name, nPoints, aniControl.getPause()); + triCanvas = new TriangulationCanvas(t, w, this); + + for (int s = 0; s < nStates; s++) + state[s] = false; + triCanvas.needToClear = true; + } + + public void setCanvas(TriangulationCanvas tc) { + triCanvas = tc; + } + + public AnimateControl control() { + return aniControl; + } + + public AlgorithmUI algorithmUI() { + return algorithmUI; + } + + public TriangulationCanvas canvas() { + return triCanvas; + } + + public void setAlgorithmState(int stateVar, boolean value) { + state[stateVar] = value; + } + + public void pause() { + if (aniControl.mode() == AnimateControl.automatic) + try { + wait(aniControl.getPause()); + } catch (InterruptedException e){} + else + try {wait();} catch (InterruptedException e){} + } + + public void animate(int state) { + if ((aniControl.animate(AnimateControl.triangles) || + aniControl.animate(AnimateControl.circles)) && + state == triangulationState) + triCanvas.needToClear = true; + + setAlgorithmState(state, true); + + triCanvas.repaint(); + + pause(); + + setAlgorithmState(state, false); + } + + public void reset() { + for (int s = 0; s < nStates; s++) + state[s] = false; + triCanvas.needToClear = true; + } + + public synchronized void nextStep() { notify(); } + abstract public void triangulate(Triangulation t); + abstract public void draw(RealWindowGraphics rWG, Triangulation t); +} + +/* + * QuarticAlgorithm class. O(n^4) algorithm. The most brute-force + * of the algorithms. + */ +class QuarticAlgorithm extends TriangulationAlgorithm { + int i, j, k, l; + Circle c = new Circle(); + final static String algName = "O(n^4)"; + + public QuarticAlgorithm(Triangulation t, RealWindow w, int nPoints) { + super(t, w, algName, nPoints); + } + + public void reset() { + i = j = k = l = 0; + super.reset(); + } + + public void draw(RealWindowGraphics rWG, Triangulation t) { + if (state[triangleState]) { + if (aniControl.animate(AnimateControl.triangles)) + rWG.drawTriangle(t.point[i], t.point[j], t.point[k], Color.green); + if (aniControl.animate(AnimateControl.circles)) + rWG.drawCircle(c, Color.green); + } else if (state[pointState]) { + if (aniControl.animate(AnimateControl.points)) + rWG.drawPoint(t.point[l], Color.orange); + } else if (state[insideState]) { + if (aniControl.animate(AnimateControl.triangles)) + rWG.drawTriangle(t.point[i], t.point[j], t.point[k], Color.red); + if (aniControl.animate(AnimateControl.circles)) + rWG.drawCircle(c, Color.red); + if (aniControl.animate(AnimateControl.points)) + rWG.drawPoint(t.point[l], Color.red); + } else if (state[triangulationState]) { + t.draw(rWG, Color.black, Color.black); + } else { + t.draw(rWG, Color.black, Color.black); + } + } + + public synchronized void triangulate(Triangulation t) { + boolean pointFree; + int n = t.nPoints; + RealPoint p[] = t.point; + + for (i = 0; i < n-2; i++) + for (j = i + 1; j < n-1; j++) + if (j != i) + for (k = j + 1; k < n; k++) + if (k != i && k != j) + { + c.circumCircle(p[i], p[j], p[k]); + animate(triangleState); + pointFree = true; + for (l = 0; l < n; l++) + if (l != i && l != j && l != k) { + animate(pointState); + if (c.inside(p[l])) { + animate(insideState); + pointFree = false; + break; + } + } + + if (pointFree) + t.addTriangle(i, j, k); + + animate(triangulationState); + } + } +} + +/* + * CubicAlgorithm class. O(n^3) algorithm. + */ +class CubicAlgorithm extends TriangulationAlgorithm { + int s, t, u, i; + Circle bC = new Circle(); + final static String algName = "O(n^3)"; + int nFaces; + + public CubicAlgorithm(Triangulation t, RealWindow w, int nPoints) { + super(t, w, algName, nPoints); + } + + public void reset() { + nFaces = 0; + triCanvas.needToClear = true; + super.reset(); + } + + public void draw(RealWindowGraphics rWG, Triangulation tri) { + if (state[triangleState]) { + if (aniControl.animate(AnimateControl.triangles)) { + rWG.drawTriangle(tri.point[s], tri.point[t], tri.point[u], + Color.green); + rWG.drawLine(tri.point[s], tri.point[t], Color.blue); + } + if (aniControl.animate(AnimateControl.circles)) + rWG.drawCircle(bC, Color.green); + } else if (state[pointState]) { + if (aniControl.animate(AnimateControl.points)) + rWG.drawPoint(tri.point[i], Color.orange); + } else if (state[insideState]) { + if (aniControl.animate(AnimateControl.triangles)) { + rWG.drawTriangle(tri.point[s], tri.point[t], tri.point[u], Color.red); + rWG.drawLine(tri.point[s], tri.point[t], Color.blue); + } + if (aniControl.animate(AnimateControl.circles)) + rWG.drawCircle(bC, Color.red); + if (aniControl.animate(AnimateControl.points)) + rWG.drawPoint(tri.point[s], Color.red); + } else if (state[triangulationState]) { + tri.draw(rWG, Color.black, Color.black); + } else { + tri.draw(rWG, Color.black, Color.black); + } + } + + public synchronized void triangulate(Triangulation tri) { + int seedEdge, currentEdge; + int nFaces; + Int s, t; + + // Initialise. + nFaces = 0; + s = new Int(); + t = new Int(); + + // Find closest neighbours and add edge to triangulation. + findClosestNeighbours(tri.point, tri.nPoints, s, t); + + // Create seed edge and add it to the triangulation. + seedEdge = tri.addEdge(s.getValue(), t.getValue(), + Triangulation.Undefined, + Triangulation.Undefined); + + currentEdge = 0; + while (currentEdge < tri.nEdges) + { + if (tri.edge[currentEdge].l == Triangulation.Undefined) { + completeFacet(currentEdge, tri, nFaces); + animate(triangulationState); + } + if (tri.edge[currentEdge].r == Triangulation.Undefined) { + completeFacet(currentEdge, tri, nFaces); + animate(triangulationState); + } + currentEdge++; + } + } + + // Find the two closest points. + public void findClosestNeighbours(RealPoint p[], int nPoints, + Int u, Int v) { + int i, j; + float d, min; + int s, t; + + s = t = 0; + min = Float.MAX_VALUE; + for (i = 0; i < nPoints-1; i++) + for (j = i+1; j < nPoints; j++) + { + d = p[i].distanceSq(p[j]); + if (d < min) + { + s = i; + t = j; + min = d; + } + } + u.setValue(s); + v.setValue(t); + } + + /* + * Complete a facet by looking for the circle free point to the left + * of the edge "e_i". Add the facet to the triangulation. + * + * This function is a bit long and may be better split. + */ + public void completeFacet(int eI, Triangulation tri, int nFaces) { + float cP; + boolean pointFree; + Edge e[] = tri.edge; + RealPoint p[] = tri.point; + + // Cache s and t. + if (e[eI].l == Triangulation.Undefined) + { + s = e[eI].s; + t = e[eI].t; + } + else if (e[eI].r == Triangulation.Undefined) + { + s = e[eI].t; + t = e[eI].s; + } + else + // Edge already completed. + return; + + // Find point free circumcircle to the left. + for (u = 0; u < tri.nPoints; u++) + if (u != s && u != t) { + if (Vector.crossProduct(p[s], p[t], p[u]) > 0.0) { + bC.circumCircle(p[s], p[t], p[u]); + animate(triangleState); + pointFree = true; + for (i = 0; i < tri.nPoints; i++) + if (i != s && i != t && i != u) { + animate(pointState); + cP = Vector.crossProduct(p[s], p[t], p[i]); + if (cP > 0.0) + if (bC.inside(p[i])) { + animate(insideState); + pointFree = false; + break; + } + } + animate(triangulationState); + if (pointFree) + break; + } + } + + // Add new triangle or update edge info if s-t is on hull. + if (u < tri.nPoints) { + int bP = u; + + // Update face information of edge being completed. + tri.updateLeftFace(eI, s, t, nFaces); + nFaces++; + + // Add new edge or update face info of old edge. + eI = tri.findEdge(bP, s); + if (eI == Triangulation.Undefined) + // New edge. + eI = tri.addEdge(bP, s, nFaces, Triangulation.Undefined); + else + // Old edge. + tri.updateLeftFace(eI, bP, s, nFaces); + + // Add new edge or update face info of old edge. + eI = tri.findEdge(t, bP); + if (eI == Triangulation.Undefined) + // New edge. + eI = tri.addEdge(t, bP, nFaces, Triangulation.Undefined); + else + // Old edge. + tri.updateLeftFace(eI, t, bP, nFaces); + } else + tri.updateLeftFace(eI, s, t, Triangulation.Universe); + } +} + +/* + * QuadraticAlgorithm class. O(n^2) algorithm. + */ +class QuadraticAlgorithm extends TriangulationAlgorithm { + int s, t, u, bP; + Circle bC = new Circle(); + final static String algName = "O(n^2)"; + int nFaces; + + public QuadraticAlgorithm(Triangulation t, RealWindow w, int nPoints) { + super(t, w, algName, nPoints); + } + + public void reset() { + nFaces = 0; + triCanvas.needToClear = true; + super.reset(); + } + + public void draw(RealWindowGraphics rWG, Triangulation tri) { + if (state[triangleState]) { + if (aniControl.animate(AnimateControl.triangles)) { + rWG.drawTriangle(tri.point[s], tri.point[t], tri.point[bP], + Color.green); + rWG.drawLine(tri.point[s], tri.point[t], Color.blue); + } + if (aniControl.animate(AnimateControl.circles)) + rWG.drawCircle(bC, Color.green); + } else if (state[pointState]) { + if (aniControl.animate(AnimateControl.points)) + rWG.drawPoint(tri.point[u], Color.orange); + } else if (state[insideState]) { + if (aniControl.animate(AnimateControl.triangles)) { + rWG.drawTriangle(tri.point[s], tri.point[t], tri.point[bP], Color.red); + rWG.drawLine(tri.point[s], tri.point[t], Color.blue); + } + if (aniControl.animate(AnimateControl.circles)) + rWG.drawCircle(bC, Color.red); + if (aniControl.animate(AnimateControl.points)) + rWG.drawPoint(tri.point[s], Color.red); + } else if (state[triangulationState]) { + tri.draw(rWG, Color.black, Color.black); + } else { + tri.draw(rWG, Color.black, Color.black); + } + } + + public synchronized void triangulate(Triangulation tri) { + int seedEdge, currentEdge; + int nFaces; + Int s, t; + + // Initialise. + nFaces = 0; + s = new Int(); + t = new Int(); + + // Find closest neighbours and add edge to triangulation. + findClosestNeighbours(tri.point, tri.nPoints, s, t); + + // Create seed edge and add it to the triangulation. + seedEdge = tri.addEdge(s.getValue(), t.getValue(), + Triangulation.Undefined, + Triangulation.Undefined); + + currentEdge = 0; + while (currentEdge < tri.nEdges) { + if (tri.edge[currentEdge].l == Triangulation.Undefined) { + completeFacet(currentEdge, tri, nFaces); + animate(triangulationState); + } + if (tri.edge[currentEdge].r == Triangulation.Undefined) { + completeFacet(currentEdge, tri, nFaces); + animate(triangulationState); + } + currentEdge++; + } + } + + // Find the two closest points. + public void findClosestNeighbours(RealPoint p[], int nPoints, + Int u, Int v) { + int i, j; + float d, min; + int s, t; + + s = t = 0; + min = Float.MAX_VALUE; + for (i = 0; i < nPoints-1; i++) + for (j = i+1; j < nPoints; j++) + { + d = p[i].distanceSq(p[j]); + if (d < min) + { + s = i; + t = j; + min = d; + } + } + u.setValue(s); + v.setValue(t); + } + + /* + * Complete a facet by looking for the circle free point to the left + * of the edge "e_i". Add the facet to the triangulation. + * + * This function is a bit long and may be better split. + */ + public void completeFacet(int eI, Triangulation tri, int nFaces) { + float cP; + int i; + Edge e[] = tri.edge; + RealPoint p[] = tri.point; + + // Cache s and t. + if (e[eI].l == Triangulation.Undefined) + { + s = e[eI].s; + t = e[eI].t; + } + else if (e[eI].r == Triangulation.Undefined) + { + s = e[eI].t; + t = e[eI].s; + } + else + // Edge already completed. + return; + + + // Find a point on left of edge. + for (u = 0; u < tri.nPoints; u++) + { + if (u == s || u == t) + continue; + if (Vector.crossProduct(p[s], p[t], p[u]) > 0.0) + break; + } + + // Find best point on left of edge. + bP = u; + if (bP < tri.nPoints) + { + bC.circumCircle(p[s], p[t], p[bP]); + + animate(triangleState); + + for (u = bP+1; u < tri.nPoints; u++) + { + if (u == s || u == t) + continue; + + animate(pointState); + + cP = Vector.crossProduct(p[s], p[t], p[u]); + + if (cP > 0.0) + if (bC.inside(p[u])) + { + animate(insideState); + bP = u; + bC.circumCircle(p[s], p[t], p[u]); + animate(triangleState); + } + } + } + + // Add new triangle or update edge info if s-t is on hull. + if (bP < tri.nPoints) + { + // Update face information of edge being completed. + tri.updateLeftFace(eI, s, t, nFaces); + nFaces++; + + // Add new edge or update face info of old edge. + eI = tri.findEdge(bP, s); + if (eI == Triangulation.Undefined) + // New edge. + eI = tri.addEdge(bP, s, nFaces, Triangulation.Undefined); + else + // Old edge. + tri.updateLeftFace(eI, bP, s, nFaces); + + // Add new edge or update face info of old edge. + eI = tri.findEdge(t, bP); + if (eI == Triangulation.Undefined) + // New edge. + eI = tri.addEdge(t, bP, nFaces, Triangulation.Undefined); + else + // Old edge. + tri.updateLeftFace(eI, t, bP, nFaces); + } else + tri.updateLeftFace(eI, s, t, Triangulation.Universe); + } +} + +/* + * AppletUI class. Provides most of the user interface for the applet. + */ +class AppletUI extends Panel { + AlgorithmUI AlgorithmUI[]; + + public AppletUI(TriangulationAlgorithm algorithm[]) { + Label l; + Panel p; + + setLayout(new BorderLayout()); + + // Per algorithm controls. + p = new Panel(); + p.setLayout(new GridLayout(0,1)); + + // Headings for algorithm controls. + p.add(new AlgorithmUIHeading()); + + // One set of controls per algorithm. + for (int i = 0; i < algorithm.length; i++) + p.add(algorithm[i].algorithmUI()); + + // Add panel to controls. + add("Center", p); + + // Applet controls. + p = new Panel(); + p.setLayout(new GridLayout(0,1)); + p.add(new Button("Start")); + p.add(new Button("Stop")); + p.add(new Button("New")); + p.add(new Label("Step Mode", Label.CENTER)); + Choice c = new Choice(); + c.addItem("Auto"); + c.addItem("Manual"); + p.add(c); + + // Add panel to controls. + add("East", p); + } +} + +/* + * TriangulationApplet class. "Main Class" + */ +@SuppressWarnings("deprecation") +public class Triangulator extends Applet implements Runnable { + Thread triangulateThread[]; + int nPoints = 10; + Triangulation triangulation[]; + TriangulationAlgorithm algorithm[]; + RealWindow w; + RealWindowGraphics rWG; + AppletUI appUI; + public static final int On2 = 0; + public static final int On3 = 1; + public static final int On4 = 2; + Panel canvases; + static final int nAlgorithms = 3; + + public void init() { + + setBackground(Color.lightGray); + resize(600,350); + + // Create a rectangle in the real plane for points. + w = new RealWindow(0.0f, 0.0f, 1.0f, 1.0f); + + // Create array of triangulations, including random points. + triangulation = new Triangulation[nAlgorithms]; + triangulation[0] = new Triangulation(nPoints); + triangulation[0].randomPoints(w); + for (int i = 1; i < nAlgorithms; i++) { + triangulation[i] = new Triangulation(nPoints); + triangulation[i].copyPoints(triangulation[0]); + } + + // Create an array of triangulation algorithms. + algorithm = new TriangulationAlgorithm[nAlgorithms]; + algorithm[0] = new QuadraticAlgorithm(triangulation[0], w, nPoints); + algorithm[1] = new CubicAlgorithm(triangulation[1], w, nPoints); + algorithm[2] = new QuarticAlgorithm(triangulation[2], w, nPoints); + + // Array of thread references (one for each algorithm). + triangulateThread = new Thread[nAlgorithms]; + + // Create user interface. + Panel heading = new Panel(); + heading.setLayout(new BorderLayout()); + heading.add("Center", new Label("The Triangulator", Label.CENTER)); + Panel algHeadings = new Panel(); + algHeadings.setLayout(new GridLayout(0, nAlgorithms)); + for (int i = 0; i < nAlgorithms; i++) + algHeadings.add(new Label(algorithm[i].algName, Label.CENTER)); + heading.add("South", algHeadings); + canvases = new Panel(); + canvases.setLayout(new GridLayout(0, nAlgorithms)); + for (int i = 0; i < nAlgorithms; i++) + canvases.add(algorithm[i].canvas()); + setLayout(new BorderLayout()); + add("North", heading); + add("Center", canvases); + appUI = new AppletUI(algorithm); + add("South", appUI); + } + + /* + * Called for each algorithm thread when started. + */ + public void run() { + int algNo; + String threadName; + + // Work out which algorithm to run from the thread name. + threadName = Thread.currentThread().getName(); + algNo = Integer.parseInt(threadName.substring(threadName.length()-1)); + algorithm[algNo].triangulate(triangulation[algNo]); + } + + public Insets insets() { + // Right offset is more than left, due to Choice bug. + return new Insets(5,10,5,15); + } + + /* + * Actually start the triangulation algorithms running. + */ + private synchronized void startTriangulate() { + for (int i = 0; i < triangulateThread.length; i++) + if (triangulateThread[i] != null && triangulateThread[i].isAlive()) { + stop(); + } + for (int i = 0; i < triangulateThread.length; i++) { + if (algorithm[i].control().getRun()) { + triangulateThread[i] = new Thread(this, "Triangulation-" + String.valueOf(i)); + triangulateThread[i].setPriority(Thread.MIN_PRIORITY); + triangulateThread[i].start(); + } + } + } + + /* + * Generate new points for the algorithms. + */ + private synchronized void newPoints() { + int max, alg; + + stop(); + + // Find algorithm with max points. + max = 0; + alg = -1; + for (int i = 0; i < nAlgorithms; i++) + if (algorithm[i].control().getRun() && + algorithm[i].control().getNPoints() > max) { + max = algorithm[i].control().getNPoints(); + alg = i; + } + + // Generate maximum number of points. + if (alg != -1) { + triangulation[alg].setNPoints(algorithm[alg].control().getNPoints()); + triangulation[alg].randomPoints(w); + } + + /* Now copy points into other algorithms. This has the effect + * that algorithms with the same number of points wind up with + * the same points. + */ + for (int i = 0; i < nAlgorithms; i++) + if (algorithm[i].control().getRun() && i != alg) { + triangulation[i].setNPoints(algorithm[i].control().getNPoints()); + triangulation[i].copyPoints(triangulation[alg]); + } + + for (int i = 0; i < nAlgorithms; i++) + if (algorithm[i].control().getRun()) { + algorithm[i].reset(); + algorithm[i].canvas().repaint(); + } + } + + /* + * Stop the applet. Kill the triangulation algorithm if + * still triangulating. + */ + + public synchronized void stop() { + for (int i = 0; i < triangulateThread.length; i++) { + if (triangulateThread[i] != null) { + try { + triangulateThread[i].stop(); + } catch (IllegalThreadStateException e) {} + triangulateThread[i] = null; + } + } + } + + /* + * Gets the current value in a text field. + */ + int getValue(TextField tF) { + int i; + try { + i = Integer.valueOf(tF.getText()).intValue(); + } catch (java.lang.NumberFormatException e) { + i = 0; + } + return i; + } + + /* + * Handle main level events. + */ + public boolean handleEvent(Event evt) { + if (evt.id == Event.ACTION_EVENT) { + if ("Start".equals(evt.arg)) { + startTriangulate(); + return true; + } else if ("Stop".equals(evt.arg)) { + stop(); + return true; + } else if ("New".equals(evt.arg)) { + newPoints(); + } else if ("Manual".equals(evt.arg)) { + for (int i = 0; i < nAlgorithms; i++) + algorithm[i].control().setManualAnimateMode(); + return true; + } else if ("Auto".equals(evt.arg)) { + for (int i = 0; i < nAlgorithms; i++) + algorithm[i].control().setAutomaticAnimateMode(); + return true; + } + } else if (evt.id == Event.MOUSE_DOWN) { + // These events only occur in the canvases. + for (int i = 0; i < nAlgorithms; i++) + if (algorithm[i].control().mode() == AnimateControl.manual) + algorithm[i].nextStep(); + return true; + } else if (evt.id == Event.MOUSE_MOVE) { + return true; + } + + return false; + } +} diff --git a/dasCore/src/main/java/org/das2/math/fft/ComplexArray.java b/dasCore/src/main/java/org/das2/math/fft/ComplexArray.java new file mode 100644 index 000000000..b1e76f60d --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/fft/ComplexArray.java @@ -0,0 +1,276 @@ +/* + * ComplexArray.java + * + * Created on November 29, 2004, 9:34 PM + */ + +package org.das2.math.fft; + +/** + * Interface for passing complex arrays to and from FFT routines. The intent is + * that the complex array can be backed by data in any format. Each elements is + * readable and writeable via get and set methods for the real and imaginary components. + * @author Jeremy + */ +public class ComplexArray { + /** + * A complex array that is accessed by floats + */ + public interface Float { + /** + * + * @param i + * @return the real component of the complex element i. + */ + float getReal(int i); + /** + * + * @param i + * @return the imaginary component of the complex element i. + */ + float getImag(int i); + /** + * + * @param i the element index + * @param value the real component, the imaginary is not changed. + */ + void setReal(int i,float value); + /** + * + * @param i the element index + * @param value the imaginary component, the real is not changed. + */ + void setImag(int i,float value); + /** + * returns the number of elements + * @return the number of elements in the array + */ + int length(); + } + + /** + * ComplexArray that is accessed by doubles + */ + public interface Double { + /** + * + * @param i + * @return the real component of the complex element i. + */ + double getReal(int i); + + /** + * + * @param i + * @return the imaginary component of the complex element i. + */ + double getImag(int i); + /** + * + * @param i the element index + * @param value the real component, the imaginary is not changed. + */ + void setReal(int i,double value); + /** + * + * @param i the element index + * @param value the imaginary component, the real is not changed. + */ + void setImag(int i,double value); + /** + * @return the number of elements in the array + */ + int length(); + } + + /** + * Implements ComplexArray that is backed by two float arrays. + */ + public static final class ComplexArrayDoubleDouble implements Double { + final double[] real; + final double[] imaginary; + private ComplexArrayDoubleDouble( final double[] real, final double[] imaginary ) { + this.real= real; + this.imaginary= imaginary; + } + + /** + * + * @param i + * @return the imaginary component of the complex element i. + */ + public double getImag(int i) { + return imaginary[i]; + } + + /** + * + * @param i + * @return the real component of the complex element i. + */ + public double getReal(int i) { + return real[i]; + } + + /** + * + * @param i the element index + * @param value the imaginary component, the real is not changed. + */ + public void setImag(int i, double value) { + imaginary[i]= value; + } + + /** + * + * @param i the element index + * @param value the real component, the imaginary is not changed. + */ + public void setReal(int i, double value) { + real[i]= value; + } + + /** + * @return the number of elements in the array + */ + public int length() { + return real.length; + } + + } + + /** + * Implements ComplexArray that is backed by two float arrays. + */ + public static final class ComplexArrayFloatFloat implements Float { + final float[] real; + final float[] imaginary; + private ComplexArrayFloatFloat( final float[] real, final float[] imaginary ) { + this.real= real; + this.imaginary= imaginary; + } + public float getImag(int i) { + return imaginary[i]; + } + + public float getReal(int i) { + return real[i]; + } + + public void setImag(int i, float value) { + imaginary[i]= value; + } + + public void setReal(int i, float value) { + real[i]= value; + } + public int length() { + return real.length; + } + + } + + /** + * Creates a new ComplexArray from an array of real numbers. The complex + * components of each element in the resulting array is zero. + */ + public static ComplexArray.Double newArray( double[] real ) { + double[] imag= new double[real.length]; + return new ComplexArrayDoubleDouble( real, imag ); + } + + /** + * Creates a new ComplexArray from a float array representing real numbers, but + * copies the original array so that it is not modified. + */ + public static ComplexArray.Double newArrayCopy( double[] real ) { + double[] imag= new double[real.length]; + double[] realCopy= new double[real.length]; + for (int i=0; iMath.pow(2,100)) throw new IllegalArgumentException("n too big or too small, n="+n); + //this.real= real; + this.real= false; // funny real,false... + if ( doublePrecision ) { + complexDoubleFFT= new ComplexDoubleFFT_Mixed(n); + } else { + complexFloatFFT= new ComplexFloatFFT_Mixed(n); + } + if ( this.real ) { + if ( n%2 != 0 ) throw new IllegalArgumentException("n must be even"); + if ( doublePrecision ) { + realDoubleFFT= new RealDoubleFFT_Even(n); + } else { + throw new UnsupportedOperationException("not implemented"); + } + } else { + if ( doublePrecision ) { + complexDoubleFFT= new ComplexDoubleFFT_Mixed(n); + } else { + complexFloatFFT= new ComplexFloatFFT_Mixed(n); + } + } + } + + /** + * creates an FFT object that operates on a ComplexArray.Float of n elements. + */ + public static GeneralFFT newFloatFFT( int n ) { + return new GeneralFFT( n, false, false ); + } + + /** + * creates an FFT object that operates on a ComplexArray.Double of n elements. + */ + public static GeneralFFT newDoubleFFT( int n ) { + return new GeneralFFT( n, true, false ); + } + + /** + * perform the forward transform on the array in situ. + */ + public void transform( ComplexArray.Double data ) { + if ( !doublePrecision ) throw new IllegalArgumentException("expected float arrays, got doubles"); + double norm; + if ( real ) { + realDoubleFFT.transform( data ); + norm= realDoubleFFT.normalization(); + } else { + complexDoubleFFT.transform( data ); + norm = complexDoubleFFT.normalization(); + } + for (int i = 0; i < n; i++) { + data.setReal( i, data.getReal(i) * norm ); + data.setImag( i, data.getImag(i) * norm ); + } + } + + /** + * perform the forward transform on the array in situ. + */ + public void transform( ComplexArray.Float data ) { + if ( doublePrecision ) throw new IllegalArgumentException("expected double arrays, got floats"); + complexFloatFFT.transform( data ); + float norm = complexFloatFFT.normalization(); + for (int i = 0; i < n; i++) { + data.setReal( i, data.getReal(i) * norm ); + data.setImag( i, data.getImag(i) * norm ); + } + } + + /** + * perform the inverse transform on the array in situ. + */ + public void invTransform( ComplexArray.Double data ) { + if ( !doublePrecision ) throw new IllegalArgumentException("expected float arrays, got doubles"); + complexDoubleFFT.inverse( data ); + } + + /** + * perform the inverse transform on the array in situ. + */ + public void invTransform( ComplexArray.Float data ) { + if ( doublePrecision ) throw new IllegalArgumentException("expected double arrays, got floats"); + complexFloatFFT.inverse( data ); + } + +} diff --git a/dasCore/src/main/java/org/das2/math/fft/SimpleFFT.java b/dasCore/src/main/java/org/das2/math/fft/SimpleFFT.java new file mode 100644 index 000000000..511ac8821 --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/fft/SimpleFFT.java @@ -0,0 +1,90 @@ +/* File: SimpleFFT.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on December 22, 2003, 11:29 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.math.fft; + +/** + * + * @author Edward West + */ +public final class SimpleFFT { + + private static final double LOG_2 = Math.log(2); + + /** Creates a new instance of SimpleFFT */ + private SimpleFFT() { + } + + public static double[][] fft( double[][] array ) { + double u_r,u_i, w_r,w_i, t_r,t_i; + int ln, nv2, k, l, le, le1, j, ip, i, n; + + n = array.length; + ln = (int)( Math.log( (double)n )/LOG_2 + 0.5 ); + nv2 = n / 2; + j = 1; + for (i = 1; i < n; i++ ) { + if (i < j) { + t_r = array[i - 1][0]; + t_i = array[i - 1][1]; + array[i - 1][0] = array[j - 1][0]; + array[i - 1][1] = array[j - 1][1]; + array[j - 1][0] = t_r; + array[j - 1][1] = t_i; + } + k = nv2; + while (k < j) { + j = j - k; + k = k / 2; + } + j = j + k; + } + + for (l = 1; l <= ln; l++) {/* loops thru stages */ + le = (int)(Math.exp( (double)l * LOG_2 ) + 0.5 ); + le1 = le / 2; + u_r = 1.0; + u_i = 0.0; + w_r = Math.cos( Math.PI / (double)le1 ); + w_i = -Math.sin( Math.PI / (double)le1 ); + for (j = 1; j <= le1; j++) {/* loops thru 1/2 twiddle values per stage */ + for (i = j; i <= n; i += le) {/* loops thru points per 1/2 twiddle */ + ip = i + le1; + t_r = array[ip - 1][0] * u_r - u_i * array[ip - 1][1]; + t_i = array[ip - 1][1] * u_r + u_i * array[ip - 1][0]; + + array[ip - 1][0] = array[i - 1][0] - t_r; + array[ip - 1][1] = array[i - 1][1] - t_i; + + array[i - 1][0] = array[i - 1][0] + t_r; + array[i - 1][1] = array[i - 1][1] + t_i; + } + t_r = u_r * w_r - w_i * u_i; + u_i = w_r * u_i + w_i * u_r; + u_r = t_r; + } + } + return array; + } + +} diff --git a/dasCore/src/main/java/org/das2/math/fft/WaveformToSpectrum.java b/dasCore/src/main/java/org/das2/math/fft/WaveformToSpectrum.java new file mode 100644 index 000000000..8949233bb --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/fft/WaveformToSpectrum.java @@ -0,0 +1,222 @@ +/* + * WaveformToSpectrum.java + * + * Created on March 25, 2004, 9:09 PM + */ + +package org.das2.math.fft; + +import org.das2.dataset.ClippedVectorDataSet; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.TableDataSetBuilder; +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.UnitsUtil; + +/** + * + * @author jbf + */ +public class WaveformToSpectrum { + + static class UnitsInverter { + static Units getInverseUnit( Units unit ) { + if ( unit==Units.seconds ) { + return Units.hertz; + } else if ( unit==Units.dimensionless ) { + return Units.dimensionless; + } else { + throw new IllegalArgumentException( "units not supported: "+unit ); + } + } + } + + static DatumVector getFrequencyDomainTags( DatumVector timeDomainTags ) { + Units timeUnit= timeDomainTags.getUnits(); + double[] x= timeDomainTags.toDoubleArray(timeUnit); + double[] result= new double[x.length]; + result[0]= 0.; + double T= x[1]-x[0]; + int n= x.length; + int n21= n/2+1; + for ( int i=0; i 0.01 && rr < 0.09 ) { + return false; + } + } + return true; + } + } + + public static double[][] fft( double[][] array ) { + double u_r,u_i, w_r,w_i, t_r,t_i; + int ln, nv2, k, l, le, le1, j, ip, i, n; + + n = array[0].length; + ln = (int)( Math.log( (double)n )/LOG_2 + 0.5 ); + if ( !(Math.pow(2,ln)==n) ) { + throw new IllegalArgumentException( "input array (["+array.length+"]["+n+"]) is not [2][2^k]" ); + } + nv2 = n / 2; + j = 1; + for (i = 1; i < n; i++ ) { + if (i < j) { + t_r = array[0][i - 1]; + t_i = array[1][i - 1]; + array[0][i - 1] = array[0][j - 1]; + array[1][i - 1] = array[1][j - 1]; + array[0][j - 1] = t_r; + array[1][j - 1] = t_i; + } + k = nv2; + while (k < j) { + j = j - k; + k = k / 2; + } + j = j + k; + } + + for (l = 1; l <= ln; l++) {/* loops thru stages */ + le = (int)(Math.exp( (double)l * LOG_2 ) + 0.5 ); + le1 = le / 2; + u_r = 1.0; + u_i = 0.0; + w_r = Math.cos( Math.PI / (double)le1 ); + w_i = -Math.sin( Math.PI / (double)le1 ); + for (j = 1; j <= le1; j++) {/* loops thru 1/2 twiddle values per stage */ + for (i = j; i <= n; i += le) {/* loops thru points per 1/2 twiddle */ + ip = i + le1; + t_r = array[0][ip - 1] * u_r - u_i * array[1][ip - 1]; + t_i = array[1][ip - 1] * u_r + u_i * array[0][ip - 1]; + + array[0][ip - 1] = array[0][i - 1] - t_r; + array[1][ip - 1] = array[1][i - 1] - t_i; + + array[0][i - 1] = array[0][i - 1] + t_r; + array[1][i - 1] = array[1][i - 1] + t_i; + } + t_r = u_r * w_r - w_i * u_i; + u_i = w_r * u_i + w_i * u_r; + u_r = t_r; + } + } + return array; + } + + public static TableDataSet getTableDataSet2( VectorDataSet vds, int windowSize ) { + GeneralFFT fft= GeneralFFT.newDoubleFFT( windowSize ); + + if ( !checkXTagsGrid(vds) ) { + throw new IllegalArgumentException( "xtags don't appear to be gridded" ); + } + + Units xUnits= vds.getXUnits(); + + double[] yt= FFTUtil.getFrequencyDomainTags( 1 / ( vds.getXTagDouble(1,xUnits) - vds.getXTagDouble(0,xUnits) ), windowSize/2 ); + DatumVector yTags= DatumVector.newDatumVector(yt,UnitsUtil.getInverseUnit(xUnits.getOffsetUnits())); + + Units zUnits= vds.getYUnits(); + TableDataSetBuilder tdsb= new TableDataSetBuilder( vds.getXUnits(), yTags.getUnits(), zUnits ); + + int nTableXTags= vds.getXLength() / windowSize; + + VectorDataSet window= FFTUtil.getWindow10PercentEdgeCosine(windowSize); + double[] d= new double[windowSize/2]; + for ( int i=0; i 0.01 && rr < 0.09 ) { + return false; + } + } + return true; + } + } + + /** Creates a new instance of WindowTableDataSet */ + public WindowTableDataSet( VectorDataSet source, int windowSize ) { + this.source= source; + this.windowSize= windowSize; + if ( !checkXTagsGrid(source) ) { + throw new IllegalArgumentException("xTags don't appear to be gridded"); + } + if ( source.getXLength()method, implement the + * FFT using some particular method. + *

    + * Complex data is represented by 2 double values in sequence: the real and imaginary + * parts. Thus, in the default case (i0=0, stride=2), N data points is represented + * by a double array dimensioned to 2*N. To support 2D (and higher) transforms, + * an offset, i0 (where the first element starts) and stride (the distance from the + * real part of one value, to the next: at least 2 for complex values) can be supplied. + * The physical layout in the array data, of the mathematical data d[i] is as follows: + *

    + *    Re(d[i]) = data[i0 + stride*i]
    + *    Im(d[i]) = data[i0 + stride*i+1]
    + *
    + * The transformed data is returned in the original data array in + * wrap-around order. + * + * @author Bruce R. Miller bruce.miller@nist.gov + * @author Contribution of the National Institute of Standards and Technology, + * @author not subject to copyright. + */ +public abstract class ComplexDoubleFFT { + + int n; + + /** Create an FFT for transforming n points of complex, double precision data. */ + public ComplexDoubleFFT(int n){ + if (n <= 0) + throw new IllegalArgumentException("The transform length must be >=0 : "+n); + this.n = n; } + + /** Creates an instance of a subclass of ComplexDoubleFFT appropriate for data + * of n elements.*/ + public ComplexDoubleFFT getInstance(int n){ + return new ComplexDoubleFFT_Mixed(n); } + + protected void checkData( ComplexArray.Double data, int i0, int stride){ + if (i0 < 0) + throw new IllegalArgumentException("The offset must be >=0 : "+i0); + if (stride < 1) + throw new IllegalArgumentException("The stride must be >=1 : "+stride); + if (i0+stride*(n-1) >= data.length()) + throw new IllegalArgumentException("The data array is too small for "+n+":"+ + "i0="+i0+" stride="+stride+ + " data.length="+data.length()); } + + /** Compute the Fast Fourier Transform of data leaving the result in data. + * The array data must be dimensioned (at least) 2*n, consisting of alternating + * real and imaginary parts. */ + public void transform( ComplexArray.Double data ) { + transform(data, 0,1); + } + + + /** Compute the Fast Fourier Transform of data leaving the result in data. + * The array data must contain the data points in the following locations: + *
    +     *    Re(d[i]) = data[i0 + stride*i]
    +     *    Im(d[i]) = data[i0 + stride*i+1]
    +     *
    + */ + public abstract void transform( ComplexArray.Double data, int i0, int stride ); + + public void backtransform( ComplexArray.Double data){ + backtransform(data,0,1); + } + + /** Compute the (unnomalized) inverse FFT of data, leaving it in place.*/ + public abstract void backtransform( ComplexArray.Double data, int i0, int stride ); + + /** Return the normalization factor. + * Multiply the elements of the backtransform'ed data to get the normalized inverse.*/ + public double normalization(){ + return 1.0/n; } + + + /** Compute the (nomalized) inverse FFT of data, leaving it in place. + */ + public void inverse( ComplexArray.Double data ) { + backtransform(data,0,2); + + /* normalize inverse fft with 1/n */ + double norm = normalization(); + for (int i = 0; i < n; i++) { + data.setReal( i, data.getReal(i) * norm ); + data.setImag( i, data.getImag(i) * norm ); + } + } +} \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/math/fft/jnt/ComplexDoubleFFT_Mixed.java b/dasCore/src/main/java/org/das2/math/fft/jnt/ComplexDoubleFFT_Mixed.java new file mode 100644 index 000000000..d8ef2b367 --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/fft/jnt/ComplexDoubleFFT_Mixed.java @@ -0,0 +1,951 @@ +package org.das2.math.fft.jnt; + +import org.das2.math.fft.ComplexArray; + +/** Computes FFT's of complex, double precision data of arbitrary length n. + * This class uses the Mixed Radix method; it has special methods to handle + * factors 2, 3, 4, 5, 6 and 7, as well as a general factor. + *

    + * This method appears to be faster than the Radix2 method, when both methods apply, + * but requires extra storage (which ComplexDoubleFFT_Mixed manages itself). + *

    + * See {@link ComplexDoubleFFT ComplexDoubleFFT} for details of data layout. + * + * @author Bruce R. Miller bruce.miller@nist.gov + * @author Contribution of the National Institute of Standards and Technology, + * @author not subject to copyright. + * @author Derived from GSL (Gnu Scientific Library) + * @author GSL's FFT Code by Brian Gough bjg@vvv.lanl.gov + * @author Since GSL is released under + * @author GPL, + * @author this package must also be. + */ +public class ComplexDoubleFFT_Mixed extends ComplexDoubleFFT{ + static final double PI = Math.PI; + + public ComplexDoubleFFT_Mixed(int n){ + super(n); + setup_wavetable(n); + } + + public void transform( ComplexArray.Double data, int i0, int stride) { + checkData(data,i0,stride); + transform_internal(data, i0, stride, -1); } + + public void backtransform ( ComplexArray.Double data, int i0, int stride){ + checkData(data,i0,stride); + transform_internal(data, i0, stride, +1); } + + /*______________________________________________________________________ + Setting up the Wavetable */ + + private int factors[]; + // Reversed the last 2 levels of the twiddle array compared to what the C version had. + private double twiddle[][][]; + private int available_factors[]={7, 6, 5, 4, 3, 2}; + + void setup_wavetable(int n){ + + if (n <= 0) + throw new Error("length must be positive integer : "+n); + this.n = n; + + factors = Factorize.factor(n, available_factors); + + double d_theta = -2.0 * PI / ((double) n); + int product = 1; + twiddle = new double[factors.length][][]; + for (int i = 0; i < factors.length; i++) { + int factor = factors[i]; + int product_1 = product; /* product_1 = p_(i-1) */ + product *= factor; + int q = n / product; + + twiddle[i] = new double[q+1][2*(factor-1)]; + double twid[][] = twiddle[i]; + for(int j=1; jmethod, implement the + * FFT using some particular method. + *

    + * Complex data is represented by 2 double values in sequence: the real and imaginary + * parts. Thus, in the default case (i0=0, stride=2), N data points is represented + * by a double array dimensioned to 2*N. To support 2D (and higher) transforms, + * an offset, i0 (where the first element starts) and stride (the distance from the + * real part of one value, to the next: at least 2 for complex values) can be supplied. + * The physical layout in the array data, of the mathematical data d[i] is as follows: + *

    +  *    Re(d[i]) = data[i0 + stride*i]
    +  *    Im(d[i]) = data[i0 + stride*i+1]
    +  *
    + * The transformed data is returned in the original data array in + * wrap-around order. + * + * @author Bruce R. Miller bruce.miller@nist.gov + * @author Contribution of the National Institute of Standards and Technology, + * @author not subject to copyright. + */ + +public abstract class ComplexFloatFFT { + + int n; + + /** Create an FFT for transforming n points of Complex, single precision data. */ + public ComplexFloatFFT(int n){ + if (n <= 0) + throw new IllegalArgumentException("The transform length must be >=0 : "+n); + this.n = n; } + + /** Creates an instance of a subclass of ComplexFloatFFT appropriate for data + * of n elements.*/ + public ComplexFloatFFT getInstance(int n){ + return new ComplexFloatFFT_Mixed(n); } + + protected void checkData(ComplexArray.Float data, int i0, int stride){ + if (i0 < 0) + throw new IllegalArgumentException("The offset must be >=0 : "+i0); + if (stride < 1) + throw new IllegalArgumentException("The stride must be >=2 : "+stride); + if (i0+stride*(n-1)+2 > data.length()) + throw new IllegalArgumentException("The data array is too small for "+n+":"+ + "i0="+i0+" stride="+stride+ + " data.length="+data.length()); } + + /** Compute the Fast Fourier Transform of data leaving the result in data. + * The array data must be dimensioned (at least) 2*n, consisting of alternating + * real and imaginary parts. */ + public void transform ( ComplexArray.Float data ) { + transform (data, 0,1); } + + + /** Compute the Fast Fourier Transform of data leaving the result in data. + * The array data must contain the data points in the following locations: + *
    +    *    Re(d[i]) = data[i0 + stride*i]
    +    *    Im(d[i]) = data[i0 + stride*i+1]
    +    *
    + */ + public abstract void transform ( ComplexArray.Float data, int i0, int stride); + + /** Compute the (unnomalized) inverse FFT of data, leaving it in place.*/ + public void backtransform ( ComplexArray.Float data ) { + backtransform(data,0,2); } + + /** Compute the (unnomalized) inverse FFT of data, leaving it in place. + * The frequency domain data must be in wrap-around order, and be stored + * in the following locations: + *
    +    *    Re(D[i]) = data[i0 + stride*i]
    +    *    Im(D[i]) = data[i0 + stride*i+1]
    +    *
    + */ + public abstract void backtransform (ComplexArray.Float data, int i0, int stride); + + /** Return the normalization factor. + * Multiply the elements of the backtransform'ed data to get the normalized inverse.*/ + public float normalization(){ + return 1.0f/((float) n); } + + /** Compute the (nomalized) inverse FFT of data, leaving it in place.*/ + public void inverse(ComplexArray.Float data) { + inverse(data,0,2); } + + /** Compute the (nomalized) inverse FFT of data, leaving it in place. + * The frequency domain data must be in wrap-around order, and be stored + * in the following locations: + *
    +    *    Re(D[i]) = data[i0 + stride*i]
    +    *    Im(D[i]) = data[i0 + stride*i+1]
    +    *
    + */ + public void inverse (ComplexArray.Float data, int i0, int stride) { + backtransform(data,0,2); + + /* normalize inverse fft with 1/n */ + float norm = normalization(); + for (int i = 0; i < n; i++) { + data.setReal( i, data.getReal(i) * norm ); + data.setImag( i, data.getImag(i) * norm ); + } + } +} diff --git a/dasCore/src/main/java/org/das2/math/fft/jnt/ComplexFloatFFT_Mixed.java b/dasCore/src/main/java/org/das2/math/fft/jnt/ComplexFloatFFT_Mixed.java new file mode 100644 index 000000000..d30b98d94 --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/fft/jnt/ComplexFloatFFT_Mixed.java @@ -0,0 +1,960 @@ +package org.das2.math.fft.jnt; + +import org.das2.math.fft.ComplexArray; + +/** Computes FFT's of complex, single precision data of arbitrary length n. + * This class uses the Mixed Radix method; it has special methods to handle + * factors 2, 3, 4, 5, 6 and 7, as well as a general factor. + *

    + * This method appears to be faster than the Radix2 method, when both methods apply, + * but requires extra storage (which ComplexDoubleFFT_Mixed manages itself). + * + *

    + * See {@link ComplexFloatFFT ComplexFloatFFT} for details of data layout. + * + * @author Bruce R. Miller bruce.miller@nist.gov + * @author Contribution of the National Institute of Standards and Technology, + * @author not subject to copyright. + * @author Derived from GSL (Gnu Scientific Library) + * @author GSL's FFT Code by Brian Gough bjg@vvv.lanl.gov + * @author Since GSL is released under + * @author GPL, + * @author this package must also be. + */ + +public class ComplexFloatFFT_Mixed extends ComplexFloatFFT{ + static final double PI = (float) Math.PI; + static final int FORWARD = -1; + static final int BACKWARD = +1; + + public ComplexFloatFFT_Mixed(int n){ + super(n); + setup_wavetable(n); + } + + public void transform(ComplexArray.Float data, int i0, int stride) { + checkData(data,i0,stride); + transform_internal(data, i0, stride, FORWARD); } + + public void backtransform(ComplexArray.Float data, int i0, int stride){ + checkData(data,i0,stride); + transform_internal(data, i0, stride, BACKWARD); } + + /*______________________________________________________________________ + Setting up the Wavetable */ + + private int factors[]; + // Reversed the last 2 levels of the twiddle array compared to what the C version had. + private float twiddle[][][]; + private int available_factors[]={7, 6, 5, 4, 3, 2}; + + void setup_wavetable(int n){ + + if (n <= 0) + throw new Error("length must be positive integer : "+n); + this.n = n; + + factors = Factorize.factor(n, available_factors); + + double d_theta = -2.0 * PI / ((double) n); + int product = 1; + twiddle = new float[factors.length][][]; + for (int i = 0; i < factors.length; i++) { + int factor = factors[i]; + int product_1 = product; /* product_1 = p_(i-1) */ + product *= factor; + int q = n / product; + + twiddle[i] = new float[q+1][2*(factor-1)]; + float twid[][] = twiddle[i]; + for(int j=1; jGPL, + * @author this package must also be. +*/ +public class Factorize { + + /** Return the prime factors of n. + * The method first extracts any factors in fromfactors, in order (which + * needn't actually be prime). Remaining factors in increasing order follow. */ + public static int[] factor (int n, int fromfactors[]){ + int factors[] = new int[64]; // Cant be more than 64 factors. + int nf = 0; + int ntest = n; + int factor; + + if (n <= 0) // Error case + throw new Error("Number ("+n+") must be positive integer"); + + /* deal with the preferred factors first */ + for(int i = 0; i < fromfactors.length && ntest != 1; i++){ + factor = fromfactors[i]; + while ((ntest % factor) == 0) { + ntest /= factor; + factors[nf++] = factor; }} + + /* deal with any other even prime factors (there is only one) */ + factor = 2; + while ((ntest % factor) == 0 && (ntest != 1)) { + ntest /= factor; + factors[nf++] = factor; } + + /* deal with any other odd prime factors */ + factor = 3; + while (ntest != 1) { + while ((ntest % factor) != 0) { + factor += 2; } + ntest /= factor; + factors[nf++] = factor; } + + /* check that the factorization is correct */ + int product = 1; + for (int i = 0; i < nf; i++) { + product *= factors[i]; } + if (product != n) + throw new Error("factorization failed for "+n); + + /* Now, make an array of the right length containing the factors... */ + int f[] = new int[nf]; + System.arraycopy(factors,0,f,0,nf); + return f; } + + /** Return the integer log, base 2, of n, or -1 if n is not an integral power of 2.*/ + public static int log2 (int n){ + int log = 0; + + for(int k=1; k < n; k *= 2, log++); + + if (n != (1 << log)) + return -1 ; /* n is not a power of 2 */ + return log; } +} + + + diff --git a/dasCore/src/main/java/org/das2/math/fft/jnt/RealDoubleFFT.java b/dasCore/src/main/java/org/das2/math/fft/jnt/RealDoubleFFT.java new file mode 100644 index 000000000..f937cc64c --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/fft/jnt/RealDoubleFFT.java @@ -0,0 +1,84 @@ +package org.das2.math.fft.jnt; + +import org.das2.math.fft.ComplexArray; +/** Abstract Class representing FFT's of real, double precision data. + * Concrete classes are typically named RealDoubleFFT_method, implement the + * FFT using some particular method. + *

    + * The physical layout of the mathematical data d[i] in the array data is as follows: + *

    +  *    d[i] = data[i0 + stride*i]
    +  *
    + * The FFT (D[i]) of real data (d[i]) is complex, but restricted by symmetry: + *
    +  *    D[n-i] = conj(D[i])
    +  *
    + * It turns out that there are still n `independent' values, so the transformation + * can still be carried out in-place. + * However, each Real FFT method tends to leave the real and imaginary parts + * distributed in the data array in its own unique arrangment. + *

    + * You must consult the documentation for the specific classes implementing + * RealDoubleFFT for the details. + * Note, however, that each class's backtransform and inverse methods understand + * thier own unique ordering of the transformed result and can invert it correctly. + * + * @author Bruce R. Miller bruce.miller@nist.gov + * @author Contribution of the National Institute of Standards and Technology, + * @author not subject to copyright. + */ + +public abstract class RealDoubleFFT { + int n; + + /** Create an FFT for transforming n points of real, double precision data. */ + public RealDoubleFFT(int n){ + if (n <= 0) + throw new IllegalArgumentException("The transform length must be >=0 : "+n); + this.n = n; } + + protected void checkData(double data[], int i0, int stride){ + if (i0 < 0) + throw new IllegalArgumentException("The offset must be >=0 : "+i0); + if (stride < 1) + throw new IllegalArgumentException("The stride must be >=1 : "+stride); + if (i0+stride*(n-1)+1 > data.length) + throw new IllegalArgumentException("The data array is too small for "+n+":"+ + "i0="+i0+" stride="+stride+ + " data.length="+data.length); } + + /** Compute the Fast Fourier Transform of data leaving the result in data. */ + public void transform ( ComplexArray.Double data) { + transform (data, 0,1); } + + /** Compute the Fast Fourier Transform of data leaving the result in data. */ + public abstract void transform ( ComplexArray.Double data, int i0, int stride); + + /** Compute the (unnomalized) inverse FFT of data, leaving it in place.*/ + public void backtransform (ComplexArray.Double data) { + backtransform(data,0,1); } + + /** Compute the (unnomalized) inverse FFT of data, leaving it in place.*/ + public abstract void backtransform (ComplexArray.Double data, int i0, int stride); + + /** Return the normalization factor. + * Multiply the elements of the backtransform'ed data to get the normalized inverse.*/ + public double normalization(){ + return 1.0/((double) n); } + + /** Compute the (nomalized) inverse FFT of data, leaving it in place.*/ + public void inverse(ComplexArray.Double data) { + inverse(data,0,1); } + + /** Compute the (nomalized) inverse FFT of data, leaving it in place.*/ + public void inverse (ComplexArray.Double data, int i0, int stride) { + backtransform(data, i0, stride); + /* normalize inverse fft with 1/n */ + double norm = normalization(); + for (int i = 0; i < n; i++) { + data.setReal( i, data.getReal(i) * norm ); + data.setImag( i, data.getImag(i) * norm ); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/math/fft/jnt/RealDoubleFFT_Even.java b/dasCore/src/main/java/org/das2/math/fft/jnt/RealDoubleFFT_Even.java new file mode 100644 index 000000000..9e8b0c92f --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/fft/jnt/RealDoubleFFT_Even.java @@ -0,0 +1,123 @@ +package org.das2.math.fft.jnt; + +import org.das2.math.fft.ComplexArray; +/** Computes FFT's of real, double precision data when n is even, by + * computing complex FFT. + * + * @author Bruce R. Miller bruce.miller@nist.gov + * @author Derived from Numerical Methods. + * @author Contribution of the National Institute of Standards and Technology, + * @author not subject to copyright. + */ + +public class RealDoubleFFT_Even extends RealDoubleFFT { + ComplexDoubleFFT fft; + + /** Create an FFT for transforming n points of real, double precision data. */ + public RealDoubleFFT_Even(int n){ + super(n); + if (true) throw new RuntimeException("Not verified--and it looks bad!"); + if (n%2 != 0) + throw new IllegalArgumentException(n+" is not even"); + fft = new ComplexDoubleFFT_Mixed(n/2); + } + + /** Compute the Fast Fourier Transform of data leaving the result in data. */ + public void transform (ComplexArray.Double data) { + fft.transform(data); + shuffle(data,+1); + } + + /** Return data in wraparound order. + * i0 and stride are used to traverse data; the new array is in + * packed (i0=0, stride=1) format. + * @see wraparound format */ + public ComplexArray.Double toWraparoundOrder(ComplexArray.Double data){ + ComplexArray.Double newdata = ComplexArray.newArray( new double[n], new double[n] ); + int nh = n/2; + newdata.setReal(0,data.getReal(0)); + newdata.setReal(1,0.0); + newdata.setReal(n,data.getReal(1)); + newdata.setImag(n,0.0); + for(int i=1; iwraparound format */ + public double[] toWraparoundOrder(ComplexArray.Double data, int i0, int stride) { + throw new Error("Not Implemented!"); } + + + /** Compute the (unnomalized) inverse FFT of data, leaving it in place.*/ + public void backtransform (ComplexArray.Double data){ + shuffle(data,-1); + fft.backtransform(data); + } + + private void shuffle(ComplexArray.Double data, int sign){ + int nh = n/2; + int nq = n/4; + double c1=0.5, c2 = -0.5*sign; + double theta = sign*Math.PI/nh; + double wtemp = Math.sin(0.5*theta); + double wpr = -2.0*wtemp*wtemp; + double wpi = -Math.sin(theta); + double wr = 1.0+wpr; + double wi = wpi; + for(int i=1; i < nq; i++){ + int i1 = 2*i; + int i3 = n - i1; + double h1r = c1*(data.getReal(i1 )+data.getReal(i3)); + double h1i = c1*(data.getReal(i1+1)-data.getImag(i3)); + double h2r = -c2*(data.getReal(i1+1)+data.getImag(i3)); + double h2i = c2*(data.getReal(i1 )-data.getReal(i3)); + data.setReal(i1 ,h1r+wr*h2r-wi*h2i); + data.setImag(i1,h1i+wr*h2i+wi*h2r); + data.setReal(i3 ,h1r-wr*h2r+wi*h2i); + data.setImag(i3,-h1i+wr*h2i+wi*h2r); + wtemp = wr; + wr += wtemp*wpr-wi*wpi; + wi += wtemp*wpi+wi*wpr; } + double d0 = data.getReal(0); + if (sign == 1){ + data.setReal(0,d0+data.getReal(1)); + data.setReal(1,d0-data.getReal(1)); } + else { + data.setReal(0,c1*(d0+data.getReal(1))); + data.setReal(1,c1*(d0-data.getReal(1))); } + if (n%4==0) + data.setImag(nh, data.getImag(nh) * (-1) ); + } + + /** Compute the Fast Fourier Transform of data leaving the result in data. */ + public void transform (ComplexArray.Double data, int i0, int stride) { + throw new Error("Not Implemented!"); } + + + /** Compute the (unnomalized) inverse FFT of data, leaving it in place.*/ + public void backtransform (ComplexArray.Double data, int i0, int stride){ + throw new Error("Not Implemented!"); } + + /** Compute the (nomalized) inverse FFT of data, leaving it in place.*/ + public void inverse (ComplexArray.Double data, int i0, int stride){ + throw new Error("Not Implemented!"); } + + /** Return the normalization factor. + * Multiply the elements of the backtransform'ed data to get the normalized inverse.*/ + public double normalization(){ + return 2.0/((double) n); } + + /** Compute the (nomalized) inverse FFT of data, leaving it in place.*/ + public void inverse (ComplexArray.Double data) { + backtransform(data); + /* normalize inverse fft with 2/n */ + double norm = normalization(); + for (int i = 0; i < n; i++) + data.setReal( i, data.getReal(i) * norm ); + } + +} diff --git a/dasCore/src/main/java/org/das2/math/fft/jnt/Refactory.java b/dasCore/src/main/java/org/das2/math/fft/jnt/Refactory.java new file mode 100644 index 000000000..9cb343263 --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/fft/jnt/Refactory.java @@ -0,0 +1,160 @@ +/* + * Refactory.java + * + * Created on November 29, 2004, 10:58 PM + */ + +package org.das2.math.fft.jnt; + +import java.io.*; +import java.util.regex.*; + +/** + * + * @author Jeremy + */ +public class Refactory { + + // these are the variable names that will be affected + //static String vars="(in|out)"; // used for Complex + static String vars="(newdata|data)"; // used for Real + + /* + * replaces "double data[]" with "ComplexArray.Double data" + */ + public static void replaceComplexArrayDoubleDeclaration( String filename, String outfilename ) throws IOException { + BufferedReader in= new BufferedReader( new FileReader(filename) ); + BufferedWriter out= new BufferedWriter( new FileWriter(outfilename) ); + String line; + + Pattern setPattern= Pattern.compile("(.*)(double\\s*)"+vars+"(\\[\\])"+"(.*)"); + Matcher m; + int iline=0; + while (( line=in.readLine())!=null ) { + iline++; + if ((m= setPattern.matcher(line)).matches() ) { + String newLine= m.group(1)+"ComplexArray.Double "+m.group(3)+m.group(5); + out.write(newLine); + System.err.println(newLine); + out.newLine(); + } else { + out.write(line); + out.newLine(); + } + } + out.close(); + + } + + public static void setArrayImag( String filename, String outfilename ) throws IOException { + BufferedReader in= new BufferedReader( new FileReader(filename) ); + BufferedWriter out= new BufferedWriter( new FileWriter(outfilename) ); + String line; + Pattern setPattern= Pattern.compile("(\\s*)"+vars+"\\[(.*)\\s*\\+\\s*1\\s*\\]\\s*=\\s*(.*)\\s*;(.*)"); + Matcher m; + int iline=0; + while (( line=in.readLine())!=null ) { + iline++; + //if ( iline>836 ) { + // System.out.println(line); + //} + if ((m= setPattern.matcher(line)).matches() ) { + String newLine= m.group(1)+m.group(2)+".setImag("+m.group(3)+","+m.group(4)+");"+m.group(5); + out.write(newLine); + System.err.println(newLine); + out.newLine(); + } else { + out.write(line); + out.newLine(); + } + } + out.close(); + } + + public static void setArrayReal( String filename, String outfilename ) throws IOException { + BufferedReader in= new BufferedReader( new FileReader(filename) ); + BufferedWriter out= new BufferedWriter( new FileWriter(outfilename) ); + String line; + Pattern setPattern= Pattern.compile("(\\s*)"+vars+"\\[(.*)\\s*\\]\\s*=\\s*(.*)\\s*;(.*)"); + Matcher m; + while (( line=in.readLine())!=null ) { + if ((m= setPattern.matcher(line)).matches() ) { + String newLine= m.group(1)+m.group(2)+".setReal("+m.group(3)+","+m.group(4)+");"+m.group(5); + out.write(newLine); + System.err.println(newLine); + out.newLine(); + } else { + out.write(line); + out.newLine(); + } + } + out.close(); + } + + + public static void getArrayImag( String filename, String outfilename ) throws IOException { + BufferedReader in= new BufferedReader( new FileReader(filename) ); + BufferedWriter out= new BufferedWriter( new FileWriter(outfilename) ); + String line; + Pattern setPattern= Pattern.compile("(.*)"+vars+"\\[(.*)\\s*\\+\\s*1\\s*\\](.*)"); + Matcher m; + while (( line=in.readLine())!=null ) { + if ((m= setPattern.matcher(line)).matches() ) { + String newLine= m.group(1)+m.group(2)+".getImag("+m.group(3)+")"+m.group(4); + out.write(newLine); + System.err.println(newLine); + out.newLine(); + } else { + out.write(line); + out.newLine(); + } + } + out.close(); + } + + public static void getArrayReal( String filename, String outfilename ) throws IOException { + BufferedReader in= new BufferedReader( new FileReader(filename) ); + BufferedWriter out= new BufferedWriter( new FileWriter(outfilename) ); + String line; + Pattern setPattern= Pattern.compile("(.*)"+vars+"\\[(.*)\\s*\\](.*)"); + Matcher m; + while (( line=in.readLine())!=null ) { + if ((m= setPattern.matcher(line)).matches() ) { + String newLine= m.group(1)+m.group(2)+".getReal("+m.group(3)+")"+m.group(4); + out.write(newLine); + System.err.println(newLine); + out.newLine(); + } else { + out.write(line); + out.newLine(); + } + } + out.close(); + } + + /** + * @param args the command line arguments + */ + public static void main(String[] args) throws Exception { + //String filename=Refactory.class.getResource("RealDoubleFFT.java").getFile(); + String filename="C:\\Documents and Settings\\Jeremy\\Desktop\\das2Brief\\working\\edu\\uiowa\\physics\\pw\\das\\math\\fft\\jnt\\RealDoubleFFT_Even.java"; + String outfilename1= filename+".1.java"; + String outfilename2= filename+".2.java"; + replaceComplexArrayDoubleDeclaration(filename,outfilename1); + replaceComplexArrayDoubleDeclaration(outfilename1,outfilename2); + setArrayImag(outfilename2,outfilename1); + setArrayImag(outfilename1,outfilename2); + setArrayImag(outfilename2,outfilename1); + setArrayReal(outfilename1,outfilename2); + setArrayReal(outfilename2,outfilename1); + getArrayImag(outfilename2,outfilename1); + getArrayImag(outfilename1,outfilename2); + getArrayReal(outfilename1,outfilename2); + getArrayReal(outfilename2,outfilename1); + getArrayReal(outfilename1,filename+".java"); + System.out.println("result is in "+filename+".java"); + System.out.flush(); + + } + +} diff --git a/dasCore/src/main/java/org/das2/math/fft/jnt/package.html b/dasCore/src/main/java/org/das2/math/fft/jnt/package.html new file mode 100644 index 000000000..4cc3fc616 --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/fft/jnt/package.html @@ -0,0 +1,4 @@ + + This is the NIST FFT package, modified to use the ComplexArray interface. Refactory is +the java code that was used to transform the code. + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/math/fft/package.html b/dasCore/src/main/java/org/das2/math/fft/package.html new file mode 100644 index 000000000..0b963bf21 --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/fft/package.html @@ -0,0 +1,7 @@ + + Classes for performing generalized FFT transformations. These codes are based on the + jnt.FFT package from NIST, with the interface changed so that data copies can be avoided and + to improve useability. (The NIST package had implicit specifications about how to load arrays, and + instead we use the ComplexArray interface.) Also classes are provided for common fft operations, + such as transforming a waveform into a spectrogram. + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/math/matrix/ArrayMatrix.java b/dasCore/src/main/java/org/das2/math/matrix/ArrayMatrix.java new file mode 100644 index 000000000..e40585b12 --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/matrix/ArrayMatrix.java @@ -0,0 +1,96 @@ +/* + * ArrayMatrix.java + * + * Created on March 7, 2005, 6:48 PM + */ + +package org.das2.math.matrix; + +/** + * + * @author eewest + */ +public class ArrayMatrix extends Matrix { + + private double[] array; + + public ArrayMatrix(int rows, int columns) { + super(rows, columns); + this.array = new double[rows * columns]; + } + + public ArrayMatrix(double[] array, int rows, int columns) { + super(rows, columns); + this.array = array; + if (rows * columns != array.length) { + throw new IllegalArgumentException("Array must be (rows * columns) in length"); + } + } + + public ArrayMatrix(Matrix m) { + super(m.rowCount(), m.columnCount()); + if (m instanceof ArrayMatrix) { + array = (double[])((ArrayMatrix)m).array.clone(); + } + else { + array = new double[nRow * nCol]; + super.copy(m); + } + } + + public void copy(Matrix m) { + if (nRow != m.rowCount() || nCol != m.columnCount()) { + throw new IllegalArgumentException(); + } + + if (m instanceof ArrayMatrix) { + System.arraycopy(((ArrayMatrix)m).array, 0, array, 0, array.length); + } + else { + super.copy(m); + } + } + + public double get(int row, int col) { + return array[row * nCol + col]; + } + + public void rowTimes(int row, double s) { + for (int iCol = 0; iCol < nCol; iCol++) { + array[iCol + row * nCol] *= s; + } + } + + public void rowTimesAddTo(int srcRow, double s, int dstRow) { + double[] tmp; + + if (srcRow == dstRow) { + rowTimes(srcRow, s + 1); + } + + for (int iCol = 0; iCol < nCol; iCol++) { + array[iCol + dstRow * nCol] += array[iCol + srcRow * nCol] * s; + } + } + + public void set(int row, int col, double d) { + array[col + row * nCol] = d; + } + + public void swapRows(int row1, int row2) { + double[] tmp; + int off1, off2; + + if (row1 == row2) { + return; + } + + tmp = new double[nCol]; + off1 = row1 * nCol; + off2 = row2 * nCol; + System.arraycopy(array, off1, tmp, 0, nCol); + System.arraycopy(array, off2, array, off1, nCol); + System.arraycopy(tmp, 0, array, off2, nCol); + } + +} diff --git a/dasCore/src/main/java/org/das2/math/matrix/CompositeMatrix.java b/dasCore/src/main/java/org/das2/math/matrix/CompositeMatrix.java new file mode 100644 index 000000000..f1bb297fc --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/matrix/CompositeMatrix.java @@ -0,0 +1,53 @@ +/* + * CompositeMatrix.java + * + * Created on March 7, 2005, 8:13 PM + */ + +package org.das2.math.matrix; + +/** All of the elementary row and column operations are applied to both + * underlying matrices. Reads are done from the first matrix. Writes + * are not allowed (except those that are side effects of elementary + * matrix operations). + * + * @author Edward West + */ +public class CompositeMatrix extends Matrix { + + private Matrix m1; + private Matrix m2; + + public CompositeMatrix(Matrix m1, Matrix m2) { + super(m1.rowCount(), m1.columnCount()); + if (m1.rowCount() != m2.rowCount() && m1.columnCount() != m2.columnCount()) { + throw new IllegalArgumentException("m1 and m2 must have the same number of rows and columns"); + } + this.m1 = m1; + this.m2 = m2; + } + + public double get(int row, int col) { + return m1.get(row, col); + } + + public void rowTimes(int row, double s) { + m1.rowTimes(row, s); + m2.rowTimes(row, s); + } + + public void rowTimesAddTo(int srcRow, double s, int dstRow) { + m1.rowTimesAddTo(srcRow, s, dstRow); + m2.rowTimesAddTo(srcRow, s, dstRow); + } + + public void set(int row, int col, double d) { + throw new UnsupportedOperationException("Setting values not supported"); + } + + public void swapRows(int row1, int row2) { + m1.swapRows(row1, row2); + m2.swapRows(row1, row2); + } + +} diff --git a/dasCore/src/main/java/org/das2/math/matrix/Matrix.java b/dasCore/src/main/java/org/das2/math/matrix/Matrix.java new file mode 100644 index 000000000..7b75d2ed0 --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/matrix/Matrix.java @@ -0,0 +1,63 @@ +/* + * Matrix.java + * + * Created on March 7, 2005, 6:33 PM + */ + +package org.das2.math.matrix; + +import java.text.DecimalFormat; + +/** + * + * @author eewest + */ +public abstract class Matrix { + + protected final int nRow; + + protected final int nCol; + + protected Matrix(int rows, int columns) { + nRow = rows; + nCol = columns; + } + + public int rowCount() { + return nRow; + } + + public int columnCount() { + return nCol; + } + + public void copy(Matrix m) { + for (int iRow = 0; iRow < nRow; iRow++) { + for (int iCol = 0; iCol < nCol; iCol++) { + set(iRow, iCol, m.get(iRow, iCol)); + } + } + } + + public abstract double get(int row, int col); + + public abstract void set(int row, int col, double d); + + public abstract void swapRows(int row1, int row2); + + public abstract void rowTimes(int row, double s); + + public abstract void rowTimesAddTo(int srcRow, double s, int dstRow); + + public String toString() { + StringBuffer buf= new StringBuffer(); + buf.append("\n"); + for ( int i=0; i + Matrix class and inversion routine. + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/math/package.html b/dasCore/src/main/java/org/das2/math/package.html new file mode 100644 index 000000000..9271563ba --- /dev/null +++ b/dasCore/src/main/java/org/das2/math/package.html @@ -0,0 +1,8 @@ + +

    + Provides implementations of complex mathematical functions, such as +FFT, Poisson distribution generator, contouring, interpolation, fitting, etc. +Note some of these are based on open source packages and due credit, copyright, +and license is given within. +

    + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/persistence/DatumPersistenceDelegate.java b/dasCore/src/main/java/org/das2/persistence/DatumPersistenceDelegate.java new file mode 100644 index 000000000..43b9a7bd3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/persistence/DatumPersistenceDelegate.java @@ -0,0 +1,57 @@ +/* + * DatumRangePersistenceDelegate.java + * + * Created on August 8, 2007, 10:43 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.persistence; + +import org.das2.datum.Datum; +import org.das2.datum.Units; +import java.beans.DefaultPersistenceDelegate; +import java.beans.Encoder; +import java.beans.Expression; + +/** + * + * @author jbf + */ +public class DatumPersistenceDelegate extends DefaultPersistenceDelegate { + + /** Creates a new instance of DatumRangePersistenceDelegate */ + public DatumPersistenceDelegate() { + } + + protected Expression instantiate(Object oldInstance, Encoder out) { + Expression retValue; + + Datum field= (Datum)oldInstance; + Units u= field.getUnits(); + + return new Expression( field, this.getClass(), "newDatum", new Object[] { field.doubleValue(u), u.toString() } ); + + } + + public static Datum newDatum( double val, String units ) { + Units u= Units.lookupUnits(units); + return u.createDatum( val ); + } + + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) { + super.initialize(type, oldInstance, newInstance, out); + } + + public void writeObject(Object oldInstance, Encoder out) { + super.writeObject(oldInstance, out); + } + + protected boolean mutatesTo(Object oldInstance, Object newInstance) { + boolean retValue; + + retValue = super.mutatesTo(oldInstance, newInstance); + return retValue; + } +} diff --git a/dasCore/src/main/java/org/das2/persistence/DatumRangePersistenceDelegate.java b/dasCore/src/main/java/org/das2/persistence/DatumRangePersistenceDelegate.java new file mode 100644 index 000000000..c115431df --- /dev/null +++ b/dasCore/src/main/java/org/das2/persistence/DatumRangePersistenceDelegate.java @@ -0,0 +1,46 @@ +/* + * DatumRangePersistenceDelegate.java + * + * Created on August 8, 2007, 10:43 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.persistence; + +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import java.beans.DefaultPersistenceDelegate; +import java.beans.Encoder; +import java.beans.Expression; + +/** + * + * @author jbf + */ +public class DatumRangePersistenceDelegate extends DefaultPersistenceDelegate { + + /** Creates a new instance of DatumRangePersistenceDelegate */ + public DatumRangePersistenceDelegate() { + } + + protected Expression instantiate(Object oldInstance, Encoder out) { + Expression retValue; + + DatumRange field= (DatumRange)oldInstance; + Units u= field.getUnits(); + + return new Expression( field, this.getClass(), "newDatumRange", new Object[] { field.min().doubleValue(u), field.max().doubleValue(u), u.toString() } ); + + } + + public static DatumRange newDatumRange( double min, double max, String units ) { + Units u= Units.lookupUnits(units); + return DatumRange.newDatumRange( min, max, u ); + } + + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) { + super.initialize(type, oldInstance, newInstance, out); + } +} diff --git a/dasCore/src/main/java/org/das2/persistence/StatePersistence.java b/dasCore/src/main/java/org/das2/persistence/StatePersistence.java new file mode 100644 index 000000000..e3b55e4f9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/persistence/StatePersistence.java @@ -0,0 +1,54 @@ +/* + * StatePersistence.java + * + * Created on August 8, 2007, 10:47 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.persistence; + +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import java.beans.ExceptionListener; +import java.beans.XMLDecoder; +import java.beans.XMLEncoder; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Provides object serialization, using delegates to handle das2 immutable objects. + * @author jbf + */ +public class StatePersistence { + + private StatePersistence() { + } + + public static void saveState( OutputStream out, Object state ) throws IOException { + XMLEncoder e = new XMLEncoder( + new BufferedOutputStream( + out ) ); + + e.setPersistenceDelegate( DatumRange.class, new DatumRangePersistenceDelegate() ); + e.setPersistenceDelegate( Units.class, new UnitsPersistenceDelegate() ); + e.setPersistenceDelegate( Datum.class, new DatumPersistenceDelegate() ); + + e.setExceptionListener( new ExceptionListener() { + public void exceptionThrown(Exception e) { + e.printStackTrace(); + } + } ); + e.writeObject(state); + e.close(); + } + + public static Object restoreState( InputStream in ) throws IOException { + XMLDecoder decode= new XMLDecoder( in ); + return decode.readObject(); + } +} diff --git a/dasCore/src/main/java/org/das2/persistence/UnitsPersistenceDelegate.java b/dasCore/src/main/java/org/das2/persistence/UnitsPersistenceDelegate.java new file mode 100644 index 000000000..06670b947 --- /dev/null +++ b/dasCore/src/main/java/org/das2/persistence/UnitsPersistenceDelegate.java @@ -0,0 +1,52 @@ +/* + * UnitsPersistenceDelegate.java + * + * Created on August 8, 2007, 11:04 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.persistence; + +import org.das2.datum.Units; +import java.beans.DefaultPersistenceDelegate; +import java.beans.Encoder; +import java.beans.Expression; + +/** + * + * @author jbf + */ +public class UnitsPersistenceDelegate extends DefaultPersistenceDelegate { + + /** Creates a new instance of UnitsPersistenceDelegate */ + public UnitsPersistenceDelegate() { + + } + + protected Expression instantiate(Object oldInstance, Encoder out) { + Expression retValue; + + Units u= (Units)oldInstance; + + return new Expression( u, u.getClass(), "getByName", new Object[] { u.toString() } ); + + } + + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) { + super.initialize(type, oldInstance, newInstance, out); + } + + public void writeObject(Object oldInstance, Encoder out) { + super.writeObject(oldInstance, out); + } + + protected boolean mutatesTo(Object oldInstance, Object newInstance) { + boolean retValue; + + retValue = super.mutatesTo(oldInstance, newInstance); + return retValue; + } + +} diff --git a/dasCore/src/main/java/org/das2/pw/ClusterSpacecraft.java b/dasCore/src/main/java/org/das2/pw/ClusterSpacecraft.java new file mode 100644 index 000000000..03a04f941 --- /dev/null +++ b/dasCore/src/main/java/org/das2/pw/ClusterSpacecraft.java @@ -0,0 +1,59 @@ +package org.das2.pw; + +import java.awt.*; + +public class ClusterSpacecraft extends Spacecraft.GroupSpacecraft { + + protected ClusterSpacecraft(String id, int number) { + super( "Cluster2", id, number ); + } + + /** + * returns the Esa Number 1,2,3 or 4 for the Cluster Spacecraft + */ + public int getEsaNumber() { + return number; + } + + + /** + * returns the Cluster Spacecraft for the Esa Number 1,2,3 or 4 + */ + public static ClusterSpacecraft getByEsaNumber(int number) { + return (ClusterSpacecraft)getByNumber("Cluster2",number); + } + + /** + * returns the Cluster Spacecraft for the wideband status byteEsa 4,5,6,7 + */ + public static ClusterSpacecraft getByWbdStatusByte( int number ) { + if (true) throw new IllegalArgumentException("looks like there's a bug here"); + final int[] esaFromStatus= new int[] { 0, 0, 0, 0, 2, 3, 4, 1 }; + return getByEsaNumber( esaFromStatus[number] ); + } + + public int getInstrumentNumber() { + final int instFromEsa[] = new int[] { 0, 9, 6, 7, 8 }; + return instFromEsa[ getEsaNumber() ]; + } + + public int getDsnNumber() { + final int dsnFromEsa[] = new int[] { 0, 183, 185, 194, 196 }; + return dsnFromEsa[ getEsaNumber() ]; + } + + public String getName() { + final String[] nameFromEsa= new String[] { "", "Rumba", "Salsa", "Samba", "Tango" }; + return nameFromEsa[ getEsaNumber() ]; + } + + public Color getColor() { + final Color[] colorFromEsa= new Color[] { null, Color.BLACK, Color.RED, Color.GREEN, Color.MAGENTA }; + return colorFromEsa[ getEsaNumber() ]; + } + + public String toString() { + return "Cluster " + getEsaNumber(); + } +} + diff --git a/dasCore/src/main/java/org/das2/pw/Spacecraft.java b/dasCore/src/main/java/org/das2/pw/Spacecraft.java new file mode 100644 index 000000000..6e6ea9b39 --- /dev/null +++ b/dasCore/src/main/java/org/das2/pw/Spacecraft.java @@ -0,0 +1,70 @@ +/* + * Spacecraft.java + * + * Created on June 18, 2004, 9:59 AM + */ + +package org.das2.pw; + +import java.util.*; + +/** + * + * @author Jeremy + */ +public class Spacecraft { + + String id; + + /* useful for a set of identical spacecraft. e.g. voyager, cluster */ + protected abstract static class GroupSpacecraft extends Spacecraft { + int number; + String group; + static HashMap groups; + protected GroupSpacecraft( String group, String id, int number ) { + super(id); + this.group= group; + this.number= number; + if ( groups==null ) { + groups= new HashMap(); + } + if ( groups.containsKey(group) ) { + HashMap x= (HashMap)groups.get(group); + x.put( new Integer(number),this ); + } else { + HashMap x= new HashMap(); + x.put(new Integer(number),this); + groups.put( group, x ); + } + } + protected static Spacecraft getByNumber( String group, int number ) { + if ( groups.containsKey(group) ) { + HashMap x= (HashMap)(groups.get(group)); + return (Spacecraft)x.get(new Integer(number)); + } else { + throw new IllegalArgumentException( "No such group: "+group ); + } + } + protected int getGroupNumber() { + return this.number; + } + + } + + private Spacecraft( String id ) { + this.id= id; + } + + public static Spacecraft voyager1= new Spacecraft( "Voyager 1" ); + public static Spacecraft voyager2= new Spacecraft( "Voyager 2" ); + public static ClusterSpacecraft clusterRumba= new ClusterSpacecraft( "Rumba", 1 ); + public static ClusterSpacecraft clusterSalsa= new ClusterSpacecraft( "Salsa", 2 ); + public static ClusterSpacecraft clusterSamba= new ClusterSpacecraft( "Samba", 3 ); + public static ClusterSpacecraft clusterTango= new ClusterSpacecraft( "Tango", 4 ); + + public static void main( String[]args ) { + System.out.println( ClusterSpacecraft.getByEsaNumber(2) ); + System.out.println( clusterSalsa ); + } + +} diff --git a/dasCore/src/main/java/org/das2/pw/package.html b/dasCore/src/main/java/org/das2/pw/package.html new file mode 100644 index 000000000..1ed7ff3d4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/pw/package.html @@ -0,0 +1,6 @@ + +

    + Classes useful for Plasma-Wave group applications. This should be moved to a + separate library. +

    + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/reader/BadQueryException.java b/dasCore/src/main/java/org/das2/reader/BadQueryException.java new file mode 100644 index 000000000..1e7ff61ca --- /dev/null +++ b/dasCore/src/main/java/org/das2/reader/BadQueryException.java @@ -0,0 +1,24 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.reader; + +/** Readers throw this type of exception when the set of selectors does not sufficiently + * nail down a dataset to return. Usually a request validator will catch bad queries + * but, this condition could conceievably still arise due to a miss configured server. + * + * @author cwp + */ +public class BadQueryException extends Exception { + + public BadQueryException(String str){ + super(str); + } + + public BadQueryException(String str, Throwable ex){ + super(str, ex); + } + +} diff --git a/dasCore/src/main/java/org/das2/reader/Constraint.java b/dasCore/src/main/java/org/das2/reader/Constraint.java new file mode 100644 index 000000000..2b1041d13 --- /dev/null +++ b/dasCore/src/main/java/org/das2/reader/Constraint.java @@ -0,0 +1,109 @@ +package org.das2.reader; + +import java.text.ParseException; +import org.das2.datum.CalendarTime; + +/** + * Inner class to hold a value plus a comparison operator + */ +public final class Constraint{ + + /** Defines a comparison type, should probably be moved out to some other class */ + public static enum Op {EQ, NE, LT, GT, LE, GE; + @Override + public String toString(){ + return "."+name()+"."; + } + + static public Op fromString(String sStr){ + if(sStr.equalsIgnoreCase(".eq.")) return EQ; + if(sStr.equalsIgnoreCase(".ne.")) return NE; + if(sStr.equalsIgnoreCase(".lt.")) return LT; + if(sStr.equalsIgnoreCase(".end.")) return LT; + if(sStr.equalsIgnoreCase(".gt.")) return GT; + if(sStr.equalsIgnoreCase(".ge.")) return GE; + if(sStr.equalsIgnoreCase(".beg.")) return GE; + if(sStr.equalsIgnoreCase(".le.")) return LE; + throw new IllegalArgumentException("Unknown comparison string '"+sStr+"'"); + } + }; + + /** Defines the value format for the selector */ + public static enum Format {BOOLEAN, INTEGER, REAL, STRING, TIMEPOINT}; + + // Basic properties + private Op m_op; + private String m_sVal; + private Format m_fmt; + // Place to park conversions so they don't have to be redone over and over + private boolean m_bVal; + private int m_nVal; + private double m_dVal; + private CalendarTime m_ctVal; + + public Constraint(Op op, Format fmt, String sVal) + throws ParseException{ + + if(sVal == null) + throw new NullPointerException("Constraint value must not be null."); + m_op = op; + m_fmt = fmt; + m_sVal = sVal; + switch(m_fmt){ + case BOOLEAN: + if(sVal.equalsIgnoreCase("false") || sVal.equals("0")) + m_bVal = false; + else + m_bVal = true; + break; + case INTEGER: + m_nVal = Integer.parseInt(sVal); + break; + case REAL: + m_dVal = Double.parseDouble(sVal); + break; + case TIMEPOINT: + m_ctVal = new CalendarTime(sVal); + break; + } + } + + public Format getFormat(){ + return m_fmt; + } + + public Op getOp(){ + return m_op; + } + + public String getOpStr(){ + return m_op.toString(); + } + + public String getValue(){ return m_sVal; } + + public int getIntValue(){ + if(m_fmt != Format.TIMEPOINT) + throw new UnsupportedOperationException("Constraint format is not INTEGER"); + return m_nVal; + } + + public double getRealValue(){ + if(m_fmt != Format.REAL) + throw new UnsupportedOperationException("Constraint format is not REAL"); + return m_dVal; + } + + public boolean getBoolValue(){ + if(m_fmt != Format.BOOLEAN) + throw new UnsupportedOperationException("Constraint format is not BOOLEAN"); + return m_bVal; + } + + public CalendarTime getTimeValue(){ + if(m_fmt != Format.TIMEPOINT) + throw new UnsupportedOperationException("Constraint format is not DATETIME"); + + return m_ctVal; + } +} diff --git a/dasCore/src/main/java/org/das2/reader/DasHdrBuf.java b/dasCore/src/main/java/org/das2/reader/DasHdrBuf.java new file mode 100644 index 000000000..8d1fbd922 --- /dev/null +++ b/dasCore/src/main/java/org/das2/reader/DasHdrBuf.java @@ -0,0 +1,63 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.reader; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; + +/** Tiny class to count bytes in a header packet and then write it to a stream + * + * @author cwp + */ +public final class DasHdrBuf { + private int m_nPktId; + private ByteArrayOutputStream m_buf; + + private static final Charset m_csUtf8 = Charset.forName("UTF-8"); + private static final Charset m_csAscii = Charset.forName("US-ASCII"); + + /** Make a new buffer for QStream/Das2 header data + * + * @param nPktId The ID for the packets this header describes, must be a number + * between 1 and 99. + * + */ + public DasHdrBuf(int nPktId){ + if((nPktId < 0 )||(nPktId > 99)){ + throw new IllegalArgumentException("Illegal packet id "+nPktId+". Legal "+ + "das/qstream packet IDs range from 1 and 99, inclusive."); + } + m_nPktId = nPktId; + m_buf = new ByteArrayOutputStream(); + add("\n"); + } + + /** Add more text data to the header. + * + * @param sTxt The text to add, will be encoded as UTF-8 for storage + */ + public void add(String sTxt){ + byte[] u1Tmp = sTxt.getBytes(m_csUtf8); + m_buf.write(u1Tmp, 0, u1Tmp.length); + } + + /** Send a completed header out the door. + * This has the side effect of clearing the buffer for re-use. + * + * @param fOut Any output stream you like + */ + public void send(OutputStream fOut) throws IOException{ + byte[] u1Tmp = String.format("[%02d]%06d", m_nPktId, m_buf.size()).getBytes(m_csAscii); + assert u1Tmp.length == 10; + fOut.write(u1Tmp); + m_buf.writeTo(fOut); + fOut.flush(); + m_buf = new ByteArrayOutputStream(); + add("\n"); + } +} diff --git a/dasCore/src/main/java/org/das2/reader/DasPktBuf.java b/dasCore/src/main/java/org/das2/reader/DasPktBuf.java new file mode 100644 index 000000000..81ca372d1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/reader/DasPktBuf.java @@ -0,0 +1,186 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.reader; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.nio.LongBuffer; +import java.nio.charset.Charset; + +/** Tiny class to count packets in a Das2 or QStream and then write it to a stream + * + * @author cwp + */ +public final class DasPktBuf { + private int m_nPktId; + private OutputFormat m_fmt; + private ByteArrayOutputStream m_buf; + + private static final Charset m_csAscii = Charset.forName("US-ASCII"); + + /** Make a new buffer for Das2/QStream packet data + * This version does not prepend lengths to the packet ID tags. + * + * @param nPktId The ID for this packets. This must match an ID previously sent for + * one of the header packets and so must be a number between 1 and 99. + */ + public DasPktBuf(int nPktId){ + this(nPktId, OutputFormat.DAS2); + } + + /** Make a new buffer for Das2/QStream/Das3 packet data + * + * @param nPktId The ID for this packets. This must match an ID previously sent for + * one of the header packets and so must be a number between 1 and 99. + * + * @param fmt The format of the output data. Das3 formats expect lengths on all + * packets + */ + public DasPktBuf(int nPktId, OutputFormat fmt){ + if((nPktId < 0 )||(nPktId > 99)){ + throw new IllegalArgumentException("Illegal packet id "+nPktId+". Legal "+ + "das/qstream packet IDs range from 1 and 99, inclusive."); + } + m_nPktId = nPktId; + m_buf = new ByteArrayOutputStream(); + m_fmt = fmt; + } + + /** Add generic bytes to the packet + * + * @param bytes The bytes to add + */ + public void add(byte[] bytes){ + m_buf.write(bytes, 0, bytes.length); + } + + /** Add an array of floats to the packet with selectable byte order. + * @param lFloats the floats to add to the packet + * @param bo define whether bytes should be sent in big-endian or little endian + * format. Warning: It's up to you to make sure that the byte order + * matches the value in the 'byte_order' attribute for the stream header. + */ + public void addFloats(float[] lFloats, ByteOrder bo){ + + if((bo == ByteOrder.LITTLE_ENDIAN)&&(m_fmt == OutputFormat.DAS1)) + throw new IllegalArgumentException("DAS1 streams only support big endian values"); + + //Successfully do nothing + if(lFloats.length == 0) return; + + byte[] lBytes = new byte[lFloats.length * 4]; + ByteBuffer bbTmp = ByteBuffer.wrap( lBytes ); + bbTmp.order(bo); + FloatBuffer fbTmp = bbTmp.asFloatBuffer(); + fbTmp.put(lFloats); + m_buf.write(lBytes, 0, lBytes.length); + } + + /** Add an array of floats to the packet as ASCII values with a format specifier. + * + * NOTE: This function adds a space after every float except the last one. + * this is to allow the caller to add a new line character, if desired, to the + * output after the last value in a packet. + * + * @param lFloats the floats to add to the packet + * @param sFmt the format string to use for each float, see documentation for the + * method String.format in the standard platform library. + */ + public void addFloats(float[] lFloats, String sFmt){ + if(m_fmt == OutputFormat.DAS1) + throw new IllegalStateException("DAS1 Streams only support binary values"); + + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < lFloats.length; i++){ + sb.append( String.format(sFmt, lFloats[i]) ); + if(i != lFloats.length - 1) + sb.append( " "); + } + + byte[] lBytes = sb.toString().getBytes(m_csAscii); + m_buf.write(lBytes, 0, lBytes.length); + } + + /** Add a single float to the packet with selectable byte order + * + * @param lDoubles the value to add to the packet + * @param bo define whether bytes should be sent in big-endian or little endian + * format. Warning: It's up to you to make sure that the byte order + * matches the value in the 'byte_order' attribute for the stream header. + */ + public void addDoubles(double[] lDoubles, ByteOrder bo){ + if((bo == ByteOrder.LITTLE_ENDIAN)&&(m_fmt == OutputFormat.DAS1)) + throw new IllegalArgumentException("DAS1 streams only support big endian values"); + + if(m_fmt == OutputFormat.DAS1) + throw new IllegalStateException("DAS1 streams do not support double precision values"); + + //Successfully do nothing + if(lDoubles.length == 0) return; + + byte[] lBytes = new byte[lDoubles.length * 8]; + ByteBuffer bbTmp = ByteBuffer.wrap( lBytes ); + bbTmp.order(bo); + DoubleBuffer dbTmp = bbTmp.asDoubleBuffer(); + dbTmp.put(lDoubles); + m_buf.write(lBytes, 0, lBytes.length); + + } + + public void addLongs(long[] lLongs, ByteOrder bo){ + if((bo == ByteOrder.LITTLE_ENDIAN)&&(m_fmt == OutputFormat.DAS1)) + throw new IllegalArgumentException("DAS1 streams only support big endian values"); + + if(m_fmt != OutputFormat.QSTREAM) + throw new IllegalStateException("Only QSTREAM format supports long integers."); + + //Successfully do nothing + if(lLongs.length == 0) return; + + byte[] lBytes = new byte[lLongs.length * 8]; + ByteBuffer bbTmp = ByteBuffer.wrap( lBytes ); + bbTmp.order(bo); + LongBuffer lbTmp = bbTmp.asLongBuffer(); + lbTmp.put(lLongs); + m_buf.write(lBytes, 0, lBytes.length); + } + + /** Encode a string in US-ASCII format, and add it to the output buffer + * + * @param s The string to encode + */ + public void addAscii(String s){ + byte[] lBytes = s.getBytes(m_csAscii); + m_buf.write(lBytes, 0, lBytes.length); + } + + /** Kick the encoded packet data onto the stream. + * @param fOut Where to write the packet + * @throws IOException + */ + public void send(OutputStream fOut) throws IOException{ + byte[] u1Tmp; + switch(m_fmt){ + case DAS3: + u1Tmp = String.format("|%02d|%06d", m_nPktId, m_buf.size()).getBytes(m_csAscii); + assert u1Tmp.length == 10; + break; + default: + u1Tmp = String.format(":%02d:", m_nPktId).getBytes(m_csAscii); + assert u1Tmp.length == 4; + } + fOut.write(u1Tmp); + m_buf.writeTo(fOut); + m_buf = new ByteArrayOutputStream(); + } + + +} diff --git a/dasCore/src/main/java/org/das2/reader/NoDataException.java b/dasCore/src/main/java/org/das2/reader/NoDataException.java new file mode 100644 index 000000000..5824db6ca --- /dev/null +++ b/dasCore/src/main/java/org/das2/reader/NoDataException.java @@ -0,0 +1,16 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.reader; + +/** + * + * @author cwp + */ +public class NoDataException extends Exception { + public NoDataException(String str){ + super(str); + } +} diff --git a/dasCore/src/main/java/org/das2/reader/OutputFormat.java b/dasCore/src/main/java/org/das2/reader/OutputFormat.java new file mode 100644 index 000000000..7a55fd120 --- /dev/null +++ b/dasCore/src/main/java/org/das2/reader/OutputFormat.java @@ -0,0 +1,13 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.reader; + +/** Output format for a reader. + * This is used in the ReaderInterface definition. + * @author cwp + */ +public enum OutputFormat { DAS1, DAS2, QSTREAM, DAS3} + diff --git a/dasCore/src/main/java/org/das2/reader/QueryEditor.java b/dasCore/src/main/java/org/das2/reader/QueryEditor.java new file mode 100644 index 000000000..711caf919 --- /dev/null +++ b/dasCore/src/main/java/org/das2/reader/QueryEditor.java @@ -0,0 +1,53 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.reader; + +import java.awt.Dimension; +import java.awt.Rectangle; +import javax.swing.JComponent; +import javax.swing.Scrollable; + +/** + * + * @author cwp + */ +public class QueryEditor extends JComponent implements Scrollable { + + @Override + public Dimension getPreferredScrollableViewportSize(){ + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction){ + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction){ + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean getScrollableTracksViewportWidth(){ + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean getScrollableTracksViewportHeight(){ + throw new UnsupportedOperationException("Not supported yet."); + } + + public String getQuery(){ + throw new UnsupportedOperationException("Not supported yet."); + + } + + public void setQuery(String sQuery){ + + } + +} diff --git a/dasCore/src/main/java/org/das2/reader/Reader.java b/dasCore/src/main/java/org/das2/reader/Reader.java new file mode 100644 index 000000000..ba142d089 --- /dev/null +++ b/dasCore/src/main/java/org/das2/reader/Reader.java @@ -0,0 +1,52 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.reader; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.logging.Logger; + +/** Generic interface for readers loadable by the tomcat server, or by the standalone + * applications. + * + * @author cwp + */ +public interface Reader { + + /** Does this reader support connected operations over a BEEP channel */ + public boolean canConnect(); + + /** Does this reader support a particular stream format */ + public boolean supportsFormat(OutputFormat fmt); + + /** Outputs all data in the range denoted by the selectors into the given + * output stream. + * + * Multiple calls to the retrieve interface for the same class instance must be + * thread safe + * + * @param selectors An array of selectors, must have at least one specified + * @param format The data and header output format requested + * @param stream The stream where the bytes should be emitted. + */ + public void retrieve(List lSel, OutputFormat fmt, OutputStream fOut, + Logger log) + throws IOException, NoDataException, BadQueryException; + + /** Setup a bidirectional communications channel with a client. + * Once the channel is connected, the server may no longer be involved. + * + * @param selectors An initial array of selectors there may be not specified at + * startup, in which case the reader just reports ready. + * @param format The initial data and header output format requested + * @param beepChannel The channel onto which bytes should be written + * @returns 0 if the connection was properly closed, or a non zero value from the + * underlying beep library if an improper shutdown occurred. + */ + public int connect(List lSel, OutputFormat fmt, Object beepChannel, + Logger log) throws IOException, BadQueryException; +} diff --git a/dasCore/src/main/java/org/das2/reader/Selector.java b/dasCore/src/main/java/org/das2/reader/Selector.java new file mode 100644 index 000000000..ef32dc967 --- /dev/null +++ b/dasCore/src/main/java/org/das2/reader/Selector.java @@ -0,0 +1,127 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.reader; + +import java.util.LinkedList; +import java.util.List; +import org.das2.datum.CalendarTime; + +/** A selector encapsulates a request parameter, or parameter set, for a reader. + * The most commonly used selector is a time range. A time range selector would + * most likely have the m_type = RANGE and m_format = TIMEPOINT. + * + * Implementation Note: We may want to break this into a selector base class + * with one derived class for each value of m_type. + * + * @author cwp + */ +public class Selector { + + // Instance Data + protected String m_sKey = null; + protected Constraint.Format m_format = null; + protected List m_lConstraints = null; + + /** Construct an empty selector */ + public Selector(String sKey, Constraint.Format fmt){ + m_sKey = sKey; + m_format = fmt; + } + + /** Add a constraint to a selector */ + public void addConstraint(Constraint cons){ + if(cons.getFormat() != m_format) + throw new IllegalArgumentException(String.format("Constraint format is %s, " + + "expected %s", cons.getFormat(), m_format)); + if(m_lConstraints == null){ + m_lConstraints = new LinkedList<>(); + } + m_lConstraints.add(cons); + } + + /** Get the selection as a single string, no matter the type of selector */ + @Override + public String toString(){ + + StringBuilder sb = new StringBuilder(); + for(Constraint cons: m_lConstraints){ + if(sb.length() != 0) sb.append(' '); + sb.append(String.format("%s%s%s", m_sKey, cons.getOpStr(), cons.getValue())); + } + return sb.toString(); + } + + /** Get the key (which is just a string) for this selector. + * Note That no two selectors for the same data-source may have the same key. + */ + public String key(){ return m_sKey; } + + /** Get the data type for the selector */ + public Constraint.Format format(){ return m_format;} + + /** Get the number of constraints defined for this selector */ + public int getConstraintCount(){ + if(m_lConstraints == null) return 0; + else return m_lConstraints.size(); + } + + /** Grab a constraint to work with */ + public Constraint getConstraint(int i){ + return m_lConstraints.get(i); + } + + /** Get a constraint by type. + * + * @param op The type of comparision to check. + * @return null if the given comparison isn't one of the one's specified, otherwise + * a constraint object is returned. It will have to be queiried to find it's + * value type. + */ + public Constraint getConstraint(Constraint.Op op){ + for(Constraint cons: m_lConstraints){ + if(cons.getOp() == op){ + return cons; + } + } + return null; + } + + // Check that we can use the convenience rountines + private void ckUniqueEq(){ + if(m_lConstraints.size() != 1) + throw new UnsupportedOperationException("Selector does not contain a unique constraint"); + + if(m_lConstraints.get(0).getOp() != Constraint.Op.EQ) + throw new UnsupportedOperationException("Selector does not have an equality constraint."); + } + + /** Convenience routine to get a string when there is only a single equality + * constraint. */ + public String getValue(){ + ckUniqueEq(); + return m_lConstraints.get(0).getValue(); + } + + public int getIntValue(){ + ckUniqueEq(); + return m_lConstraints.get(0).getIntValue(); + } + + public double getRealValue(){ + ckUniqueEq(); + return m_lConstraints.get(0).getRealValue(); + } + + public boolean getBoolValue(){ + ckUniqueEq(); + return m_lConstraints.get(0).getBoolValue(); + } + + public CalendarTime getTimeValue(){ + ckUniqueEq(); + return m_lConstraints.get(0).getTimeValue(); + } +} diff --git a/dasCore/src/main/java/org/das2/stream/Das1ToDas2.java b/dasCore/src/main/java/org/das2/stream/Das1ToDas2.java new file mode 100755 index 000000000..52e2d1b57 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/Das1ToDas2.java @@ -0,0 +1,265 @@ +/* + * Das1ToDas2.java + * + * Created on December 10, 2003, 8:40 PM + */ + +package org.das2.stream; + +import org.das2.datum.Datum; +import org.das2.datum.TimeUtil; +import org.das2.DasProperties; +import org.das2.DasException; +import org.das2.util.IDLParser; +import java.io.*; +import java.util.*; + +/** + * + * @author jbf + */ +public class Das1ToDas2 { + + static Das1ToDas2 _instance; + + String createStreamDescriptor( Map dsdp, Datum start, Datum end ) { + String header= "\n"+ + " \n" + + " \n" + + " "; + } else { + throw new IllegalArgumentException("not implemented yet for anything besides x_tagged_y_scan"); + } + } + + Map getDsdfProperties( String dsdf ) throws IOException { + FileReader r= new FileReader( dsdf ); + BufferedReader in = new BufferedReader(r); + IDLParser parser = new IDLParser(); + double[] array; + String key; + String value; + String line; + int index, lineNumber; + + line = in.readLine(); + lineNumber = 1; + + HashMap properties = new HashMap(); + + while (line != null) { + //Get rid of any comments + index = line.trim().indexOf(';'); + if (index == 0) { + lineNumber++; + line = in.readLine(); + continue; + } + else if (index != -1) { + line = line.substring(0, index); + } + + //Break line into key-value pairs + index = line.indexOf('='); + key = line.substring(0,index).trim(); + value = line.substring(index+1).trim(); + + //deterimine type of value + + if (key.equals("description")) { + String description = value.substring(1, value.length()-1); + properties.put(key, description); + } + else if (key.equals("groupAccess")) { + properties.put(key, value.substring(1, value.length()-1)); + } + else if (key.equals("form")) { + properties.put(key, value); + } + else if (key.equals("reader")) { + String reader = value.substring(1, value.length()-1); + properties.put(key, reader); + } + else if (key.equals("x_parameter")) { + String x_parameter = value.substring(1, value.length()-1); + properties.put(key, x_parameter); + } + else if (key.equals("x_unit")) { + String x_unit = value.substring(1, value.length()-1); + properties.put(key, x_unit); + } + else if (key.equals("y_parameter")) { + String y_parameter = value.substring(1, value.length()-1); + properties.put(key, y_parameter); + } + else if (key.equals("y_unit")) { + String y_unit = value.substring(1, value.length()-1); + properties.put(key, y_unit); + } + else if (key.equals("z_parameter")) { + String z_parameter = value.substring(1, value.length()-1); + properties.put(key, z_parameter); + } + else if (key.equals("z_unit")) { + String z_unit = value.substring(1, value.length()-1); + properties.put(key, z_unit); + } + else if (key.equals("x_sample_width")) { + double x_sample_width = parser.parseIDLScalar(value); + if (x_sample_width == Double.NaN) + throw new IOException("Could not parse \"" + value + "\" at line " + lineNumber); + properties.put(key, new Double(x_sample_width)); + } + else if (key.equals("y_fill")) { + double y_fill = parser.parseIDLScalar(value); + if (y_fill == Double.NaN) + throw new IOException("Could not parse \"" + value + "\" at line " + lineNumber); + properties.put(key, new Double(y_fill)); + } + else if (key.equals("z_fill")) { + double z_fill = (float)parser.parseIDLScalar(value); + if (z_fill == Float.NaN) + throw new IOException("Could not parse \"" + value + "\" at line " + lineNumber); + properties.put(key, new Float(z_fill)); + } + else if (key.equals("y_coordinate")) { + array = parser.parseIDLArray(value); + if (array == null) { + throw new IOException("Could not parse \"" + value + "\" at line " + lineNumber); + } + properties.put(key, array); + } + else if (key.equals("ny")) { + int ny; + try { + ny = Integer.parseInt(value); + } + catch (NumberFormatException nfe) { + throw new IOException("Could not parse \"" + value + "\" at line " + lineNumber); + } + properties.put(key, new Integer(ny)); + } + else if (key.equals("items")) { + int items; + try { + items = Integer.parseInt(value); + } + catch (NumberFormatException nfe) { + throw new IOException("Could not parse \"" + value + "\" at line " + lineNumber); + } + properties.put(key, new Integer(items)); + } + else if (value.charAt(0)=='\'' && value.charAt(value.length()-1)=='\'') { + properties.put(key, value.substring(1, value.length()-1)); + } + else if (value.charAt(0)=='"' && value.charAt(value.length()-1)=='"') { + properties.put(key, value.substring(1, value.length()-1)); + } + else { + properties.put(key, value); + } + line = in.readLine(); + lineNumber++; + } + return properties; + } + + public static Das1ToDas2 getInstance() { + if ( _instance==null ) { + _instance= new Das1ToDas2(); + } + return _instance; + } + + public void das1ToDas2( String dsdf, InputStream in, OutputStream out, Datum start, Datum end ) throws DasException, IOException { + Map properties= getDsdfProperties( dsdf ); + String header= createStreamDescriptor( properties, start, end ); + String packet= createPacketDescriptor( properties, start ); + out.write( "[00]".getBytes() ); + out.write( header.getBytes() ); + out.write( "[01]".getBytes() ); + out.write( packet.getBytes() ); + + int nItems= ((double[])properties.get("y_coordinate")).length; + int bytesPerPacket= ( nItems + 1 ) * 4; + + byte[] buffer= new byte[bytesPerPacket]; + + int b= in.read(buffer); + + if ( b==-1 ) { + out.write("[xx]".getBytes()); + } + + int offset= b; + + while ( b!=-1 ) { + while ( b!=-1 && offset "); + } + + } + + +} diff --git a/dasCore/src/main/java/org/das2/stream/DasStreamFormatException.java b/dasCore/src/main/java/org/das2/stream/DasStreamFormatException.java new file mode 100644 index 000000000..12b728b89 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/DasStreamFormatException.java @@ -0,0 +1,53 @@ +/* File: DasStreamFormatException.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + +package org.das2.stream; + +import org.das2.DasException; + +/** This exception indicates that a das2 stream is not formatted properly, + * and can indicate that a das2 stream was expected but not received. + * @author jbf + */ +public class DasStreamFormatException extends DasException { + + + + /** + * Creates a new instance of DasStreamFormatException without detail message. + */ + + public DasStreamFormatException() { + } + + + /** + * Constructs an instance of DasStreamFormatException with the specified detail message. + * @param msg the detail message. + */ + public DasStreamFormatException(String msg) { + super(msg); + } +} diff --git a/dasCore/src/main/java/org/das2/stream/DataTransferType.java b/dasCore/src/main/java/org/das2/stream/DataTransferType.java new file mode 100644 index 000000000..9654c4b9e --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/DataTransferType.java @@ -0,0 +1,267 @@ +/* File: DataTransferType.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on December 18, 2003, 9:01 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream; + +import org.das2.datum.TimeUtil; +import org.das2.datum.Units; +import org.das2.datum.format.TimeDatumFormatter; +import org.das2.util.FixedWidthFormatter; +import org.das2.util.NumberFormatUtil; +import java.io.UnsupportedEncodingException; +import java.text.DecimalFormat; +import java.text.ParseException; + +import java.util.Map; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @author Edward West + */ +public class DataTransferType { + + private static final int I_SUN_REAL4 = 0; + private static final int I_SUN_REAL8 = 1; + private static final int I_LITTLE_ENDIAN_REAL4 = 4; + private static final int I_LITTLE_ENDIAN_REAL8 = 5; + private static final int I_ASCII = 2; + private static final int I_TIME = 3; + + private static final Map map = new HashMap(); + + public static final DataTransferType SUN_REAL4 = new DataTransferType("sun_real4", I_SUN_REAL4, 4, false); + public static final DataTransferType SUN_REAL8 = new DataTransferType("sun_real8", I_SUN_REAL8, 8, false); + + public static final DataTransferType LITTLE_ENDIAN_REAL4 = new DataTransferType("little_endian_real4", I_LITTLE_ENDIAN_REAL4, 4, false); + public static final DataTransferType LITTLE_ENDIAN_REAL8 = new DataTransferType("little_endian_real8", I_LITTLE_ENDIAN_REAL8, 8, false); + + private static final Pattern ASCII_PATTERN = Pattern.compile("ascii([1-9][0-9]?)"); + private static final Pattern TIME_PATTERN = Pattern.compile("time([1-9][0-9]?)"); + + private final String name; + + private final int sizeBytes; + + private final boolean ascii; + + private final int id; + + private DecimalFormat doubleFormatter; + + private DataTransferType(String name, int id, int sizeBytes, boolean ascii) { + this.name = name; + this.id = id; + this.sizeBytes = sizeBytes; + this.ascii = ascii; + map.put(name, this); + if (ascii) { + doubleFormatter = NumberFormatUtil.getDecimalFormat(getFormat(sizeBytes-1)); + } + } + + static class Time extends DataTransferType { + Units units; + TimeDatumFormatter formatter; + int sizeBytes; + + Time( int size ) { + super( "time"+size, I_TIME, size, true ); + this.sizeBytes= size; // yuk + this.units= Units.us2000; + this.formatter= TimeDatumFormatter.DEFAULT; + } + + public Units getUnits() { + return units; + } + + public double read(final java.nio.ByteBuffer buffer) { + try { + byte[] bytes = new byte[sizeBytes]; + buffer.get(bytes); + String str = new String(bytes, "ASCII").trim(); + double result = TimeUtil.create( str ).doubleValue(units); + return result; + } catch ( UnsupportedEncodingException e ) { + throw new RuntimeException(e); + } catch ( ParseException e ) { + throw new RuntimeException(e); + } + } + + public void write(double d, java.nio.ByteBuffer buffer) { + try { + String s = formatter.format(units.createDatum(d)); + s= s.substring(0, sizeBytes); + byte[] bytes = s.getBytes("US-ASCII"); + bytes[sizeBytes-1]= 32; // " " space + buffer.put(bytes); + } catch ( UnsupportedEncodingException e ) { + throw new RuntimeException(e); + } + } + } + + public String toString() { + return name; + } + + public int getSizeBytes() { + return sizeBytes; + } + + public static DataTransferType getByName(String name) { + DataTransferType type = (DataTransferType)map.get(name); + if (type == null ) { + Matcher m = ASCII_PATTERN.matcher(name); + if (m.matches()) { + int charCount = Integer.parseInt(m.group(1)); + type = new DataTransferType(name, I_ASCII, charCount, true); + map.put(name, type); + } else { + m= TIME_PATTERN.matcher(name); + if ( m.matches()) { + int charCount = Integer.parseInt(m.group(1)); + type = new DataTransferType.Time( charCount ); + map.put(name, type); + } else { + throw new RuntimeException( "Unsupported type: "+name ); + } + } + } + return type; + } + + /** + * If type terminates a line, then use \n to delineate + */ + public boolean isAscii() { + return ascii; + } + + private static final java.nio.ByteOrder BIG_ENDIAN = java.nio.ByteOrder.BIG_ENDIAN; + private static final java.nio.ByteOrder LITTLE_ENDIAN = java.nio.ByteOrder.LITTLE_ENDIAN; + + public double read(final java.nio.ByteBuffer buffer) { + final java.nio.ByteOrder bo = buffer.order(); + try { + double result; + switch(id) { + case I_SUN_REAL4: { + buffer.order(BIG_ENDIAN); + result = buffer.getFloat(); + } break; + case I_SUN_REAL8: { + buffer.order(BIG_ENDIAN); + result = buffer.getDouble(); + } break; + case I_LITTLE_ENDIAN_REAL4: { + buffer.order(LITTLE_ENDIAN); + result = buffer.getFloat(); + } break; + case I_LITTLE_ENDIAN_REAL8: { + buffer.order(LITTLE_ENDIAN); + result = buffer.getDouble(); + } break; + case I_ASCII: { + byte[] bytes = new byte[sizeBytes]; + buffer.get(bytes); + String str = new String(bytes, "ASCII").trim(); + result = Double.parseDouble(str); + } break; + default: { + throw new IllegalStateException("Invalid id: " + id); + } + } + return result; + } catch (java.io.UnsupportedEncodingException uee) { + //NOT LIKELY TO HAPPEN + throw new RuntimeException(uee); + } finally { + buffer.order(bo); + } + } + + public void write(double d, java.nio.ByteBuffer buffer) { + final java.nio.ByteOrder bo = buffer.order(); + try { + switch(id) { + case I_SUN_REAL4: { + buffer.order(BIG_ENDIAN); + buffer.putFloat((float)d); + } break; + case I_SUN_REAL8: { + buffer.order(BIG_ENDIAN); + buffer.putDouble(d); + } break; + case I_LITTLE_ENDIAN_REAL4: { + buffer.order(LITTLE_ENDIAN); + buffer.putFloat((float)d); + } break; + case I_LITTLE_ENDIAN_REAL8: { + buffer.order(LITTLE_ENDIAN); + buffer.putDouble(d); + } break; + case I_ASCII: { + String s = doubleFormatter.format(d); + if ( s.length()==sizeBytes-2 ) { + s= " "+s+" "; // bug 232 + } else if ( s.length()==sizeBytes-1 ) { + s= s+" "; + } + if (sizeBytes < 10) { + s = FixedWidthFormatter.format(s, sizeBytes - 1); + } + byte[] bytes = s.getBytes("US-ASCII"); + buffer.put(bytes); + } break; + default: { + throw new IllegalStateException("Invalid id: " + id); + } + } + } catch (java.io.UnsupportedEncodingException uee) { + //US-ASCII encoding should be supported by all JVM implementations. + throw new RuntimeException(uee); + } finally { + buffer.order(bo); + } + } + + private static String getFormat(int length) { + if (length < 9) { + return "0.#"; + } else { + StringBuffer buffer = new StringBuffer(length); + buffer.append("+0."); + for (int i = 0; i < length - 7; i++) { + buffer.append('0'); + } + buffer.append("E00;-#"); + return buffer.toString(); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/GUnzipStreamProcessor.java b/dasCore/src/main/java/org/das2/stream/GUnzipStreamProcessor.java new file mode 100755 index 000000000..6b66d7071 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/GUnzipStreamProcessor.java @@ -0,0 +1,110 @@ +/* + * gzip.java + * + * Created on July 11, 2003, 9:39 AM + */ + +package org.das2.stream; + +import org.das2.DasException; +import org.das2.util.StreamTool; +import org.w3c.dom.*; + +import java.io.*; +import java.util.*; +import java.util.zip.GZIPInputStream; + +/** + * + * @author jbf + */ +public class GUnzipStreamProcessor extends StreamProcessor { + + public void process( InputStream in0, OutputStream out) throws IOException { + + PushbackInputStream in = new PushbackInputStream(in0); + byte[] header; + byte[] tag=new byte[4]; + boolean isCompressed=false; // true if input stream is already compressed + + int bytesRead= in.read( tag, 0, 4 ); + int offset= bytesRead; + while ( bytesRead!=-1 && offset<4 ) { + bytesRead= in.read( tag, offset, 4-offset ); + offset+= bytesRead; + } + if ( ! Arrays.equals(tag,"[00]".getBytes()) ) { + throw new IOException( "Expected [00], got "+new String(tag) ); + } + + header= StreamTool.readXML(in); + + try { + Reader reader = new InputStreamReader(new ByteArrayInputStream(header)); + Document document= StreamDescriptor.parseHeader(reader); + Element docNode= document.getDocumentElement(); + if ( ! docNode.getAttribute("compression").equals("") ) { + String compression= docNode.getAttribute("compression"); + isCompressed= compression.equals("gzip"); + if ( !isCompressed ) { + throw new IOException( "unsupported compression used: "+compression ); + } + } + docNode.setAttribute("compression","none"); + header= StreamDescriptor.createHeader(document).getBytes(); + + out.write("[00]".getBytes()); + out.write(header); + } catch ( DasException e ) { + out.write( getDasExceptionStream(e) ); + } + + if ( isCompressed ) { + in = new PushbackInputStream(new GZIPInputStream(in)); + } + + int ib=0; + byte[] buf= new byte[2048]; + while (ib!=-1) { + ib= in.read(buf); + if (ib>-1) out.write(buf,0, ib); + } + + } + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + InputStream in=null; + OutputStream out=null; + if ( args.length>0 ) { + try { + in= new FileInputStream(args[0]); + } catch ( FileNotFoundException ex) { + System.err.println("Input file not found"); + System.err.println(" file="+args[0]); + System.exit(-1); + } + } else { + in= System.in; + } + + if ( args.length>1 ) { + try { + out= new FileOutputStream(args[1]); + } catch ( FileNotFoundException ex) { + } + + } else { + out= System.out; + } + + try { + new GUnzipStreamProcessor().process(in,out); + } catch ( IOException ex) { + System.err.println(ex.getMessage()); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/GZipStreamProcessor.java b/dasCore/src/main/java/org/das2/stream/GZipStreamProcessor.java new file mode 100755 index 000000000..1aa980296 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/GZipStreamProcessor.java @@ -0,0 +1,109 @@ +/* + * gzip.java + * + * Created on July 11, 2003, 9:39 AM + */ + +package org.das2.stream; + +import org.das2.DasException; +import org.das2.util.StreamTool; +import org.w3c.dom.*; + +import java.io.*; +import java.util.*; + +/** + * + * @author jbf + */ +public class GZipStreamProcessor extends StreamProcessor { + + public void process( InputStream in0, OutputStream out) throws IOException { + + PushbackInputStream in = new PushbackInputStream(in0); + byte[] header; + byte[] tag=new byte[4]; + boolean isCompressed=false; // true if input stream is already compressed + + int bytesRead= in.read( tag, 0, 4 ); + int offset= bytesRead; + while ( bytesRead!=-1 && offset<4 ) { + bytesRead= in.read( tag, offset, 4-offset ); + offset+= bytesRead; + } + if ( ! Arrays.equals(tag,"[00]".getBytes()) ) { + throw new IOException( "Expected [00], got "+new String(tag) ); + } + + header= StreamTool.readXML(in); + + try { + Reader reader = new InputStreamReader(new ByteArrayInputStream(header)); + Document document= StreamDescriptor.parseHeader(reader); + Element docNode= document.getDocumentElement(); + if ( ! docNode.getAttribute("compression").equals("") ) { + String compression= docNode.getAttribute("compression"); + isCompressed= compression.equals("gzip"); + if ( !isCompressed ) { + throw new IOException( "unsupported compression used: "+compression ); + } + } + docNode.setAttribute("compression","gzip"); + header= StreamDescriptor.createHeader(document).getBytes(); + + out.write("[00]".getBytes()); + out.write(header); + } catch ( DasException e ) { + out.write( getDasExceptionStream(e) ); + } + + if ( ! isCompressed ) { + out= new java.util.zip.GZIPOutputStream(out); + } + + int ib=0; + byte[] buf= new byte[2048]; + while (ib!=-1) { + ib= in.read(buf); + if (ib>-1) out.write(buf,0, ib); + } + + if ( ! isCompressed ) { + ((java.util.zip.GZIPOutputStream)out).finish(); + } + } + + /** + * @param args the command line arguments + */ + public static void main(String[] args) throws Exception { + InputStream in=null; + OutputStream out=null; + if ( args.length>0 ) { + try { + in= new FileInputStream(args[0]); + } catch ( FileNotFoundException ex) { + System.err.println("Input file not found"); + System.err.println(" file="+args[0]); + System.exit(-1); + } + } else { + in= System.in; + } + + if ( args.length>1 ) { + try { + out= new FileOutputStream(args[1]); + } catch ( FileNotFoundException ex) { + } + + } else { + out= System.out; + } + + new GZipStreamProcessor().process(in,out); + + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/MicrophoneStream.java b/dasCore/src/main/java/org/das2/stream/MicrophoneStream.java new file mode 100644 index 000000000..a6b1d9800 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/MicrophoneStream.java @@ -0,0 +1,229 @@ + +package org.das2.stream; + + +import java.io.IOException; +import java.io.File; + +import javax.sound.sampled.DataLine; +import javax.sound.sampled.TargetDataLine; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.AudioFileFormat; + +public class MicrophoneStream extends Thread +{ + private TargetDataLine m_line; + private AudioFileFormat.Type m_targetType; + private AudioInputStream m_audioInputStream; + private File m_outputFile; + + + + public MicrophoneStream(TargetDataLine line, AudioFileFormat.Type targetType, File file) + { + m_line = line; + m_audioInputStream = new AudioInputStream(line); + m_targetType = targetType; + m_outputFile = file; + } + + + + /** Starts the recording. + To accomplish this, (i) the line is started and (ii) the + thread is started. + */ + public void start() + { + /* Starting the TargetDataLine. It tells the line that + we now want to read data from it. If this method + isn't called, we won't + be able to read data from the line at all. + */ + m_line.start(); + /* Starting the thread. This call results in the + method 'run()' (see below) being called. There, the + data is actually read from the line. + */ + super.start(); + } + + + /** Stops the recording. + + Note that stopping the thread explicitely is not necessary. Once + no more data can be read from the TargetDataLine, no more data + be read from our AudioInputStream. And if there is no more + data from the AudioInputStream, the method 'AudioSystem.write()' + (called in 'run()' returns. Returning from 'AudioSystem.write()' + is followed by returning from 'run()', and thus, the thread + is terminated automatically. + + It's not a good idea to call this method just 'stop()' + because stop() is a (deprecated) method of the class 'Thread'. + And we don't want to override this method. + */ + public void stopRecording() + { + m_line.stop(); + m_line.close(); + } + + + + + /** Main working method. + You may be surprised that here, just 'AudioSystem.write()' is + called. But internally, it works like this: AudioSystem.write() + contains a loop that is trying to read from the passed + AudioInputStream. Since we have a special AudioInputStream + that gets its data from a TargetDataLine, reading from the + AudioInputStream leads to reading from the TargetDataLine. The + data read this way is then written to the passed File. Before + writing of audio data starts, a header is written according + to the desired audio file type. Reading continues untill no + more data can be read from the AudioInputStream. In our case, + this happens if no more data can be read from the TargetDataLine. + This, in turn, happens if the TargetDataLine is stopped or closed + (which implies stopping). (Also see the comment above.) Then, + the file is closed and 'AudioSystem.write()' returns. + */ + public void run() + { + try + { + AudioSystem.write( + m_audioInputStream, + m_targetType, + m_outputFile); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + + + public static void main(String[] args) + { + if (args.length != 1 || args[0].equals("-h")) + { + printUsageAndExit(); + } + + /* We have made shure that there is only one command line + argument. This is taken as the filename of the soundfile + to store to. + */ + String strFilename = args[0]; + File outputFile = new File(strFilename); + + /* For simplicity, the audio data format used for recording + is hardcoded here. We use PCM 44.1 kHz, 16 bit signed, + stereo. + */ + AudioFormat audioFormat = new AudioFormat( + AudioFormat.Encoding.PCM_SIGNED, + 44100.0F, 16, 2, 4, 44100.0F, false); + + /* Now, we are trying to get a TargetDataLine. The + TargetDataLine is used later to read audio data from it. + If requesting the line was successful, we are opening + it (important!). + */ + DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat); + TargetDataLine targetDataLine = null; + try + { + targetDataLine = (TargetDataLine) AudioSystem.getLine(info); + targetDataLine.open(audioFormat); + } + catch (LineUnavailableException e) + { + out("unable to get a recording line"); + e.printStackTrace(); + System.exit(1); + } + + /* Again for simplicity, we've hardcoded the audio file + type, too. + */ + AudioFileFormat.Type targetType = AudioFileFormat.Type.WAVE; + + /* Now, we are creating an SimpleAudioRecorder object. It + contains the logic of starting and stopping the + recording, reading audio data from the TargetDataLine + and writing the data to a file. + */ + MicrophoneStream recorder = new MicrophoneStream( + targetDataLine, + targetType, + outputFile); + + /* We are waiting for the user to press ENTER to + start the recording. (You might find it + inconvenient if recording starts immediately.) + */ + out("Press ENTER to start the recording."); + try + { + System.in.read(); + } + catch (IOException e) + { + e.printStackTrace(); + } + /* Here, the recording is actually started. + */ + recorder.start(); + out("Recording..."); + + /* And now, we are waiting again for the user to press ENTER, + this time to signal that the recording should be stopped. + */ + out("Press ENTER to stop the recording."); + try + { + System.in.read(); + } + catch (IOException e) + { + e.printStackTrace(); + } + + /* Here, the recording is actually stopped. + */ + recorder.stopRecording(); + out("Recording stopped."); + // System.exit(0); + } + + + + private static void printUsageAndExit() + { + out("SimpleAudioRecorder: usage:"); + out("\tjava SimpleAudioRecorder -h"); + out("\tjava SimpleAudioRecorder "); + System.exit(0); + } + + + + private static void out(String strMessage) + { + System.out.println(strMessage); + } +} + + + +/*** SimpleAudioRecorder.java ***/ + + + + diff --git a/dasCore/src/main/java/org/das2/stream/PacketDescriptor.java b/dasCore/src/main/java/org/das2/stream/PacketDescriptor.java new file mode 100755 index 000000000..7ddca1af4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/PacketDescriptor.java @@ -0,0 +1,285 @@ +/* File: PacketDescriptor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream; + +import org.das2.util.StreamTool; +import org.das2.datum.Datum; +import org.das2.datum.DatumVector; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +/** Represents the global properties of the stream, that are accessible to + * datasets within. + * @author jbf + */ +public class PacketDescriptor implements Cloneable { + + private StreamXDescriptor xDescriptor; + private SkeletonDescriptor[] yDescriptors = new SkeletonDescriptor[6]; + private int yCount = 0; + private Map properties; + + /** Creates a new instance of StreamProperties */ + public PacketDescriptor( Element element ) throws StreamException + { + properties= new HashMap(); + if (element.getTagName().equals("packet")) { + processElement(element); + } else { + processLegacyElement(element); + } + } + + private void processElement(Element element) throws StreamException + { + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node node = children.item(i); + if ( node instanceof Element ) { + Element child = (Element)node; + String name = child.getTagName(); + if ( name.equals("x") ) { + xDescriptor = new StreamXDescriptor(child); + } else if ( name.equals("y") ) { + StreamMultiYDescriptor d = new StreamMultiYDescriptor(child); + addYDescriptor(d); + } else if ( name.equals("yscan") ) { + StreamYScanDescriptor d = new StreamYScanDescriptor(child); + addYDescriptor(d); + } else if ( name.equals("properties") ) { + try { + NodeList list = element.getElementsByTagName("properties"); + if (list.getLength() != 0) { + Element propertiesElement = (Element)list.item(0); + Map m = StreamTool.processPropertiesElement(propertiesElement); + properties.putAll(m); + } + } catch ( StreamException e ) { + throw new RuntimeException(e); + } + } + } + } + } + + private void processLegacyElement(Element element) + throws StreamException + { + NodeList children= element.getChildNodes(); + for (int i=0; i= yCount) { + throw new IndexOutOfBoundsException("index = " + index + ", yCount = " + yCount); + } + return yDescriptors[index]; + } + + public int getSizeBytes() { + int sizeBytes = xDescriptor.getSizeBytes(); + for (int i = 0; i < yCount; i++) { + sizeBytes += yDescriptors[i].getSizeBytes(); + } + return sizeBytes; + } + + public DatumVector[] read(ByteBuffer input) { + DatumVector[] vectors = new DatumVector[yCount + 1]; + vectors[0] = xDescriptor.read(input); + for (int i = 0; i < yCount; i++) { + vectors[i + 1] = yDescriptors[i].read(input); + } + return vectors; + } + + public void write(Datum xTag, DatumVector[] vectors, ByteBuffer output) { + xDescriptor.writeDatum(xTag, output); + for (int i = 0; i < yCount; i++) { + yDescriptors[i].write(vectors[i], output); + } + //ASCII KLUDGE!!!! + if (yDescriptors[yCount - 1] instanceof StreamYScanDescriptor + && ((StreamYScanDescriptor)yDescriptors[yCount - 1]).getDataTransferType().isAscii() + && Character.isWhitespace((char)output.get(output.position() - 1))) { + output.put(output.position() - 1, (byte)'\n'); + } else if (yDescriptors[yCount - 1] instanceof StreamMultiYDescriptor + && ((StreamMultiYDescriptor)yDescriptors[yCount - 1]).getDataTransferType().isAscii() + && Character.isWhitespace((char)output.get(output.position() - 1))) { + output.put(output.position() - 1, (byte)'\n'); + } + } + + private static String trimComment(String line) { + int index = line.indexOf(';'); + if (index == 0) { + return ""; + } else if (index != -1) { + return line.substring(0, index); + } else { + return line; + } + } + + private static final Pattern LABEL_PATTERN = Pattern.compile("\\s*label\\((\\d+)\\)\\s*"); + + public static PacketDescriptor createLegacyPacketDescriptor(Map dsdf) { + PacketDescriptor packetDescriptor = new PacketDescriptor(); + packetDescriptor.setXDescriptor(new StreamXDescriptor()); + if (dsdf.get("form").equals("x_tagged_y_scan")) { + StreamYScanDescriptor yscan = new StreamYScanDescriptor(); + yscan.setYCoordinates((double[])dsdf.get("y_coordinate")); + packetDescriptor.addYDescriptor(yscan); + } else if (dsdf.get("form").equals("x_multi_y") && dsdf.get("ny") != null) { + StreamMultiYDescriptor y = new StreamMultiYDescriptor(); + packetDescriptor.addYDescriptor(y); + } else if (dsdf.get("form").equals("x_multi_y") && dsdf.get("items") != null) { + List planeList = (List)dsdf.get("plane-list"); + packetDescriptor.addYDescriptor(new StreamMultiYDescriptor()); + for (int index = 0; index < planeList.size(); index++) { + StreamMultiYDescriptor y = new StreamMultiYDescriptor(); + y.setName((String)planeList.get(index)); + packetDescriptor.addYDescriptor(y); + } + } + return packetDescriptor; + } + + private static String[] ensureCapacity(String[] array, int capacity) { + if (array == null) { + return new String[capacity]; + } else if (array.length >= capacity) { + return array; + } else { + String[] temp = new String[capacity]; + System.arraycopy(array, 0, temp, 0, array.length); + return temp; + } + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("packet"); + element.appendChild(xDescriptor.getDOMElement(document)); + for (int i = 0; i < yCount; i++) { + element.appendChild(yDescriptors[i].getDOMElement(document)); + } + return element; + } + + public Object getProperty(String name) { + return properties.get(name); + } + + public Map getProperties() { + return Collections.unmodifiableMap(properties); + } + + public void setProperty(String name, Object value) { + properties.put(name, value); + } + + public Object clone() { + try { + PacketDescriptor clone = (PacketDescriptor)super.clone(); + clone.xDescriptor = (StreamXDescriptor)xDescriptor.clone(); + clone.yDescriptors = new SkeletonDescriptor[yCount]; + for (int i = 0; i < yCount; i++) { + clone.yDescriptors[i] = (SkeletonDescriptor)yDescriptors[i].clone(); + } + return clone; + } catch (CloneNotSupportedException cnse) { + throw new RuntimeException(cnse); + } + } + + public String toString() { + String result= "\n"; + for ( int iplane= 0; iplane + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream; + +import org.das2.datum.Units; +import java.util.HashMap; +import java.util.Map; +import org.das2.datum.CalendarTime; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumRangeUtil; + +/** + * + * @author eew + */ +public enum PropertyType { + + DOUBLE("double"), + DOUBLE_ARRAY("doubleArray"), + DATUM("Datum"), + DATUM_RANGE("DatumRange"), + INTEGER("int"), + STRING("String"), + TIME("Time"), + TIME_RANGE("TimeRange"), + ; + + private static final Map map = new HashMap(); + static { + for (PropertyType t : values()) + map.put(t.name, t); + } + + public static PropertyType getByName(String name) { + PropertyType result= (PropertyType)map.get(name); + if ( result==null ) { + throw new IllegalArgumentException( "Unrecognized property type: "+name ); + } + return result; + } + + private final String name; + + /** Creates a new instance of PropertyType */ + private PropertyType(String name) { + this.name = name; + } + + public Object parse(String s) throws java.text.ParseException { + switch(this){ + case STRING: + { + return s; + } + case DOUBLE: + try { + return new Double(s); + } + catch (NumberFormatException nfe) { + throw new java.text.ParseException(nfe.getMessage(), 0); + } + case INTEGER: + try { + return new Integer(s); + } + catch (NumberFormatException nfe) { + throw new java.text.ParseException(nfe.getMessage(), 0); + } + case DOUBLE_ARRAY: + try { + String[] strings = s.split(","); + double[] doubles = new double[strings.length]; + for (int i = 0; i < strings.length; i++) { + doubles[i] = Double.parseDouble(strings[i]); + } + return doubles; + } + catch (NumberFormatException nfe) { + throw new java.text.ParseException(nfe.getMessage(), 0); + } + case DATUM: + { + String[] split = s.split("\\s+"); + if (split.length == 1) { + return Units.dimensionless.parse(split[0]); + } + else if (split.length == 2) { + Units units = Units.lookupUnits(split[1]); + return units.parse(split[0]); + } + else { + throw new IllegalArgumentException("Too many tokens: '" + s + "'"); + } + } + case DATUM_RANGE: + { + String[] split = s.split("\\s+"); + if (split.length < 3){ + throw new IllegalArgumentException("Too few tokens in range: '" + s + "'"); + } + if(! split[1].toLowerCase().equals("to")) + throw new java.text.ParseException("Range '"+s+"' is missing the word 'to'", 0); + + if (split.length == 3){ + Datum begin = Units.dimensionless.parse(split[0]); + Datum end = Units.dimensionless.parse(split[2]); + return new DatumRange(begin, end); + } + + // New for 2014-06-12, allow units strings to have spaces, thus + // "V**2 m**-2 Hz**-1" is legal + StringBuilder bldr = new StringBuilder(); + for(int i = 3; i < split.length; i++){ + if(i > 3) bldr.append(" "); + bldr.append(split[i]); + } + + Units units = Units.lookupUnits(bldr.toString()); + Datum begin = units.parse(split[0]); + Datum end = units.parse(split[2]); + return new DatumRange(begin, end); + + } + case TIME: + { + return new CalendarTime(s).toDatum(); + } + case TIME_RANGE: + { + return DatumRangeUtil.parseTimeRange(s); + } + default: + throw new IllegalStateException("unrecognized name: " + name); + } + } + + @Override + public String toString() { + return name; + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/SkeletonDescriptor.java b/dasCore/src/main/java/org/das2/stream/SkeletonDescriptor.java new file mode 100644 index 000000000..ee3f248cb --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/SkeletonDescriptor.java @@ -0,0 +1,48 @@ +/* File: SkeletonDescriptor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream; + +import org.das2.datum.DatumVector; +import java.nio.ByteBuffer; +import java.util.Map; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public interface SkeletonDescriptor { + + int getSizeBytes(); + + DatumVector read(ByteBuffer input); + + void write(DatumVector input, ByteBuffer output); + + Element getDOMElement(Document document); + + Object clone(); + + Object getProperty(String name); + + public Map getProperties(); + +} diff --git a/dasCore/src/main/java/org/das2/stream/Sonifier.java b/dasCore/src/main/java/org/das2/stream/Sonifier.java new file mode 100644 index 000000000..9bc1bc615 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/Sonifier.java @@ -0,0 +1,77 @@ +package org.das2.stream; + +import org.das2.util.StreamTool; +import java.io.*; +import java.io.File; +import java.io.IOException; +import java.nio.channels.*; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.SourceDataLine; + +public class Sonifier implements StreamHandler { + private static final int EXTERNAL_BUFFER_SIZE = 128000; + byte[] buffer; + SourceDataLine line = null; + int bufferInputIndex; + + + public void packet(PacketDescriptor pd, org.das2.datum.Datum xTag, org.das2.datum.DatumVector[] vectors) throws StreamException { + double max= 1.0; + buffer[bufferInputIndex++]= (byte) ( 256 * vectors[0].doubleValue(0, vectors[0].getUnits() ) ); + if ( bufferInputIndex==100 ) { + line.write(buffer, 0, bufferInputIndex); + bufferInputIndex=0; + } + } + + public void packetDescriptor(PacketDescriptor pd) throws StreamException { + } + + public void streamClosed(StreamDescriptor sd) throws StreamException { + line.drain(); + line.close(); + } + + public void streamDescriptor(StreamDescriptor sd) throws StreamException { + AudioFormat audioFormat= new AudioFormat( 8000, 8, 1, true, false ); + + DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); + try { + line = (SourceDataLine) AudioSystem.getLine(info); + line.open(audioFormat); + } + catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + buffer = new byte[EXTERNAL_BUFFER_SIZE]; + bufferInputIndex= 0; + line.start(); + } + + public void streamException(StreamException se) throws StreamException { + se.printStackTrace(); + System.exit(0); + } + public void streamComment( StreamComment sc ) throws StreamException { + + } + + public static void main( String[] args ) throws Exception { + InputStream in; + if ( args.length==0 ) { + in= System.in; + } else { + in= new FileInputStream(args[0]); + } + ReadableByteChannel channel= Channels.newChannel(in); + StreamTool.readStream(channel, new Sonifier() ); + } +} + + diff --git a/dasCore/src/main/java/org/das2/stream/StreamComment.java b/dasCore/src/main/java/org/das2/stream/StreamComment.java new file mode 100644 index 000000000..ccdefa2ee --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamComment.java @@ -0,0 +1,49 @@ +/* File: StreamException.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on February 11, 2004, 11:03 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream; + +import org.w3c.dom.*; + +/** + * + * @author jbf + */ +public class StreamComment { + + Element element; + + public final String TYPE_TASK_SIZE="taskSize"; + public final String TYPE_TASK_PROGRESS="taskProgress"; + public final String TYPE_LOG="log:(.*)"; + + public StreamComment( Element element ) { + this.element= element; + } + + public String getType() { return element.getAttribute("type"); } + public String getValue() { return element.getAttribute("value"); } + + @Override + public String toString() { return "stream comment: "+getType()+"="+getValue(); } +} diff --git a/dasCore/src/main/java/org/das2/stream/StreamDescriptor.java b/dasCore/src/main/java/org/das2/stream/StreamDescriptor.java new file mode 100644 index 000000000..d3c2dc804 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamDescriptor.java @@ -0,0 +1,429 @@ +/* File: StreamDescriptor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream; + +import org.das2.util.StreamTool; +import org.das2.DasIOException; +import org.das2.datum.DatumVector; +import org.das2.util.IDLParser; +import java.io.*; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +//import org.apache.xml.serialize.OutputFormat; +//import org.apache.xml.serialize.XMLSerializer; +import org.w3c.dom.*; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; +import org.xml.sax.SAXParseException; + +/** Represents the global properties of the stream, that are accessible to + * datasets within. + * @author jbf + */ +public class StreamDescriptor implements SkeletonDescriptor, Cloneable { + + private Map properties = new HashMap(); + + private StreamXDescriptor xDescriptor; + private ArrayList yDescriptors = new ArrayList(); + private String compression; + + /** Creates a new instance of StreamProperties */ + public StreamDescriptor(Element element) throws StreamException { + if (element.getTagName().equals("stream")) { + processElement(element); + } + else { + processLegacyElement(element); + } + } + + private void processElement(Element element) throws StreamException { + compression = element.getAttribute("compression"); + NodeList list = element.getElementsByTagName("properties"); + if (list.getLength() != 0) { + Element propertiesElement = (Element)list.item(0); + Map m = StreamTool.processPropertiesElement(propertiesElement); + properties.putAll(m); + } + } + + private void processLegacyElement(Element element) throws StreamException { + NodeList children= element.getChildNodes(); + for (int i=0; i= capacity) { + return array; + } + else { + String[] temp = new String[capacity]; + System.arraycopy(array, 0, temp, 0, array.length); + return temp; + } + } + + /** Getter for property compression. + * @return Value of property compression. + * + */ + public String getCompression() { + return compression; + } + + /** Setter for property compression. + * @param compression New value of property compression. + * + */ + public void setCompression(String compression) { + this.compression = compression; + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("stream"); + if (compression != null && !compression.equals("")) { + element.setAttribute("compression", compression); + } + if (!properties.isEmpty()) { + Element propertiesElement = StreamTool.processPropertiesMap( document, properties ); + element.appendChild(propertiesElement); + } + return element; + } + + public Object clone() { + try { + StreamDescriptor clone = (StreamDescriptor)super.clone(); + clone.properties = new HashMap(this.properties); + return clone; + } + catch (CloneNotSupportedException cnse) { + throw new RuntimeException(cnse); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/StreamException.java b/dasCore/src/main/java/org/das2/stream/StreamException.java new file mode 100644 index 000000000..1b89251fb --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamException.java @@ -0,0 +1,51 @@ +/* File: StreamException.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on February 11, 2004, 11:03 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream; + +import org.das2.DasException; +import java.io.IOException; +import org.xml.sax.SAXException; + +/** + * + * @author eew + */ +public class StreamException extends DasException { + + /** Creates a new instance of StreamException */ + public StreamException(String message) { + super(message); + } + + public StreamException(SAXException se) { + super(se.getMessage()); + initCause(se); + } + + public StreamException(IOException ioe) { + super(ioe.getMessage()); + initCause(ioe); + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/StreamHandler.java b/dasCore/src/main/java/org/das2/stream/StreamHandler.java new file mode 100644 index 000000000..6fafbaaff --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamHandler.java @@ -0,0 +1,40 @@ +/* File: StreamHandler.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on February 11, 2004, 10:57 AM + * by Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream; + +import org.das2.datum.Datum; +import org.das2.datum.DatumVector; + +/** + * + * @author Edward E. West + */ +public interface StreamHandler { + void streamDescriptor(StreamDescriptor sd) throws StreamException; + void packetDescriptor(PacketDescriptor pd) throws StreamException; + void packet(PacketDescriptor pd, Datum xTag, DatumVector[] vectors) throws StreamException; + void streamClosed(StreamDescriptor sd) throws StreamException; + void streamException(StreamException se) throws StreamException; + void streamComment(StreamComment sc) throws StreamException; +} diff --git a/dasCore/src/main/java/org/das2/stream/StreamMultiYDescriptor.java b/dasCore/src/main/java/org/das2/stream/StreamMultiYDescriptor.java new file mode 100755 index 000000000..cd05b6f75 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamMultiYDescriptor.java @@ -0,0 +1,180 @@ +/* File: StreamMultiYDescriptor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.stream; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import org.das2.datum.DatumVector; +import org.das2.datum.Units; +import org.das2.util.StreamTool; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class StreamMultiYDescriptor implements SkeletonDescriptor, Cloneable { + + private String name = ""; + private Units units = Units.dimensionless; + private DataTransferType transferType = DataTransferType.SUN_REAL4; + + public StreamMultiYDescriptor(Element element) throws StreamException + { + if (element.getTagName().equals("y")) { + processElement(element); + } else { + processLegacyElement(element); + } + } + + private void processElement(Element element) throws StreamException { + + //element.getAttribute returns empty string if attr is not specified + //so safe to just use it directly + String name = element.getAttribute("name"); + if ( name != null) { + this.name = name; + } + String typeStr = element.getAttribute("type"); + DataTransferType type = DataTransferType.getByName(typeStr); + + String unitsString = element.getAttribute("units"); + if (unitsString != null) { + units = Units.lookupUnits(unitsString); + } + + NamedNodeMap attrs = element.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + Node n = attrs.item(i); + properties.put(n.getNodeName(), n.getNodeValue()); + } + + NodeList nl = element.getElementsByTagName("properties"); + for (int i = 0; i < nl.getLength(); i++) { + Element el = (Element) nl.item(i); + Map m = StreamTool.processPropertiesElement(el); + + // make sure we don't conflict with the 3 reserved properites, + // name, type and units + for(String sPropName: m.keySet()){ + if(sPropName.equals("name")||sPropName.equals("type")|| + sPropName.equals("units")) + throw new StreamException("Can't use reserved property names 'name'"+ + "'type' or 'units' in side a y-plane properties element."); + } + properties.putAll(m); + } + + if (type != null) { + transferType = type; + } else { + throw new RuntimeException("Illegal transfer type: " + typeStr); + } + } + + private void processLegacyElement(Element element) { + if (element.getAttribute("name") != null) { + name = element.getAttribute("name"); + } else { + name = ""; + } + String typeStr = element.getAttribute("type"); + DataTransferType type = DataTransferType.getByName(typeStr); + if (type != null) { + transferType = type; + } else { + throw new RuntimeException("Illegal transfer type: " + typeStr); + } + } + + public StreamMultiYDescriptor() { + } + + public Units getUnits() { + return units; + } + + public void setUnits(Units units) { + this.units = units; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public int getSizeBytes() { + return transferType.getSizeBytes(); + } + + public void setDataTransferType(DataTransferType transferType) { + this.transferType = transferType; + } + + public DataTransferType getDataTransferType() { + return transferType; + } + + public DatumVector read(ByteBuffer input) { + return DatumVector.newDatumVector(new double[]{transferType.read(input)}, units); + } + + public void write(DatumVector input, ByteBuffer output) { + transferType.write(input.doubleValue(0, units), output); + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("y"); + element.setAttribute("units", units.toString()); + element.setAttribute("type", transferType.toString()); + if (!name.equals("")) { + element.setAttribute("name", name); + } + return element; + } + + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException cnse) { + throw new RuntimeException(cnse); + } + } + + Map properties = new HashMap(); + + public Object getProperty(String name) { + return properties.get(name); + } + + public Map getProperties() { + return new HashMap(properties); + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/StreamProcessor.java b/dasCore/src/main/java/org/das2/stream/StreamProcessor.java new file mode 100755 index 000000000..8738e60c6 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamProcessor.java @@ -0,0 +1,49 @@ +/* + * StreamProcessor.java + * + * Created on December 16, 2003, 10:09 AM + */ + +package org.das2.stream; + +import org.das2.DasProperties; +import java.io.*; + +/** + * + * @author Jeremy + */ +public abstract class StreamProcessor { + + public abstract void process( InputStream in, OutputStream out ) throws IOException ; + + public InputStream process(final InputStream in) throws IOException { + + final PipedOutputStream out= new PipedOutputStream(); + final PipedInputStream pin= new PipedInputStream(out); + Runnable r= new Runnable() { + public void run() { + try { + process( in, out ); + out.close(); + } catch ( IOException e ) { + try { + out.write( getDasExceptionStream(e) ); + } catch ( IOException e2 ) { + DasProperties.getLogger().severe(e2.toString()); + throw new RuntimeException(e2); + } + } + } + }; + Thread t= new Thread(r); + t.start(); + return pin; + + } + + public byte[] getDasExceptionStream( Throwable t ) { + String exceptionString= "[xx]"; + return exceptionString.getBytes(); + } +} diff --git a/dasCore/src/main/java/org/das2/stream/StreamProducer.java b/dasCore/src/main/java/org/das2/stream/StreamProducer.java new file mode 100644 index 000000000..f58991c60 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamProducer.java @@ -0,0 +1,291 @@ +/* File: StreamProducer.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on February 12, 2004, 2:59 PM + * by Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream; + +import org.das2.datum.Datum; +import org.das2.datum.DatumVector; +import org.das2.util.DeflaterChannel; +import org.das2.util.StreamTool; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.ByteBuffer; +import java.nio.channels.WritableByteChannel; +import java.util.*; +import java.util.IdentityHashMap; +import java.util.Map; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + + +/** + * + * @author eew + */ +public class StreamProducer implements StreamHandler { + + private Map descriptors = new IdentityHashMap(); + private Map idMap = new HashMap(); + private WritableByteChannel stream; + private ByteBuffer bigBuffer = ByteBuffer.allocate(4096); + private byte[] six = new byte[6]; + private int nextAvail = 1; + private DocumentBuilder builder; + + private static class IdentitySet extends AbstractCollection implements Set { + + private IdentityHashMap map = new IdentityHashMap(); + private static final Object VALUE = new Object(); + + public Iterator iterator() { + return map.keySet().iterator(); + } + + public int size() { + return map.size(); + } + + } + + /** Creates a new instance of StreamProducer */ + public StreamProducer(WritableByteChannel stream) { + this.stream = stream; + try { + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } + catch (ParserConfigurationException pce) { + throw new RuntimeException(pce); + } + } + + public void packet(PacketDescriptor pd, Datum xTag, DatumVector[] vectors) throws StreamException { + try { + if (!descriptors.containsKey(pd)) { + packetDescriptor(pd); + } + String header = (String)descriptors.get(pd); + if (pd.getSizeBytes() > bigBuffer.capacity()) { + resizeBuffer(pd.getSizeBytes() + pd.getSizeBytes() >> 1); + } + if ((pd.getSizeBytes() + 4) > bigBuffer.remaining()) { + flush(); + } + six[0] = six[3] = (byte)':'; + six[1] = (byte)header.charAt(0); + six[2] = (byte)header.charAt(1); + bigBuffer.put(six, 0, 4); + pd.getXDescriptor().writeDatum(xTag, bigBuffer); + for (int i = 0; i < pd.getYCount(); i++) { + pd.getYDescriptor(i).write(vectors[i], bigBuffer); + } + //Ascii format hack + int lastChar = bigBuffer.position() - 1; + SkeletonDescriptor lastY = pd.getYDescriptor(pd.getYCount() - 1); + if (((lastY instanceof StreamYScanDescriptor + && ((StreamYScanDescriptor)lastY).getDataTransferType().isAscii()) + || (lastY instanceof StreamMultiYDescriptor + && ((StreamMultiYDescriptor)lastY).getDataTransferType().isAscii())) + && Character.isWhitespace((char)bigBuffer.get(lastChar))) { + bigBuffer.put(lastChar, (byte)'\n'); + } + //End of Ascii format hack + bigBuffer.flip(); + stream.write(bigBuffer); + bigBuffer.compact(); + } + catch (IOException ioe) { + throw new StreamException(ioe); + } + } + + private int nextAvailable() { + int result = nextAvail; + if (nextAvail == 99) { + nextAvail = 1; + } + else { + nextAvail++; + } + return result; + } + + public void packetDescriptor(PacketDescriptor pd) throws StreamException { + try { + String id = toString2(nextAvailable()); + if (idMap.containsKey(id)) { + Object d = idMap.get(id); + descriptors.remove(d); + } + descriptors.put(pd, id); + idMap.put(id, pd); + Document document = builder.newDocument(); + Element root = pd.getDOMElement(document); + document.appendChild(root); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(out, "US-ASCII"); + StreamTool.formatHeader(document, writer); + writer.append("\n"); + writer.flush(); + byte[] header = out.toByteArray(); + int length = header.length; + if (bigBuffer.remaining() < (length + 10)) { + flush(); + } + if (bigBuffer.capacity() < (length + 10)) { + resizeBuffer(length + (length / 2) + 15); + System.err.println("length: " + length); + } + six[0] = '['; + six[1] = (byte)id.charAt(0); + six[2] = (byte)id.charAt(1); + six[3] = ']'; + bigBuffer.put(six, 0, 4); + six[0] = (byte)Character.forDigit((length / 100000) % 10, 10); + six[1] = (byte)Character.forDigit((length / 10000) % 10, 10); + six[2] = (byte)Character.forDigit((length / 1000) % 10, 10); + six[3] = (byte)Character.forDigit((length / 100) % 10, 10); + six[4] = (byte)Character.forDigit((length / 10) % 10, 10); + six[5] = (byte)Character.forDigit(length % 10, 10); + bigBuffer.put(six); + bigBuffer.put(header); + bigBuffer.flip(); + stream.write(bigBuffer); + bigBuffer.compact(); + } + catch (IOException ioe) { + throw new StreamException(ioe); + } + } + + public void resizeBuffer(int size) throws StreamException { + flush(); + System.err.println("resizeBuffer(" + size + ")"); + bigBuffer = ByteBuffer.allocate(size); + } + + public void streamClosed(StreamDescriptor sd) throws StreamException { + flush(); + try { + stream.close(); + } + catch (IOException ioe) { + throw new StreamException(ioe); + } + } + + public void streamDescriptor(StreamDescriptor sd) throws StreamException { + try { + Document document = builder.newDocument(); + Element root = sd.getDOMElement(document); + document.appendChild(root); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(out, "US-ASCII"); + StreamTool.formatHeader(document, writer); + writer.append("\n"); + writer.flush(); + byte[] header = out.toByteArray(); + int length = header.length; + six[0] = '['; + six[1] = six[2] = '0'; + six[3] = ']'; + bigBuffer.put(six, 0, 4); + six[0] = (byte)Character.forDigit((length / 100000) % 10, 10); + six[1] = (byte)Character.forDigit((length / 10000) % 10, 10); + six[2] = (byte)Character.forDigit((length / 1000) % 10, 10); + six[3] = (byte)Character.forDigit((length / 100) % 10, 10); + six[4] = (byte)Character.forDigit((length / 10) % 10, 10); + six[5] = (byte)Character.forDigit(length % 10, 10); + bigBuffer.put(six); + bigBuffer.put(header); + flush(); + if ("deflate".equals(sd.getCompression())) { + stream = getDeflaterChannel(stream); + } + } + catch (IOException ioe) { + throw new StreamException(ioe); + } + } + + public void streamException(StreamException se) throws StreamException { + } + + + + public void flush() throws StreamException { + try { + bigBuffer.flip(); + while (bigBuffer.hasRemaining()) { + stream.write(bigBuffer); + } + bigBuffer.clear(); + } + catch (IOException ioe) { + throw new StreamException(ioe); + } + } + + private static String toString4(int i) { + if (i > 9999) { + throw new IllegalArgumentException("header is too big"); + } + else if (i < 10) { + return "000" + i; + } + else if (i < 100) { + return "00" + i; + } + else if (i < 1000) { + return "0" + i; + } + else { + return String.valueOf(i); + } + } + + private static String toString2(int i) { + if (i > 99) { + throw new IllegalArgumentException("header number cannot be > 99"); + } + else if (i < 10) { + return "0" + i; + } + else { + return String.valueOf(i); + } + } + + private static WritableByteChannel getDeflaterChannel(WritableByteChannel channel) throws IOException { + return new DeflaterChannel(channel); + //return Channels.newChannel(new DeflaterOutputStream(Channels.newOutputStream(channel))); + } + + public void streamComment(StreamComment sc) throws StreamException { + } + +} + diff --git a/dasCore/src/main/java/org/das2/stream/StreamRequirements.txt b/dasCore/src/main/java/org/das2/stream/StreamRequirements.txt new file mode 100755 index 000000000..b48d84b8b --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamRequirements.txt @@ -0,0 +1,55 @@ +Requirements for das2 Streams + +1. self describing + 1. the stream must contain all information needed to be "useful." + 2. dataset interface in das2 defines useful. +2. extensible + 1. metadata, annotating data ( e.g. via xml headers ) + 2. introduction of new data types ( yscan, multiy, ... ) +3. mode changes, table geometry +4. multiple data "planes" + 1. define planes as datasets that share tags + 2. e.g. Peaks and Averages +5. supports data compression + 1. compression type documented + 2. internal typeness preserved +6. allow for progress indication during transport + 1. total size attribute + 2. indication of parametric tag location + ( the tableBuilder knows the start,end parameters, plus the last paremeter + read from the stream, therefore progress can be indicated. ) +7. streamable so that there needn't be any server side storage +8. chainable operators can be built without difficulty + 1. reduction, fft, peaksAndAverages +9. non-streaming operators + 1. append +10. parallelizable + 1. divide request into set of subtasks that can be easily combined (e.g. via append) + 2. redirect to workers +11. easily appendable + 1. caching becomes trivial when a cache of streams can be collected and easily + appended to satisfy the request + 2. support for parallelizable +12. das1 streams can be wrapped to make das2 streams +13. easily identified (e.g. "das2" is first four bytes) +14. non-monotonic tags +15. indication of failure modes, heartbeats + + +Existing das2Stream lacks support for: + 2.2, 3, 6.2, 10 + +I'm providing a glossary in case I'm misusing words, and so we share a common +core vocabulary when talking about these things. + + reduction: reduction of a dataset statistically, such as bin averaging + compaction: lossless compression of a data set for transmission or storage, such as gzip + planes: datasets that share tags + tags: data that indicates the context of science data, such as a timetag. + For efficiency, tags are generally required to be monotonically increasing. + worker: server that satisfies requests of a master server + scalability: ability to handle exponentially increasing demands. + metadata: data about data + annotating data: data useful for documentation but not required for normal operations + extensibility: ability to expand functionality + streamable: not requiring temporary local storage of data. diff --git a/dasCore/src/main/java/org/das2/stream/StreamUtil.java b/dasCore/src/main/java/org/das2/stream/StreamUtil.java new file mode 100644 index 000000000..40d4af200 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamUtil.java @@ -0,0 +1,71 @@ +/* + * Util.java + * + * Created on September 23, 2005, 1:01 PM + * + * + */ + +package org.das2.stream; + +import org.das2.DasException; +import org.das2.client.DataSetStreamHandler; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.TableDataSet; +import org.das2.util.monitor.NullProgressMonitor; +import org.das2.util.StreamTool; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.channels.ReadableByteChannel; +import java.util.HashMap; + +/** + * + * @author Jeremy + */ +public class StreamUtil { + + private static final String DATA_SET_ID_PREFIX + = "class:org.das2.stream.test.LocalFileStandardDataStreamSource?file="; + + public static TableDataSet loadTableDataSet( String filename ) { + try { + filename= URLEncoder.encode(filename,"UTF-8"); + DataSetDescriptor dsd= DataSetDescriptor.create( DATA_SET_ID_PREFIX+filename ); + dsd.setDefaultCaching(false); + DataSet ds= dsd.getDataSet(null,null,null,null); + return (TableDataSet)ds; + } catch ( UnsupportedEncodingException e ) { + throw new RuntimeException(e); + } catch ( DasException e ) { + throw new RuntimeException(e); + } + } + + public static DataSet loadDataSetNew( String filename ) throws IOException, StreamException { + FileInputStream in= new FileInputStream( filename ); + ReadableByteChannel channel = in.getChannel(); + + DataSetStreamHandler handler = new DataSetStreamHandler( new HashMap(), new NullProgressMonitor() ); + + StreamTool.readStream(channel, handler); + return handler.getDataSet(); + } + + public static DataSet loadDataSet( String filename ) { + try { + filename= URLEncoder.encode(filename,"UTF-8"); + DataSetDescriptor dsd= DataSetDescriptor.create( DATA_SET_ID_PREFIX+filename ); + dsd.setDefaultCaching(false); + DataSet ds= dsd.getDataSet(null,null,null,null); + return ds; + } catch ( UnsupportedEncodingException e ) { + throw new RuntimeException(e); + } catch ( DasException e ) { + throw new RuntimeException(e); + } + } +} diff --git a/dasCore/src/main/java/org/das2/stream/StreamXDescriptor.java b/dasCore/src/main/java/org/das2/stream/StreamXDescriptor.java new file mode 100644 index 000000000..6336c54f5 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamXDescriptor.java @@ -0,0 +1,174 @@ +/* File: StreamXDescriptor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.stream; + +import org.das2.datum.Datum; +import org.das2.datum.DatumVector; +import org.das2.datum.TimeLocationUnits; +import org.das2.datum.TimeUtil; +import org.das2.datum.Units; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class StreamXDescriptor implements SkeletonDescriptor, Cloneable { + + private Datum base; + + private Units baseUnits = Units.us2000; + + private Units units = null; + + private DataTransferType transferType = DataTransferType.SUN_REAL4; + + public StreamXDescriptor() { + } + + public StreamXDescriptor( Element element ) { + if ( element.getTagName().equals("x") ) { + processElement(element); + } + else { + processLegacyElement(element); + } + } + + private void processElement( Element element ) { + String typeStr = element.getAttribute("type"); + DataTransferType type = DataTransferType.getByName(typeStr); + if ( type != null ) { + transferType = type; + } + else { + throw new RuntimeException("Illegal transfer type: " + typeStr); + } + if ( type instanceof DataTransferType.Time ) { + units= ((DataTransferType.Time)type).getUnits(); + } else { + String unitsString = element.getAttribute("units"); + units = Units.lookupUnits(unitsString); + } + + String baseString = element.getAttribute("base"); + if (baseString != null && !baseString.equals("")) { + if (baseUnits instanceof TimeLocationUnits) { + base = TimeUtil.createValid(baseString); + } + } + } + + private void processLegacyElement( Element element ) { + String typeStr = element.getAttribute("type"); + DataTransferType type = DataTransferType.getByName(typeStr); + if (type != null) { + transferType = type; + } + else { + throw new RuntimeException("Illegal transfer type: " + typeStr); + } + } + + public Datum getBase() { + return base; + } + + public void setBase(Datum base) { + this.base = base; + } + + public int getSizeBytes() { + return transferType.getSizeBytes(); + } + + public Units getUnits() { + return units; + } + + /* Units must be set now!!! */ + public void setUnits(Units units) { + this.units = units; + } + + public void setDataTransferType(DataTransferType transferType) { + this.transferType = transferType; + if ( transferType instanceof DataTransferType.Time ) { + if ( units==null ) throw new IllegalArgumentException("please set the units first!!!"); + ((DataTransferType.Time)transferType).units= units; + } + } + + public DataTransferType getDataTransferType() { + return transferType; + } + + + public Datum readDatum(ByteBuffer input) { + return Datum.create(transferType.read(input), units); + } + + public DatumVector read(ByteBuffer input) { + return DatumVector.newDatumVector(new double[]{transferType.read(input)}, units); + } + + public void writeDatum(Datum datum, ByteBuffer output) { + transferType.write(datum.doubleValue(units), output); + } + + public void write(DatumVector input, ByteBuffer output) { + transferType.write(input.doubleValue(0, units), output); + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("x"); + if (base != null) { + element.setAttribute("base", base.toString()); + } + element.setAttribute("units", units.toString()); + element.setAttribute("type", transferType.toString()); + return element; + } + + public Object clone() { + try { + return super.clone(); + } + catch (CloneNotSupportedException cnse) { + throw new RuntimeException(cnse); + } + } + + Map properties= new HashMap(); + + public Object getProperty(String name) { + return properties.get(name); + } + + public Map getProperties() { + return new HashMap(properties); + } + +} + diff --git a/dasCore/src/main/java/org/das2/stream/StreamYScanDescriptor.java b/dasCore/src/main/java/org/das2/stream/StreamYScanDescriptor.java new file mode 100755 index 000000000..91bab3a07 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StreamYScanDescriptor.java @@ -0,0 +1,240 @@ +/* File: StreamYScanDescriptor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.stream; + +import org.das2.datum.DatumVector; +import org.das2.datum.Units; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class StreamYScanDescriptor implements SkeletonDescriptor, Cloneable { + + private Units yUnits = Units.dimensionless; + private Units zUnits = Units.dimensionless; + private double[] yTags; + private int nitems; + private String name = ""; + private DataTransferType transferType = DataTransferType.SUN_REAL4; + + public StreamYScanDescriptor( Element element ) { + if ( element.getTagName().equals("yscan") ) { + processElement(element); + } + else { + processLegacyElement(element); + } + } + + private void processElement(Element element) { + nitems = Integer.parseInt(element.getAttribute("nitems")); + String yTagsText = element.getAttribute("yTags"); + if (yTagsText != null) { + yTags = new double[nitems]; + int parseInt = 0; + String[] tokens = yTagsText.split("\\s*,\\s*"); + for (int i = 0; i < nitems; i++) { + yTags[i] = Double.parseDouble(tokens[i]); + } + } + String typeStr = element.getAttribute("type"); + DataTransferType type = DataTransferType.getByName(typeStr); + if (type != null) { + transferType = type; + } + else { + throw new RuntimeException("Illegal transfer type: " + typeStr); + } + String yUnitsString = element.getAttribute("yUnits"); + if (yUnitsString != null) { + yUnits = Units.lookupUnits(yUnitsString); + } + String zUnitsString = element.getAttribute("zUnits"); + if (zUnitsString != null) { + zUnits = Units.lookupUnits(zUnitsString); + } + String name = element.getAttribute("name"); + if ( name != null ) { + this.name = name; + } + } + + private void processLegacyElement(Element element) { + try { + if ( !element.getTagName().equals("YScan") ) { + throw new IllegalArgumentException("xml tree root node is not the right type. "+ + "Node type is: "+element.getTagName()); + } + nitems= Integer.parseInt(element.getAttribute("nitems")); + if ( element.getAttribute("yCoordinate") != null ) { + String yCoordinateString= element.getAttribute("yCoordinate"); + yTags= new double[nitems]; + int parseIdx=0; + for (int i=0; i"; + } + + Map properties= new HashMap(); + + public Object getProperty(String name) { + return properties.get(name); + } + + public Map getProperties() { + return new HashMap(properties); + } + +} + diff --git a/dasCore/src/main/java/org/das2/stream/StripHeader.java b/dasCore/src/main/java/org/das2/stream/StripHeader.java new file mode 100755 index 000000000..60f3227cb --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/StripHeader.java @@ -0,0 +1,27 @@ +/* + * StripHeader.java + * + * Created on December 11, 2003, 12:35 PM + */ + +package org.das2.stream; + +import org.das2.util.StreamTool; +import java.io.*; + +/** + * + * @author Jeremy + */ +public class StripHeader { + + public static void stripHeader( InputStream in, OutputStream out ) throws IOException { + byte[] header= StreamTool.readXML(new PushbackInputStream(in)); + out.write(header); + } + + public static void main(String[] args) throws IOException { + stripHeader( System.in, System.out ); + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/TAvStreamProcessor.java b/dasCore/src/main/java/org/das2/stream/TAvStreamProcessor.java new file mode 100644 index 000000000..33622d2c9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/TAvStreamProcessor.java @@ -0,0 +1,57 @@ +/* + * gzip.java + * + * Created on July 11, 2003, 9:39 AM + */ + +package org.das2.stream; + + +import java.io.*; + + +/** + * + * @author jbf + */ +public class TAvStreamProcessor extends StreamProcessor { + + public void process(InputStream in, OutputStream out) throws IOException { + + } + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + InputStream in=null; + OutputStream out=null; + if ( args.length>0 ) { + try { + in= new FileInputStream(args[0]); + } catch ( FileNotFoundException ex) { + System.err.println("Input file not found"); + System.exit(-1); + } + } else { + in= System.in; + } + + if ( args.length>1 ) { + try { + out= new FileOutputStream(args[1]); + } catch ( FileNotFoundException ex) { + } + + } else { + out= System.out; + } + + try { + new TAvStreamProcessor().process(in,out); + } catch ( IOException ex) { + System.err.println(ex.getMessage()); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/ToAscii.java b/dasCore/src/main/java/org/das2/stream/ToAscii.java new file mode 100644 index 000000000..d1a4bf61c --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/ToAscii.java @@ -0,0 +1,86 @@ +/* File: ToAscii.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on February 17, 2004, 11:36 AM + * by Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream; + +import org.das2.datum.Datum; +import org.das2.datum.DatumVector; +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * + * @author eew + */ +public class ToAscii implements StreamHandler { + + private Map descriptors = new IdentityHashMap(); + private StreamHandler handler; + + /** Creates a new instance of ToAscii */ + public ToAscii(StreamHandler handler) { + this.handler = handler; + } + + public void packet(PacketDescriptor pd, Datum xTag, DatumVector[] vectors) throws StreamException { + PacketDescriptor outpd = (PacketDescriptor)descriptors.get(pd); + handler.packet(outpd, xTag, vectors); + } + + public void packetDescriptor(PacketDescriptor pd) throws StreamException { + DataTransferType ascii24 = DataTransferType.getByName("ascii24"); + DataTransferType ascii10 = DataTransferType.getByName("ascii10"); + PacketDescriptor outpd = (PacketDescriptor)pd.clone(); + outpd.getXDescriptor().setDataTransferType(ascii24); + for (int i = 0; i < outpd.getYCount(); i++) { + if (outpd.getYDescriptor(i) instanceof StreamMultiYDescriptor) { + ((StreamMultiYDescriptor)outpd.getYDescriptor(i)).setDataTransferType(ascii10); + } + else if (outpd.getYDescriptor(i) instanceof StreamYScanDescriptor) { + ((StreamYScanDescriptor)outpd.getYDescriptor(i)).setDataTransferType(ascii10); + } + } + handler.packetDescriptor(outpd); + descriptors.put(pd, outpd); + } + + public void streamClosed(StreamDescriptor sd) throws StreamException { + handler.streamClosed(sd); + } + + public void streamDescriptor(StreamDescriptor sd) throws StreamException { + handler.streamDescriptor(sd); + } + + public void streamException(StreamException se) throws StreamException { + handler.streamException(se); + } + + public void streamComment(StreamComment se) throws StreamException { + handler.streamComment(se); + } + + public void packet(PacketDescriptor pd, DatumVector vector) throws StreamException { + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/package.html b/dasCore/src/main/java/org/das2/stream/package.html new file mode 100644 index 000000000..a138539b8 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/package.html @@ -0,0 +1,9 @@ + +

    + Provides classes for parsing and formatting das2Streams, and a few stream +proof-of-concept stream operators that are not used. das2Streams are self-describing +data streams suitable for transmitting DataSets. A C library for reading and writing +streams is available and provides a means to convey large amounts of data between +C and java processes. +

    + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/stream/test/LocalFileStandardDataStreamSource.java b/dasCore/src/main/java/org/das2/stream/test/LocalFileStandardDataStreamSource.java new file mode 100644 index 000000000..8fe12a195 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/test/LocalFileStandardDataStreamSource.java @@ -0,0 +1,74 @@ +/* File: LocalFileStandardDataStreamSource.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on January 14, 2004, 3:26 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream.test; + +import org.das2.client.StandardDataStreamSource; +import org.das2.client.DataSetDescriptorNotAvailableException; +import org.das2.client.StreamDataSetDescriptor; +import org.das2.DasException; +import org.das2.DasIOException; +import org.das2.dataset.DataSetDescriptor; +import org.das2.datum.Datum; + +import java.io.*; +import java.util.Map; +import org.das2.stream.StreamDescriptor; + +/** + * + * @author Edward West + */ +public class LocalFileStandardDataStreamSource implements StandardDataStreamSource { + + private File file; + + /** Creates a new instance of LocalFileStandardDataStreamSource */ + public LocalFileStandardDataStreamSource(File file) { + this.file = file; + } + + public InputStream getInputStream(StreamDataSetDescriptor dsd, Datum start, Datum end) throws DasException { + try { + return new FileInputStream(file); + } + catch (IOException ioe) { + throw new DasIOException(ioe); + } + } + + public InputStream getReducedInputStream(StreamDataSetDescriptor dsd, Datum start, Datum end, Datum timeResolution) throws DasException { + return getInputStream(dsd, start, end); + } + + public void reset() { + } + + public static DataSetDescriptor newDataSetDescriptor(Map map) throws DataSetDescriptorNotAvailableException { + String filename = (String)map.get("file"); + File file = new File(filename); + StreamDescriptor sd = new StreamDescriptor(); + return new StreamDataSetDescriptor(sd, new LocalFileStandardDataStreamSource(file)); + } + +} diff --git a/dasCore/src/main/java/org/das2/stream/test/RipplesStream.java b/dasCore/src/main/java/org/das2/stream/test/RipplesStream.java new file mode 100644 index 000000000..1a24f8a71 --- /dev/null +++ b/dasCore/src/main/java/org/das2/stream/test/RipplesStream.java @@ -0,0 +1,123 @@ +/* File: RipplesStream.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on March 29, 2004, 10:13 AM + * by Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.stream.test; + +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import java.nio.channels.*; +import java.util.*; +import org.das2.stream.DataTransferType; +import org.das2.stream.PacketDescriptor; +import org.das2.stream.StreamDescriptor; +import org.das2.stream.StreamException; +import org.das2.stream.StreamProducer; +import org.das2.stream.StreamXDescriptor; +import org.das2.stream.StreamYScanDescriptor; + +/** + * + * @author eew + */ +public class RipplesStream { + + private boolean compress; + + double x1, y1, p1, x2, y2, p2; + + /** Creates a new instance of RipplesStream */ + public RipplesStream( boolean compress ) { + this( 14, 17, 10, 20, 60, 15, compress ); + } + + /** Creates a new instance of RipplesDataSetDescriptor */ + public RipplesStream( double x1, double y1, double p1, double x2, double y2, double p2, boolean compress ) { + this.x1= x1; + this.y1= y1; + this.p1= p1; + this.x2= x2; + this.y2= y2; + this.p2= p2; + this.compress = compress; + } + + public void produceRipplesStream(WritableByteChannel out) { + try { + StreamProducer producer = new StreamProducer(out); + StreamDescriptor sd = new StreamDescriptor(); + if (compress) { sd.setCompression("deflate"); } + producer.streamDescriptor(sd); + + int nx=100; + int ny=100; + + double[] y= new double[ny]; + for (int j=0; j +Classes for producing das2 Streams, useful for fun and testing only. +LocalFileStandardDataStreamSource presents + a local file to das2 innards as a StandardDataStreamSource for testing. + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/system/ConsoleExceptionHandler.java b/dasCore/src/main/java/org/das2/system/ConsoleExceptionHandler.java new file mode 100644 index 000000000..461f4fb53 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/ConsoleExceptionHandler.java @@ -0,0 +1,30 @@ +/* + * ConsoleExceptionHandler.java + * + * Created on November 16, 2006, 12:34 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.system; + +/** + * ExceptionHandler that prints stack traces out to the stderr. + * @author jbf + */ +public class ConsoleExceptionHandler implements ExceptionHandler { + + /** Creates a new instance of ConsoleExceptionHandler */ + public ConsoleExceptionHandler() { + } + + public void handle(Throwable t) { + t.printStackTrace(); + } + + public void handleUncaught(Throwable t) { + t.printStackTrace(); + } + +} diff --git a/dasCore/src/main/java/org/das2/system/ContextMonitorFactory.java b/dasCore/src/main/java/org/das2/system/ContextMonitorFactory.java new file mode 100644 index 000000000..473fe2cd9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/ContextMonitorFactory.java @@ -0,0 +1,29 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.system; + +import org.das2.graph.DasCanvasComponent; +import org.das2.util.monitor.ProgressMonitor; + +/** + * + * @author eew + */ +public class ContextMonitorFactory extends DefaultMonitorFactory { + + private DasCanvasComponent context; + + public ContextMonitorFactory(DasCanvasComponent context) { + if (context == null) throw new NullPointerException(); + this.context = context; + } + + @Override + public ProgressMonitor getMonitor(String label, String description) { + return super.getMonitor(context, label, description); + } + +} diff --git a/dasCore/src/main/java/org/das2/system/DasLogger.java b/dasCore/src/main/java/org/das2/system/DasLogger.java new file mode 100644 index 000000000..24fddafca --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/DasLogger.java @@ -0,0 +1,174 @@ +/* + * DasLogger.java + * + * Created on June 6, 2005, 12:12 PM + */ + +package org.das2.system; + +import org.das2.DasApplication; +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.logging.*; + +/** Das Logging Facility + * + * This class was created before Java had a standard log facility. Don't use it for new + * code. Use the standard Java Logger instead and get logger names from the LogCategory + * class. + * + * @author Jeremy + */ +@Deprecated +public class DasLogger { + + public static void reload() throws IOException { + try { + java.net.URL logConfigURL; + + File local; + if ( DasApplication.getProperty("user.name","applet").equals("Web") ) { + local= new File("/tmp"); + } else if ( DasApplication.getProperty("user.name","applet").equals("applet") ) { + return; + } else { + local= new File( DasApplication.getProperty("user.home","applet") ); + } + + File userDirectory=new File( local, ".das2" ); + File localLogConfig= new File( userDirectory, "logging.properties" ); + + if ( localLogConfig.exists() ) { + Logger.getLogger("").info("using "+localLogConfig); + logConfigURL= localLogConfig.toURI().toURL(); + } else { + logConfigURL= DasLogger.class.getResource("logging.properties"); + } + if ( logConfigURL==null ) { + System.err.println("unable to locate logging properties file logging.properties, using defaults"); + } else { + //dumpUrl(logConfigURL); + InputStream in= logConfigURL.openStream(); + LogManager.getLogManager().readConfiguration( in ); + in.close(); + //System.err.println( "read log configuration from "+logConfigURL ); + //printStatus(); + } + } catch ( MalformedURLException e ) { + throw new RuntimeException(e); // this shouldn't happen + } + } + + private static void dumpUrl( URL url ) throws IOException { + BufferedReader reader= new BufferedReader( new InputStreamReader( url.openStream() ) ); + String s= reader.readLine(); + while( s!=null ) { + System.out.println(s); + s= reader.readLine(); + } + reader.close(); + + } + + public static void printStatus() { + String[] loggers= new String[] { "", "das2.system", "das2.gui", "das2.graphics", "das2.dataOperations", "das2.dataTransfer", }; + for ( int i=0; i 50 ) + desc= "..."+description.substring( description.length() - 50 ); + return String.valueOf(monitor)+" "+desc; + } + + } + + + public ProgressMonitor getMonitor(DasCanvas canvas, String label, String description ) { + ProgressMonitor result= DasProgressPanel.createComponentPanel( canvas, label ); + putMonitor( result, label, description ); + return result; + } + + public ProgressMonitor getMonitor( DasCanvasComponent context, String label, String description ) { + ProgressMonitor result= DasProgressPanel.createComponentPanel( context, label ); + putMonitor( result, label, description ); + return result; + } + + public ProgressMonitor getMonitor( String label, String description ) { + ProgressMonitor result= DasProgressPanel.createFramed( label ); + putMonitor( result, label, description ); + return result; + } + + private void putMonitor( ProgressMonitor monitor, String label, String description ) { + Long key= new Long( System.currentTimeMillis() ); + monitors.put( key, new MonitorEntry( monitor, description ) ); + } + + public MonitorEntry[] getMonitors() { + Collection set= monitors.values(); + return (MonitorEntry[])set.toArray( new MonitorEntry[ set.size() ] ); + } + + public MonitorEntry getMonitors( int i ) { + return getMonitors()[i]; + } + + public void setClear( boolean clear ) { + if ( clear ) { + monitors.keySet().removeAll(monitors.keySet()); + } + } + + public boolean isClear() { + return false; + } + + + +} diff --git a/dasCore/src/main/java/org/das2/system/ExceptionHandler.java b/dasCore/src/main/java/org/das2/system/ExceptionHandler.java new file mode 100644 index 000000000..d648060f8 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/ExceptionHandler.java @@ -0,0 +1,19 @@ +/* + * ExceptionHandler.java + * + * Created on November 16, 2006, 12:31 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.system; + +/** + * + * @author jbf + */ +public interface ExceptionHandler { + void handle(Throwable t); + void handleUncaught(Throwable t); +} diff --git a/dasCore/src/main/java/org/das2/system/LogCategory.java b/dasCore/src/main/java/org/das2/system/LogCategory.java new file mode 100644 index 000000000..3a40f50d5 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/LogCategory.java @@ -0,0 +1,40 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.system; + +/** This class takes the strings from DasLogger, and uses them as names for the + * standard Java Logging facility + * + * Original Constants by jbf, copied here by cwp + */ +public class LogCategory{ + /** messages having to do with the application-specific Das 2 Application */ + public static final String APPLICATION_LOG = ""; + + /** system messages such as RequestProcessor activity*/ + public static final String SYSTEM_LOG = "das2.system"; + + /** events, gestures, user feedback */ + public static final String GUI_LOG = "das2.gui"; + + /** renders, drawing */ + public static final String GRAPHICS_LOG = "das2.graphics"; + + /** renderer's logger */ + public static final String RENDERER_LOG = "das2.graphics"; + + /** rebinning and dataset operators */ + public static final String DATA_OPERATIONS_LOG = "das2.dataOperations"; + + /** internet transactions, file I/O */ + public static final String DATA_TRANSFER_LOG = "das2.dataTransfer"; + + /** virtual file system activities */ + public static final String FILESYSTEM_LOG = "das2.filesystem"; + + /** das2 application description files */ + public static final String DASML_LOG = "das2.dasml"; +} diff --git a/dasCore/src/main/java/org/das2/system/LoggerId.java b/dasCore/src/main/java/org/das2/system/LoggerId.java new file mode 100644 index 000000000..05924e6c6 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/LoggerId.java @@ -0,0 +1,29 @@ +/* + * LoggerId.java + * + * Created on June 6, 2005, 12:24 PM + */ + +package org.das2.system; + +import java.util.logging.*; + +/** + * + * @author Jeremy + */ +public class LoggerId { + private String name; + private Logger logger; + public LoggerId( String name ) { // public to change to protected after DasApplication.getLogger factored out + this.name= name; + this.logger= Logger.getLogger(name); + this.logger.fine( name +" logging at "+this.logger.getLevel() ); + } + public String toString() { + return this.name; + } + Logger getLogger() { + return this.logger; + } +} diff --git a/dasCore/src/main/java/org/das2/system/MonitorFactory.java b/dasCore/src/main/java/org/das2/system/MonitorFactory.java new file mode 100644 index 000000000..bf4c10b04 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/MonitorFactory.java @@ -0,0 +1,35 @@ +/* + * MonitorFactory.java + * + * Created on November 13, 2006, 11:55 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.system; + +import org.das2.graph.DasCanvas; +import org.das2.graph.DasCanvasComponent; +import org.das2.util.monitor.ProgressMonitor; + +/** + * + * @author jbf + */ +public interface MonitorFactory { + + ProgressMonitor getMonitor(DasCanvas canvas, String string, String string0); + + /** + * returns a monitor in the given context. For example, if the user is waiting for a DasPlot to be drawn, then + * the context is the plot, and therefore a DasProgressPanel will be added on top of the plot. + */ + ProgressMonitor getMonitor(DasCanvasComponent context, String label, String description); + + /** + * returns a monitor without regard to context. + */ + ProgressMonitor getMonitor(String label, String description); + +} diff --git a/dasCore/src/main/java/org/das2/system/MutatorLock.java b/dasCore/src/main/java/org/das2/system/MutatorLock.java new file mode 100644 index 000000000..9b9df489c --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/MutatorLock.java @@ -0,0 +1,17 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.system; + +/** + * see DasAxis.getMutatorLock, DasDevicePosition.getMutatorLock + * + * @author jbf + */ +public interface MutatorLock { + + public void lock(); + + public void unlock(); +} diff --git a/dasCore/src/main/java/org/das2/system/NullMonitorFactory.java b/dasCore/src/main/java/org/das2/system/NullMonitorFactory.java new file mode 100644 index 000000000..c73e75981 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/NullMonitorFactory.java @@ -0,0 +1,42 @@ +/* + * NullMonitorFactory.java + * + * Created on November 13, 2006, 11:59 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.system; + +import org.das2.graph.DasCanvas; +import org.das2.graph.DasCanvasComponent; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.monitor.NullProgressMonitor; + +/** + * MonitorFactory implementation that always returns a null monitor. + * @author jbf + */ +public class NullMonitorFactory implements MonitorFactory { + + public NullMonitorFactory() { + } + + private ProgressMonitor createNullMonitor() { + return new NullProgressMonitor(); + } + + public ProgressMonitor getMonitor(DasCanvasComponent context, String label, String description) { + return createNullMonitor(); + } + + public ProgressMonitor getMonitor(String label, String description) { + return createNullMonitor(); + } + + public ProgressMonitor getMonitor(DasCanvas canvas, String string, String string0) { + return createNullMonitor(); + } + +} diff --git a/dasCore/src/main/java/org/das2/system/NullPreferences.java b/dasCore/src/main/java/org/das2/system/NullPreferences.java new file mode 100755 index 000000000..54eacb1da --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/NullPreferences.java @@ -0,0 +1,62 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.system; + +import java.util.HashMap; +import java.util.Map; +import java.util.prefs.AbstractPreferences; +import java.util.prefs.BackingStoreException; + +/** + * NullPreferences object for allowing applications to work in applet environment. + * Changes that would have been stored are simply ignored. + * @author jbf + */ +public class NullPreferences extends AbstractPreferences { + + Map values; + + public NullPreferences() { + super(null, ""); + values= new HashMap(); + } + + protected void putSpi(String key, String value) { + values.put(key, value); + } + + protected String getSpi(String key) { + return values.get(key); + } + + protected void removeSpi(String key) { + // do nothing + } + + protected void removeNodeSpi() throws BackingStoreException { + // do nothing + } + + protected String[] keysSpi() throws BackingStoreException { + return values.keySet().toArray(new String[values.size()]); + } + + protected String[] childrenNamesSpi() throws BackingStoreException { + return new String[0]; + } + + protected AbstractPreferences childSpi(String name) { + return new NullPreferences(); + } + + protected void syncSpi() throws BackingStoreException { + // do nothing + } + + protected void flushSpi() throws BackingStoreException { + // do nothing + } + +} diff --git a/dasCore/src/main/java/org/das2/system/NullPreferencesFactory.java b/dasCore/src/main/java/org/das2/system/NullPreferencesFactory.java new file mode 100755 index 000000000..08db59ce1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/NullPreferencesFactory.java @@ -0,0 +1,35 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.system; + +import java.util.prefs.Preferences; +import java.util.prefs.PreferencesFactory; + +/** + * Creates NullPreferences for use with applets. The system property + * java.util.prefs.Preferences should be set to + * org.das2.system.NullPreferencesFactory to use this class. + * + * System.setProperty( "java.util.prefs.Preferences", "org.das2.system.NullPreferencesFactory" ) doesn't + * work in applets--security exception. + + * @author jbf + */ +public class NullPreferencesFactory implements PreferencesFactory { + + public NullPreferencesFactory() { + System.err.println("using NullPreferencesFactory"); + } + + public Preferences systemRoot() { + return new NullPreferences(); + } + + public Preferences userRoot() { + return new NullPreferences(); + } + +} diff --git a/dasCore/src/main/java/org/das2/system/RequestProcessor.java b/dasCore/src/main/java/org/das2/system/RequestProcessor.java new file mode 100644 index 000000000..7f9dbdbc1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/RequestProcessor.java @@ -0,0 +1,306 @@ +/* File: TableDataSet.java + * Copyright (C) 2002-2004 The University of Iowa + * + * Created on June 1, 2004, 11:46 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.system; + +import org.das2.util.DasExceptionHandler; +import java.lang.ref.WeakReference; +import java.util.*; +import java.util.logging.Logger; + +/** Utility class for synchronous execution. + * This class maintains a pool of threads that are used to execute arbitrary + * code. This class also serves as a central place to catch and handle + * unchecked exceptions. + * + * The {@link #invokeLater(java.lang.Runnable)} method is similar to the + * SwingUtilities {@link javax.swing.SwingUtilities#invokeLater(java.lang.Runnable)} + * method, except that the request is not executed on the event thread. + * + * The {@link #invokeLater(java.lang.Runnable, java.lang.Object)}, + * the {@link #invokeAfter(java.lang.Runnable, java.lang.Object)}, + * and the {@link #waitFor(java.lang.Object)} methods are designed to work + * together. Both of the first two methods execute code asynchronously with + * respect to the calling thread. Multiple requests made with a call to + * invokeLater that specified the same lock can execute at the same time, + * but not while a request made with the invokeAfter with the same lock + * is processing. Any requests made before an invokeAfter request with the + * same lock will finish before that invokeAfter request begins. An + * invokeAfter request will finish before any requests with the same lock made + * after that invokeAfter request begins. The {@link #waitFor(java.lang.Object)} + * method will cause the calling thread to block until all requests with the + * specified lock finish. + */ +public final class RequestProcessor { + + private static final BlockingRequestQueue queue = new BlockingRequestQueue(); + private static final WeakHashMap runnableQueueMap = new WeakHashMap(); + private static final Runner runner = new Runner(); + + private static int maxThreadCount = 4; + private static int threadCount = 0; + private static final Object THREAD_COUNT_LOCK = new Object(); + + private final static Logger logger= DasLogger.getLogger( DasLogger.SYSTEM_LOG ); + + private static int threadOrdinal = 0; + + private RequestProcessor() {} + + private static void setJob(Runnable job) { + RequestThread thread = (RequestThread)Thread.currentThread(); + thread.setJob(job); + } + + private static class RequestThread extends Thread { + private WeakReference job; + private RequestThread(Runnable run, String name) { + super(run, name); + } + private void setJob(Runnable job) { + this.job = new WeakReference(job); + } + private Runnable getJob() { + return (Runnable)job.get(); + } + } + + private static void newThread() { + String name = "RequestProcessor[" + (threadOrdinal++) + "]"; + RequestThread t = new RequestThread(runner, name); + t.setPriority(Thread.NORM_PRIORITY); + t.start(); + } + + /** Executes run.run() asynchronously on a thread from the thread pool. + * @param run the task to be executed. + */ + public static void invokeLater(Runnable run) { + logger.fine("invokeLater "+run); + + synchronized (THREAD_COUNT_LOCK) { + if (threadCount < maxThreadCount) { + newThread(); + } + } + queue.add(run); + } + + /** Executes run.run() asynchronously on a thread from the thread pool. + * The task will not be executed until after all requests made with + * {@link #invokeAfter(java.lang.Runnable, java.lang.Object)} with the same + * lock have finished. + * @param run the taks to be executed. + * @param lock associates run with other tasks. + */ + public static void invokeLater(Runnable run, Object lock) { + logger.fine("invokeLater "+run+" "+lock); + synchronized (THREAD_COUNT_LOCK) { + if (threadCount < maxThreadCount) { + newThread(); + } + } + synchronized (runnableQueueMap) { + RunnableQueue rq = (RunnableQueue)runnableQueueMap.get(lock); + if (rq == null) { + rq = new RunnableQueue(); + runnableQueueMap.put(lock, rq); + } + rq.add(run, false); + queue.add(rq); + } + } + + /** Executes run.run() asynchronously on a thread from the thread pool. + * The task will not be executed until after all requests made with + * {@link #invokeAfter(java.lang.Runnable, java.lang.Object)} or + * {@link #invokeLater(java.lang.Runnable, java.lang.Object)} with the same + * lock have finished. + * @param run the taks to be executed. + * @param lock associates run with other tasks. + */ + public static void invokeAfter(Runnable run, Object lock) { + logger.fine("invokeAfter "+run+" "+lock); + synchronized (THREAD_COUNT_LOCK) { + if (threadCount < maxThreadCount) { + newThread(); + } + } + synchronized (runnableQueueMap) { + RunnableQueue rq = (RunnableQueue)runnableQueueMap.get(lock); + if (rq == null) { + rq = new RunnableQueue(); + runnableQueueMap.put(lock, rq); + } + rq.add(run, true); + queue.add(rq); + } + } + + /** Blocks until all tasks with the same lock have finished. + * @param lock + * @throws InterruptedException if the current thread is + * interrupted while waiting. + */ + public static void waitFor(Object lock) throws InterruptedException { + WaitTask wt = new WaitTask(); + synchronized (wt) { + while (true) { + invokeLater(wt, lock); + wt.wait(); + return; + } + } + } + + /* + public static int getMaximumThreadCount(int i) { + return maxThreadCount; + } + + public static void setMaximumThreadCount(int i) { + if (i < 5) { + throw new IllegalArgumentException("Must be >= 5: " + i); + } + maxThreadCount = i; + } + */ + + private static class Runner implements Runnable { + public void run() { + synchronized (THREAD_COUNT_LOCK) { + threadCount++; + } + try { + while (true) { + try { + Runnable run = queue.remove(); + logger.fine("running "+run); + if (run != null) { + setJob(run); + run.run(); + logger.fine("completed "+run); + } + synchronized (THREAD_COUNT_LOCK) { + if (threadCount > maxThreadCount) { + break; + } + } + } + catch (ThreadDeath td) { + // See documentation for ThreadDeath. If this error is caught but not thrown, then the thread doesn't die. + throw td; + } + catch (Throwable t) { + logger.fine("uncaught exception "+t); + DasExceptionHandler.handleUncaught(t); + //Clear interrupted status (if set) + Thread.interrupted(); + } + } + } + finally { + synchronized (THREAD_COUNT_LOCK) { + threadCount--; + } + } + } + } + + private static class WaitTask implements Runnable { + public synchronized void run() { + notifyAll(); + } + } + + private static class RunnableQueue implements Runnable { + + private LinkedList list = new LinkedList(); + private int readCount = 0; + private Object writer; + + public void run() { + Runnable run = null; + RequestEntry entry = null; + Logger logger = DasLogger.getLogger(DasLogger.SYSTEM_LOG); + while (run == null) { + synchronized (this) { + //entry = (RequestEntry)list.removeFirst(); + entry = (RequestEntry)list.getFirst(); + if (entry.async && readCount == 0 && writer == null) { + list.removeFirst(); + writer = entry; + run = entry.run; + } + else if (!entry.async && writer == null) { + list.removeFirst(); + readCount++; + run = entry.run; + } + } + } + logger.fine("Starting :" + run); + run.run(); + logger.fine("Finished :" + run); + synchronized (this) { + if (entry.async) { + writer = null; + } + else { + readCount--; + } + notifyAll(); + } + } + + synchronized void add(Runnable run, boolean async) { + RequestEntry entry = new RequestEntry(); + entry.run = run; + entry.async = async; + list.add(entry); + } + } + + private static class RequestEntry { Runnable run; boolean async; } + + private static class BlockingRequestQueue { + private LinkedList list; + + BlockingRequestQueue() { + list = new LinkedList(); + } + + synchronized void add(Runnable r) { + list.add(r); + notify(); + } + + synchronized Runnable remove() { + while (list.isEmpty()) { + try { wait(); } catch (InterruptedException ie) {}; + } + return (Runnable)list.removeFirst(); + } + + } +} diff --git a/dasCore/src/main/java/org/das2/system/ThrowRuntimeExceptionHandler.java b/dasCore/src/main/java/org/das2/system/ThrowRuntimeExceptionHandler.java new file mode 100644 index 000000000..8cd195f81 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/ThrowRuntimeExceptionHandler.java @@ -0,0 +1,33 @@ +/* + * NullExceptionHandler.java + * + * Created on November 16, 2006, 12:59 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.system; + +/** + * ExceptionHandler that throws a RuntimeException caused by the Exception. + * This is useful for server-side applications that need to handle the + * exception externally. + * + * @author jbf + */ +public class ThrowRuntimeExceptionHandler implements ExceptionHandler { + + /** Creates a new instance of NullExceptionHandler */ + public ThrowRuntimeExceptionHandler() { + } + + public void handle(Throwable t) { + throw new RuntimeException(t); + } + + public void handleUncaught(Throwable t) { + throw new RuntimeException(t); + } + +} diff --git a/dasCore/src/main/java/org/das2/system/UserMessageCenter.java b/dasCore/src/main/java/org/das2/system/UserMessageCenter.java new file mode 100644 index 000000000..ad87eb5c9 --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/UserMessageCenter.java @@ -0,0 +1,165 @@ +/* + * UserMessageCenter.java + * + * Created on March 28, 2006, 2:58 PM + * + * + */ + +package org.das2.system; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.JTextPane; +import javax.swing.text.BadLocationException; + +/** + * + * @author Jeremy + */ +public class UserMessageCenter { + + private static UserMessageCenter instance; + + class MessageRecord { + JButton nextButton; + Component component; + } + + List messageRecords; + + public static UserMessageCenter getDefault() { + if ( instance==null ) { + instance= new UserMessageCenter(); + } + return instance; + } + + private UserMessageCenter() { + createComponents(); + messageRecords= new ArrayList(); + } + + HashMap sources= new HashMap(); // + + /** + * Notify the user of the message, coalescing redundant messages from the same + * source, etc. + */ + public void notifyUser( Object source, String message ) { + JTextPane textComponent= new JTextPane(); + if ( message.startsWith("" ) ) textComponent.setContentType("text/html"); + try { + textComponent.getEditorKit().read( new StringReader(message), textComponent.getDocument(), 0 ); + } catch (IOException ex) { + ex.printStackTrace(); + } catch (BadLocationException ex) { + ex.printStackTrace(); + } + notifyUser( source, textComponent); + } + + private void notifyUser( Object source, JTextPane message ) { + HashMap sourceMessages= (HashMap)sources.get( source ); + if ( sourceMessages!=null ) { + if ( sourceMessages.containsKey(message.getText() ) ) { + frame.setVisible(true); + return; + } + } + if ( sourceMessages==null ) { + sourceMessages= new HashMap(); + sources.put( source, sourceMessages ); + } + sourceMessages.put( message.getText(), null ); + + JPanel panel= new JPanel(); + panel.setLayout( new BorderLayout( ) ); + panel.add( message, BorderLayout.CENTER ); + + JButton nextButton= new JButton( getNextAction() ); + panel.add( nextButton, BorderLayout.SOUTH ); + pane.add( panel, tabCount ); + tabCount++; + + if ( tabCount>0 ) { + frame.setVisible(true); + } + + MessageRecord record= new MessageRecord(); + record.component= panel; + record.nextButton= nextButton; + + messageRecords.add( record ); + update(); + } + + private void update() { + for ( int i=0; i>" ) { + public void actionPerformed(ActionEvent e) { + next(); + } + }; + } + + int tabCount; + private void next() { + int currentTab= pane.getSelectedIndex(); + if ( currentTab<(tabCount-1) ) { + currentTab++; + pane.setSelectedIndex(currentTab); + } + } + + private void prev() { + int currentTab= pane.getSelectedIndex(); + if ( currentTab>0 ) { + currentTab--; + pane.setSelectedIndex(currentTab); + } + } + + private JTabbedPane pane; + private JFrame frame; + + private void createComponents() { + frame= new JFrame( "das2 messages" ); + frame.setDefaultCloseOperation( JFrame.HIDE_ON_CLOSE ); + pane= new JTabbedPane(); + pane.setOpaque(true); + pane.setPreferredSize( new Dimension( 400, 300 ) ); + pane.setMinimumSize( pane.getPreferredSize() ); + frame.setContentPane( pane ); + frame.pack(); + } + + /** + * Notify user that an exception occured, presumably because they are capable of handling the exception. + */ + public void notifyUser( Object source, Throwable e) { + notifyUser( source, e.getMessage() ); + } + + + +} diff --git a/dasCore/src/main/java/org/das2/system/package.html b/dasCore/src/main/java/org/das2/system/package.html new file mode 100644 index 000000000..dc191198a --- /dev/null +++ b/dasCore/src/main/java/org/das2/system/package.html @@ -0,0 +1,4 @@ + + Application-level infrastructure, such as data set caching, worker threads, progress monitors, + exception handling, logging. + \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/util/AboutUtil.java b/dasCore/src/main/java/org/das2/util/AboutUtil.java new file mode 100644 index 000000000..097575e02 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/AboutUtil.java @@ -0,0 +1,130 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; + +/** + * method for getting useful build and version information. + * TODO: Splash should call this to get version, not the other way around. + * @author jbf + */ +public class AboutUtil { + + public static String getAboutHtml() { + String dasVersion = Splash.getVersion(); + String javaVersion = System.getProperty("java.version"); // applet okay + String buildTime = "???"; + java.net.URL buildURL = AboutUtil.class.getResource("/buildTime.txt"); + if (buildURL != null) { + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(buildURL.openStream())); + buildTime = reader.readLine(); + reader.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + String arch = System.getProperty("os.arch"); // applet okay + DecimalFormat nf = new DecimalFormat("0.0"); + String mem = nf.format(Runtime.getRuntime().maxMemory() / (1024 * 1024)); + String aboutContent = "" + + //"
    " + + "release version: " + dasVersion + + "
    build time: " + buildTime + + "
    java version: " + javaVersion + + "
    max memory (Mb): " + mem + + "
    arch: " + arch + + "

    "; + + try { + List bis = getBuildInfos(); + for (int i = 0; i < bis.size(); i++) { + aboutContent += "
    " + bis.get(i) + ""; + } + } catch (IOException ex) { + + } + + aboutContent += ""; + + return aboutContent; + } + + /** + * searches class path for META-INF/build.txt, returns nice strings + * @return one line per jar + */ + public static List getBuildInfos() throws IOException { + ClassLoader loader= AboutUtil.class.getClassLoader(); + if ( loader==null ) loader= ClassLoader.getSystemClassLoader(); + Enumeration urls = loader.getResources("META-INF/build.txt"); + + List result = new ArrayList(); + + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + + String jar = url.toString(); + + System.err.println(jar); + + int i = jar.indexOf(".jar"); + int i0 = jar.lastIndexOf("/", i - 1); + + String name; + if (i != -1) { + name = jar.substring(i0 + 1, i + 4); + } else { + name = jar.substring(6); + } + + Properties props = new Properties(); + props.load(url.openStream()); + + String cvsTagName = props.getProperty("build.tag"); + String version; + if (cvsTagName == null || cvsTagName.length() <= 9) { + version = "untagged_version"; + } else { + version = cvsTagName.substring(6, cvsTagName.length() - 2); + } + + result.add(name + ": " + version + "(" + props.getProperty("build.timestamp") + " " + props.getProperty("build.user.name") + ")"); + + } + return result; + + } + + /** + * Identify the release version by looking a non-null build.tag. It's expected + * that the build script will insert build.tag into META-INF/build.txt + * @return build tag, which should not contain spaces, or + * null if no tag is found. + * @throws java.io.IOException + */ + public static String getReleaseTag() throws IOException { + Enumeration urls = AboutUtil.class.getClassLoader().getResources("META-INF/build.txt"); + Properties props = new Properties(); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + props.load(url.openStream()); + String tagName = props.getProperty("build.tag"); + if ( tagName!=null && tagName.trim().length()>0 ) { + return tagName; + } + } + return null; + } +} diff --git a/dasCore/src/main/java/org/das2/util/ArgumentList.java b/dasCore/src/main/java/org/das2/util/ArgumentList.java new file mode 100755 index 000000000..c22ec40d0 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/ArgumentList.java @@ -0,0 +1,520 @@ +package org.das2.util; + +import org.das2.system.DasLogger; +import java.util.*; +import java.util.logging.Logger; +import java.util.prefs.AbstractPreferences; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +/** + * Utility class for processing the String[] arguments passed into the main routine, + * handing positional and switch parameters. Also automatically generates the + * usage documentation. + */ +public class ArgumentList { + + int nposition; + + String programName; + + String[] positionKeys; + + HashMap values; + + HashMap descriptions; + + HashMap names; + + HashMap reverseNames; + + HashMap formUsed; + + HashMap abbrevs; + + HashMap isBoolean; + + ArrayList requireOneOfList; + + /** + * if false, then any unrecognized switch is an error. + */ + boolean allowUndefinedSwitch= false; + + private String UNSPECIFIED = "__unspecified__"; + + private String REFERENCEWITHOUTVALUE = "__referencewithoutvalue__"; + + private String UNDEFINED_SWITCH = "__undefinedSwitch__"; + + // This is supposed to be compatable with the preferences API, so "__false__" must + // become "false", ditto for "__true__" + //private String FALSE = new String("__false__"); + //private String TRUE = new String("__true__"); + private String FALSE = "false"; + private String TRUE = "true"; + + private static final Logger logger= DasLogger.getLogger( DasLogger.GUI_LOG ); + + /** + * creates the processor for the program. programName is provided + * for the usage statement. After creating the object, arguments are + * specified one by one, and then the process method is called. + */ + public ArgumentList(String programName) { + this.programName= programName; + positionKeys= new String[10]; + values= new HashMap(); + descriptions= new HashMap(); + names= new HashMap(); + reverseNames= new HashMap(); + abbrevs= new HashMap(); + formUsed= new HashMap(); + requireOneOfList= new ArrayList(); + } + + /** + * get the value for this parameter + * @return the parameter's value. + * @throws IllegalArgumentException if the parameter name was never described. + */ + public String getValue(String key) { + if ( values.containsKey(key) ) { + return (String)values.get( key ); + } else { + throw new IllegalArgumentException( "No such key: "+key ); + } + } + + /** + * returns the options as a java.util.prefs.Preferences object, for + * batch processes. The idea is that a process which grabs default + * settings from the user Preferences can instead get them from the command + * line, to support batch processes. See the Vg1pws app for an example of + * how this is used. + * + * @return a Preferences object, loaded with the command line values. + */ + public Preferences getPreferences() { + return new AbstractPreferences(null,"") { + @Override + protected void putSpi(String key, String value) { + formUsed.put(key,value); + values.put(key,value); + } + + @Override + protected String getSpi(String key) { + if ( formUsed.containsKey(key) ) { + return (String) values.get(key); + } else { + return null; + } + } + + @Override + protected void removeSpi(String key) { + // do nothing + } + + @Override + protected void removeNodeSpi() throws BackingStoreException { + // do nothing + } + + @Override + protected String[] keysSpi() throws BackingStoreException { + return (String[])values.keySet().toArray(new String[values.size()]); + } + + @Override + protected String[] childrenNamesSpi() throws BackingStoreException { + return new String[0]; + } + + @Override + protected AbstractPreferences childSpi(String name) { + return null; + } + + @Override + protected void syncSpi() throws BackingStoreException { + // do nothing + } + + @Override + protected void flushSpi() throws BackingStoreException { + // do nothing + } + }; + } + + public boolean getBooleanValue(String key) { + return values.get( key ) == TRUE; + } + + /** + * Specify the ith positional argument. + * + * @param position the position number, 0 is the first argument position after the class name. + * @param key the internal reference name to get the value specified. + * @param description a short (40 character) description of the argument. + */ + public void addPositionArgument(int position, String key, String description) { + if ( position>nposition ) { + throw new IllegalArgumentException( "Position arguments must be specified 0,1,2,3: position="+position ); + } + if ( position>positionKeys.length ) { + throw new IllegalArgumentException( "Position too big: position="+position ); + } + nposition= position+1; + positionKeys[position]= key; + descriptions.put(key, description); + values.put( key, UNSPECIFIED ); + } + + /** + * requires the user specify one of these values, otherwise the usage + * statement is printed. + * @param keyNames an array of internal key names that identify parameters. + */ + public void requireOneOf( String[] keyNames ) { + requireOneOfList.add(keyNames); + } + + /** + * Specify the ith positional argument, which may be left unspecified by + * the user. Note that all positional arguments after this one must also be + * optional. + * + * @param position the position number, 0 is the first argument position after the class name. + * @param key the internal reference name to get the value specified. + * @param defaultValue the value that is returned if a value is not provided by the user. + * @param description a short (40 character) description of the argument. + */ + public void addOptionalPositionArgument(int position, String key, String defaultValue, String description) { + addPositionArgument( position, key, description ); + values.put(key,defaultValue); + } + + /** + * specify a named switch argument that must be specified by the user. For example, --level=3 or -l=3 + * @param name the long parameter name, which the user may enter. e.g. "level" + * @param abbrev short (one letter) parameter version. e.g. "l" for -l=3 + * @param key the internal reference name to get the value specified, not necessarily but often the same as name. + * @param description a short (40 character) description of the argument. + */ + public void addSwitchArgument(String name, String abbrev, String key, String description) { + if ( abbrev==null && name==null ) { + throw new IllegalArgumentException( "both abbrev and name are null, one must be specified" ); + } + descriptions.put( key, description ); + if ( abbrev!=null ) { + if ( abbrevs.containsKey(abbrev) ) { + throw new IllegalArgumentException( "abbrev already used: "+abbrev ); + } + abbrevs.put( abbrev, key ); + } + if ( name!=null ) { + names.put( name, key ); + reverseNames.put( key, name ); + } + values.put( key, UNSPECIFIED ); + } + + /** + * specify a named switch argument that may be specified by the user. For example, --level=3 or -l=3 + * @param name the long parameter name, which the user may enter. e.g. "level" + * @param abbrev short (one letter) parameter version. e.g. "l" for -l=3 + * @param defaultValue value to return if the user doesn't specify. + * @param key the internal reference name to get the value specified, not necessarily but often the same as name. + * @param description a short (40 character) description of the argument. + */ + public void addOptionalSwitchArgument(String name, String abbrev, String key, String defaultValue, String description) { + addSwitchArgument( name, abbrev, key, description ); + values.put( key, defaultValue ); + } + + /** + * specify a named switch argument that is named, and we only care whether it was used or not. e.g. --debug + * @param name the long parameter name, which the user may enter. e.g. "level" + * @param abbrev short (one letter) parameter version. e.g. "l" for -l=3 + * @param key the internal reference name to get the value specified, not necessarily but often the same as name. + * @param description a short (40 character) description of the argument. + */ + public void addBooleanSwitchArgument(String name, String abbrev, String key, String description) { + if ( key.equals("commandLinePrefs") ) allowUndefinedSwitch=true; + addOptionalSwitchArgument( name, abbrev, key, FALSE, description ); + } + + /** + * print the usage statement out to stderr. + */ + public void printUsage() { + String s; + s= "Usage: "+this.programName+" "; + for ( int i=0; i "; + } + } + + System.err.println(s); + + Set set= names.keySet(); + Iterator i= set.iterator(); + + while ( i.hasNext() ) { + Object name= i.next(); + Object key= names.get(name); + s= " "; + Object description= descriptions.get(key); + if ( values.get(key)!=this.UNSPECIFIED ) { + if ( values.get(key)==this.FALSE || values.get(key)==this.TRUE ) { + s+= "--"+name+" "+description; + } else { + s+= "--"+name+"="+description+" "; + } + } else { + s+= "--"+name+"="+description+" (required)"; + } + System.err.println(s); + } + + set= abbrevs.keySet(); + i= set.iterator(); + + while ( i.hasNext() ) { + Object abbrev= i.next(); + Object key= abbrevs.get(abbrev); + s= " "; + Object description= descriptions.get(key); + if ( values.get(key)!=this.UNSPECIFIED ) { + if ( values.get(key)==this.FALSE || values.get(key)==this.TRUE ) { + s+= "-"+abbrev+" \t"+description; + } else { + s+= "-"+abbrev+"="+description+" "; + } + } else { + s+= "-"+abbrev+"="+description+" (required)"; + } + System.err.println(s); + } + } + + /** + * check that the user's specified arguments are valid. + */ + private void checkArgs() { + boolean error= false; + java.util.List errorList= new java.util.ArrayList(); // add strings to here + for ( int i=0; !error & i0 ) { + printUsage(); + System.err.println( "" ); + for ( int ii=0; iiEncodes and decodes to and from Base64 notation.

    + *

    Homepage: http://iharder.net/base64.

    + * + *

    + * Change Log: + *

    + *
      + *
    • v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug + * when using very small files (~< 40 bytes).
    • + *
    • v2.2 - Added some helper methods for encoding/decoding directly from + * one file to the next. Also added a main() method to support command line + * encoding/decoding from one file to the next. Also added these Base64 dialects: + *
        + *
      1. The default is RFC3548 format.
      2. + *
      3. Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates + * URL and file name friendly format as described in Section 4 of RFC3548. + * http://www.faqs.org/rfcs/rfc3548.html
      4. + *
      5. Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates + * URL and file name friendly format that preserves lexical ordering as described + * in http://www.faqs.org/qa/rfcc-1940.html
      6. + *
      + * Special thanks to Jim Kellerman at http://www.powerset.com/ + * for contributing the new Base64 dialects. + *
    • + * + *
    • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added + * some convenience methods for reading and writing to and from files.
    • + *
    • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems + * with other encodings (like EBCDIC).
    • + *
    • v2.0.1 - Fixed an error when decoding a single byte, that is, when the + * encoded data was a single byte.
    • + *
    • v2.0 - I got rid of methods that used booleans to set options. + * Now everything is more consolidated and cleaner. The code now detects + * when data that's being decoded is gzip-compressed and will decompress it + * automatically. Generally things are cleaner. You'll probably have to + * change some method calls that you were making to support the new + * options format (ints that you "OR" together).
    • + *
    • v1.5.1 - Fixed bug when decompressing and decoding to a + * byte[] using decode( String s, boolean gzipCompressed ). + * Added the ability to "suspend" encoding in the Output Stream so + * you can turn on and off the encoding if you need to embed base64 + * data in an otherwise "normal" stream (like an XML file).
    • + *
    • v1.5 - Output stream pases on flush() command but doesn't do anything itself. + * This helps when using GZIP streams. + * Added the ability to GZip-compress objects before encoding them.
    • + *
    • v1.4 - Added helper methods to read/write files.
    • + *
    • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
    • + *
    • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream + * where last buffer being read, if not completely full, was not returned.
    • + *
    • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
    • + *
    • v1.3.3 - Fixed I/O streams which were totally messed up.
    • + *
    + * + *

    + * I am placing this code in the Public Domain. Do with it as you will. + * This software comes with no guarantees or warranties but with + * plenty of well-wishing instead! + * Please visit http://iharder.net/base64 + * periodically to check for updates or to contribute improvements. + *

    + * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.2.1 + */ +package org.das2.util; + +public class Base64 +{ + +/* ******** P U B L I C F I E L D S ******** */ + + + /** No options specified. Value is zero. */ + public final static int NO_OPTIONS = 0; + + /** Specify encoding. */ + public final static int ENCODE = 1; + + + /** Specify decoding. */ + public final static int DECODE = 0; + + + /** Specify that data should be gzip-compressed. */ + public final static int GZIP = 2; + + + /** Don't break lines when encoding (violates strict Base64 specification) */ + public final static int DONT_BREAK_LINES = 8; + + /** + * Encode using Base64-like encoding that is URL- and Filename-safe as described + * in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * It is important to note that data encoded this way is not officially valid Base64, + * or at the very least should not be called Base64 without also specifying that is + * was encoded using the URL- and Filename-safe dialect. + */ + public final static int URL_SAFE = 16; + + + /** + * Encode using the special "ordered" dialect of Base64 described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + public final static int ORDERED = 32; + + +/* ******** P R I V A T E F I E L D S ******** */ + + + /** Maximum line length (76) of Base64 output. */ + private final static int MAX_LINE_LENGTH = 76; + + + /** The equals sign (=) as a byte. */ + private final static byte EQUALS_SIGN = (byte)'='; + + + /** The new line character (\n) as a byte. */ + private final static byte NEW_LINE = (byte)'\n'; + + + /** Preferred encoding. */ + private final static String PREFERRED_ENCODING = "UTF-8"; + + + // I think I end up not using the BAD_ENCODING indicator. + //private final static byte BAD_ENCODING = -9; // Indicates error in encoding + private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding + private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding + + +/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ + + /** The 64 valid Base64 values. */ + //private final static byte[] ALPHABET; + /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ + private final static byte[] _STANDARD_ALPHABET = + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' + }; + + + /** + * Translates a Base64 value to either its 6-bit reconstruction value + * or a negative number indicating some other meaning. + **/ + private final static byte[] _STANDARD_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9,-9,-9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ + + /** + * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." + */ + private final static byte[] _URL_SAFE_ALPHABET = + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' + }; + + /** + * Used in decoding URL- and Filename-safe dialects of Base64. + */ + private final static byte[] _URL_SAFE_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 62, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 63, // Underscore at decimal 95 + -9, // Decimal 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + + +/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ + + /** + * I don't get the point of this technique, but it is described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + private final static byte[] _ORDERED_ALPHABET = + { + (byte)'-', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', + (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'_', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' + }; + + /** + * Used in decoding the "ordered" dialect of Base64. + */ + private final static byte[] _ORDERED_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 0, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' + 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 37, // Underscore at decimal 95 + -9, // Decimal 96 + 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' + 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ + + + /** + * Returns one of the _SOMETHING_ALPHABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URLSAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getAlphabet( int options ) + { + if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET; + else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET; + else return _STANDARD_ALPHABET; + + } // end getAlphabet + + + /** + * Returns one of the _SOMETHING_DECODABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URL_SAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getDecodabet( int options ) + { + if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET; + else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET; + else return _STANDARD_DECODABET; + + } // end getAlphabet + + + + /** Defeats instantiation. */ + private Base64(){} + + + /** + * Encodes or decodes two files from the command line; + * feel free to delete this method (in fact you probably should) + * if you're embedding this code into a larger program. + */ + public final static void main( String[] args ) + { + if( args.length < 3 ){ + usage("Not enough arguments."); + } // end if: args.length < 3 + else { + String flag = args[0]; + String infile = args[1]; + String outfile = args[2]; + if( flag.equals( "-e" ) ){ + Base64.encodeFileToFile( infile, outfile ); + } // end if: encode + else if( flag.equals( "-d" ) ) { + Base64.decodeFileToFile( infile, outfile ); + } // end else if: decode + else { + usage( "Unknown flag: " + flag ); + } // end else + } // end else + } // end main + + /** + * Prints command line usage. + * + * @param msg A message to include with usage info. + */ + private final static void usage( String msg ) + { + System.err.println( msg ); + System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" ); + } // end usage + + +/* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * Code can reuse a byte array by passing a four-byte array as b4. + * + * @param b4 A reusable byte array to reduce array instantiation + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.5.1 + */ + private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) + { + encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); + return b4; + } // end encode3to4 + + + /** + *

    Encodes up to three bytes of the array source + * and writes the resulting four Base64 bytes to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 3 for + * the source array or destOffset + 4 for + * the destination array. + * The actual number of significant bytes in your array is + * given by numSigBytes.

    + *

    This is the lowest level of the encoding methods with + * all possible parameters.

    + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param numSigBytes the number of significant bytes in your array + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the destination array + * @since 1.3 + */ + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset, int options ) + { + byte[] ALPHABET = getAlphabet( options ); + + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) + | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) + | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); + + switch( numSigBytes ) + { + case 3: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; + return destination; + + case 2: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + case 1: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = EQUALS_SIGN; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + default: + return destination; + } // end switch + } // end encode3to4 + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + * The object is not GZip-compressed before being encoded. + * + * @param serializableObject The object to encode + * @return The Base64-encoded object + * @since 1.4 + */ + public static String encodeObject( java.io.Serializable serializableObject ) + { + return encodeObject( serializableObject, NO_OPTIONS ); + } // end encodeObject + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + *

    + * Valid options:

    +     *   GZIP: gzip-compresses object before encoding it.
    +     *   DONT_BREAK_LINES: don't break lines at 76 characters
    +     *     Note: Technically, this makes your encoding non-compliant.
    +     * 
    + *

    + * Example: encodeObject( myObj, Base64.GZIP ) or + *

    + * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * @param serializableObject The object to encode + * @param options Specified options + * @return The Base64-encoded object + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeObject( java.io.Serializable serializableObject, int options ) + { + // Streams + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.io.ObjectOutputStream oos = null; + java.util.zip.GZIPOutputStream gzos = null; + + // Isolate options + int gzip = (options & GZIP); + int dontBreakLines = (options & DONT_BREAK_LINES); + + try + { + // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | options ); + + // GZip? + if( gzip == GZIP ) + { + gzos = new java.util.zip.GZIPOutputStream( b64os ); + oos = new java.io.ObjectOutputStream( gzos ); + } // end if: gzip + else + oos = new java.io.ObjectOutputStream( b64os ); + + oos.writeObject( serializableObject ); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + return null; + } // end catch + finally + { + try{ oos.close(); } catch( Exception e ){} + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + + } // end encode + + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @since 1.4 + */ + public static String encodeBytes( byte[] source ) + { + return encodeBytes( source, 0, source.length, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

    + * Valid options:

    +     *   GZIP: gzip-compresses object before encoding it.
    +     *   DONT_BREAK_LINES: don't break lines at 76 characters
    +     *     Note: Technically, this makes your encoding non-compliant.
    +     * 
    + *

    + * Example: encodeBytes( myData, Base64.GZIP ) or + *

    + * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param options Specified options + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int options ) + { + return encodeBytes( source, 0, source.length, options ); + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @since 1.4 + */ + public static String encodeBytes( byte[] source, int off, int len ) + { + return encodeBytes( source, off, len, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

    + * Valid options:

    +     *   GZIP: gzip-compresses object before encoding it.
    +     *   DONT_BREAK_LINES: don't break lines at 76 characters
    +     *     Note: Technically, this makes your encoding non-compliant.
    +     * 
    + *

    + * Example: encodeBytes( myData, Base64.GZIP ) or + *

    + * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options, alphabet type is pulled from this + * (standard, url-safe, ordered) + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int off, int len, int options ) + { + // Isolate options + int dontBreakLines = ( options & DONT_BREAK_LINES ); + int gzip = ( options & GZIP ); + + // Compress? + if( gzip == GZIP ) + { + java.io.ByteArrayOutputStream baos = null; + java.util.zip.GZIPOutputStream gzos = null; + Base64.OutputStream b64os = null; + + + try + { + // GZip -> Base64 -> ByteArray + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | options ); + gzos = new java.util.zip.GZIPOutputStream( b64os ); + + gzos.write( source, off, len ); + gzos.close(); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + return null; + } // end catch + finally + { + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + } // end if: compress + + // Else, don't compress. Better not to use streams at all then. + else + { + // Convert option to boolean in way that code likes it. + boolean breakLines = dontBreakLines == 0; + + int len43 = len * 4 / 3; + byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + for( ; d < len2; d+=3, e+=4 ) + { + encode3to4( source, d+off, 3, outBuff, e, options ); + + lineLength += 4; + if( breakLines && lineLength == MAX_LINE_LENGTH ) + { + outBuff[e+4] = NEW_LINE; + e++; + lineLength = 0; + } // end if: end of line + } // en dfor: each piece of array + + if( d < len ) + { + encode3to4( source, d+off, len - d, outBuff, e, options ); + e += 4; + } // end if: some padding needed + + + // Return value according to relevant encoding. + try + { + return new String( outBuff, 0, e, PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( outBuff, 0, e ); + } // end catch + + } // end else: don't compress + + } // end encodeBytes + + + + + +/* ******** D E C O D I N G M E T H O D S ******** */ + + + /** + * Decodes four bytes from array source + * and writes the resulting bytes (up to three of them) + * to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 4 for + * the source array or destOffset + 3 for + * the destination array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + *

    This is the lowest level of the decoding methods with + * all possible parameters.

    + * + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @param options alphabet type is pulled from this (standard, url-safe, ordered) + * @return the number of decoded bytes converted + * @since 1.3 + */ + private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options ) + { + byte[] DECODABET = getDecodabet( options ); + + // Example: Dk== + if( source[ srcOffset + 2] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + return 1; + } + + // Example: DkL= + else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); + return 2; + } + + // Example: DkLE + else + { + try{ + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) + // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) + | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); + + + destination[ destOffset ] = (byte)( outBuff >> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); + destination[ destOffset + 2 ] = (byte)( outBuff ); + + return 3; + }catch( Exception e){ + System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); + System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); + System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); + System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); + return -1; + } // end catch + } + } // end decodeToBytes + + + + + /** + * Very low-level access to decoding ASCII characters in + * the form of a byte array. Does not support automatically + * gunzipping or any other "fancy" features. + * + * @param source The Base64 encoded data + * @param off The offset of where to begin decoding + * @param len The length of characters to decode + * @return decoded data + * @since 1.3 + */ + public static byte[] decode( byte[] source, int off, int len, int options ) + { + byte[] DECODABET = getDecodabet( options ); + + int len34 = len * 3 / 4; + byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output + int outBuffPosn = 0; + + byte[] b4 = new byte[4]; + int b4Posn = 0; + int i = 0; + byte sbiCrop = 0; + byte sbiDecode = 0; + for( i = off; i < off+len; i++ ) + { + sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits + sbiDecode = DECODABET[ sbiCrop ]; + + if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better + { + if( sbiDecode >= EQUALS_SIGN_ENC ) + { + b4[ b4Posn++ ] = sbiCrop; + if( b4Posn > 3 ) + { + outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if( sbiCrop == EQUALS_SIGN ) + break; + } // end if: quartet built + + } // end if: equals sign or better + + } // end if: white space, equals sign or better + else + { + System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); + return null; + } // end else: + } // each input character + + byte[] out = new byte[ outBuffPosn ]; + System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); + return out; + } // end decode + + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @return the decoded data + * @since 1.4 + */ + public static byte[] decode( String s ) + { + return decode( s, NO_OPTIONS ); + } + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @param options encode options such as URL_SAFE + * @return the decoded data + * @since 1.4 + */ + public static byte[] decode( String s, int options ) + { + byte[] bytes; + try + { + bytes = s.getBytes( PREFERRED_ENCODING ); + } // end try + catch( java.io.UnsupportedEncodingException uee ) + { + bytes = s.getBytes(); + } // end catch + // + + // Decode + bytes = decode( bytes, 0, bytes.length, options ); + + + // Check to see if it's gzip-compressed + // GZIP Magic Two-Byte Number: 0x8b1f (35615) + if( bytes != null && bytes.length >= 4 ) + { + + int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) + { + java.io.ByteArrayInputStream bais = null; + java.util.zip.GZIPInputStream gzis = null; + java.io.ByteArrayOutputStream baos = null; + byte[] buffer = new byte[2048]; + int length = 0; + + try + { + baos = new java.io.ByteArrayOutputStream(); + bais = new java.io.ByteArrayInputStream( bytes ); + gzis = new java.util.zip.GZIPInputStream( bais ); + + while( ( length = gzis.read( buffer ) ) >= 0 ) + { + baos.write(buffer,0,length); + } // end while: reading input + + // No error? Get new bytes. + bytes = baos.toByteArray(); + + } // end try + catch( java.io.IOException e ) + { + // Just return originally-decoded bytes + } // end catch + finally + { + try{ baos.close(); } catch( Exception e ){} + try{ gzis.close(); } catch( Exception e ){} + try{ bais.close(); } catch( Exception e ){} + } // end finally + + } // end if: gzipped + } // end if: bytes.length >= 2 + + return bytes; + } // end decode + + + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * + * @param encodedObject The Base64 data to decode + * @return The decoded and deserialized object + * @since 1.5 + */ + public static Object decodeToObject( String encodedObject ) + { + // Decode and gunzip if necessary + byte[] objBytes = decode( encodedObject ); + + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + Object obj = null; + + try + { + bais = new java.io.ByteArrayInputStream( objBytes ); + ois = new java.io.ObjectInputStream( bais ); + + obj = ois.readObject(); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + obj = null; + } // end catch + catch( java.lang.ClassNotFoundException e ) + { + e.printStackTrace(); + obj = null; + } // end catch + finally + { + try{ bais.close(); } catch( Exception e ){} + try{ ois.close(); } catch( Exception e ){} + } // end finally + + return obj; + } // end decodeObject + + + + /** + * Convenience method for encoding data to a file. + * + * @param dataToEncode byte array of data to encode in base64 form + * @param filename Filename for saving encoded data + * @return true if successful, false otherwise + * + * @since 2.1 + */ + public static boolean encodeToFile( byte[] dataToEncode, String filename ) + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.ENCODE ); + bos.write( dataToEncode ); + success = true; + } // end try + catch( java.io.IOException e ) + { + + success = false; + } // end catch: IOException + finally + { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + return success; + } // end encodeToFile + + + /** + * Convenience method for decoding data to a file. + * + * @param dataToDecode Base64-encoded data as a string + * @param filename Filename for saving decoded data + * @return true if successful, false otherwise + * + * @since 2.1 + */ + public static boolean decodeToFile( String dataToDecode, String filename ) + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.DECODE ); + bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); + success = true; + } // end try + catch( java.io.IOException e ) + { + success = false; + } // end catch: IOException + finally + { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + return success; + } // end decodeToFile + + + + + /** + * Convenience method for reading a base64-encoded + * file and decoding it. + * + * @param filename Filename for reading encoded data + * @return decoded byte array or null if unsuccessful + * + * @since 2.1 + */ + public static byte[] decodeFromFile( String filename ) + { + byte[] decodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = null; + int length = 0; + int numBytes = 0; + + // Check for size of file + if( file.length() > Integer.MAX_VALUE ) + { + System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." ); + return null; + } // end if: file too big for int index + buffer = new byte[ (int)file.length() ]; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.DECODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + decodedData = new byte[ length ]; + System.arraycopy( buffer, 0, decodedData, 0, length ); + + } // end try + catch( java.io.IOException e ) + { + System.err.println( "Error decoding from file " + filename ); + } // end catch: IOException + finally + { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return decodedData; + } // end decodeFromFile + + + + /** + * Convenience method for reading a binary file + * and base64-encoding it. + * + * @param filename Filename for reading binary data + * @return base64-encoded string or null if unsuccessful + * + * @since 2.1 + */ + public static String encodeFromFile( String filename ) + { + String encodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1) + int length = 0; + int numBytes = 0; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.ENCODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); + + } // end try + catch( java.io.IOException e ) + { + System.err.println( "Error encoding from file " + filename ); + } // end catch: IOException + finally + { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return encodedData; + } // end encodeFromFile + + /** + * Reads infile and encodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @since 2.2 + */ + public static void encodeFileToFile( String infile, String outfile ) + { + String encoded = Base64.encodeFromFile( infile ); + java.io.OutputStream out = null; + try{ + out = new java.io.BufferedOutputStream( + new java.io.FileOutputStream( outfile ) ); + out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output. + } // end try + catch( java.io.IOException ex ) { + ex.printStackTrace(); + } // end catch + finally { + try { out.close(); } + catch( Exception ex ){} + } // end finally + } // end encodeFileToFile + + + /** + * Reads infile and decodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @since 2.2 + */ + public static void decodeFileToFile( String infile, String outfile ) + { + byte[] decoded = Base64.decodeFromFile( infile ); + java.io.OutputStream out = null; + try{ + out = new java.io.BufferedOutputStream( + new java.io.FileOutputStream( outfile ) ); + out.write( decoded ); + } // end try + catch( java.io.IOException ex ) { + ex.printStackTrace(); + } // end catch + finally { + try { out.close(); } + catch( Exception ex ){} + } // end finally + } // end decodeFileToFile + + + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.InputStream} will read data from another + * java.io.InputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class InputStream extends java.io.FilterInputStream + { + private boolean encode; // Encoding or decoding + private int position; // Current position in the buffer + private byte[] buffer; // Small buffer holding converted data + private int bufferLength; // Length of buffer (3 or 4) + private int numSigBytes; // Number of meaningful bytes in the buffer + private int lineLength; + private boolean breakLines; // Break lines at less than 80 characters + private int options; // Record options used to create the stream. + private byte[] alphabet; // Local copies to avoid extra method calls + private byte[] decodabet; // Local copies to avoid extra method calls + + + /** + * Constructs a {@link Base64.InputStream} in DECODE mode. + * + * @param in the java.io.InputStream from which to read data. + * @since 1.3 + */ + public InputStream( java.io.InputStream in ) + { + this( in, DECODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.InputStream} in + * either ENCODE or DECODE mode. + *

    + * Valid options:

    +         *   ENCODE or DECODE: Encode or Decode as data is read.
    +         *   DONT_BREAK_LINES: don't break lines at 76 characters
    +         *     (only meaningful when encoding)
    +         *     Note: Technically, this makes your encoding non-compliant.
    +         * 
    + *

    + * Example: new Base64.InputStream( in, Base64.DECODE ) + * + * + * @param in the java.io.InputStream from which to read data. + * @param options Specified options + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public InputStream( java.io.InputStream in, int options ) + { + super( in ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[ bufferLength ]; + this.position = -1; + this.lineLength = 0; + this.options = options; // Record for later, mostly to determine which alphabet to use + this.alphabet = getAlphabet(options); + this.decodabet = getDecodabet(options); + } // end constructor + + /** + * Reads enough of the input stream to convert + * to/from Base64 and returns the next byte. + * + * @return next byte + * @since 1.3 + */ + public int read() throws java.io.IOException + { + // Do we need to get data? + if( position < 0 ) + { + if( encode ) + { + byte[] b3 = new byte[3]; + int numBinaryBytes = 0; + for( int i = 0; i < 3; i++ ) + { + try + { + int b = in.read(); + + // If end of stream, b is -1. + if( b >= 0 ) + { + b3[i] = (byte)b; + numBinaryBytes++; + } // end if: not end of stream + + } // end try: read + catch( java.io.IOException e ) + { + // Only a problem if we got no data at all. + if( i == 0 ) + throw e; + + } // end catch + } // end for: each needed input byte + + if( numBinaryBytes > 0 ) + { + encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); + position = 0; + numSigBytes = 4; + } // end if: got data + else + { + return -1; + } // end else + } // end if: encoding + + // Else decoding + else + { + byte[] b4 = new byte[4]; + int i = 0; + for( i = 0; i < 4; i++ ) + { + // Read four "meaningful" bytes: + int b = 0; + do{ b = in.read(); } + while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); + + if( b < 0 ) + break; // Reads a -1 if end of stream + + b4[i] = (byte)b; + } // end for: each needed input byte + + if( i == 4 ) + { + numSigBytes = decode4to3( b4, 0, buffer, 0, options ); + position = 0; + } // end if: got four characters + else if( i == 0 ){ + return -1; + } // end else if: also padded correctly + else + { + // Must have broken out from above. + throw new java.io.IOException( "Improperly padded Base64 input." ); + } // end + + } // end else: decode + } // end else: get data + + // Got data? + if( position >= 0 ) + { + // End of relevant data? + if( /*!encode &&*/ position >= numSigBytes ) + return -1; + + if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) + { + lineLength = 0; + return '\n'; + } // end if + else + { + lineLength++; // This isn't important when decoding + // but throwing an extra "if" seems + // just as wasteful. + + int b = buffer[ position++ ]; + + if( position >= bufferLength ) + position = -1; + + return b & 0xFF; // This is how you "cast" a byte that's + // intended to be unsigned. + } // end else + } // end if: position >= 0 + + // Else error + else + { + // When JDK1.4 is more accepted, use an assertion here. + throw new java.io.IOException( "Error in Base64 code reading stream." ); + } // end else + } // end read + + + /** + * Calls {@link #read()} repeatedly until the end of stream + * is reached or len bytes are read. + * Returns number of bytes read into array or -1 if + * end of stream is encountered. + * + * @param dest array to hold values + * @param off offset for array + * @param len max number of bytes to read into array + * @return bytes read into array or -1 if end of stream is encountered. + * @since 1.3 + */ + public int read( byte[] dest, int off, int len ) throws java.io.IOException + { + int i; + int b; + for( i = 0; i < len; i++ ) + { + b = read(); + + //if( b < 0 && i == 0 ) + // return -1; + + if( b >= 0 ) + dest[off + i] = (byte)b; + else if( i == 0 ) + return -1; + else + break; // Out of 'for' loop + } // end for: each byte read + return i; + } // end read + + } // end inner class InputStream + + + + + + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.OutputStream} will write data to another + * java.io.OutputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class OutputStream extends java.io.FilterOutputStream + { + private boolean encode; + private int position; + private byte[] buffer; + private int bufferLength; + private int lineLength; + private boolean breakLines; + private byte[] b4; // Scratch used in a few places + private boolean suspendEncoding; + private int options; // Record for later + private byte[] alphabet; // Local copies to avoid extra method calls + private byte[] decodabet; // Local copies to avoid extra method calls + + /** + * Constructs a {@link Base64.OutputStream} in ENCODE mode. + * + * @param out the java.io.OutputStream to which data will be written. + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out ) + { + this( out, ENCODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.OutputStream} in + * either ENCODE or DECODE mode. + *

    + * Valid options:

    +         *   ENCODE or DECODE: Encode or Decode as data is read.
    +         *   DONT_BREAK_LINES: don't break lines at 76 characters
    +         *     (only meaningful when encoding)
    +         *     Note: Technically, this makes your encoding non-compliant.
    +         * 
    + *

    + * Example: new Base64.OutputStream( out, Base64.ENCODE ) + * + * @param out the java.io.OutputStream to which data will be written. + * @param options Specified options. + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out, int options ) + { + super( out ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[ bufferLength ]; + this.position = 0; + this.lineLength = 0; + this.suspendEncoding = false; + this.b4 = new byte[4]; + this.options = options; + this.alphabet = getAlphabet(options); + this.decodabet = getDecodabet(options); + } // end constructor + + + /** + * Writes the byte to the output stream after + * converting to/from Base64 notation. + * When encoding, bytes are buffered three + * at a time before the output stream actually + * gets a write() call. + * When decoding, bytes are buffered four + * at a time. + * + * @param theByte the byte to write + * @since 1.3 + */ + public void write(int theByte) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theByte ); + return; + } // end if: supsended + + // Encode? + if( encode ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to encode. + { + out.write( encode3to4( b4, buffer, bufferLength, options ) ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) + { + out.write( NEW_LINE ); + lineLength = 0; + } // end if: end of line + + position = 0; + } // end if: enough to output + } // end if: encoding + + // Else, Decoding + else + { + // Meaningful Base64 character? + if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to output. + { + int len = Base64.decode4to3( buffer, 0, b4, 0, options ); + out.write( b4, 0, len ); + //out.write( Base64.decode4to3( buffer ) ); + position = 0; + } // end if: enough to output + } // end if: meaningful base64 character + else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) + { + throw new java.io.IOException( "Invalid character in Base64 data." ); + } // end else: not white space either + } // end else: decoding + } // end write + + + + /** + * Calls {@link #write(int)} repeatedly until len + * bytes are written. + * + * @param theBytes array from which to read bytes + * @param off offset for array + * @param len max number of bytes to read into array + * @since 1.3 + */ + public void write( byte[] theBytes, int off, int len ) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theBytes, off, len ); + return; + } // end if: supsended + + for( int i = 0; i < len; i++ ) + { + write( theBytes[ off + i ] ); + } // end for: each byte written + + } // end write + + + + /** + * Method added by PHIL. [Thanks, PHIL. -Rob] + * This pads the buffer without closing the stream. + */ + public void flushBase64() throws java.io.IOException + { + if( position > 0 ) + { + if( encode ) + { + out.write( encode3to4( b4, buffer, position, options ) ); + position = 0; + } // end if: encoding + else + { + throw new java.io.IOException( "Base64 input not properly padded." ); + } // end else: decoding + } // end if: buffer partially full + + } // end flush + + + /** + * Flushes and closes (I think, in the superclass) the stream. + * + * @since 1.3 + */ + public void close() throws java.io.IOException + { + // 1. Ensure that pending characters are written + flushBase64(); + + // 2. Actually close the stream + // Base class both flushes and closes. + super.close(); + + buffer = null; + out = null; + } // end close + + + + /** + * Suspends encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void suspendEncoding() throws java.io.IOException + { + flushBase64(); + this.suspendEncoding = true; + } // end suspendEncoding + + + /** + * Resumes encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void resumeEncoding() + { + this.suspendEncoding = false; + } // end resumeEncoding + + + + } // end inner class OutputStream + + +} // end class Base64 diff --git a/dasCoreUtil/src/org/das2/util/ByteBufferInputStream.java b/dasCore/src/main/java/org/das2/util/ByteBufferInputStream.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/ByteBufferInputStream.java rename to dasCore/src/main/java/org/das2/util/ByteBufferInputStream.java diff --git a/dasCore/src/main/java/org/das2/util/ClassMap.java b/dasCore/src/main/java/org/das2/util/ClassMap.java new file mode 100644 index 000000000..b1ee4c6ab --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/ClassMap.java @@ -0,0 +1,95 @@ +/* + * ClassMap.java + * + * Created on April 28, 2006, 2:11 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.util; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Map that takes a Class for keys, and the get method finds the closest matching class. + * @author Jeremy + */ +public class ClassMap implements Map { + HashMap map; + + /** Creates a new instance of ClassMap */ + public ClassMap() { + map= new HashMap(); + } + + public int size() { + return map.size(); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public boolean containsKey(Object key) { + Object close= closestKey((Class)key); + return close!=null; + } + + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + private Object closestKey( Object key ) { + Object result= key; + if ( map.containsKey(result) ) return result; + Class clas= (Class)key; + while ( clas!=null ) { + if ( map.containsKey(clas) ) return clas; + clas= clas.getSuperclass(); + } + return clas; + } + + public T get(Object key) { + Object close= closestKey(key); + return ( close==null ) ? null : map.get(close); + } + + public T put(Class key, T value) { + if ( key.isInterface() ) { + // System.err.println("interfaces not supported"); + } + T result= map.get(key); + map.put( key, value ); + return result; + } + + public T remove(Object key) { + return map.remove(key); + } + + public void putAll(Map t) { + if ( t instanceof ClassMap ) map.putAll(t); + } + + public void clear() { + map.clear(); + } + + public Set keySet() { + return map.keySet(); + } + + public Collection values() { + return map.values(); + } + + public Set entrySet() { + return map.entrySet(); + } + +} diff --git a/dasCore/src/main/java/org/das2/util/CombinedTreeModel.java b/dasCore/src/main/java/org/das2/util/CombinedTreeModel.java new file mode 100644 index 000000000..d05982368 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/CombinedTreeModel.java @@ -0,0 +1,227 @@ +/* + * DataSetTreeModel.java + * + * Created on July 25, 2006, 2:17 PM + * + * + */ +package org.das2.util; + +import java.awt.EventQueue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.WeakHashMap; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +/** + * Forms a tree by connecting sub-trees. + * @author Jeremy + */ +public class CombinedTreeModel implements TreeModel { + + List treeModels; + List treeModelRoots; + List treeModelSortIndexes; + Object root = null; + WeakHashMap sourceMap; + List listeners; // generally, the model should only be modified on the event thread. + // this is useful for debugging. + final private boolean checkEvent = false; + + public CombinedTreeModel(Object root) { + this.root = root; + treeModels = new ArrayList(); + treeModelRoots = new ArrayList(); + treeModelSortIndexes = new ArrayList(); + sourceMap = new WeakHashMap(); + listeners = new ArrayList(); + } + + class SubTreeModelListener implements TreeModelListener { + + private TreeModelEvent prependTreeModelEvent(TreeModelEvent e) { + Object[] path = e.getPath(); + Object[] path2 = new Object[path.length + 1]; + path2[0] = root; + System.arraycopy(path, 0, path2, 1, path.length); + TreeModelEvent result = new TreeModelEvent(this, path2, e.getChildIndices(), e.getChildren()); + return result; + } + + public void treeNodesChanged(TreeModelEvent e) { + fireTreeNodesChanged(prependTreeModelEvent(e)); + } + + public void treeNodesInserted(TreeModelEvent e) { + fireTreeNodesInserted(prependTreeModelEvent(e)); + } + + public void treeNodesRemoved(TreeModelEvent e) { + fireTreeNodesRemoved(prependTreeModelEvent(e)); + } + + public void treeStructureChanged(TreeModelEvent e) { + fireTreeStructureChanged(prependTreeModelEvent(e)); + } + } + SubTreeModelListener myListener = new SubTreeModelListener(); + + public Object getRoot() { + return root; + } + + public void mountTree(TreeModel treeModel) { + mountTree(treeModel, -1); + } + + /** + * mounts the tree. Note each treeModel must have a unique root. + */ + @SuppressWarnings("unchecked") + public void mountTree(TreeModel treeModel, int sortIndex) { + if (checkEvent && !EventQueue.isDispatchThread()) + throw new IllegalArgumentException("must be called from AWT thread"); // useful for debugging concurrent exception + + if (treeModelRoots.contains(treeModel.getRoot())) { + int index = treeModelRoots.indexOf(treeModel.getRoot()); + treeModels.set(index, treeModel); + treeModelRoots.set(index, treeModel.getRoot()); + treeModelSortIndexes.set(index, (float)sortIndex ); + TreePath path = new TreePath(root); + treeModel.addTreeModelListener(myListener); + fireTreeNodesChanged(path, new int[]{index}, new Object[]{treeModel.getRoot()}); + } else { + int index= Collections.binarySearch( treeModelSortIndexes, sortIndex+0.5f ); + index= -1 - index; + treeModels.add( index, treeModel ); + treeModelRoots.add( index, treeModel.getRoot() ); + treeModelSortIndexes.add( index, (float)sortIndex ); + TreePath path = new TreePath(root); + treeModel.addTreeModelListener(myListener); + fireTreeNodesInserted(path, new int[]{ index }, new Object[]{treeModel.getRoot()}); + } + } + + public void unmountTree(TreeModel treeModel) { + if (checkEvent && !EventQueue.isDispatchThread()) + throw new IllegalArgumentException("must be called from AWT thread"); // useful for debugging concurrent exception + + int index = treeModelRoots.indexOf(treeModel.getRoot()); + if ( index!=-1 ) { + treeModels.remove(index); + treeModelRoots.remove(index); + treeModelSortIndexes.remove(index); + TreePath path = new TreePath(root); + fireTreeNodesRemoved( new TreeModelEvent(this, path, new int[] { index }, new Object[] { treeModel.getRoot() } ) ); + } + } + + public synchronized Object getChild(Object parent, int index) { + if (checkEvent && !EventQueue.isDispatchThread()) + throw new IllegalArgumentException("must be called from AWT thread"); // useful for debugging concurrent exception + Object result; + TreeModel mt; + if (parent == root) { + mt = (TreeModel) treeModels.get(index); + result = mt.getRoot(); + } else { + mt = (TreeModel) sourceMap.get(parent); + result = mt.getChild(parent, index); + } + sourceMap.put(result, mt); + return result; + } + + public int getChildCount(Object parent) { + if (checkEvent && !EventQueue.isDispatchThread()) + throw new IllegalArgumentException("must be called from AWT thread"); // useful for debugging concurrent exception + if (parent == root) { + return this.treeModels.size(); + } else { + TreeModel mt = (TreeModel) sourceMap.get(parent); + return mt.getChildCount(parent); + } + } + + public boolean isLeaf(Object node) { + if (node == root) { + return treeModels.size() == 0; + } else { + TreeModel mt = (TreeModel) sourceMap.get(node); + if ( mt==null ) { + System.err.println("null on "+node); + return true; + } + return mt.isLeaf(node); + } + } + + public void valueForPathChanged(TreePath path, Object newValue) { + Object parent = path.getPathComponent(path.getPathCount() - 2); + Object child = path.getPathComponent(path.getPathCount() - 1); + int index = getIndexOfChild(parent, child); + fireTreeNodesChanged(path, new int[]{index}, new Object[]{newValue}); + } + + public int getIndexOfChild(Object parent, Object child) { + if (parent == root) { + for (int i = 0; i < treeModels.size(); i++) { + if (child == ((TreeModel) treeModels.get(i)).getRoot()) + return i; + } + return -1; + } else { + TreeModel mt = (TreeModel) sourceMap.get(parent); + return mt.getIndexOfChild(parent, child); + } + } + + public void addTreeModelListener(TreeModelListener l) { + listeners.add(l); + } + + public void removeTreeModelListener(TreeModelListener l) { + listeners.remove(l); + } + + private void fireTreeNodesInserted(TreePath path, + int[] insertedIndeces, Object[] children) { + TreeModelEvent e = new TreeModelEvent(this, path, insertedIndeces, children); + fireTreeNodesInserted(e); + } + + private void fireTreeNodesChanged(TreePath path, + int[] changedIndeces, Object[] children) { + TreeModelEvent e = new TreeModelEvent(this, path, changedIndeces, children); + fireTreeNodesChanged(e); + } + + private void fireTreeNodesChanged(TreeModelEvent e) { + for (Iterator i = listeners.iterator(); i.hasNext();) { + ((TreeModelListener) i.next()).treeNodesChanged(e); + } + } + + private void fireTreeNodesInserted(TreeModelEvent e) { + for (Iterator i = listeners.iterator(); i.hasNext();) { + ((TreeModelListener) i.next()).treeNodesInserted(e); + } + } + + public void fireTreeNodesRemoved(TreeModelEvent e) { + for (Iterator i = listeners.iterator(); i.hasNext();) { + ((TreeModelListener) i.next()).treeNodesRemoved(e); + } + } + + public void fireTreeStructureChanged(TreeModelEvent e) { + for (Iterator i = listeners.iterator(); i.hasNext();) { + ((TreeModelListener) i.next()).treeStructureChanged(e); + } + } +} diff --git a/dasCoreUtil/src/org/das2/util/CredentialsDialog.form b/dasCore/src/main/java/org/das2/util/CredentialsDialog.form similarity index 100% rename from dasCoreUtil/src/org/das2/util/CredentialsDialog.form rename to dasCore/src/main/java/org/das2/util/CredentialsDialog.form diff --git a/dasCore/src/main/java/org/das2/util/CredentialsDialog.java b/dasCore/src/main/java/org/das2/util/CredentialsDialog.java new file mode 100644 index 000000000..a26c5d616 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/CredentialsDialog.java @@ -0,0 +1,214 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.util; + +import java.awt.Dialog; +import java.awt.event.KeyEvent; +import javax.swing.Icon; +import javax.swing.JDialog; +import javax.swing.JOptionPane; + +/** + * + * @author cwp + */ +public class CredentialsDialog extends JDialog{ + + protected int m_nRet; + protected String m_sUser; + protected String m_sPasswd; + + /** Creates new form CredentialsDialog */ + public CredentialsDialog(java.awt.Frame parent){ + super(parent, "Authentication Required", Dialog.ModalityType.APPLICATION_MODAL); + m_nRet = 0; + setResizable(true); + if(parent != null){ + setLocationRelativeTo(parent); + } + initComponents(); + } + + /** Show the dialog, get user input, and then close */ + public int runDialog(String sDesc, Icon icon, String sUser, String sPasswd){ + m_nRet = JOptionPane.CANCEL_OPTION; + + // Code halts here until some other location calls setVisible(false) on this + // dialog object + if(icon != null) lblIcon.setIcon(icon); + lblDesc.setText(sDesc); + tfUser.setText(sUser); + tfPasswd.setText(sPasswd); + + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + + pack(); // Recalculate internal size of sub components + validate(); + + setVisible(true); + m_sUser = tfUser.getText(); + m_sPasswd = new String( tfPasswd.getPassword() ); + + return m_nRet; + }; + + int getReturn(){return m_nRet;} + String getUser(){return m_sUser;} + String getPasswd(){return m_sPasswd;} + + /** This method is called from within the constructor to initialize the form. WARNING: + * Do NOT modify this code. The content of this method is always regenerated by the + * Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + lblIcon = new javax.swing.JLabel(); + lblDesc = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + jLabel4 = new javax.swing.JLabel(); + tfUser = new javax.swing.JTextField(); + tfPasswd = new javax.swing.JPasswordField(); + jSeparator1 = new javax.swing.JSeparator(); + btnOK = new javax.swing.JButton(); + btnCancel = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("Authorization Required"); + setModalityType(java.awt.Dialog.ModalityType.APPLICATION_MODAL); + setName("Authorization"); // NOI18N + getContentPane().setLayout(new java.awt.GridBagLayout()); + + lblIcon.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/das2logo-64.png"))); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 0); + getContentPane().add(lblIcon, gridBagConstraints); + + lblDesc.setText("

    Some Long Complete Site Name

    Server: some.server.org/das
    Data Set: Some Dataset"); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridwidth = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + getContentPane().add(lblDesc, gridBagConstraints); + + jLabel3.setText("User name:"); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; + getContentPane().add(jLabel3, gridBagConstraints); + + jLabel4.setText("Password:"); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; + getContentPane().add(jLabel4, gridBagConstraints); + + tfUser.setText("someone"); + tfUser.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + tfUserActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = java.awt.GridBagConstraints.RELATIVE; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.weightx = 0.67; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + getContentPane().add(tfUser, gridBagConstraints); + + tfPasswd.setText("jPasswor"); + tfPasswd.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + tfPasswdActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 3; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + getContentPane().add(tfPasswd, gridBagConstraints); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 5; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + getContentPane().add(jSeparator1, gridBagConstraints); + + btnOK.setText("OK"); + btnOK.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnOKActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 4; + gridBagConstraints.gridy = 4; + gridBagConstraints.ipadx = 10; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + getContentPane().add(btnOK, gridBagConstraints); + + btnCancel.setText("Cancel"); + btnCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnCancelActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 3; + gridBagConstraints.gridy = 4; + gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + getContentPane().add(btnCancel, gridBagConstraints); + + pack(); + }// //GEN-END:initComponents + + private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed + m_nRet = JOptionPane.OK_OPTION; + setVisible(false); + }//GEN-LAST:event_btnOKActionPerformed + + private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed + setVisible(false); + }//GEN-LAST:event_btnCancelActionPerformed + + private void tfPasswdActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tfPasswdActionPerformed + btnOKActionPerformed(evt); + }//GEN-LAST:event_tfPasswdActionPerformed + + private void tfUserActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tfUserActionPerformed + btnOKActionPerformed(evt); + }//GEN-LAST:event_tfUserActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton btnCancel; + private javax.swing.JButton btnOK; + private javax.swing.JLabel jLabel3; + private javax.swing.JLabel jLabel4; + private javax.swing.JSeparator jSeparator1; + private javax.swing.JLabel lblDesc; + private javax.swing.JLabel lblIcon; + private javax.swing.JPasswordField tfPasswd; + private javax.swing.JTextField tfUser; + // End of variables declaration//GEN-END:variables +} diff --git a/dasCore/src/main/java/org/das2/util/CredentialsManager.java b/dasCore/src/main/java/org/das2/util/CredentialsManager.java new file mode 100644 index 000000000..44bf55b6c --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/CredentialsManager.java @@ -0,0 +1,314 @@ +/* Part of the Das2 libraries, which the LGPL with class-path exception license */ +package org.das2.util; + +import java.awt.Frame; +import java.awt.Window; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.logging.Logger; +import javax.swing.ImageIcon; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; +import org.das2.system.LogCategory; + + +/** Provides per-resource login credentials + * + * This class maintains a map of login credentials by resource ID. The resource ID's + * themselves are just strings. The only expectation on resource ID strings is that they + * should be suitable for use as the Keys to a hash map. Otherwise no formation rules are + * assumed nor expected. User names and passwords for multiple web-sites, ftp sites, etc. + * are maintained by a single instance of this class. Call: + * + * CredentialsManager.getManager() + * + * to get a reference to that single instance. + * + * In a graphical environment this class handles presenting dialogs to the user to gather + * logon credentials. In a shell environment it will interact with the TTY to get user + * information. + * + * An example of using this class to handle Das 2.1 server authentication which html + * location information formatting follows: + * + * + * CredentialsManage cm = CrentialsManager.getMannager(); + * String sLocId = "planet.physics.uiowa.edu/das/das2Server|voyager1/pwi/SpecAnalyzer-4s-Efield"; + * + * if(!cm.hasCredentials(sLocId)){ + * DasServer svr = DasServer.create(sDsdf); + * String sDesc = String.Format("

    %s

    Server: %s

    DataSource: %s

    ", + * DasServer.getName(), "planet.physics.uiowa.edu", + * "voyager1 > pwi > SpecAnalyzer-4s-Efield"); + * cm.setDescription(sLocId, sDesc, DasServer.getLogo()); + * } + * + * String sHash = getHttpBasicHash(sLocId) + * + *
    + * + * Two previous classes, org.das2.util.filesystem.KeyChain (autoplot) and + * org.das2.client.Authenticator have approached this problem as well. However both of + * those classes make assumptions that are not valid in general. The first assumes that + * the caller somehow knows the username. The second assumes that you are talking to + * a first generation Das 2.1 server. Details of server communication are beyond the + * scope of this class. + * + * @author cwp + */ +public class CredentialsManager{ + + /////////////////////////////////////////////////////////////////////////////////// + // Static Section + + // A map of credentials managers versus lookup key + static final HashMap g_dManagers = new HashMap<>(); + static{ + g_dManagers.put(null, new CredentialsManager(null)); + } + + /** Get a reference to the authentication manager. + * + * Typically this is the function you want to use to get started. + */ + public static CredentialsManager getMannager(){ + return g_dManagers.get(null); + } + + /** Get an authentication manager associated with an associated tracking string. + * This is probably not the function you are looking for. It only exists for + * odd cases where two different credential managers are active in a single + * application at the same time. + * + * @param sWhich - A string used to differentiate this credentials manager from the + * default instance. + */ + public static CredentialsManager getMannager(String sWhich){ + if(!g_dManagers.containsKey(sWhich)){ + synchronized(g_dManagers) { + g_dManagers.put(sWhich, new CredentialsManager(sWhich)); + } + } + return g_dManagers.get(sWhich); + } + + //////////////////////////////////////////////////////////////////////////////////// + // Instance Section + + protected class Location{ + String sLocId; + String sDesc; + ImageIcon iconLogo; + String sUser; + String sPasswd; + + protected Location(String sLocationId, String sDescription, ImageIcon icon){ + sLocId = sLocationId; + sDesc = sDescription; + iconLogo = icon; + sUser = null; + sPasswd = null; + } + + protected boolean hasCredentials(){ + return (sUser != null)||(sPasswd != null); + } + } + + String m_sName; + HashMap m_dLocs; + CredentialsDialog m_dlg; + + protected CredentialsManager(String sName){ + m_sName = sName; + m_dLocs = new HashMap<>(); + m_dlg = null; + } + + /** Provide a description of a location for use in authentication dialogs. + * + * Use this function to tie a string description to a location id. This description + * will be used when interacting with the user. If no description is present, then just + * the location ID itself will be used to identify the site to the end user. + * Usually location strings aren't that easy to read so use of this function or the + * version with an icon argument is recommended, though not required. + * + * @param sLocationId The location to describe, can not be null. + * @param sDescription A string to present to a user when prompting for a credentials + * for this location, may be a basic HTML string. + */ + public void setDescription(String sLocationId, String sDescription){ + setDescription(sLocationId, sDescription, null); + } + + /** Provide a description of a location with an Image Icon + * + * Use this function to tie a string description and an Icon to a location. + * + * @param sLocationId The location to describe, can not be null. + * @param sDescription The description, may be a simply formatted HTML string. + * @param icon An icon to display for the server. + */ + public synchronized void setDescription(String sLocationId, String sDescription, + ImageIcon icon) + { + if(!m_dLocs.containsKey(sLocationId)){ + m_dLocs.put(sLocationId, new Location(sLocationId, sDescription, icon)); + } + else{ + Location loc = m_dLocs.get(sLocationId); + loc.sDesc = sDescription; + loc.iconLogo = icon; + } + } + + /** Determine if there are any stored credentials for this location + * + * If either a username or a password have been provided for the location + * then it is considered to have credentials + * + * @param sLocationId The location to describe, can not be null. + * @return true if there are stored credentials, false otherwise + */ + public boolean hasCredentials(String sLocationId){ + if(!m_dLocs.containsKey(sLocationId)) return false; + Location loc = m_dLocs.get(sLocationId); + return loc.hasCredentials(); + } + + /** Determine if a given location has been described + * + * Gathering descriptive information about a remote location may trigger communication + * with a remote site. Use this function to see if such communication is needed. + * + * @param sLocationId The location in question + * @return true if the location has been described, false otherwise + */ + public boolean hasDescription(String sLocationId){ + if(!m_dLocs.containsKey(sLocationId)) return false; + Location loc = m_dLocs.get(sLocationId); + return (loc.sDesc != null)&&(!loc.sDesc.isEmpty()); + } + + /** Determine is a site image has been set for a location ID. + * + * This function is provided because retrieving the logo for a site may trigger remote + * host communication. Use this function to see if such communication is needed. + * @param sLocationId the location in question + * @return true if the location has as attached icon logo + */ + public boolean hasIcon(String sLocationId){ + if(!m_dLocs.containsKey(sLocationId)) return false; + Location loc = m_dLocs.get(sLocationId); + return loc.iconLogo != null; + } + + /** Get credentials in the form of a hashed HTTP Basic authentication string + * + * If there are no credentials stored for the given location id, this function may + * trigger interaction with the user, such as presenting modal dialogs, or changing the + * TTY to non-echo. + * + * @param sLocationId - A unique string identifying a location. There are no formation + * rules on the string, but convenience functions are provided if a uniform naming + * convention is desired. + * + * @return The string USERNAME + ":" + PASSWORD that is then run through a base64 + * encoding. If no credentials are available for the given location ID and none can be + * gathered from the user (possibly due to the java.awt.headless being set) null is + * returned. + */ + public String getHttpBasicHash(String sLocationId){ + String sHash = null; + + if(!m_dLocs.containsKey(sLocationId)){ + synchronized(this){ + //Check again. Though unlikely, the key could have been added between + //the call above and the start of the sychronized section + if(!m_dLocs.containsKey(sLocationId)) + m_dLocs.put(sLocationId, new Location(sLocationId, null, null)); + } + } + + Location loc = m_dLocs.get(sLocationId); + if(!hasCredentials(sLocationId)){ + if("true".equals( System.getProperty("java.awt.headless"))){ + if(!getCredentialsCmdLine(loc)) return null; + } + else{ + if(!getCredentialsGUI(loc)) return null; + } + } + + String sTmp = loc.sUser + ":" + loc.sPasswd; + sHash = Base64.encodeBytes( sTmp.getBytes()); + return sHash; + } + + /** Let the credentials manager know that stored credentials for a location are invalid + * + * @param sLocationId + * @return + */ + public synchronized void invalidate(String sLocationId){ + if(!m_dLocs.containsKey(sLocationId)) return; + Location loc = m_dLocs.get(sLocationId); + loc.sUser = null; + loc.sPasswd = null; + } + + ////////////////////////// User Interaction //////////////////////////////////// + + /** Gather User Credentials + * + * @param loc The Location in question + * @return True if user hit OK, False if user canceled the operation + */ + protected synchronized boolean getCredentialsGUI(final Location loc) { + + // Check again to see if another thread managed to set the credentials before + // this method started. Need to avoid the double-authenticate dialogs problem + // I'm not sure how to prevent the double cancel problem at this time. --cwp + if( loc.hasCredentials()) return true; + + try{ + SwingUtilities.invokeAndWait( + new Runnable(){ + @Override + public void run(){ + // make the dialog if it doesn't exist + if(m_dlg == null){ + Frame wParent = null; + Window[] lTopWnds = Window.getOwnerlessWindows(); + for(Window wnd: lTopWnds){ + if(wnd.isVisible() && wnd instanceof Frame){ + wParent = (Frame)wnd; + break; + } + } + m_dlg = new CredentialsDialog(wParent); + } + String sTmp = loc.sDesc; + if((sTmp == null)||(sTmp.isEmpty())) sTmp = loc.sLocId; + m_dlg.runDialog(sTmp, loc.iconLogo, loc.sUser, loc.sPasswd); + } + } + ); + } + catch(InterruptedException | InvocationTargetException ex){ + Logger.getLogger(LogCategory.GRAPHICS_LOG).severe(ex.toString()); + return false; + } + + if(m_dlg.getReturn() == JOptionPane.CANCEL_OPTION) return false; + loc.sUser = m_dlg.getUser(); + loc.sPasswd = m_dlg.getPasswd(); + + return true; + } + + protected synchronized boolean getCredentialsCmdLine(Location loc){ + throw new UnsupportedOperationException("Command Line Credential collection is " + + "not yet implemented"); + } +} diff --git a/dasCore/src/main/java/org/das2/util/Crypt.java b/dasCore/src/main/java/org/das2/util/Crypt.java new file mode 100644 index 000000000..e7d676cfb --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/Crypt.java @@ -0,0 +1,71 @@ +/* File: crypt.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * + * @author jbf + */ +public class Crypt { + + /** Creates a new instance of crypt */ + private Crypt() { + } + + public static String crypt(java.lang.String s) { + return JCrypt.crypt("do", s); +// try { +// return new String(MessageDigest.getInstance("MD5").digest(s.getBytes())); +// } catch ( NoSuchAlgorithmException ex ) { +// RuntimeException e= new IllegalStateException( "MD5 algorythm not available" ); +// e.initCause(ex); +// throw e; +// } + } + + public static void main(String args[]) throws Exception { + String arg; + if(args.length >= 1) { + arg= args[0]; + } else { + arg= "ask1st"; + org.das2.util.DasDie.println("java crypt "); + org.das2.util.DasDie.println(" using "+arg ); + } + System.out.println( + "[" + arg + "] => [" + + Crypt.crypt(arg) + "]" + ); + + //byte[] bytes= MessageDigest.getInstance("MD5").digest(arg.getBytes()); + //String[] hex= new String[] { "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f" }; + //for ( int i=0; i>4] + hex[( (bytes[i]&0x0F) )] + " "); + //} + + } +} diff --git a/dasCore/src/main/java/org/das2/util/DasDie.java b/dasCore/src/main/java/org/das2/util/DasDie.java new file mode 100644 index 000000000..fbe725888 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/DasDie.java @@ -0,0 +1,118 @@ +/* File: DasDie.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * + * @author jbf + */ +public class DasDie { + + public static int DEBUG=-20; // info useful only to das developers + public static int VERBOSE=-10; // info useful to end users in debugging + public static int INFORM=0; // warm-fuzzy operational messages + public static int WARN=10; // end user needs to be aware of, no action required + public static int ALARM=20; // end user needs to take action + public static int CRITICAL=30; // abnormal system condition that cannot be supressed. + + public static int verbosity; + + static { + String debugLevel= org.das2.DasProperties.getInstance().getProperty("debugLevel"); + setDebugVerbosityLevel(debugLevel); + } + + /** Creates a new instance of DasDie */ + private DasDie() { + } + + public static void setDebugVerbosityLevel(String debugLevel) { + if (debugLevel.equals("endUser")) verbosity= WARN; + else if (debugLevel.equals("dasDeveloper")) verbosity= DEBUG; + else verbosity= INFORM; + } + + private static final String calledBy() { + StringWriter sw = new StringWriter(); + new Throwable().printStackTrace( + new PrintWriter( sw ) + ); + String callStack = sw.toString(); + + int atPos = callStack.indexOf( "at" ); + //org.das2.util.DasDie.println(callStack.substring(atPos)); + atPos = callStack.indexOf( "at" , atPos+2 ); + //org.das2.util.DasDie.println(callStack.substring(atPos)); + atPos = callStack.indexOf( "at" , atPos+2 ); + //org.das2.util.DasDie.println(callStack.substring(atPos)); + atPos = callStack.indexOf( "at" , atPos+2 ); + //org.das2.util.DasDie.println(callStack.substring(atPos)); + int nextAtPos= callStack.indexOf( "at" , atPos+2 ); + + String calledBy= callStack.substring(atPos,nextAtPos-2); + return calledBy; + } + + public static final void die(String message) { + + System.out.print(calledBy()+": "); + org.das2.util.DasDie.println(CRITICAL,message); + System.exit(-1); + } + + public static final void println(java.lang.String message) { + println(DEBUG,message); + } + + public static final void println(Object o) { + println(o.toString()); + } + + public static final void println(int verbosity, java.lang.String message) { + if (verbosity>=DasDie.verbosity) { + //System.out.print(calledBy()+": "); + System.err.println(message); + } + } + + public static final void print(java.lang.String message) { + print(DEBUG,message); + } + + public static final void print(Object o) { + print(o.toString()); + } + + public static final void print(int verbosity, java.lang.String message) { + if (verbosity>=DasDie.verbosity) { + //System.out.print(calledBy()+": "); + System.out.print(message); + } + } + + +} diff --git a/dasCore/src/main/java/org/das2/util/DasExceptionHandler.java b/dasCore/src/main/java/org/das2/util/DasExceptionHandler.java new file mode 100755 index 000000000..dcd4da00e --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/DasExceptionHandler.java @@ -0,0 +1,175 @@ +/* File: DasExceptionHandler.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import org.das2.DasApplication; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.*; +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * + * @author jbf + */ +public final class DasExceptionHandler { + + //private static JDialog dialog; + //private static JTextArea messageArea; + //private static JTextArea traceArea; + private static final String UNCAUGHT = "An unexpected error has occurred. " + + "The system may not be able to recover properly. Please report this " + + "error to the Das2 bug database at http://bugs-pw.physics.uiowa.edu/." + + " Please include all error information and a description of how you" + + " encountered the error. For your convenience, you may click the " + + "\"Show Details\" button then click the \"Save to file\" button to save" + + " all the relevant error messages to a file.\n"; + + /* this is public so that the AWT thread can create it */ + public DasExceptionHandler() { + } + + public static void handle(Throwable t) { + if ( DasApplication.getDefaultApplication().isHeadless() ) { + t.printStackTrace(); + } + else { + showExceptionDialog(t, ""); + } + } + + public static void handleUncaught(Throwable t) { + if ( DasApplication.getDefaultApplication().isHeadless() ) { + t.printStackTrace(); + } + else { + showExceptionDialog(t, UNCAUGHT); + } + } + + private static void showExceptionDialog(final Throwable t, String extraInfo) { + String errorMessage = extraInfo + t.getClass().getName() + "\n" + + (t.getMessage() == null ? "" : t.getMessage()); + final JDialog dialog = new JDialog( DasApplication.getDefaultApplication().getMainFrame() ); + dialog.setTitle("Error in das2"); + dialog.setModal(false); + dialog.setResizable(false); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + final JTextArea messageArea = new JTextArea(10, 40); + messageArea.setLineWrap(true); + messageArea.setWrapStyleWord(true); + messageArea.setEditable(false); + messageArea.setText(errorMessage); + JScrollPane message = new JScrollPane(messageArea); + message.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.add(message, BorderLayout.CENTER); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + JButton ok = new JButton("Ok"); + final JToggleButton details = new JToggleButton("Show Details"); + buttonPanel.add(ok); + buttonPanel.add(details); + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + dialog.getContentPane().add(mainPanel, BorderLayout.CENTER); + + final JTextArea traceArea = new JTextArea(10, 40); + traceArea.setLineWrap(false); + traceArea.setEditable(false); + traceArea.setTabSize(4); + + StringWriter writer = new StringWriter(); + t.printStackTrace(new PrintWriter(writer)); + traceArea.setText(writer.toString()); + + final JPanel stackPane = new JPanel(new BorderLayout()); + stackPane.add(new JScrollPane(traceArea), BorderLayout.NORTH); + stackPane.setBorder(new javax.swing.border.EmptyBorder(10, 10, 10, 10)); + JPanel buttonPanel2 = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel2.setBorder(new javax.swing.border.EmptyBorder(10, 0, 0, 0)); + JButton dump = new JButton("Dump to STDERR"); + JButton save = new JButton("Save to file"); + buttonPanel2.add(dump); + buttonPanel2.add(save); + stackPane.add(buttonPanel2, BorderLayout.SOUTH); + Dimension size = message.getPreferredSize(); + size.width = stackPane.getPreferredSize().width; + message.setPreferredSize(size); + + ok.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + dialog.dispose(); + } + }); + + details.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (details.isSelected()) { + details.setText("Less Details"); + dialog.getContentPane().add(stackPane, BorderLayout.SOUTH); + dialog.pack(); + } + else { + details.setText("More Details"); + dialog.getContentPane().remove(stackPane); + dialog.pack(); + } + } + }); + + dump.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String text = traceArea.getText(); + System.err.print(text); + } + }); + + save.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + JFileChooser chooser = new JFileChooser(); + int result = chooser.showSaveDialog(dialog); + if (result == chooser.APPROVE_OPTION) { + File selected = chooser.getSelectedFile(); + PrintWriter out = new PrintWriter(new FileOutputStream(selected)); + t.printStackTrace(out); + out.close(); + } + } + catch (IOException ioe) { + handle(ioe); + } + } + }); + + dialog.pack(); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + +} diff --git a/dasCore/src/main/java/org/das2/util/DasMath.java b/dasCore/src/main/java/org/das2/util/DasMath.java new file mode 100644 index 000000000..275f8dd60 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/DasMath.java @@ -0,0 +1,259 @@ +/* File: DasMath.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +/** + * + * @author jbf + */ + +public class DasMath { + + /** Creates a new instance of DasMath */ + public DasMath() { + } + + private static final double log10=Math.log(10); + + public static double log10(double x) { + return Math.log(x)/log10; + } + + public static double exp10(double x) { + double result= Math.pow(10,x); + return result; + } + + // what does this function do? + public static double exp10(int x) { + double result= Math.pow(10,x); + return result; + } + + public static double roundNFractionalDigits(double x,int n) { + double tenToN= exp10(n-1); + return Math.round( x * tenToN ) / tenToN; + } + + public static double roundNSignificantDigits(double x,int n) { + double sign= x<0 ? -1 : 1; + double exp= exp10(Math.floor(log10(sign*x))); + double mant= x/exp; + double tenToN= exp10(n-1); + mant= Math.round( mant * tenToN ) / tenToN; + return mant*exp; + } + + public static double tanh( double x ) { + double sinh = ( Math.exp(x) - Math.exp(-x) )/2; + double cosh = ( Math.exp(x) + Math.exp(-x) )/2; + double tanh = sinh / cosh; + return tanh; + } + + /** Interpolate just one point */ + public static double interpolate( double[] datay, double findex ) { + int index= (int)findex; + double alpha= findex - index; + double result; + if ( findex<0. ) return datay[0]; + if ( findex>datay.length-1. ) return datay[datay.length-1]; + if ( alpha == 0. ) { // optimization and handle findex=(data.length-1); + result= datay[index]; + } else { + result= datay[index] * ( 1.0 - alpha ) + datay[index+1] * alpha ; + } + return result; + } + + /** interpolate an array of points. */ + public static double[] interpolate( double[] datay, double[] findex ) { + double[] result= new double[ findex.length ]; + for ( int i=0; i0 && datax[index]>x ) index--; + if ( index==datax.length-1 ) index--; + return index + ( x - datax[index] ) / ( datax[index+1] - datax[index] ); + } + + public static void main(String[] args) { + double x= 1e-18; + org.das2.util.DasDie.println("x:"+x); + org.das2.util.DasDie.println("roundNDigits:"+roundNSignificantDigits(x,3)); + + double[] x1= new double[] { 1,2,3,4,5 }; + double[] y1= new double[] { 4,6,7,3,1 }; + double[] interpx= new double [] { 1.0, 1.5, 4.5, 5.0, 1.5 }; + double[] interpy= interpolate( y1, findex( x1, interpx ) ); + for ( int i=0; i= 0 ? result : t + result; + } + + /** + * just like modulo (%) function, but negative numbers return positive phase. + */ + public static int modp( int x, int t) { + int result= x % t; + return result >= 0 ? result : t + result; + } + + public static double biggerOf(double x1, double x2) { + return ( x1>x2 ) ? x1 : x2; + } + + private static double gcd( double a, double d, double error ) { + + if ( error>0 ) { + a= Math.round( a/error ); + d= Math.round( d/error ); + } + + if ( a0 ) { + return a * error; + } else { + return a; + } + } + + double r= a % d; + + int iterations=0; + + while ( r > 0 && iterations<15 ) { + d= r; + r= a % d; + iterations++; + } + + if ( error>0 ) { + return d * error; + } else { + return d; + } + } + + + /* + * Returns the greatest common divisor of a group of numbers. This is useful for + * a number of visualization techniques, for instance when you need to integerize + * your data, the binsize should be the gcd. An error parameter is provided to + * avoid numerical noise, and in case there is a granularity that needn't be + * surpassed. + */ + public static double gcd( double[] A, double error ) { + double guess= A[0]; + + double result= guess; + + for ( int i=1; i A[i] ? max : A[i] ); + } + return max; + } + + public static double min( double[] A ) { + double min= A[0]; + for ( int i=0; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +/** + * + * @author eew + */ +public abstract class DasPNGConstants { + + /* + * Chunk type constants + */ + static final String CHUNK_TYPE_IHDR = "IHDR"; + static final String CHUNK_TYPE_PLTE = "PLTE"; + static final String CHUNK_TYPE_IDAT = "IDAT"; + static final String CHUNK_TYPE_IEND = "IEND"; + static final String CHUNK_TYPE_bKGD = "bKGD"; //CURRENTLY UNSUPPORTED + static final String CHUNK_TYPE_cHRM = "cHRM"; //CURRENTLY UNSUPPORTED + static final String CHUNK_TYPE_gAMA = "gAMA"; //CURRENTLY UNSUPPORTED + static final String CHUNK_TYPE_hIST = "hIST"; //CURRENTLY UNSUPPORTED + static final String CHUNK_TYPE_pHYs = "pHYs"; //CURRENTLY UNSUPPORTED + static final String CHUNK_TYPE_sBIT = "sBIT"; //CURRENTLY UNSUPPORTED + static final String CHUNK_TYPE_tEXT = "tEXt"; + static final String CHUNK_TYPE_tIME = "tIME"; //CURRENTLY UNSUPPORTED + static final String CHUNK_TYPE_tRNS = "tRNS"; //CURRENTLY UNSUPPORTED + static final String CHUNK_TYPE_zTXT = "zTXt"; //CURRENTLY UNSUPPORTED + + public static final int DEFAULT_GAMMA = 45000; //gamma of .45 (multiplied by 100000) + + public static final String KEYWORD_TITLE = "Title"; + public static final String KEYWORD_AUTHOR = "Author"; + public static final String KEYWORD_DESCRIPTION = "Description"; + public static final String KEYWORD_COPYRIGHT = "Copyright"; + public static final String KEYWORD_CREATION_TIME = "Creation Time"; + public static final String KEYWORD_SOFTWARE = "Software"; + public static final String KEYWORD_DISCLAIMER = "Disclaimer"; + public static final String KEYWORD_WARNING = "Warning"; + public static final String KEYWORD_SOURCE = "Source"; + public static final String KEYWORD_COMMENT = "Comment"; + + protected HashMap textMap = new HashMap(); + + protected int gamma = DEFAULT_GAMMA; + + DasPNGConstants() { + } + + /** Returns an unmodifiable java.util.List containing the contents of + * all of the tEXT chunks with the specified keyword. The return value + * is guaranteed to be non-null. If no tEXT chunks with the specified + * keyword exist, then an empty list is returned. + * @param keyword the specified keyword + * @return a java.util.List of the contents of tEXT chunks. + */ + public List getText(String keyword) { + List list = (List)textMap.get(keyword); + if (list == null) { + return Collections.EMPTY_LIST; + } + return Collections.unmodifiableList(list); + } + + protected static byte[] getISO8859_1Bytes(String header) { + try { + return header.getBytes("ISO-8859-1"); + } + catch (java.io.UnsupportedEncodingException uee) { + throw new AssertionError(uee); + } + } + + public int getGamma() { + return gamma; + } + +} diff --git a/dasCore/src/main/java/org/das2/util/DasPNGEncoder.java b/dasCore/src/main/java/org/das2/util/DasPNGEncoder.java new file mode 100644 index 000000000..861e0804f --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/DasPNGEncoder.java @@ -0,0 +1,349 @@ +/* File: DasPNGEncoder.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import java.awt.image.BufferedImage; +import java.awt.image.IndexColorModel; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.zip.CRC32; +import java.util.zip.Deflater; + +/** + * + * @author eew + */ +public class DasPNGEncoder extends DasPNGConstants { + + + /** Creates a new instance of DasPNGEncoder */ + public DasPNGEncoder() { + } + + /** Adds a tEXT chunk with the specified keyword and content. + * @param keyword the specified keyword + * @param content the content for the tEXT chunk + */ + public void addText(String keyword, String content) { + List list = (List)textMap.get(keyword); + if (list == null) { + list = new ArrayList(); + textMap.put(keyword, list); + } + list.add(content); + } + + /** Removes the tEXT chunk with the specifed keyword and content. + * @param keyword the specified keyword + * @param content the specified content to be removed + */ + public void removeText(String keyword, String content) { + List list = (List)textMap.get(keyword); + if (list != null) { + list.remove(content); + } + } + + /** Removes all tEXT chunk with the specified keyword + * @param keyword the specified keyword. + */ + public void removeAllText(String keyword) { + textMap.remove(keyword); + } + + public void setGamma(int gamma) { + this.gamma = gamma; + } + + public void write(BufferedImage image, OutputStream out) throws IOException { + LinkedList chunkList = new LinkedList(); + int totalSize = 0; + chunkList.add(getHeaderBytes()); + chunkList.add(getIHDRBytes(image)); + gettEXtBytes(chunkList); + chunkList.add(getgAMABytes()); + chunkList.add(getPLTEBytes(image)); + chunkList.add(getIDATBytes(image)); + chunkList.add(getIENDBytes()); + Iterator iterator = chunkList.iterator(); + while (iterator.hasNext()) { + totalSize += ((byte[])iterator.next()).length; + } + ByteBuffer buffer = ByteBuffer.allocate(totalSize); + iterator = chunkList.iterator(); + while (iterator.hasNext()) { + buffer.put((byte[])iterator.next()); + } + out.write(buffer.array()); + } + + private byte[] getHeaderBytes() { + return new byte[] { + (byte)137, (byte)80, (byte)78, (byte)71, + (byte)13, (byte)10, (byte)26, (byte)10 + }; + } + + /** + * Width: 4 bytes + * Height: 4 bytes + * Bit depth: 1 byte (allowed values: 1, 2, 4, 8, 16) + * Color type: 1 byte + Color Allowed Interpretation + Type Bit Depths + 0 1,2,4,8,16 Each pixel is a grayscale sample. + 2 8,16 Each pixel is an R,G,B triple. + 3 1,2,4,8 Each pixel is a palette index; + a PLTE chunk must appear. + 4 8,16 Each pixel is a grayscale sample, + followed by an alpha sample. + 6 8,16 Each pixel is an R,G,B triple, + followed by an alpha sample. + * Compression method: 1 byte (must be 0) + * Filter method: 1 byte (must be 0) + * Interlace method: 1 byte (interlacing not supported, will be 0) + */ + private byte[] getIHDRBytes(BufferedImage image) { + byte bitDepth; + byte colorType; + int imageType = image.getType(); + switch (imageType) { + //24 bit image types + case BufferedImage.TYPE_3BYTE_BGR: + case BufferedImage.TYPE_INT_BGR: + case BufferedImage.TYPE_INT_RGB: + case BufferedImage.TYPE_USHORT_555_RGB: + case BufferedImage.TYPE_USHORT_565_RGB: + bitDepth = 8; + colorType = 2; + break; + + //32 bit alpha + case BufferedImage.TYPE_4BYTE_ABGR: + case BufferedImage.TYPE_INT_ARGB: + bitDepth = 8; + colorType = 6; + break; + + case BufferedImage.TYPE_BYTE_INDEXED: + bitDepth = 8; + colorType = 3; + break; + + case BufferedImage.TYPE_BYTE_GRAY: + bitDepth = 8; + colorType = 0; + break; + + case BufferedImage.TYPE_USHORT_GRAY: + bitDepth = 16; + colorType = 0; + break; + + //Currently unsupported types + case BufferedImage.TYPE_BYTE_BINARY: + case BufferedImage.TYPE_4BYTE_ABGR_PRE: + case BufferedImage.TYPE_INT_ARGB_PRE: + case BufferedImage.TYPE_CUSTOM: + default: + throw new RuntimeException("Unsupported image type"); + } + byte compressionMethod = 0; + byte filterMethod = 0; + byte interlaceMethod = 0; + + byte[] array = new byte[25]; + ByteBuffer buffer = ByteBuffer.wrap(array); + buffer.putInt(13); + buffer.put(getISO8859_1Bytes(CHUNK_TYPE_IHDR)); + buffer.putInt(image.getWidth()); + buffer.putInt(image.getHeight()); + buffer.put(bitDepth); + buffer.put(colorType); + buffer.put(compressionMethod); + buffer.put(filterMethod); + buffer.put(interlaceMethod); + CRC32 crc = new CRC32(); + crc.update(array, 4, 17); + buffer.putInt((int)crc.getValue()); + return array; + } + + private byte[] getgAMABytes() { + byte[] array = new byte[16]; + ByteBuffer buffer = ByteBuffer.wrap(array); + buffer.putInt(4); + buffer.put(getISO8859_1Bytes(CHUNK_TYPE_gAMA)); + buffer.putInt(gamma); + CRC32 crc = new CRC32(); + crc.update(array, 4, 8); + buffer.putInt((int)crc.getValue()); + return array; + } + + private byte[] getPLTEBytes(BufferedImage image) { + if (image.getType() != BufferedImage.TYPE_BYTE_INDEXED) { + return new byte[0]; + } + IndexColorModel cm = (IndexColorModel)image.getColorModel(); + int colorCount = cm.getMapSize(); + throw new UnsupportedOperationException(); + } + + private byte[] getIDATBytes(BufferedImage image) { + byte[] imageData; + int imageType = image.getType(); + switch (imageType) { + case BufferedImage.TYPE_3BYTE_BGR: + case BufferedImage.TYPE_INT_BGR: + case BufferedImage.TYPE_INT_RGB: + case BufferedImage.TYPE_USHORT_555_RGB: + case BufferedImage.TYPE_USHORT_565_RGB: + imageData = getRGBBytes(image); + break; + + //32 bit alpha + case BufferedImage.TYPE_4BYTE_ABGR: + imageData = getABGRBytes(image); + break; + + case BufferedImage.TYPE_INT_ARGB: + imageData = getARGBBytes(image); + break; + + case BufferedImage.TYPE_BYTE_INDEXED: + case BufferedImage.TYPE_BYTE_GRAY: + imageData = get8BitSampleBytes(image); + break; + + case BufferedImage.TYPE_USHORT_GRAY: + imageData = get16BitSampleBytes(image); + break; + + //Currently unsupported types + case BufferedImage.TYPE_BYTE_BINARY: + case BufferedImage.TYPE_4BYTE_ABGR_PRE: + case BufferedImage.TYPE_INT_ARGB_PRE: + case BufferedImage.TYPE_CUSTOM: + default: + throw new RuntimeException("Unsupported image type"); + } + + byte[] compressedImageData = new byte[imageData.length]; + Deflater deflater = new Deflater(); + deflater.setInput(imageData); + deflater.finish(); + int compressedSize = deflater.deflate(compressedImageData); + + byte[] array = new byte[compressedSize + 12]; + ByteBuffer buffer = ByteBuffer.wrap(array); + buffer.putInt(compressedSize); + buffer.put(getISO8859_1Bytes(CHUNK_TYPE_IDAT)); + buffer.put(compressedImageData, 0, compressedSize); + CRC32 crc = new CRC32(); + crc.update(array, 4, compressedSize + 4); + buffer.putInt((int)crc.getValue()); + return array; + } + + private byte[] getRGBBytes(BufferedImage image) { + int width = image.getWidth(); + int height = image.getHeight(); + int[] intPixels = new int[width * height]; + image.getRGB(0, 0, width, height, intPixels, 0, width); + byte[] bytePixels = new byte[intPixels.length * 3 + height]; + + for (int line = 0; line < height; line++) { + int offset = (width * 3 + 1) * line; + bytePixels[offset] = (byte)0; + for (int pixel = 0; pixel < width; pixel++) { + int intIndex = line * width + pixel; + int byteIndex = offset + (pixel * 3 + 1); + bytePixels[byteIndex] = (byte)((0xFF0000 & intPixels[intIndex]) >> 16); + bytePixels[byteIndex + 1] = (byte)((0x00FF00 & intPixels[intIndex]) >> 8); + bytePixels[byteIndex + 2] = (byte)(0x0000FF & intPixels[intIndex]); + } + } + return bytePixels; + } + + private byte[] getARGBBytes(BufferedImage image) { + throw new UnsupportedOperationException(); + } + + private byte[] getABGRBytes(BufferedImage image) { + throw new UnsupportedOperationException(); + } + + private byte[] get8BitSampleBytes(BufferedImage image) { + throw new UnsupportedOperationException(); + } + + private byte[] get16BitSampleBytes(BufferedImage image) { + throw new UnsupportedOperationException(); + } + + private byte[] getIENDBytes() { + byte[] array = new byte[12]; + byte[] typeBytes = getISO8859_1Bytes(CHUNK_TYPE_IEND); + ByteBuffer buffer = ByteBuffer.wrap(array); + buffer.putInt(0); + buffer.put(typeBytes); + CRC32 crc = new CRC32(); + crc.update(typeBytes); + buffer.putInt((int)crc.getValue()); + return array; + } + + private void gettEXtBytes(List list) { + Iterator entries = textMap.entrySet().iterator(); + while (entries.hasNext()) { + Map.Entry entry = (Map.Entry)entries.next(); + List contentList = (List)entry.getValue(); + Iterator content = contentList.iterator(); + while (content.hasNext()) { + list.add(gettEXtBytes((String)entry.getKey(), (String)content.next())); + } + } + } + + private byte[] gettEXtBytes(String keyword, String content) { + byte[] keywordBytes = getISO8859_1Bytes(keyword); + byte[] contentBytes = getISO8859_1Bytes(content); + byte[] array = new byte[keywordBytes.length + contentBytes.length + 13]; + ByteBuffer buffer = ByteBuffer.wrap(array); + buffer.putInt(keywordBytes.length + contentBytes.length + 1); + buffer.put(getISO8859_1Bytes(CHUNK_TYPE_tEXT)); + buffer.put(keywordBytes); + buffer.put((byte)0); + buffer.put(contentBytes); + CRC32 crc = new CRC32(); + crc.update(array, 4, keywordBytes.length + contentBytes.length + 5); + buffer.putInt((int)crc.getValue()); + return array; + } + +} diff --git a/dasCore/src/main/java/org/das2/util/DasProgressMonitorInputStream.java b/dasCore/src/main/java/org/das2/util/DasProgressMonitorInputStream.java new file mode 100755 index 000000000..5da3e6414 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/DasProgressMonitorInputStream.java @@ -0,0 +1,210 @@ +/* File: DasProgressMonitorInputStream.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 23, 2003, 5:00 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import org.das2.util.monitor.ProgressMonitor; +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.text.*; + +/** + * + * @author Edward West + */ +public class DasProgressMonitorInputStream extends java.io.FilterInputStream { + + private ProgressMonitor monitor; + private boolean started = false; + private int bytesRead = 0; + long birthTimeMilli; + long deathTimeMilli; + DecimalFormat transferRateFormat; + boolean enableProgressPosition= true; + + private long streamLength= 1000000; // this is usually close because of server side averaging. + private long taskSize= streamLength/1000; + + /** Creates a new instance of DasProgressMonitorInputStream */ + public DasProgressMonitorInputStream( InputStream in, ProgressMonitor monitor ) { + super(in); + this.monitor = monitor; + this.birthTimeMilli= System.currentTimeMillis(); + this.deathTimeMilli= -1; + } + + private void reportTransmitSpeed() { + if (transferRateFormat==null ) { + transferRateFormat= new DecimalFormat(); + transferRateFormat.setMaximumFractionDigits(2); + transferRateFormat.setMinimumFractionDigits(2); + } + monitor.setProgressMessage("("+ transferRateFormat.format(calcTransmitSpeed()/1024) +"kB/s)"); + if ( enableProgressPosition ) monitor.setTaskProgress(bytesRead/1000); + } + + + private double calcTransmitSpeed() { + // return speed in bytes/second. + long totalBytesRead= bytesRead; + long timeElapsed; + if ( deathTimeMilli>-1 ) { + timeElapsed= deathTimeMilli-birthTimeMilli; + } else { + timeElapsed= System.currentTimeMillis()-birthTimeMilli; + } + if ( timeElapsed==0 ) return Double.POSITIVE_INFINITY; + return 1000 * totalBytesRead / timeElapsed; + } + + public int read() throws IOException { + checkCancelled(); + int result = super.read(); + if (monitor != null) { + if (!started) { + started = true; + monitor.setTaskSize(taskSize); + monitor.started(); + } + if (bytesRead == -1) { + monitor.finished(); + } + else { + bytesRead++; + checkCancelled(); + reportTransmitSpeed(); + } + } + return result; + } + + public int read(byte[] b) throws IOException { + checkCancelled(); + int result = super.read(b); + if (monitor != null) { + if (!started) { + started = true; + monitor.setTaskSize(taskSize); + monitor.started(); + } + if (bytesRead == -1) { + monitor.finished(); + } + else { + bytesRead += result; + checkCancelled(); + reportTransmitSpeed(); + } + } + return result; + } + + public int read(byte[] b, int off, int len) throws IOException { + checkCancelled(); + int result = super.read(b, off, len); + if (monitor != null) { + if (!started) { + started = true; + monitor.setTaskSize( taskSize ); + monitor.started(); + } + if (bytesRead == -1) { + monitor.finished(); + } + else { + bytesRead += result; + checkCancelled(); + reportTransmitSpeed(); + } + } + return result; + } + + private void checkCancelled() throws IOException { + if (monitor != null && monitor.isCancelled()) { + close(); + throw new InterruptedIOException("Operation cancelled"); + } + } + + public void close() throws IOException { + super.close(); + deathTimeMilli= System.currentTimeMillis(); + if (monitor != null) { + monitor.finished(); + started= false; + } + } + + /** + * disable/enable setting of progress position, true by default. Transfer + * rate will still be reported. This is introduced in case another agent + * (the das2Stream reader, in particular) can set the progress position + * more accurately. + */ + public void setEnableProgressPosition( boolean value ) { + this.enableProgressPosition= value; + } + + /** + * Utility field used by bound properties. + */ + private java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); + + /** + * Adds a PropertyChangeListener to the listener list. + * @param l The listener to add. + */ + public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.addPropertyChangeListener(l); + } + + /** + * Removes a PropertyChangeListener from the listener list. + * @param l The listener to remove. + */ + public void removePropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.removePropertyChangeListener(l); + } + + /** + * Getter for property taskSize. + * @return Value of property taskSize. + */ + public long getStreamLength() { + return this.streamLength; + } + + /** + * Setter for property taskSize. + * @param taskSize New value of property taskSize. + */ + public void setStreamLength(long taskSize) { + long oldTaskSize = this.streamLength; + this.streamLength = taskSize; + this.taskSize= taskSize==-1 ? taskSize : streamLength/1000; + propertyChangeSupport.firePropertyChange ("streamLength", new Long (oldTaskSize), new Long (taskSize)); + } + +} diff --git a/dasCore/src/main/java/org/das2/util/DasProgressMonitorReadableByteChannel.java b/dasCore/src/main/java/org/das2/util/DasProgressMonitorReadableByteChannel.java new file mode 100644 index 000000000..23021a212 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/DasProgressMonitorReadableByteChannel.java @@ -0,0 +1,171 @@ +/* File: DasProgressMonitorInputStream.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 23, 2003, 5:00 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.util; + +import org.das2.util.monitor.ProgressMonitor; +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.text.*; + +/** + * + * @author Edward West + */ +public class DasProgressMonitorReadableByteChannel implements ReadableByteChannel { + + private ProgressMonitor monitor; + private boolean started = false; + private int bytesRead = 0; + long birthTimeMilli; + long deathTimeMilli; + DecimalFormat transferRateFormat; + boolean enableProgressPosition = true; + private long streamLength = 1000000; // this is usually close because of server side averaging. + private long taskSize = streamLength / 1000; + // we're wrapping this channel + ReadableByteChannel in; + + /** Creates a new instance of DasProgressMonitorInputStream */ + public DasProgressMonitorReadableByteChannel(ReadableByteChannel in, ProgressMonitor monitor) { + this.monitor = monitor; + this.birthTimeMilli = System.currentTimeMillis(); + this.deathTimeMilli = -1; + this.in = in; + } + + private void reportTransmitSpeed() { + if (transferRateFormat == null) { + transferRateFormat = new DecimalFormat(); + transferRateFormat.setMaximumFractionDigits(2); + transferRateFormat.setMinimumFractionDigits(2); + } + monitor.setProgressMessage("(" + transferRateFormat.format(calcTransmitSpeed() / 1024) + "kB/s)"); + if (enableProgressPosition) { + monitor.setTaskProgress(bytesRead / 1000); + } + } + + private double calcTransmitSpeed() { + // return speed in bytes/second. + long totalBytesRead = bytesRead; + long timeElapsed; + if (deathTimeMilli > -1) { + timeElapsed = deathTimeMilli - birthTimeMilli; + } else { + timeElapsed = System.currentTimeMillis() - birthTimeMilli; + } + if (timeElapsed == 0) { + return Double.POSITIVE_INFINITY; + } + return 1000 * totalBytesRead / timeElapsed; + } + + private void checkCancelled() throws IOException { + if (monitor != null && monitor.isCancelled()) { + close(); + throw new InterruptedIOException("Operation cancelled"); + } + } + + public void close() throws IOException { + in.close(); + deathTimeMilli = System.currentTimeMillis(); + if (monitor != null) { + monitor.finished(); + started = false; + } + } + + /** + * disable/enable setting of progress position, true by default. Transfer + * rate will still be reported. This is introduced in case another agent + * (the das2Stream reader, in particular) can set the progress position + * more accurately. + */ + public void setEnableProgressPosition(boolean value) { + this.enableProgressPosition = value; + } + /** + * Utility field used by bound properties. + */ + private java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); + + /** + * Adds a PropertyChangeListener to the listener list. + * @param l The listener to add. + */ + public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.addPropertyChangeListener(l); + } + + /** + * Removes a PropertyChangeListener from the listener list. + * @param l The listener to remove. + */ + public void removePropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.removePropertyChangeListener(l); + } + + /** + * Getter for property taskSize. + * @return Value of property taskSize. + */ + public long getStreamLength() { + return this.streamLength; + } + + /** + * Setter for property taskSize. + * @param taskSize New value of property taskSize. + */ + public void setStreamLength(long taskSize) { + long oldTaskSize = this.streamLength; + this.streamLength = taskSize; + this.taskSize = taskSize == -1 ? taskSize : streamLength / 1000; + propertyChangeSupport.firePropertyChange("streamLength", new Long(oldTaskSize), new Long(taskSize)); + } + + public int read(ByteBuffer dst) throws IOException { + int result = in.read(dst); + + if (!started) { + started = true; + monitor.setTaskSize(taskSize); + monitor.started(); + } + if (bytesRead == -1) { + monitor.finished(); + } else { + bytesRead += result; + checkCancelled(); + reportTransmitSpeed(); + } + + return result; + } + + public boolean isOpen() { + return in.isOpen(); + } +} diff --git a/dasCoreUtil/src/org/das2/util/DeflaterChannel.java b/dasCore/src/main/java/org/das2/util/DeflaterChannel.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/DeflaterChannel.java rename to dasCore/src/main/java/org/das2/util/DeflaterChannel.java diff --git a/dasCore/src/main/java/org/das2/util/DenseConsoleFormatter.java b/dasCore/src/main/java/org/das2/util/DenseConsoleFormatter.java new file mode 100644 index 000000000..acabd75e8 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/DenseConsoleFormatter.java @@ -0,0 +1,26 @@ +/* + * NBConsoleFormatter.java + * + * Created on April 7, 2005, 12:13 PM + */ + +package org.das2.util; + +import java.util.logging.Formatter; +import java.util.logging.LogRecord; + +/** + * + * @author Jeremy + */ +public class DenseConsoleFormatter extends Formatter { + + public String format( LogRecord rec ) { + StackTraceElement[] st= new Throwable().getStackTrace(); + return rec.getLoggerName()+": "+rec.getLevel().getLocalizedName()+": "+rec.getMessage()+"\n"; + } + + public DenseConsoleFormatter() { + } + +} diff --git a/dasCoreUtil/src/org/das2/util/DnDSupport.java b/dasCore/src/main/java/org/das2/util/DnDSupport.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/DnDSupport.java rename to dasCore/src/main/java/org/das2/util/DnDSupport.java diff --git a/dasCore/src/main/java/org/das2/util/Entities.java b/dasCore/src/main/java/org/das2/util/Entities.java new file mode 100755 index 000000000..8afe739c6 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/Entities.java @@ -0,0 +1,354 @@ +/** + * From Lucene Search Engine. + * code found at http://www.koders.com, decodeEntities() added. + * + */ +package org.das2.util; + +import java.util.*; + +public class Entities { + + static final Hashtable decoder = new Hashtable(300); + static final String[] encoder = new String[0x100]; + + /** + * utility method for decoding entities like ρ into unicode. + * @param str + * @return string with unicode characters for entities. + */ + public static String decodeEntities(String str) { + int i0=0, i=0; + + int MAX_ENTITY_LEN=10; + StringBuffer result= new StringBuffer(); + while ( true ) { + i= str.indexOf("&",i0); + + if ( i==-1 ) { + result.append( str.substring(i0) ); + return result.toString(); + } else { + int i1= str.indexOf(";",i); + if ( i1!=-1 && i1-i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Stack; +import java.util.Vector; + +/** + * Renders Grandle and Nystrom strings, like "E=mc!e2" + * @author Edward West + */ +public class GrannyTextRenderer { + + public static final int LEFT_ALIGNMENT = 0; + public static final int CENTER_ALIGNMENT = 1; + public static final int RIGHT_ALIGNMENT = 2; + + private Rectangle bounds=null; + private ArrayList lineBounds; + private String str; + private String[] tokens; + private int alignment = LEFT_ALIGNMENT; + private Component parent; + + public GrannyTextRenderer( ) { + + } + + /** + * returns the bounds of the current string. The lower-left corner of + * the first character will be roughly (0,0), to be compatible with + * FontMetrics.getStringBounds(). + * + * @return a Rectangle indicating the text boundaries. + * @throws IllegalArgumentException if the string has not been set. + */ + public Rectangle getBounds() { + if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); + maybeInitBounds(); + return new Rectangle(bounds); // defensive copy + } + + private void maybeInitBounds() { + if (bounds == null) { + bounds = new Rectangle((Rectangle)lineBounds.get(0)); + for (int i = 1; i < lineBounds.size(); i++) { + bounds.add((Rectangle)lineBounds.get(i)); + } + } + } + + /** + * returns the width of the bounding box, in pixels. + * @return the width of the bounding box, in pixels. + * @throws IllegalArgumentException if the string has not been set. + */ + public double getWidth() { + if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); + maybeInitBounds(); + return bounds.getWidth(); + } + + /** + * returns the width in pixels of the first line. + * @return the width in pixels of the first line. + * @throws IllegalArgumentException if the string has not been set. + * + */ + public double getLineOneWidth() { + if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); + return getLineWidth(1); + } + + /** + * returns the calculated width each line. + * @param lineNumber the index of the line, starting with 1. + * @return the width of the bounding box, in pixels. + * @throws IndexOutOfBoundsException if no such line exists. + */ + private double getLineWidth(int lineNumber) { + if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); + return ((Rectangle)lineBounds.get(lineNumber - 1)).getWidth(); + } + + /** + * returns the hieght of the calculated bounding box. + * @return the height of the bounding box, in pixels. + * @throws IllegalArgumentException if the string has not been set. + */ + public double getHeight() { + if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); + maybeInitBounds(); + return bounds.getHeight(); + } + + /** + * return the amount that the bounding box will go above the baseline. + * This is also the height of the first line. + * @return the amount that the bounding box will go above the baseline. + * @throws IllegalArgumentException if the string has not been set. + */ + public double getAscent() { + if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); + return -1*((Rectangle)lineBounds.get(0)).getY(); + } + + /** + * return the amount that the bounding box will go below the baseline. + * @return the amount that the bounding box will go below the baseline. + * @throws IllegalArgumentException if the string has not been set. + */ + public double getDescent() { + if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); + maybeInitBounds(); + return bounds.getHeight() + bounds.getY(); + } + + /** + * reset the current string for the GTR to draw, calculating the boundaries + * of the string. For greek and math symbols, unicode characters should be + * used. (See www.unicode.org). + * @deprecated use setString( Graphics g, String str ) instead. + * @param c the component which will provide the graphics. + * @param str the granny string, such as "E=mc!e2" + */ + public void setString( Component c, String str ) { + this.parent= c; + bounds = null; + lineBounds = new ArrayList(); + this.str = Entities.decodeEntities(str); + this.tokens = buildTokenArray(this.str); + this.draw( c.getGraphics(), c.getFont(), 0f, 0f, false ); + } + + /** + * reset the current string for the GTR to draw, calculating the boundaries + * of the string. For greek and math symbols, unicode characters should be + * used. (See www.unicode.org). + * + * @param g the graphics context which will supply the FontMetrics. + * @param str the granny string, such as "E=mc!e2" + */ + public void setString( Graphics g, String str) { + bounds = null; + lineBounds = new ArrayList(); + this.str = Entities.decodeEntities(str); + this.tokens = buildTokenArray(this.str); + this.draw( g, g.getFont(), 0f, 0f, false ); + } + + /** + * reset the current string for the GTR to draw, calculating the boundaries + * of the string. For greek and math symbols, unicode characters should be + * used. (See www.unicode.org). + * + * @param Font the font. This should be consistent + * with the Font used when drawing. + * @param str the granny string, such as "E=mc!e2" + */ + public void setString( Font font, String label) { + bounds = null; + lineBounds = new ArrayList(); + this.str = Entities.decodeEntities(label); + this.tokens = buildTokenArray(this.str); + this.draw( null, font, 0f, 0f, false ); + } + + /** + * returns the current alignment, by default LEFT_ALIGNMENT. + * @return the current alignment. + */ + public int getAlignment() { + return alignment; + } + + /** + * set the alignment for rendering, one of LEFT_ALIGNMENT CENTER_ALIGNMENT or RIGHT_ALIGNMENT. + * @param a the alignment, one of LEFT_ALIGNMENT CENTER_ALIGNMENT or RIGHT_ALIGNMENT. + */ + public void setAlignment(int a) { + if (a != LEFT_ALIGNMENT && a != CENTER_ALIGNMENT && a != RIGHT_ALIGNMENT) { + throw new IllegalArgumentException(); + } + alignment = a; + } + + /** + * draw the current string. Note the first line will be above iy, and following lines will + * be below iy. This is to be consistent with Graphics2D.drawString. + * + * @param ig Graphic object to use to render the text. + * @param ix The x position of the first character of text. + * @param iy The y position of the baseline of the first line of text. + */ + public void draw( Graphics ig, float ix, float iy ) { + this.draw( ig, ig.getFont(), ix, iy, true); + } + + + /** + * Draws the given string and/or computes its bounds. + * + * This method is intended to be called by both {@link #drawString(Graphics,String,float,float)} + * and {@link #getBounds(String,float,float,Component)}. + * + * All added string rendering capabilities should be handled here so that any changes + * will be incorporated into both the rendering algorithm and the bounds finding + * algorithm at the same time. + * + * @param ig Graphic object to use to render the text. This can be null if + * draw is false. + * @param ix The x position of the first character of text. + * @param iy The y position of the baseline of the first line of text. + * @param c The Component that the String will be rendered in. + * This can be null if draw is true + * @param draw A boolean flag indicating whether or not the drawing code should be executed. + * @throws NullPointerException if ig is null AND draw is true. + * @throws NullPointerException if c is null AND draw is false. + */ + private void draw(Graphics ig, Font baseFont, float ix, float iy, boolean draw ) { + Graphics2D g = null; + Rectangle bounds = null; + + if (draw) { + g = (Graphics2D)ig.create(); +// RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, +// RenderingHints.VALUE_ANTIALIAS_ON); +// hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); +// g.setRenderingHints(hints); + } + + final int NONE = 0; + final int SUB_U = 1; + final int SUB_D = 2; + final int SUB_L = 3; + final int EXP = 4; + final int IND = 5; + + final int LOWCAPS= 10; // not in IDL's set + final int SUB_A = 11; + final int SUB_B = 12; + + class TextPosition { + public TextPosition(int sub, int ei, float x, float y) { + this.sub = sub; this.ei = ei; this.x = x; this.y = y; } + public TextPosition(TextPosition p) { + copy(p); } + public void copy(TextPosition p) { + sub = p.sub; ei = p.ei; x = p.x; y = p.y; } + public int sub; + public int ei; + public float x; + public float y; + } + + if ( ig==null ) { + ig= getHeadlessGraphicsContext(); + } + + if ( baseFont==null ) { + baseFont= Font.decode("sans-10"); + } + + int lineNum=1; + + TextPosition current = new TextPosition(NONE, NONE, ix, iy); + if (draw) { + if (alignment == CENTER_ALIGNMENT) { + current.x += (getWidth() - getLineOneWidth()) / 2.0; + } else if (alignment == RIGHT_ALIGNMENT) { + current.x += (getWidth() - getLineOneWidth()); + } + } + + if (!draw) { + bounds= new Rectangle((int)ix,(int)iy,0,0); + } + + Stack saveStack = new Stack(); + + for (int i = 0; i < tokens.length; i++) { + if (tokens[i].charAt(0) == '!') { + switch (tokens[i].charAt(1)) { + case 'A': + case 'a': + current.sub= SUB_A; + current.ei = NONE; + break; + case 'B': + case 'b': + current.sub= SUB_B; + current.ei = NONE; + break; + case 'C': + case 'c': + lineNum++; + current.sub = NONE; + current.ei = NONE; + current.x = ix; + current.y += baseFont.getSize2D(); + if (draw) { + g.setFont(baseFont); + if (alignment == CENTER_ALIGNMENT) { + current.x += (getWidth() - getLineWidth(lineNum)) / 2.0; + } else if (alignment == RIGHT_ALIGNMENT) { + current.x += (getWidth() - getLineWidth(lineNum)); + } + } + saveStack.clear(); + if (!draw) { + lineBounds.add(bounds); + bounds = new Rectangle((int)current.x, (int)current.y, 0, 0); + } + break; + case 'U': + case 'u': + current.sub = SUB_U; + current.ei = NONE; + break; + case 'D': + case 'd': + current.sub = SUB_D; + current.ei = NONE; + break; + case 'L': + case 'l': + current.sub = SUB_L; + current.ei = NONE; + break; + case 'K': + case 'k': + current.ei = LOWCAPS; + break; + case 'E': + case 'e': + current.ei = EXP; + break; + case 'I': + case 'i': + current.ei = IND; + break; + case 'S': + case 's': + saveStack.push(new TextPosition(current)); + break; + case 'R': + case 'r': + if (saveStack.peek() == null) return; + current.copy((TextPosition)saveStack.pop()); + break; + case 'N': + case 'n': + current.sub = NONE; + current.ei = NONE; + break; + case '!': + break; + default:break; + } + } else { + Font font = baseFont; + float size = baseFont.getSize2D(); + float y = current.y; + switch (current.sub) { + case SUB_U: + font = baseFont.deriveFont(size * 0.62f); + y = y - 0.38f * size; + size = size * 0.62f; + break; + case SUB_D: + font = baseFont.deriveFont(size * 0.62f); + y = y + 0.31f * size; + size = size * 0.62f; + break; + case SUB_L: + font = baseFont.deriveFont(size * 0.62f); + y = y + 0.62f * size; + size = size * 0.62f; + break; + case SUB_A: + y= current.y - size/2; + break; + case SUB_B: + y= current.y + size/2; + break; + + default:break; + } + switch (current.ei) { + case EXP: + font = font.deriveFont(size * 0.44f); + y = y - 0.56f * size; + break; + case IND: + font = font.deriveFont(size * 0.44f); + y = y + 0.22f * size; + break; + case LOWCAPS: + font= font.deriveFont(size * 0.80f ); + break; + default:break; + } + if (draw) { + g.setFont(font); + g.drawString(tokens[i], current.x, y); + current.x += g.getFontMetrics(font).stringWidth(tokens[i]); + //bounds.translate((int)ix,(int)iy); + //g.draw(bounds); //useful for debugging + //g.drawLine((int)ix,(int)iy,(int)ix+4,(int)iy); + } else { + FontMetrics fm= ig.getFontMetrics(font); + bounds.add(current.x, y+fm.getDescent()); + bounds.add(current.x+fm.stringWidth(tokens[i]),y-fm.getAscent() ); // removed -5.0 pixels + current.x += ig.getFontMetrics(font).stringWidth(tokens[i]); + } + } + } // for (int i = 0; i < tokens.length; i++) + if (!draw) { + lineBounds.add(bounds); + } + } + + private static String[] buildTokenArray(String str) { + Vector vector = new Vector(); + int begin; + int end = 0; + int index = 0; + while(end < str.length()) { + begin = end; + if (str.charAt(begin) == '!') { + end = begin + 2; + } else { + end = str.indexOf('!', begin); + if (end == -1) end = str.length(); + } + vector.add(str.substring(begin, end)); + } + + String[] list = new String[vector.size()]; + + vector.copyInto((Object[])list); + + return list; + } + + public String toString() { + maybeInitBounds(); + StringBuffer buffer = new StringBuffer(getClass().getName()); + buffer.append(": ").append(str).append(", "); + buffer.append("bounds: "+bounds).append(", ").append("lineBounds:"+lineBounds).append(", "); + return buffer.toString(); + } + + /** + * useful for debugging. + */ + private void drawBounds(Graphics g, int ix, int iy) { + g.setColor(Color.BLUE); + g.drawRect(bounds.x + ix, bounds.y + iy, bounds.width, bounds.height); + g.setColor(Color.GREEN); + for (java.util.Iterator i = lineBounds.iterator(); i.hasNext();) { + Rectangle rc = (Rectangle)i.next(); + g.drawRect(rc.x + ix, rc.y + iy, rc.width, rc.height); + } + } + + private static Graphics headlessGraphics=null; + private static synchronized Graphics getHeadlessGraphicsContext() { + if ( headlessGraphics==null ) { + headlessGraphics= new BufferedImage(10,10,BufferedImage.TYPE_INT_ARGB).getGraphics(); + } + return headlessGraphics; + } + +} diff --git a/dasCore/src/main/java/org/das2/util/GraphicalLogHandler.java b/dasCore/src/main/java/org/das2/util/GraphicalLogHandler.java new file mode 100644 index 000000000..b1352b324 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/GraphicalLogHandler.java @@ -0,0 +1,502 @@ +/* + * GraphicalLogFormatter.java + * + * Created on December 8, 2005, 2:32 PM + * + * + */ + +package org.das2.util; +import org.das2.util.GrannyTextRenderer; +import org.das2.util.ObjectLocator; +import org.das2.util.DenseConsoleFormatter; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.DasApplication; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.event.BoxRenderer; +import org.das2.event.BoxSelectionEvent; +import org.das2.event.BoxSelectionListener; +import org.das2.event.BoxSelectorMouseModule; +import org.das2.event.LabelDragRenderer; +import org.das2.event.MouseModule; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasColumn; +import org.das2.graph.DasPlot; +import org.das2.graph.DasRow; +import org.das2.graph.Legend; +import org.das2.graph.Renderer; +import org.das2.system.DasLogger; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + + + + + +/** + * + * @author Jeremy + */ +public class GraphicalLogHandler extends Handler { + + List records= new ArrayList(); + List yAxisValues= new ArrayList(); + List times= new ArrayList(); + + Renderer renderer; + boolean updating= false; + Thread updateThread; + + long time0; + + HashMap loggerMap= new HashMap(); + HashMap yaxisMap= new HashMap(); + + private final int YAXIS_THREAD = -199; + private final int YAXIS_CLASS = -198; + //private final int yaxisDimension = YAXIS_THREAD; + private final int yaxisDimension = YAXIS_CLASS; + + DasAxis xaxis; + Legend legend; + + JFrame frame; + + // this is to avoid initialization failures + long sleepInitiallyTime= 2000; // milliseconds + + public GraphicalLogHandler() { + time0= System.currentTimeMillis(); + } + + private void createCanvas() { + if ( loggerMap.size()==0 ) { + loggerMap.put( DasLogger.getLogger(DasLogger.APPLICATION_LOG).getName(), Color.black ); + loggerMap.put( DasLogger.getLogger(DasLogger.DATA_OPERATIONS_LOG).getName(), Color.blue ); + loggerMap.put( DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG).getName(), Color.YELLOW ); + loggerMap.put( DasLogger.getLogger(DasLogger.GRAPHICS_LOG ).getName(), Color.PINK ); + loggerMap.put( DasLogger.getLogger(DasLogger.SYSTEM_LOG ).getName(), Color.gray ); + loggerMap.put( DasLogger.getLogger(DasLogger.GUI_LOG ).getName(), Color.green ); + loggerMap.put( DasLogger.getLogger(DasLogger.DASML_LOG).getName(), Color.LIGHT_GRAY ); + } + + DasCanvas canvas= new DasCanvas(800,400); + DasPlot plot= DasPlot.createPlot( new DatumRange( 0, 10, Units.seconds ) , + new DatumRange( 0, 10, Units.dimensionless ) ); + xaxis= plot.getXAxis(); + xaxis.setAnimated(false); + + renderer.setDataSetLoader(null); + plot.addRenderer( renderer ); + + canvas.add( plot, DasRow.create( canvas ), DasColumn.create(canvas) ); + + MouseModule mm= getMouseModule(); + plot.getDasMouseInputAdapter().addMouseModule( mm ); + plot.getDasMouseInputAdapter().setPrimaryModule( mm ); + + mm= getShowLogMouseModule( plot ); + plot.getDasMouseInputAdapter().addMouseModule( mm ); + plot.getDasMouseInputAdapter().setSecondaryModule( mm ); + + legend= new Legend(); + canvas.add( legend, new DasRow( canvas, 0.1, 0.5 ), new DasColumn( canvas, 0.8, 0.98 ) ); + + for ( Iterator i= loggerMap.keySet().iterator(); i.hasNext(); ) { + Object key= i.next(); + String name= String.valueOf(key); + if ( name.equals("") ) name=""; + legend.add( legend.getIcon( (Color)loggerMap.get(key) ), name ); + } + + frame= DasApplication.getDefaultApplication().createMainFrame( "GraphicalLogHandler" ); + JPanel appPanel= new JPanel( new BorderLayout() ); + appPanel.add( canvas, BorderLayout.CENTER ); + + JPanel controlPanel= new JPanel(); + controlPanel.setLayout( new BoxLayout( controlPanel, BoxLayout.X_AXIS ) ); + + JCheckBox jcb= new JCheckBox( getUpdatingAction() ); + jcb.setSelected(updating); + + startUpdateThread(); + + controlPanel.add( jcb ); + + JButton x= new JButton( getUpdateAction() ); + controlPanel.add( x ); + + appPanel.add( controlPanel, BorderLayout.SOUTH ); + + + + frame.getContentPane().add( appPanel ); + frame.setVisible( true ); + frame.pack(); + frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); + } + + private Action getUpdatingAction() { + return new AbstractAction( "Updating" ) { + public void actionPerformed( ActionEvent e ) { + JCheckBox source= (JCheckBox)e.getSource(); + updating= source.isSelected(); + if ( updating ) startUpdateThread(); + } + }; + } + + private Action getUpdateAction() { + return new AbstractAction( "Update" ) { + public void actionPerformed( ActionEvent e ) { + update(); + } + }; + } + + private void update() { + long endMillis= System.currentTimeMillis() - time0 + 2000; + if ( endMillis < 10000 ) endMillis= 10000; + Datum end= Units.seconds.createDatum( endMillis/1000. ); + DatumRange range= new DatumRange( end.subtract( xaxis.getDatumRange().width() ), end ); + xaxis.setDatumRange( range ); + } + + private void startUpdateThread() { + if ( updateThread==null ) { + updateThread= new Thread( new Runnable() { + public void run() { + while ( true ) { + try { Thread.sleep(500); } catch ( InterruptedException e ) { } + if ( updating ) update(); + } + } + }, "graphicalHandlerUpdateThread" ); + updateThread.start(); + } + } + + private boolean checkMyMessages( StackTraceElement[] st ) { + String myName= this.getClass().getName(); + boolean result= false; + for ( int i=1; i-1 ) result=true; + } + return result; + } + + public void publish( LogRecord rec ) { + StackTraceElement[] st= new Throwable().getStackTrace(); + + if ( checkMyMessages(st) ) return; + if ( Thread.currentThread().getName().equals( "graphicalHandlerUpdateThread" ) ) return; + + if ( renderer==null && + ( System.currentTimeMillis() - this.time0 ) > sleepInitiallyTime ) getRenderer(); + + String yAxisName; + if ( yaxisDimension==YAXIS_THREAD ) { + yAxisName= Thread.currentThread().getName() ; + } else if ( yaxisDimension==YAXIS_CLASS ) { + yAxisName= rec.getSourceClassName(); + } + + Integer yValue= (Integer)yaxisMap.get( yAxisName ); + if ( yValue==null ) { + yValue= new Integer( yaxisMap.size() ); + yaxisMap.put( yAxisName, yValue ); + } + synchronized (this) { + Long time= new Long( rec.getMillis() - time0 ) ; + int index= Collections.binarySearch(times, time ); + if ( index<0 ) { + index= -1-index; + } else { + int fudge=0; + while ( index>=0 ) { + fudge++; + time= new Long( rec.getMillis() - time0 + fudge ) ; + index= Collections.binarySearch(times, time ); + } + index= -1-index; + } + records.add( index, rec ); + yAxisValues.add( index, yValue ); + times.add( index, time ); + + } + + // consider how to not record it's own messages + } + + public void flush() { + if ( renderer==null ) getRenderer(); + renderer.update(); + } + + public void close() { + } + + ObjectLocator objectLocator; + public class LogRenderer extends Renderer { + String searchRegex=""; + + public String getSearchRegex() { + return searchRegex; + } + public void setSearchRegex( String regex ) { + this.searchRegex= regex; + update(); + } + + public synchronized void render(Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { + + Graphics2D g= (Graphics2D)g1; + g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); + + int ix0= (int) xAxis.transform( xAxis.getDataMinimum() ); + g.setColor( Color.lightGray ); + for ( Iterator iterator= yaxisMap.keySet().iterator(); iterator.hasNext(); ) { + Object name= iterator.next(); + Integer ithread= (Integer)yaxisMap.get(name); + int iy= (int)yAxis.transform( Units.dimensionless.createDatum(ithread.intValue()) ); + g.drawString( ""+name, ix0+2, iy ); + } + + synchronized(GraphicalLogHandler.this) { + + objectLocator= new ObjectLocator(); + + long minMilli= (long)xAxis.getDataMinimum().doubleValue( Units.milliseconds ); + long maxMilli= (long)xAxis.getDataMaximum().doubleValue( Units.milliseconds ); + + int firstIndex= Collections.binarySearch( times, new Long( minMilli ) ); + if ( firstIndex<0 ) firstIndex= -1 - firstIndex; + int lastIndex= Collections.binarySearch( times, new Long( maxMilli ) ); + if ( lastIndex<0 ) { + lastIndex= -1 - lastIndex; + } else { + lastIndex++; + } + + int lastX=-999; + int lastY=-999; + int collisionCount=0; + + if ( !searchRegex.equals("") ) { + for ( int i=firstIndex; i 0 ) { + return new Rectangle[] { dirtyBounds[0], myDirtyBounds[0], myDirtyBounds[1] } ; + } else { + return new Rectangle[] { myDirtyBounds[0], myDirtyBounds[1] } ; + } + } + + } + + + public MouseModule getMouseModule( ) { + DasPlot parent= renderer.getParent(); + LabelDragRenderer dr= new LookupDragRenderer( parent ); + MouseModule mouseModule= new MouseModule( parent, dr, "DataSetMonitor" ); + return mouseModule; + } + + public MouseModule getShowLogMouseModule( DasPlot plot2 ) { + BoxSelectorMouseModule result= new BoxSelectorMouseModule( plot2, plot2.getXAxis(), plot2.getYAxis(), + plot2.getRenderer(0), new BoxRenderer( plot2 ), "View Messages" ); + result.setDragEvents( false ); + result.setReleaseEvents( true ); + result.addBoxSelectionListener( new BoxSelectionListener() { + BoxSelectionListener l; + public void BoxSelected( BoxSelectionEvent e ) { + StringBuffer buf= new StringBuffer(1000); + + //Handler h= new ConsoleHandler(); + //Formatter f= h.getFormatter(); + Formatter f= new DenseConsoleFormatter(); + + ArrayList rec= new ArrayList(); + DatumRange threadsRange= e.getYRange(); + DatumRange timeRange= e.getXRange(); + + int messageCount=0; + for ( int i=0; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import java.util.StringTokenizer; +import java.util.Vector; +/** + * + * @author jbf + */ +public class IDLParser { + + public static int EXPR=1; + public static int EXPR_LIST=2; + public static int FACTOR=3; + public static int TERM=4; + public static int NUMBER=5; + public static int NOPARSER=6; + + /** Creates a new instance of idlParser */ + public IDLParser() { + } + + public String[] IDLTokenizer(String s) { + String delimiters=" \t[,]()^*/+-"; + StringTokenizer st = new StringTokenizer(s, delimiters, true); + int countTokens=st.countTokens(); + String[] tokens=new String[countTokens]; + for (int i=0; i 0) + { + String[] temp = new String[tokens.length-nullcount]; + int tIndex = 0; + for (int i = 0; i < tokens.length; i++) + { + if (tokens[i] == null) continue; + temp[tIndex] = tokens[i]; + tIndex++; + } + tokens = temp; + } + return tokens; + } + + public double parseIDLScalar(String s) { + String[] tokens= IDLTokenizer(s); + IDLValue expr= parseIDLArrayTokens(tokens,EXPR); + if (expr == null) return Double.NaN; + else return expr.toScalar(); + } + public double[] parseIDLArray(String s) { + String[] tokens= IDLTokenizer(s); + IDLValue expr= parseIDLArrayTokens(tokens,EXPR); + if (expr == null) return null; + else return expr.toArray(); + } + + private IDLValue parseIDLExprList(String[] tokens) { + int ileft= 0; + int nleft= 0; + int iright=0; + int itok; + + Vector exprs= new Vector(); + + ileft=1; + for (itok=1; itok + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import org.das2.util.DasMath; + +public class IDLValue { + + static int SCALAR = 1; + static int ARRAY = 2; + + protected double[] aValue; + + protected double sValue; + + protected int type; + + IDLValue() { + } + + public IDLValue(double a) { + sValue= a; + type= SCALAR; + } + + public IDLValue(double[] a) { + aValue= a; + type= ARRAY; + } + + public IDLValue IDLmultiply(IDLValue b) { + IDLValue result= new IDLValue(); + if ((type==SCALAR) && (b.type==SCALAR)) { + result.type=SCALAR; + result.sValue=sValue*b.sValue; + } else if ((type==ARRAY) && (b.type==SCALAR)) { + result.type=ARRAY; + result.aValue= new double[aValue.length]; + for (int i=0; i= 0 ? value : value + 256); + } + + private static int fourBytesToInt(byte b[], int offset) + { + int value; + + value = byteToUnsigned(b[offset++]); + value |= (byteToUnsigned(b[offset++]) << 8); + value |= (byteToUnsigned(b[offset++]) << 16); + value |= (byteToUnsigned(b[offset++]) << 24); + + return(value); + } + + private static final void intToFourBytes(int iValue, byte b[], int offset) + { + b[offset++] = (byte)((iValue) & 0xff); + b[offset++] = (byte)((iValue >>> 8 ) & 0xff); + b[offset++] = (byte)((iValue >>> 16) & 0xff); + b[offset++] = (byte)((iValue >>> 24) & 0xff); + } + + private static final void PERM_OP(int a, int b, int n, int m, int results[]) + { + int t; + + t = ((a >>> n) ^ b) & m; + a ^= t << n; + b ^= t; + + results[0] = a; + results[1] = b; + } + + private static final int HPERM_OP(int a, int n, int m) + { + int t; + + t = ((a << (16 - n)) ^ a) & m; + a = a ^ t ^ (t >>> (16 - n)); + + return(a); + } + + private static int [] des_set_key(byte key[]) + { + int schedule[] = new int[ITERATIONS * 2]; + + int c = fourBytesToInt(key, 0); + int d = fourBytesToInt(key, 4); + + int results[] = new int[2]; + + PERM_OP(d, c, 4, 0x0f0f0f0f, results); + d = results[0]; c = results[1]; + + c = HPERM_OP(c, -2, 0xcccc0000); + d = HPERM_OP(d, -2, 0xcccc0000); + + PERM_OP(d, c, 1, 0x55555555, results); + d = results[0]; c = results[1]; + + PERM_OP(c, d, 8, 0x00ff00ff, results); + c = results[0]; d = results[1]; + + PERM_OP(d, c, 1, 0x55555555, results); + d = results[0]; c = results[1]; + + d = (((d & 0x000000ff) << 16) | (d & 0x0000ff00) | + ((d & 0x00ff0000) >>> 16) | ((c & 0xf0000000) >>> 4)); + c &= 0x0fffffff; + + int s, t; + int j = 0; + + for(int i = 0; i < ITERATIONS; i ++) + { + if(shifts2[i]) + { + c = (c >>> 2) | (c << 26); + d = (d >>> 2) | (d << 26); + } + else + { + c = (c >>> 1) | (c << 27); + d = (d >>> 1) | (d << 27); + } + + c &= 0x0fffffff; + d &= 0x0fffffff; + + s = skb[0][ (c ) & 0x3f ]| + skb[1][((c >>> 6) & 0x03) | ((c >>> 7) & 0x3c)]| + skb[2][((c >>> 13) & 0x0f) | ((c >>> 14) & 0x30)]| + skb[3][((c >>> 20) & 0x01) | ((c >>> 21) & 0x06) | + ((c >>> 22) & 0x38)]; + + t = skb[4][ (d ) & 0x3f ]| + skb[5][((d >>> 7) & 0x03) | ((d >>> 8) & 0x3c)]| + skb[6][ (d >>>15) & 0x3f ]| + skb[7][((d >>>21) & 0x0f) | ((d >>> 22) & 0x30)]; + + schedule[j++] = ((t << 16) | (s & 0x0000ffff)) & 0xffffffff; + s = ((s >>> 16) | (t & 0xffff0000)); + + s = (s << 4) | (s >>> 28); + schedule[j++] = s & 0xffffffff; + } + return(schedule); + } + + private static final int D_ENCRYPT + ( + int L, int R, int S, int E0, int E1, int s[] + ) + { + int t, u, v; + + v = R ^ (R >>> 16); + u = v & E0; + v = v & E1; + u = (u ^ (u << 16)) ^ R ^ s[S]; + t = (v ^ (v << 16)) ^ R ^ s[S + 1]; + t = (t >>> 4) | (t << 28); + + L ^= SPtrans[1][(t ) & 0x3f] | + SPtrans[3][(t >>> 8) & 0x3f] | + SPtrans[5][(t >>> 16) & 0x3f] | + SPtrans[7][(t >>> 24) & 0x3f] | + SPtrans[0][(u ) & 0x3f] | + SPtrans[2][(u >>> 8) & 0x3f] | + SPtrans[4][(u >>> 16) & 0x3f] | + SPtrans[6][(u >>> 24) & 0x3f]; + + return(L); + } + + private static final int [] body(int schedule[], int Eswap0, int Eswap1) + { + int left = 0; + int right = 0; + int t = 0; + + for(int j = 0; j < 25; j ++) + { + for(int i = 0; i < ITERATIONS * 2; i += 4) + { + left = D_ENCRYPT(left, right, i, Eswap0, Eswap1, schedule); + right = D_ENCRYPT(right, left, i + 2, Eswap0, Eswap1, schedule); + } + t = left; + left = right; + right = t; + } + + t = right; + + right = (left >>> 1) | (left << 31); + left = (t >>> 1) | (t << 31); + + left &= 0xffffffff; + right &= 0xffffffff; + + int results[] = new int[2]; + + PERM_OP(right, left, 1, 0x55555555, results); + right = results[0]; left = results[1]; + + PERM_OP(left, right, 8, 0x00ff00ff, results); + left = results[0]; right = results[1]; + + PERM_OP(right, left, 2, 0x33333333, results); + right = results[0]; left = results[1]; + + PERM_OP(left, right, 16, 0x0000ffff, results); + left = results[0]; right = results[1]; + + PERM_OP(right, left, 4, 0x0f0f0f0f, results); + right = results[0]; left = results[1]; + + int out[] = new int[2]; + + out[0] = left; out[1] = right; + + return(out); + } + + public static final String crypt(String salt, String original) + { + while(salt.length() < 2) + salt += "A"; + + StringBuffer buffer = new StringBuffer(" "); + + char charZero = salt.charAt(0); + char charOne = salt.charAt(1); + + buffer.setCharAt(0, charZero); + buffer.setCharAt(1, charOne); + + int Eswap0 = con_salt[(int)charZero]; + int Eswap1 = con_salt[(int)charOne] << 4; + + byte key[] = new byte[8]; + + for(int i = 0; i < key.length; i ++) + key[i] = (byte)0; + + for(int i = 0; i < key.length && i < original.length(); i ++) + { + int iChar = (int)original.charAt(i); + + key[i] = (byte)(iChar << 1); + } + + int schedule[] = des_set_key(key); + int out[] = body(schedule, Eswap0, Eswap1); + + byte b[] = new byte[9]; + + intToFourBytes(out[0], b, 0); + intToFourBytes(out[1], b, 4); + b[8] = 0; + + for(int i = 2, y = 0, u = 0x80; i < 13; i ++) + { + for(int j = 0, c = 0; j < 6; j ++) + { + c <<= 1; + + if(((int)b[y] & u) != 0) + c |= 1; + + u >>>= 1; + + if(u == 0) + { + y++; + u = 0x80; + } + buffer.setCharAt(i, (char)cov_2char[c]); + } + } + return(buffer.toString()); + } + + public static void main(String args[]) + { + if(args.length >= 2) + { + org.das2.util.DasDie.println + ( + "[" + args[0] + "] [" + args[1] + "] => [" + + JCrypt.crypt(args[0], args[1]) + "]" + ); + } else { + org.das2.util.DasDie.println("java crypt "); + } + } +} diff --git a/dasCore/src/main/java/org/das2/util/MemoryPreferences.java b/dasCore/src/main/java/org/das2/util/MemoryPreferences.java new file mode 100644 index 000000000..aeb624205 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/MemoryPreferences.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2008, SQL Power Group Inc. + * + * This file is part of SQL Power Library. + * + * SQL Power Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * SQL Power 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Trivial Modifications at the University of Iowa. 2012. + */ +package org.das2.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.prefs.AbstractPreferences; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +/** + * A java.util.prefs.Preferences that does NOT persist anything, so it has no + * effect on (nor is affected by!) any use of the "regular" Preferences. + *

    + * To use, run with Java command-line option + * -Djava.util.prefs.PreferencesFactory=org.das2.util.PreferencesFactory + */ +public class MemoryPreferences extends AbstractPreferences{ + + private static final Logger logger = Logger.getLogger(MemoryPreferences.class.getName()); + /** + * The map of all data in this particular node. + */ + final Map values = new HashMap(); + /** The map of all Preferences nodes immediately below this node + */ + final Map children = new HashMap(); + public final static String SYSTEM_PROPS_ERROR_MESSAGE = + "Did you remember to run with -D" + + MemoryPreferencesFactory.PREFS_FACTORY_SYSTEM_PROPERTY + + "=" + MemoryPreferencesFactory.MY_CLASS_NAME + "?"; + + /** + * Constructor, non-public, only for use by my PrefencesFactory; should only be called from + * the PreferencesFactory and from node() below; node() takes care of finding the full path + * if the incoming path is relative. + * @param fullPath + */ + MemoryPreferences(AbstractPreferences parent, String name){ + super(parent, name); + + // note, logger should never be null because it's statically initialised. However, + // it comes out null every time we run the MatchMaker SwingSessionContextTest! Hmm... + if(logger != null && logger.isLoggable(Level.FINE)){ + logger.fine(String.format("MemoryPreferences.MemoryPreferences(%s, %s)", parent, name)); + } + } + + @Override + protected void putSpi(String key, String value){ + values.put(key, value); + } + + @Override + protected String getSpi(String key){ + String value = values.get(key); + logger.fine(String.format("get: %s=%s", key, value)); + return value; + } + + @Override + protected void removeSpi(String key){ + values.remove(key); + } + + @Override + protected void removeNodeSpi() throws BackingStoreException{ + // nothing to do here? + } + + @Override + protected String[] keysSpi() throws BackingStoreException{ + return values.keySet().toArray(new String[values.size()]); + } + + @Override + protected String[] childrenNamesSpi() throws BackingStoreException{ + return children.keySet().toArray(new String[children.size()]); + } + + @Override + protected AbstractPreferences childSpi(String name){ + logger.fine(String.format("MemoryPreferences.node(%s)", name)); + AbstractPreferences n = new MemoryPreferences(this, name); + children.put(name, n); + return n; + } + + @Override + protected void syncSpi() throws BackingStoreException{ + // nothing to do + } + + @Override + protected void flushSpi() throws BackingStoreException{ + // nothing to do + } +} diff --git a/dasCore/src/main/java/org/das2/util/MemoryPreferencesFactory.java b/dasCore/src/main/java/org/das2/util/MemoryPreferencesFactory.java new file mode 100644 index 000000000..5b8687f45 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/MemoryPreferencesFactory.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2008, SQL Power Group Inc. + * + * This file is part of SQL Power Library. + * + * SQL Power Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * SQL Power 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.das2.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; +import java.util.prefs.Preferences; +import java.util.prefs.PreferencesFactory; + +/** + * A java.util.prefs.PreferencesFactory that lets us use the MemoryPreferences + * so that tests will not affect (nor be affected by) preferences previously created + * by the user running the tests. + */ +public class MemoryPreferencesFactory implements PreferencesFactory{ + + public static final String PREFS_FACTORY_SYSTEM_PROPERTY = "java.util.prefs.PreferencesFactory"; + public static final String MY_CLASS_NAME = "org.das2.util.PreferencesFactory"; + private static final Logger logger = Logger.getLogger(MemoryPreferencesFactory.class.getName()); + final static Map systemNodes = new HashMap(); + final Map userNodes = new HashMap(); + /** + * There is always only one System Root node + */ + final MemoryPreferences systemRoot = new MemoryPreferences(null, ""); + + public Preferences systemRoot(){ + logger.fine("PreferencesFactory.systemRoot()"); + return systemRoot; + } + /** + * In this implementation there is only one UserRoot, because this + * implementation is only used for in-memory testing. + */ + final MemoryPreferences userRoot = new MemoryPreferences(null, ""); + + public Preferences userRoot(){ + logger.fine("PreferencesFactory.userRoot()"); + return userRoot; + } +} diff --git a/dasCoreUtil/src/org/das2/util/MessageBox.java b/dasCore/src/main/java/org/das2/util/MessageBox.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/MessageBox.java rename to dasCore/src/main/java/org/das2/util/MessageBox.java diff --git a/dasCore/src/main/java/org/das2/util/NBConsoleFormatter.java b/dasCore/src/main/java/org/das2/util/NBConsoleFormatter.java new file mode 100644 index 000000000..532a52e3f --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/NBConsoleFormatter.java @@ -0,0 +1,42 @@ +/* + * NBConsoleFormatter.java + * + * Created on April 7, 2005, 12:13 PM + */ + +package org.das2.util; + +import java.util.logging.Formatter; +import java.util.logging.LogRecord; + +/** + * + * @author Jeremy + */ +public class NBConsoleFormatter extends Formatter { + boolean coalesce=true; + String lastMessage=null; + int coalesceHits=0; + + public String format( LogRecord rec ) { + if ( coalesce && lastMessage!=null && lastMessage.equals(rec.getMessage()) ) { + coalesceHits++; + return ""; + } else { + StackTraceElement[] st= new Throwable().getStackTrace(); + String result= rec.getLoggerName()+" ["+Thread.currentThread().getName()+"]\n"+rec.getLevel().getLocalizedName()+": "+rec.getMessage() + +"\n\tat "+st[7] + +( st.length>8 ? "\n\tat "+st[8]+"\n" : "\n" ); + if ( coalesceHits>0 ) { + result= "(Last message repeats "+(coalesceHits+1)+" times)\n"+result; + } + coalesceHits= 0; + lastMessage= rec.getMessage(); + return result; + } + } + + public NBConsoleFormatter() { + } + +} diff --git a/dasCoreUtil/src/org/das2/util/NumberFormatUtil.java b/dasCore/src/main/java/org/das2/util/NumberFormatUtil.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/NumberFormatUtil.java rename to dasCore/src/main/java/org/das2/util/NumberFormatUtil.java diff --git a/dasCoreUtil/src/org/das2/util/ObjectLocator.java b/dasCore/src/main/java/org/das2/util/ObjectLocator.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/ObjectLocator.java rename to dasCore/src/main/java/org/das2/util/ObjectLocator.java diff --git a/dasCore/src/main/java/org/das2/util/PersistentStateSupport.java b/dasCore/src/main/java/org/das2/util/PersistentStateSupport.java new file mode 100644 index 000000000..aa11fcc1e --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/PersistentStateSupport.java @@ -0,0 +1,520 @@ +/* + * PersistentStateSupport.java + * + * Created on April 20, 2006, 1:23 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.util; + +import org.das2.util.monitor.ProgressMonitor; +import org.das2.components.DasProgressPanel; +import org.das2.dasml.SerializeUtil; +import org.das2.dasml.DOMBuilder; +import org.das2.graph.DasCanvas; +import org.das2.util.filesystem.Glob; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.prefs.Preferences; +import java.util.regex.Pattern; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileFilter; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +//import org.apache.xml.serialize.OutputFormat; +//import org.apache.xml.serialize.XMLSerializer; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * + * @author Jeremy + */ +public class PersistentStateSupport { + + String ext; + private File currentFile; + JMenu openRecentMenu; + SerializationStrategy strategy; + Component component; + + private JMenuItem saveMenuItem; + private JLabel currentFileLabel; + private List recentFiles; + + /** state has been modified and needs to be saved */ + private boolean dirty; + + public static final String PROPERTY_OPENING="opening"; + public static final String PROPERTY_SAVING="saving"; + public static final String PROPERTY_DIRTY="dirty"; + public static final String PROPERTY_CURRENT_FILE="currentFile"; + + public interface SerializationStrategy { + // give me a document to serialize + public Element serialize( Document document, ProgressMonitor monitor ) throws IOException; + + // here's a document you gave me + public void deserialize( Document doc, ProgressMonitor monitor ); + } + + private static SerializationStrategy getCanvasStrategy( final DasCanvas canvas ) { + return new SerializationStrategy() { + public Element serialize(Document document, ProgressMonitor monitor) { + DOMBuilder builder= new DOMBuilder( canvas ); + Element element= builder.serialize( document, DasProgressPanel.createFramed("Serializing Canvas") ); + return element; + } + + public void deserialize(Document document, ProgressMonitor monitor) { + Element element= document.getDocumentElement(); + SerializeUtil.processElement(element,canvas ); + } + }; + } + + + /** + * Provides a means for saving the application persistently, undo/redo support (TODO). + * canvas is the canvas to be serialized, extension identifies the application. Note that + * internal changes to das may break saved files. + */ + public PersistentStateSupport( DasCanvas canvas, String extension ) { + this( canvas, getCanvasStrategy( canvas ), extension ); + + } + + private void refreshRecentFilesMenu() { + if ( openRecentMenu!=null ) { + openRecentMenu.removeAll(); + for ( int i=0; i7) { + recentFiles.remove(7); + } + Preferences prefs= Preferences.userNodeForPackage(PersistentStateSupport.class); + prefs.put( "PersistentStateSupport"+ext+"_recent", getRencentFilesString() ); + refreshRecentFilesMenu(); + } + + public Action createOpenAction() { + return new AbstractAction("Open...") { + public void actionPerformed( ActionEvent ev ) { + try { + JFileChooser chooser = new JFileChooser(); + if ( getCurrentFile()!=null ) chooser.setCurrentDirectory(getCurrentFile().getParentFile()); + chooser.setFileFilter( simpleFilter( "*"+ext ) ); + int result = chooser.showOpenDialog(component); + if (result == JFileChooser.APPROVE_OPTION) { + open( chooser.getSelectedFile() ); + addToRecent(getCurrentFile()); + if ( saveMenuItem!=null ) saveMenuItem.setText("Save"); + } + } catch ( Exception e ) { + throw new RuntimeException(e); + } + } + }; + } + + /** + * override me. If open fails, throw an exception. + */ + protected void openImpl( File file ) throws Exception { + Document document= readDocument( file ); + strategy.deserialize( document, DasProgressPanel.createFramed("deserializing") ); + } + + private void open( final File file ) { + setOpening( true ); + Runnable run = new Runnable() { + public void run() { + try { + if ( !file.exists() ) { + JOptionPane.showMessageDialog(component,"File not found: "+file, "File not found", JOptionPane.WARNING_MESSAGE ); + return; + } + openImpl(file); + setOpening( false ); + setDirty(false); + setCurrentFile(file); + setCurrentFileOpened(true); + update(); + } catch ( IOException e ) { + throw new RuntimeException(e); + } catch ( ParserConfigurationException e ) { + throw new RuntimeException(e); + } catch ( SAXException e ) { + throw new RuntimeException(e); + } catch ( Exception e ) { + throw new RuntimeException(e); + } + } + }; + new Thread( run, "PersistentStateSupport.open" ).start(); + } + + /** + * @deprecated. What is the purpose of this method? + */ + public void close() { + setCurrentFile(null); + } + + public void markDirty() { + this.setDirty( true ); + update(); + } + + private void update() { + if ( currentFileLabel!=null ) this.currentFileLabel.setText( getCurrentFile() + ( dirty ? " *" : "" ) ); + } + /** Creates a new instance of PersistentStateSupport */ + public PersistentStateSupport() { + } + + /** + * Utility field used by bound properties. + */ + private java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); + + /** + * Adds a PropertyChangeListener to the listener list. + * @param l The listener to add. + */ + public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.addPropertyChangeListener(l); + } + + /** + * Removes a PropertyChangeListener from the listener list. + * @param l The listener to remove. + */ + public void removePropertyChangeListener(java.beans.PropertyChangeListener l) { + propertyChangeSupport.removePropertyChangeListener(l); + } + + /** + * Getter for property dirty. + * @return Value of property dirty. + */ + public boolean isDirty() { + return this.dirty; + } + + /** + * Setter for property dirty. + * @param dirty New value of property dirty. + */ + public void setDirty(boolean dirty) { + boolean oldDirty = this.dirty; + this.dirty = dirty; + propertyChangeSupport.firePropertyChange ( PROPERTY_DIRTY, new Boolean (oldDirty), new Boolean (dirty)); + } + + public File getCurrentFile() { + return currentFile; + } + + public void setCurrentFile(File currentFile) { + File oldFile = this.currentFile; + this.currentFile = currentFile; + propertyChangeSupport.firePropertyChange ( PROPERTY_CURRENT_FILE, oldFile, currentFile ); + } + + /** + * Holds value of property loading. + */ + private boolean opening; + + /** + * Property loading is true when a load operation is being performed. + * @return Value of property loading. + */ + public boolean isOpening() { + return this.opening; + } + + /** + * Holds value of property saving. + */ + private boolean saving; + + /** + * Property saving is true when a save operation is being performed. + * @return Value of property saving. + */ + public boolean isSaving() { + return this.saving; + } + + private void setOpening(boolean b) { + boolean old= this.opening; + this.opening= b; + propertyChangeSupport.firePropertyChange( PROPERTY_OPENING, Boolean.valueOf(old), Boolean.valueOf(b) ); + + } + + + private void setSaving(boolean b) { + boolean old= this.saving; + this.saving= b; + propertyChangeSupport.firePropertyChange( PROPERTY_SAVING, Boolean.valueOf(old), Boolean.valueOf(b) ); + + } + + /** + * Holds value of property currentFileOpened. + */ + private boolean currentFileOpened; + + /** + * Property currentFileOpened indicates if the current file has ever been opened. This + * is to handle the initial state where the current file is set, but should not be + * displayed because it has not been opened. + * @return Value of property currentFileOpened. + */ + public boolean isCurrentFileOpened() { + return this.currentFileOpened; + } + + /** + * Setter for property currentFileOpened. + * @param currentFileOpened New value of property currentFileOpened. + */ + public void setCurrentFileOpened(boolean currentFileOpened) { + boolean oldCurrentFileOpened = this.currentFileOpened; + this.currentFileOpened = currentFileOpened; + propertyChangeSupport.firePropertyChange ("currentFileOpened", new Boolean (oldCurrentFileOpened), new Boolean (currentFileOpened)); + } + +} diff --git a/dasCore/src/main/java/org/das2/util/Probe.java b/dasCore/src/main/java/org/das2/util/Probe.java new file mode 100644 index 000000000..acafbcc8b --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/Probe.java @@ -0,0 +1,594 @@ +/* + * Probe.java + * + * Created on September 22, 2004, 1:21 PM + */ + +package org.das2.util; + +import org.das2.graph.SymbolLineRenderer; +import org.das2.graph.Leveler; +import org.das2.graph.DasColumn; +import org.das2.graph.Psym; +import org.das2.graph.DasAnnotation; +import org.das2.graph.DasCanvas; +import org.das2.graph.SymColor; +import org.das2.graph.DasRow; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; +import org.das2.graph.Legend; +import org.das2.event.DumpToFileMouseModule; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.dataset.DataSet; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.Datum; +import org.das2.datum.DatumRangeUtil; +import org.das2.DasApplication; +import org.das2.system.DasLogger; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; + +/** + * + * @author Jeremy + */ +public class Probe { + + final long refreshRateMillis=1000; + + HashMap histograms; + + HashMap agents; + + boolean ignoring=false; + String triggerName=null; + double triggerMin; + double triggerMax; + + //DasPlot plot; + Legend legend; + Leveler leveler; + DasCanvas canvas; + DasAnnotation titleAnnotation; + + DatumRange xrange; + DatumRange yrange; + + DasColumn column; + DasAxis xAxis; + + boolean isWidgetCreated; + JFrame frame; + + String title; + + boolean isNull; + + long t0millis; + boolean needUpdate=false; + boolean updating=true; + + /** + * Holds value of property xsize. + */ + private int xsize; + + /** + * Holds value of property ysize. + */ + private int ysize; + + /* agent has responsibility of conveying info */ + private class Agent { + VectorDataSetBuilder builder; + SymbolLineRenderer renderer; + DasPlot plot; + DatumRange xrange, yrange; + boolean yrangeSet= false; + boolean histogram= false; + + int hits; + Legend legend; + String name; + + Agent( String name, int index, Agent underplotAgent ) { + builder= new VectorDataSetBuilder(Units.dimensionless,Units.dimensionless); + renderer= new SymbolLineRenderer(); + this.name= name; + xrange= new DatumRange( Datum.create(0), Datum.create(1) ); + yrange= DatumRangeUtil.newDimensionless(0,0.000001); + + + DasAxis thisXAxis; + if ( index==0 ) { + xAxis= new DasAxis( xrange, DasAxis.HORIZONTAL); + thisXAxis= xAxis; + } else { + thisXAxis= xAxis.createAttachedAxis(); + thisXAxis.setTickLabelsVisible(false); + } + + if ( underplotAgent==null ) { + //plot= org.das2.graph.Util.newDasPlot(canvas, xrange, yrange ); + plot= new DasPlot( thisXAxis, new DasAxis( yrange, DasAxis.VERTICAL ) ); + plot.addMouseModule( new DumpToFileMouseModule( plot, renderer, thisXAxis, plot.getYAxis() ) ); + } else { + plot= underplotAgent.plot; + } + + renderer.setPsym(Psym.NONE); + // renderer.setPsymConnector(PsymConnector.NONE); + //renderer.setSymSize(2.0); + + final SymColor[] symColors= { SymColor.black, SymColor.blue, SymColor.lightRed, SymColor.red, SymColor.darkGreen, SymColor.gray }; + renderer.setColor(symColors[index%symColors.length]); + + plot.getXAxis().setAnimated(true); + plot.getYAxis().setAnimated(true); + plot.getYAxis().setLabel(name); + plot.addRenderer(renderer); + + if ( underplotAgent==null ) { + DasRow row= leveler.addRow(0.); + canvas.add( plot, row, column ); + } else { + underplotAgent.addToLegend(this); + } + + hits= 0; + } + + void addToLegend( Agent overplotAgent ) { + if ( legend==null ) { + DasCanvas c= plot.getCanvas(); + legend= new Legend(); + legend.add( renderer, name ); + c.add( legend, plot.getRow().createAttachedRow(0.2,0.95), plot.getColumn().createAttachedColumn(0.7,0.99) ); + } + legend.add( overplotAgent.renderer, overplotAgent.name ); + } + + void add( double value ) { + double xvalue= ++hits; + synchronized( builder ) { + builder.insertY(xvalue,value); + } + if ( !xrange.contains( Units.dimensionless.createDatum(xvalue)) ) { + xrange= include( xrange, Units.dimensionless.createDatum(xvalue)) ; + if ( !xrange.width().isFinite() ) { + throw new IllegalStateException(); + } + } + + if ( !yrangeSet ) { + yrange= new DatumRange( value, value+1e-7, Units.dimensionless ); + yrangeSet= true; + } + if ( !yrange.contains( Units.dimensionless.createDatum(value) ) ) { + yrange= include( yrange, Units.dimensionless.createDatum(value) ); + if ( !yrange.width().isFinite() ) { + throw new IllegalStateException(); + } + } + + needUpdate=true; + } + + void add( int value ) { + histogram= true; + this.add((float)value); + } + + void update() { + DataSet ds; + synchronized( builder ) { + ds= builder.toVectorDataSet(); + } + plot.getXAxis().setDatumRange( xrange ); + plot.getYAxis().setDatumRange( yrange ); + renderer.setHistogram(histogram); + renderer.setDataSet(ds); + + } + + void destroy() { + if ( isWidgetCreated ) { + plot.removeRenderer(renderer); + } + } + + } + + public DasCanvas getCanvas() { + if ( isNull ) throw new IllegalArgumentException("getCanvas called for null canvas"); + return this.canvas; + } + + public void reset() { + if ( isNull ) return; + if ( agents==null ) agents= new HashMap(); + + for ( Iterator i=agents.keySet().iterator(); i.hasNext(); ) { + Object key= i.next(); + Agent a= (Agent)agents.get(key); + a.destroy(); + } + + agents= new HashMap(); + + t0millis= System.currentTimeMillis(); + needUpdate= true; + } + + private DatumRange include( DatumRange dr, Datum d ) { + if ( dr.contains(d) ) { + return dr; + } + + Datum w= dr.width(); + if ( w.doubleValue( w.getUnits() ) == 0. ) { + dr= dr.include(d); + return dr; + } else if ( d.lt(dr.min()) ) { + while ( !dr.contains(d) ) dr= DatumRangeUtil.rescale(dr,-1,1); + } else { + while ( !dr.contains(d) ) dr= DatumRangeUtil.rescale(dr,0,2); + } + DasLogger.getLogger( DasLogger.SYSTEM_LOG ).fine( "dr="+dr ); + return dr; + } + + /* public synchronized void add( String name, double value, String name2, double value2 ) { + // this doesn't work. + VectorDataSetBuilder builder; + org.das2.graph.CurveRenderer renderer; + if ( ! builders.containsKey(name) ) { + maybeCreateWidget(); + builder= new VectorDataSetBuilder(Units.seconds,Units.dimensionless); + builder.addPlane( name2, Units.dimensionless ); + builders.put(name, builder); + renderer= new CurveRenderer( null, "", name2 ); + + renderers.put( name, renderer ); + + plot.addRenderer(renderer); + + } else { + builder= (VectorDataSetBuilder)builders.get(name); + } + + //long c= ((Long)currentCount.get(name)).longValue(); + double seconds= ( System.currentTimeMillis() - t0millis ) / 1000.; + builder.insertY(seconds,value); + builder.insertY( seconds, value2, name2 ); + //currentCount.put(name,new Long(++c)); + + if ( !xrange.contains( Units.seconds.createDatum(seconds)) ) { + xrange= include( xrange, Units.seconds.createDatum(seconds)) ; + plot.getXAxis().setDataRange( xrange ); + } + if ( !yrange.contains( Units.dimensionless.createDatum(value) ) ) { + yrange= include( yrange, Units.dimensionless.createDatum(value) ); + plot.getYAxis().setDataRange( yrange ); + } + + needUpdate=true; + }*/ + + /* public synchronized void addHistogram( String name, double value ) { + int[] histogram; + if ( !histograms.containsKey(name) ) { + maybeCreateWidget(); + histogram= new int[50]; + histograms.put(name,histogram); + org.das2.graph.SymbolLineRenderer renderer; + renderer= new SymbolLineRenderer((DataSet)null); + renderer.setHistogram(true); + renderer.setPsym( Psym.NONE ); + renderers.put( name, renderer ); + plot.addRenderer(renderer); + legend.add( renderer, name ); + } else { + histogram= (int[])histograms.get(name); + } + int ivalue= (int)value; + if ( ivalue < 0 ) ivalue=0; + + if ( (int)value > histogram.length ) { + int[] histNew= new int[ 2*(int)value ]; + System.arraycopy(histogram, 0, histNew, 0, histogram.length ); + histogram= histNew; + histograms.put( name, histogram ); + } + histogram[ivalue]++; + + if ( !xrange.contains( Units.seconds.createDatum(histogram.length)) ) { + xrange= include( xrange, Units.seconds.createDatum(histogram.length)) ; + plot.getXAxis().setDataRange( xrange ); + } + if ( !yrange.contains( Units.dimensionless.createDatum(histogram[ivalue]) ) ) { + yrange= include( yrange, Units.dimensionless.createDatum(histogram[ivalue]) ); + plot.getYAxis().setDataRange( yrange ); + } + + needUpdate= true; + + } */ + + private void checkTrigger( String name, double value ) { + if ( this.triggerName==null ) return; + if ( this.triggerName.equals(name) ) { + boolean notIgnoring= this.triggerMin < value && value < this.triggerMax; + ignoring= !notIgnoring; + } + } + + public boolean isTriggered() { + return !ignoring; + } + + public synchronized void add( String name, int value ) { + if ( isNull ) return; + checkTrigger( name, value ); + if ( ignoring ) return; + if ( Double.isInfinite(value) ) { + throw new IllegalStateException("value is not finite: "+name); + } + VectorDataSetBuilder builder; + org.das2.graph.SymbolLineRenderer renderer; + Agent a; + if ( ! agents.containsKey(name) ) { + maybeCreateWidget(); + a= new Agent( name, agents.size(), null ); + agents.put( name, a ); + } else { + a= (Agent)agents.get(name); + } + + a.add(value); + + } + + public synchronized void add( String name, double value ) { + if ( isNull ) return; + checkTrigger( name, value ); + if ( ignoring ) return; + if ( Double.isInfinite(value) ) { + throw new IllegalStateException("value is not finite: "+name); + } + VectorDataSetBuilder builder; + Agent a; + if ( ! agents.containsKey(name) ) { + maybeCreateWidget(); + a= new Agent( name, agents.size(), null ); + agents.put( name, a ); + } else { + a= (Agent)agents.get(name); + } + + a.add(value); + } + + public synchronized void addOverplot( String overplotName, String underplotName, double value ) { + if ( isNull ) return; + checkTrigger( overplotName, value ); + if ( ignoring ) return; + if ( Double.isInfinite(value) ) { + throw new IllegalStateException("value is not finite: "+overplotName); + } + VectorDataSetBuilder builder; + Agent a; + if ( ! agents.containsKey(overplotName) ) { + maybeCreateWidget(); + Agent underplotAgent= (Agent)agents.get(underplotName); + if ( underplotAgent==null ) { + a= new Agent( overplotName, agents.size(), null ); + } else { + a= new Agent( overplotName, agents.size(), underplotAgent ); + } + agents.put( overplotName, a ); + } else { + a= (Agent)agents.get(overplotName); + } + + a.add(value); + } + + public synchronized void addOverplot( String overplotName, String underplotName, int value ) { + if ( isNull ) return; + checkTrigger( overplotName, value ); + if ( ignoring ) return; + VectorDataSetBuilder builder; + Agent a; + if ( ! agents.containsKey(overplotName) ) { + maybeCreateWidget(); + Agent underplotAgent= (Agent)agents.get(underplotName); + if ( underplotAgent==null ) { + a= new Agent( overplotName, agents.size(), null ); + } else { + a= new Agent( overplotName, agents.size(), underplotAgent ); + } + agents.put( overplotName, a ); + } else { + a= (Agent)agents.get(overplotName); + } + + a.add(value); + } + + public void setTrigger( String name, double min, double max ) { + if ( name!=this.triggerName || min!=this.triggerMin || max!=this.triggerMax ) { + this.triggerName= name; + this.triggerMin= min; + this.triggerMax= max; + this.ignoring= true; + } + } + + private void startUpdateThread() { + if ( updating ) { + new Thread( new Runnable() { + public void run() { + while ( true ) { + try { Thread.sleep(refreshRateMillis); } catch ( InterruptedException e ) { throw new RuntimeException(e); } + if ( updating & isWidgetCreated ) update(); + } + } + }, "probeUpdateThread" ).start(); + } + } + + public synchronized void update() { + if ( isNull ) return; + if ( isWidgetCreated && needUpdate ) { + for (Iterator i=agents.keySet().iterator(); i.hasNext(); ) { + Object name= i.next(); + Agent a= (Agent) agents.get(name); + a.update(); + } + } + needUpdate= false; + } + + public synchronized void pause() { + updating= false; + } + + private Action getUpdateAction() { + return new AbstractAction("Update") { + public void actionPerformed( ActionEvent e ) { + update(); + } + }; + } + + private Action getPauseAction() { + return new AbstractAction("Pause") { + public void actionPerformed( ActionEvent e ) { + pause(); + } + }; + } + + private void maybeCreateWidget() { + if ( isWidgetCreated ) { + return; + } else { + + frame= DasApplication.getDefaultApplication().createMainFrame("Das 2"); + JPanel panel= new JPanel(); + panel.setLayout( new BorderLayout()); + + canvas= new DasCanvas(xsize,ysize); + + panel.add(canvas, BorderLayout.CENTER ); + + Box box= Box.createHorizontalBox(); + box.add( new JButton( getUpdateAction() ) ); + box.add( new JButton( getPauseAction() ) ); + + panel.add( box, BorderLayout.NORTH ); + + column= new DasColumn( canvas, 0.15, 0.9 ); + + frame.setContentPane(panel); + + frame.setVisible(true); + frame.pack(); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + //xrange= new DatumRange( Datum.create(0), Datum.create(1) ); + //yrange= DatumRangeUtil.newDimensionless(0,0.000001); + + //plot= org.das2.graph.Util.newDasPlot(canvas, xrange, yrange ); + // MouseModule mm= new MouseModule( plot, new PointSlopeDragRenderer( plot, plot.getXAxis(), plot.getYAxis() ), "Point,Slope" ); + // plot.addMouseModule( mm ); + + // plot.addMouseModule( new BoxRangeSelectorMouseModule( plot, plot.getXAxis(), plot.getYAxis() ) ); + // plot.getMouseAdapter().setPrimaryModule(mm); + + // plot.getMouseAdapter().addMouseModule( new MouseModule( plot, new LengthDragRenderer( plot, plot.getXAxis(), plot.getYAxis() ), "Length" ) ); + + // plot.getXAxis().setAnimated(true); + // plot.getYAxis().setAnimated(true); + + // legend= new Legend(); + // no attachedRow method! + // canvas.add( legend, plot.getRow().createSubRow(0.95,0.80), plot.getColumn().createAttachedColumn( 0.5, 0.8 ) ); + leveler= new Leveler(canvas); + titleAnnotation= new DasAnnotation( title ); + canvas.add( titleAnnotation, new DasRow(canvas,0.,0.05), new DasColumn( canvas, 0., 1. ) ); + canvas.revalidate(); + + isWidgetCreated= true; + startUpdateThread(); + } + + } + + public void setUpdating( boolean updating ) { + this.updating= updating; + } + + private Probe( String title, boolean notNull, int xsize, int ysize ) { + this.title= title; + this.isWidgetCreated= false; + this.updating= notNull; + this.isNull= !notNull; + this.xsize= xsize; + this.ysize= ysize; + reset(); + } + + public static Probe newProbe( String title ) { + return new Probe(title,true, 400, 600 ); + } + + public static Probe newProbe( int xsize, int ysize ) { + return new Probe( "Probe", true, xsize, ysize ); + } + + public static Probe nullProbe() { + return new Probe("",false, 400,400 ); + } + + public static void main(String[] args) throws Exception { + Probe p= new Probe("Test of Probe with fake data",true,400,600); + Thread.sleep(500); + for ( int i=0; i<500; i++ ) { + Thread.sleep(200); + p.add("i", i); + p.add("j", i % 10 ); + } + } + + public int getXsize() { + return this.xsize; + } + + public void setXsize(int xsize) { + this.xsize = xsize; + } + + public int getYsize() { + return this.ysize; + } + + public void setYsize(int ysize) { + this.ysize = ysize; + } + + public String getTitle() { + return this.title; + } + + public void setTitle(String title) { + this.title = title; + if ( titleAnnotation!=null ) titleAnnotation.setText(title); + } + +} diff --git a/dasCore/src/main/java/org/das2/util/Splash.java b/dasCore/src/main/java/org/das2/util/Splash.java new file mode 100644 index 000000000..58a27dc6b --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/Splash.java @@ -0,0 +1,132 @@ +/* File: Splash.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import java.util.logging.*; +import javax.swing.*; +import java.awt.*; +import java.net.URL; + +/** + * + * @author jbf + */ +public class Splash extends JWindow { + + public static Splash instance=null; + + private Handler handler; + private JLabel messageLabel; + + public static String getVersion() { + //DName: das_20030505_01_beta D + String cvsTagName= "$Name$"; + String version; + if (cvsTagName.length()<=9) { + version="untagged_version"; + } else { + version= cvsTagName.substring(6,cvsTagName.length()-2); + } + return version; + } + + public Handler getLogHandler() { + if ( handler==null ) { + handler= createhandler(); + } + return handler; + } + + private Handler createhandler() { + Handler result= new Handler() { + Handler handler; + public void publish( LogRecord logRecord ) { + System.out.println( logRecord.getMessage() ); + messageLabel.setText(logRecord.getMessage() ); + } + public void flush() {} + public void close() {} + }; + return result; + } + + private static ImageIcon getSplashImage() { + URL url= Splash.class.getResource("/images/dasSplash.gif"); + if ( url==null ) return null; + return new ImageIcon(url); + } + + public static Splash getInstance() { + if ( instance==null ) { + instance= new Splash(); + } + return instance; + } + + public static void showSplash() { + getInstance(); + instance.setVisible(true); + } + + public static void hideSplash() { + getInstance(); + instance.setVisible(false); + } + + /** Creates a new instance of Splash */ + public Splash() { + super(); + JPanel panel= new JPanel(new BorderLayout()); + panel.add(new JLabel(getSplashImage()),BorderLayout.CENTER); + + Box bottomPanel= Box.createHorizontalBox(); + + messageLabel= new JLabel(""); + messageLabel.setMinimumSize( new Dimension( 200, 10 ) ); + bottomPanel.add( messageLabel ); + bottomPanel.add( Box.createHorizontalGlue() ); + bottomPanel.add( new JLabel("version "+getVersion()+" ",JLabel.RIGHT) ); + + panel.add( bottomPanel, BorderLayout.SOUTH ); + this.setContentPane(panel); + this.pack(); + //this.setLocation(300,300); + this.setLocationRelativeTo(null); + } + + public static void main( String[] args ) { + System.out.println("This is das2 version "+getVersion()); + Splash.showSplash(); + Logger.getLogger("").addHandler( Splash.getInstance().getLogHandler() ); + try { + for ( int i=0; i<6; i++ ) { + Thread.sleep(500); + Logger.getLogger("").fine("i="+i); + //Splash.getInstance().messageLabel.setText( "ii-="+i ); + } + } catch ( java.lang.InterruptedException e ) {} + Splash.hideSplash(); + } + +} diff --git a/dasCore/src/main/java/org/das2/util/StreamTool.java b/dasCore/src/main/java/org/das2/util/StreamTool.java new file mode 100755 index 000000000..bef5b5cee --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/StreamTool.java @@ -0,0 +1,656 @@ +/* File: StreamTool.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.util; + +import org.das2.stream.StreamComment; +import org.das2.stream.StreamDescriptor; +import org.das2.stream.StreamException; +import org.das2.stream.StreamHandler; +import org.das2.stream.PropertyType; +import org.das2.stream.PacketDescriptor; +import org.das2.util.InflaterChannel; +import org.das2.util.ByteBufferInputStream; +import org.das2.datum.Datum; +import org.das2.datum.DatumVector; +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +/** + * + * @author jbf + */ +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +//import org.apache.xml.serialize.Method; +//import org.apache.xml.serialize.OutputFormat; +//import org.apache.xml.serialize.XMLSerializer; +import org.w3c.dom.*; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +public class StreamTool { + + /** Creates a new instance of StreamTool */ + public StreamTool() { + } + + public static class DelimeterNotFoundException extends Exception { + } + + public static byte[] advanceTo(InputStream in, byte[] delim) throws IOException, DelimeterNotFoundException { + + // Read from stream to delimeter, leaving the InputStream immediately after + // and returning bytes from stream. + + byte[] data = new byte[4096]; + + ArrayList list = new ArrayList(); + + int bytesMatched = 0; + int matchIndex = 0; + + int streamIndex = 0; // offset in bytes from the beginning of the stream + + int index = 0; + boolean notDone = true; + + int unreadOffset = -99999; + int unreadLength = -99999; + + int totalBytesRead = 0; + int offset = 0; // offset within byte[4096] + + while (notDone) { + + int byteRead = in.read(); + totalBytesRead++; + + if (byteRead == -1) { + notDone = false; + + } else { + + data[offset] = (byte) byteRead; + + if (delim[bytesMatched] == byteRead) { + bytesMatched++; + } else { + bytesMatched = 0; + } + if (bytesMatched == delim.length) { + notDone = false; + index = totalBytesRead - delim.length; + } + } + + if (notDone) { + offset++; + if (offset == 4096) { + list.add(data); + offset = 0; + data = new byte[4096]; + } + } + } + + + if (bytesMatched != delim.length) { + throw new StreamTool.DelimeterNotFoundException(); + } + + byte[] result = new byte[index]; + for (int i = 0; i < list.size(); i++) { + System.arraycopy(list.get(i), 0, result, i * 4096, 4096); + } + System.arraycopy(data, 0, result, list.size() * 4096, index - (list.size() * 4096)); + return result; + + } + + /** Read off XML data from the InputStream up to the termination of the XML. + * XML data is returned in a byte array. The InputStream is left just + * following the XML terminator. Processing is done with as little interpretation + * as possible, so invalid XML will cause problems with little feedback about + * what's wrong. + */ + public static byte[] readXML(PushbackInputStream in) throws IOException { + ReadableByteChannel channel = Channels.newChannel(in); + byte[] back = new byte[4096]; + ByteBuffer buffer = ByteBuffer.wrap(back); + channel.read(buffer); + buffer.flip(); + ByteBuffer xml = readXML(buffer); + byte[] bytes = new byte[xml.remaining()]; + xml.get(bytes); + return bytes; + } + + private static void eatWhiteSpace(ByteBuffer buffer) { + while (buffer.hasRemaining()) { + char c = (char) (0xFF & buffer.get()); + if (!Character.isWhitespace(c)) { + buffer.position(buffer.position() - 1); + return; + } + } + } + + public static ByteBuffer readXML(ByteBuffer input) throws IOException { + int gtCount = 0; + int tagCount = 0; + int bufidx = 0; + char lastChar; + boolean inQuotes = false; + boolean inTag = false; + boolean tagContainsSlash = false; + int b; + ByteBuffer buffer = input.duplicate(); + buffer.mark(); + + eatWhiteSpace(buffer); + + b = 0xFF & buffer.get(); + + if (((char) b) != '<') { + throw new IOException("found '" + ((char) b) + "', expected '<' at offset=" + buffer.position() + ".\n"); + } else { + gtCount++; + tagContainsSlash = false; + } + + while (buffer.hasRemaining() && (gtCount > 0 || tagCount > 0)) { + lastChar = (char) b; + b = 0xFF & buffer.get(); + + if (inQuotes && ((char) b) == '"' && lastChar != '\\') { + inQuotes = false; + } else if (((char) b) == '<') { + gtCount++; + inTag = true; + tagContainsSlash = false; /* until proven otherwise */ + } else if (b == (int) '>') { + gtCount--; + inTag = false; + if (lastChar != '/') { + if (tagContainsSlash) { + tagCount--; + } else { + tagCount++; + } + } + } else if (b == (int) '/') { + if (lastChar == (int) '<') { + tagContainsSlash = true; + } + } else if (((char) b) == '"' && inTag) { + inQuotes = true; + } + } + + if (b == -1) { + throw new IOException("unexpected end of file before xml termination\n"); + } + + eatWhiteSpace(buffer); + + int limit = buffer.limit(); + buffer.limit(buffer.position()); + buffer.reset(); + + ByteBuffer result = buffer.slice(); + + buffer.position(buffer.limit()); + buffer.limit(limit); + + return result; + } + + private static class ReadStreamStructure { + + private ReadableByteChannel stream; + private ByteBuffer bigBuffer = ByteBuffer.allocate(4096); + private byte[] four = new byte[4]; + private StreamHandler handler; + private Map descriptors = new HashMap(); + private int byteOffset = 0; // byte offset into file of the end of the buffer. + private int descriptorCount = 0; // successfully read descriptors + private int packetCount = 0; // successfully read packets + + private ReadStreamStructure(ReadableByteChannel stream, StreamHandler handler) { + this.stream = stream; + this.handler = handler; + } + public String toString() { + return "\ndescriptorCount="+descriptorCount+ + "\npacketCount="+packetCount+ + "\nbyteOffset="+byteOffset+ + "\ncarotPos="+(byteOffset-bigBuffer.limit()+bigBuffer.position())+ + "\nbuffer="+String.valueOf(bigBuffer); + } + } + + public static void readStream(ReadableByteChannel stream, StreamHandler handler) throws StreamException { + ReadStreamStructure struct = new ReadStreamStructure(stream, handler); + try { + StreamDescriptor sd = getStreamDescriptor(struct); + if ("deflate".equals(sd.getCompression())) { + stream = getInflaterChannel(stream); + } + handler.streamDescriptor(sd); + struct.descriptorCount++; + int bytesRead; + int totalBytesRead = 0; + while ((bytesRead = stream.read(struct.bigBuffer)) != -1) { + struct.byteOffset += struct.bigBuffer.position(); + struct.bigBuffer.flip(); + + totalBytesRead += bytesRead; + //System.err.println("d2s bytesRead="+bytesRead+" total="+totalBytesRead ); + //if ( totalBytesRead>318260 ) { + // System.err.println("here"); + //} + while (getChunk(struct)) { + // this block is empty + } + struct.bigBuffer.compact(); + } + handler.streamClosed(sd); + } catch (StreamException se) { + handler.streamException(se); + throw se; + } catch (IOException ioe) { + StreamException se = new StreamException(ioe); + handler.streamException(se); + throw se; + } + } + + private static StreamDescriptor getStreamDescriptor(ReadStreamStructure struct) throws StreamException, IOException { + struct.bigBuffer.clear().limit(10); + while (struct.bigBuffer.hasRemaining() && struct.stream.read(struct.bigBuffer) != -1) { + ; + } + if (struct.bigBuffer.hasRemaining()) { + throw new StreamException("Reached end of stream before encountering stream descriptor"); + } + struct.byteOffset += struct.bigBuffer.position(); + struct.bigBuffer.flip(); + struct.bigBuffer.get(struct.four); + if (isStreamDescriptorHeader(struct.four)) { + int contentLength = getContentLength(struct.bigBuffer); + if (contentLength == 0) { + throw new StreamException("streamDescriptor content length is 0."); + } + struct.byteOffset += struct.bigBuffer.position(); + struct.bigBuffer.clear().limit(contentLength); + while (struct.bigBuffer.hasRemaining() && struct.stream.read(struct.bigBuffer) != -1) { + ; + } + if (struct.bigBuffer.hasRemaining()) { + throw new StreamException("Reached end of stream before encountering stream descriptor"); + } + struct.byteOffset += struct.bigBuffer.position(); + struct.bigBuffer.flip(); + + try { + Document doc = getXMLDocument(struct.bigBuffer, contentLength); + Element root = doc.getDocumentElement(); + if (root.getTagName().equals("stream")) { + StreamDescriptor sd = new StreamDescriptor(doc.getDocumentElement()); + struct.bigBuffer.clear(); + return sd; + } else if (root.getTagName().equals("exception")) { + throw exception(root); + } else { + throw new StreamException("Unexpected xml header, expecting stream or exception, received: " + root.getTagName()); + } + } catch (SAXException ex) { + String msg = getSAXParseExceptionMessage(ex, struct, contentLength ); + throw new StreamException(msg); + } + } else { + String s = readMore(struct); + throw new StreamException("Expecting stream descriptor header, found: '" + asciiBytesToString(struct.four, 0, 4) + "' beginning \n'" + s + "'"); + } + } + + /** + * call this after error, to get another 100 bytes off the stream + */ + private static String readMore(ReadStreamStructure struct) throws IOException { + struct.bigBuffer.position(0); + struct.bigBuffer.limit(10); + byte[] bytes10 = new byte[10]; + struct.bigBuffer.get(bytes10); + String s = new String(bytes10); + struct.bigBuffer.limit(1000); + struct.bigBuffer.position(0); + while (struct.bigBuffer.hasRemaining() && struct.stream.read(struct.bigBuffer) != -1) { + ; + } + int p = struct.bigBuffer.position(); + byte[] bytes = new byte[p]; + struct.bigBuffer.flip(); + struct.bigBuffer.get(bytes); + s = s + new String(bytes); + return s; + } + + private static String getSAXParseExceptionMessage(final SAXException ex, final ReadStreamStructure struct,int contentLength) { + String loc = null; + if (ex instanceof SAXParseException) { + SAXParseException spe = (SAXParseException) ex; + loc = "Relative to packet start, line number is " + spe.getLineNumber() + ", column is " + spe.getColumnNumber(); + } + + int bufOffset= struct.byteOffset - struct.bigBuffer.limit(); + + String msg = "xml parser fails with the message: \"" + ex.getMessage() + + "\" within the packet ending at byte offset " + ( bufOffset + struct.bigBuffer.position() ) + "."; + if (ex.getMessage().contains("trailing")) { + msg += "\nNon-whitespace data found after xml closing tag, probably caused by content length error."; + int i; + // find the end of the closing xml tag. + for (i = bufOffset + struct.bigBuffer.position() - 1; i > 0; i--) { + int bpos= i - bufOffset ; + if (struct.bigBuffer.get( bpos ) == '>' ) { + break; + } else { + System.err.println( (char)struct.bigBuffer.get(bpos) ); + } + } + for ( ; i < bufOffset + struct.bigBuffer.position() ; i++) { + int bpos= i - bufOffset; + if (struct.bigBuffer.get(bpos) == '[' || struct.bigBuffer.get(bpos)==':' ) { + break; + } + } + if (i > 0) { + int error= i - ( struct.bigBuffer.position() + bufOffset ); + //int error= ( i + bufOffset ) - ( struct.byteOffset - 10 ); + NumberFormat nf = new DecimalFormat("000000"); + msg += "\nContent length was " + nf.format(contentLength) + + ", maybe it should have been " + + nf.format(contentLength+error) + "."; + } + } + + if (loc != null) { + msg += " " + loc; + } + return msg; + } + + private static final StreamException exception(Element exception) { + String type = exception.getAttribute("type"); + return new StreamException(type); + } + + private static boolean getChunk(ReadStreamStructure struct) throws StreamException, IOException { + struct.bigBuffer.mark(); + if (struct.bigBuffer.remaining() < 4) { + return false; + } + struct.bigBuffer.get(struct.four); + if (isPacketDescriptorHeader(struct.four)) { + if (struct.bigBuffer.remaining() < 6) { + struct.bigBuffer.reset(); + return false; + } + int contentLength = getContentLength(struct.bigBuffer); + if (contentLength == 0) { + throw new StreamException("packetDescriptor content length is 0."); + } + if (struct.bigBuffer.capacity() < contentLength) { + struct.bigBuffer.reset(); + ByteBuffer temp = ByteBuffer.allocate(8 + contentLength + contentLength / 10); + temp.put(struct.bigBuffer); + temp.flip(); + struct.bigBuffer = temp; + return false; + } else if (struct.bigBuffer.remaining() < contentLength) { + struct.bigBuffer.reset(); + return false; + } + + try { + Document doc = getXMLDocument(struct.bigBuffer, contentLength); + Element root = doc.getDocumentElement(); + if (root.getTagName().equals("packet")) { + PacketDescriptor pd = new PacketDescriptor(doc.getDocumentElement()); + struct.handler.packetDescriptor(pd); + struct.descriptors.put(asciiBytesToString(struct.four, 1, 2), pd); + } else if (root.getTagName().equals("exception")) { + throw exception(root); + } else if (root.getTagName().equals("comment")) { + struct.handler.streamComment(new StreamComment(doc.getDocumentElement())); + } else { + throw new StreamException("Unexpected xml header, expecting stream or exception, received: " + root.getTagName()); + } + struct.descriptorCount++; + } catch (SAXException ex) { + String msg = getSAXParseExceptionMessage(ex, struct, contentLength ); + throw new StreamException(msg); + } + } else if (isPacketHeader(struct.four)) { + String key = asciiBytesToString(struct.four, 1, 2); + PacketDescriptor pd = (PacketDescriptor) struct.descriptors.get(key); + int contentLength = pd.getSizeBytes(); + if (struct.bigBuffer.remaining() < contentLength) { + struct.bigBuffer.reset(); + return false; + } + int yCount = pd.getYCount(); + Datum xTag = pd.getXDescriptor().readDatum(struct.bigBuffer); + DatumVector[] vectors = new DatumVector[yCount]; + for (int i = 0; i < yCount; i++) { + vectors[i] = pd.getYDescriptor(i).read(struct.bigBuffer); + } + struct.handler.packet(pd, xTag, vectors); + struct.packetCount++; + } else { + String msg = "Expected four byte header, found '"; + String s = new String(struct.four); + s = s.replaceAll("\n", "\\\\n"); // TODO: what's the right wat to say this? + msg += s; + msg += "' at byteOffset=" + (struct.byteOffset + struct.bigBuffer.position() - 4); + msg += " after reading " + struct.descriptorCount + " descriptors and " + struct.packetCount + " packets."; + throw new StreamException(msg); + } + return true; + } + + private static ByteBuffer sliceBuffer(ByteBuffer buffer, int length) { + ByteBuffer dup = buffer.duplicate(); + dup.limit(dup.position() + length); + return dup.slice(); + } + + private static String asciiBytesToString(byte[] bytes, int offset, int length) { + try { + return new String(bytes, offset, length, "US-ASCII"); + } catch (UnsupportedEncodingException uee) { + //All JVM implementations are required to support US-ASCII + throw new RuntimeException(uee); + } + } + + private static boolean isStreamDescriptorHeader(byte[] four) { + return four[0] == (byte) '[' && four[1] == (byte) '0' && four[2] == (byte) '0' && four[3] == (byte) ']'; + } + + private static boolean isPacketDescriptorHeader(byte[] four) { + return four[0] == (byte) '[' && four[3] == (byte) ']' && (Character.isDigit((char) four[1]) && Character.isDigit((char) four[2]) || (char) four[1] == 'x' && (char) four[2] == 'x'); + } + + private static boolean isPacketHeader(byte[] four) { + return four[0] == (byte) ':' && four[3] == (byte) ':' && Character.isDigit((char) four[1]) && Character.isDigit((char) four[2]); + } + + private static Document getXMLDocument(ByteBuffer buffer, int contentLength) throws StreamException, IOException, SAXException { + ByteBuffer xml = buffer.duplicate(); + xml.limit(xml.position() + contentLength); + buffer.position(buffer.position() + contentLength); + final boolean DEBUG = false; + if (DEBUG) { + int pos = xml.position(); + byte[] bytes = new byte[xml.limit() - xml.position()]; + xml.get(bytes); + xml.position(pos); + System.err.println(new String(bytes)); + } + ByteBufferInputStream bbin = new ByteBufferInputStream(xml); + InputStreamReader isr = new InputStreamReader(bbin); + + try { + DocumentBuilder builder; + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + InputSource source = new InputSource(isr); + Document document = builder.parse(source); + return document; + } catch (ParserConfigurationException ex) { + throw new RuntimeException(ex); + } + + } + + private static int getContentLength(ByteBuffer buffer) throws StreamException { + int contentLength = 0; + for (int i = 0; i < 6; i++) { + char c = (char) (0xFF & buffer.get()); + if (c == ' ') { + continue; + } + if (!Character.isDigit(c)) { + throw new StreamException("Invalid character in contentLength: '" + c + "'"); + } + int digit = Character.digit(c, 10); + contentLength = contentLength * 10 + digit; + } + return contentLength; + } + + public static void formatHeader(Document document, Writer writer) throws StreamException { + DOMImplementationLS ls = (DOMImplementationLS) + document.getImplementation().getFeature("LS", "3.0"); + LSOutput output = ls.createLSOutput(); + + output.setCharacterStream(writer); + LSSerializer serializer = ls.createLSSerializer(); + try { + if ( serializer.getDomConfig().canSetParameter( "format-pretty-print", Boolean.TRUE ) ) { + serializer.getDomConfig().setParameter( "format-pretty-print", Boolean.TRUE ); + } + } catch ( Error e ) { + e.printStackTrace(); + } + serializer.write(document, output); + /* + try { + OutputFormat format = new OutputFormat(Method.XML, "US-ASCII", true); + XMLSerializer serializer = new XMLSerializer(writer, format); + serializer.serialize(document); + } catch (IOException ioe) { + throw new StreamException(ioe); + } + */ + } + + public static Map processPropertiesElement(Element element) throws StreamException { + try { + if (!element.getTagName().equals("properties")) { + // TODO maybe this should be a RuntimeException + throw new StreamException("expecting 'properties' element, encountered '" + element.getTagName() + "'"); + } + HashMap map = new HashMap(); + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + Attr attr = (Attr) attributes.item(i); + String name = attr.getName(); + String[] split = name.split(":"); + if (split.length == 1) { + map.put(name, attr.getValue()); + } else if (split.length == 2) { + try { + PropertyType type = PropertyType.getByName(split[0]); + Object value = type.parse(attr.getValue()); + map.put(split[1], value); + } + catch (IllegalArgumentException ex) { + map.put(name, attr.getValue()); + } + } else { + throw new IllegalArgumentException("Invalid typed name: " + name); + } + } + return map; + } catch (ParseException pe) { + StreamException se = new StreamException(pe.getMessage()); + se.initCause(pe); + throw se; + } + } + private static HashMap typesMap; + + static { + typesMap = new HashMap(); + typesMap.put(Datum.class, "Datum"); + typesMap.put(Datum.Double.class, "Datum"); + typesMap.put(Integer.class, "int"); + } + + public static Element processPropertiesMap(Document document, Map properties) { + Element propertiesElement = document.createElement("properties"); + for (Iterator i = properties.entrySet().iterator(); i.hasNext();) { + Map.Entry entry = (Map.Entry) i.next(); + String key = (String) entry.getKey(); + Object value = entry.getValue(); + if (value == null) { + continue; + } + if (typesMap.containsKey(value.getClass())) { + key = (String) typesMap.get(value.getClass()) + ":" + key; + } + propertiesElement.setAttribute(key, value.toString()); + } + return propertiesElement; + } + + private static ReadableByteChannel getInflaterChannel(ReadableByteChannel channel) throws IOException { + return new InflaterChannel(channel); + //return Channels.newChannel(new InflaterInputStream(Channels.newInputStream(channel))); + } +} diff --git a/dasCore/src/main/java/org/das2/util/TextUtil.java b/dasCore/src/main/java/org/das2/util/TextUtil.java new file mode 100644 index 000000000..cc2968396 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/TextUtil.java @@ -0,0 +1,101 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.util; + +import java.util.Enumeration; +import java.util.Vector; + +/** A grab bag of text processing utilities + * + * @author cwp + */ +public class TextUtil { + /** Line wrap function, taken from + * http://progcookbook.blogspot.com/2006/02/text-wrapping-function-for-java.html + * and then customized a little + * + * @param sText - The text to wrap + * @param nLineLen - The length of each lines text area + * @param sPrefix - A prefix string, to be added to each line, if not null + * @return + */ + public static String[] wrapText(String sText, int nLineLen, String sPrefix){ + // return empty array for null text + if(sText == null){ + return new String[]{}; + } + + sText = sText.trim(); + + // Strip out all the newlines and tabs that might happen to be in the text + sText = sText.replaceAll("\t\r\n", ""); + + // Collapse 2+ spaces to a single space + sText = sText.replaceAll("\\s+", " "); + + // return text if len is zero or less + if(nLineLen <= 0){ + return new String[]{sText}; + } + + // return text if less than length + if(sText.length() <= nLineLen){ + if(sPrefix == null) + return new String[]{sText}; + else + return new String[]{sPrefix + sText}; + } + + char[] chars = sText.toCharArray(); + @SuppressWarnings("UseOfObsoleteCollectionType") + Vector lines = new Vector(); + StringBuffer line = new StringBuffer(); + StringBuffer word = new StringBuffer(); + + for(int i = 0; i < chars.length; i++){ + word.append(chars[i]); + + if(chars[i] == ' '){ + if((line.length() + word.length()) > nLineLen){ + lines.add(line.toString()); + line.delete(0, line.length()); + } + + line.append(word); + word.delete(0, word.length()); + } + } + + // handle any extra chars in current word + if(word.length() > 0){ + if((line.length() + word.length()) > nLineLen){ + lines.add(line.toString()); + line.delete(0, line.length()); + } + line.append(word); + } + + // handle extra line + if(line.length() > 0){ + lines.add(line.toString()); + } + + String[] lRet = new String[lines.size()]; + int c = 0; // counter + if(sPrefix == null){ + for(Enumeration e = lines.elements(); e.hasMoreElements(); c++){ + lRet[c] = (String) e.nextElement(); + } + } + else{ + for(Enumeration e = lines.elements(); e.hasMoreElements(); c++){ + lRet[c] = sPrefix + (String) e.nextElement(); + } + } + + return lRet; + } +} diff --git a/dasCoreUtil/src/org/das2/util/ThreadDenseConsoleFormatter.java b/dasCore/src/main/java/org/das2/util/ThreadDenseConsoleFormatter.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/ThreadDenseConsoleFormatter.java rename to dasCore/src/main/java/org/das2/util/ThreadDenseConsoleFormatter.java diff --git a/dasCore/src/main/java/org/das2/util/TimeParser.java b/dasCore/src/main/java/org/das2/util/TimeParser.java new file mode 100644 index 000000000..a8d3f42ea --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/TimeParser.java @@ -0,0 +1,591 @@ +/* + * TimeParser.java + * + * Created on January 27, 2006, 3:51 PM + * + * + */ +package org.das2.util; + +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.TimeUtil; +import org.das2.datum.CalendarTime; +import org.das2.datum.Units; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +/** + * TimeParser designed to quickly parse strings with a specified format. This parser has been + * shown to perform around 20 times faster than that the generalized parser. + * + * @author Jeremy + */ +public class TimeParser { + + final static Logger logger = Logger.getLogger("TimeParser"); + /** + * %Y-%m-%dT%H:%M:%S.%{milli}Z + */ + public static final String TIMEFORMAT_Z = "%Y-%m-%dT%H:%M:%S.%{milli}Z"; + CalendarTime time; + int[] timeWidth; + int ndigits; + String[] valid_formatCodes = new String[]{"Y", "y", "j", "m", "d", "H", "M", "S", "milli", "micro", "p", "z", "ignore", "b"}; + String[] formatName = new String[]{"Year", "2-digit-year", "day-of-year", "month", "day", "Hour", "Minute", "Second", "millisecond", "microsecond", + "am/pm", "RFC-822 numeric time zone", "ignore", "3-char-month-name", + }; + int[] formatCode_lengths = new int[]{4, 2, 3, 2, 2, 2, 2, 2, 3, 3, 2, 5, -1, 3}; + int[] precision = new int[]{0, 0, 2, 1, 2, 3, 4, 5, 6, 7, -1, -1, -1, 1}; + int[] handlers; + /** + * set of custom handlers to allow for extension + */ + Map/**/ fieldHandlers; + /** + * positions of each digit, within the string to be parsed. If position is -1, then we need to + * compute it along the way. + */ + int[] offsets; + int[] lengths; + String[] delims; + String[] fc; + String regex; + String formatString; + /** + * Least significant digit in format. + *0=year, 1=month, 2=day, 3=hour, 4=min, 5=sec, 6=milli, 7=micro + */ + int lsd; + + public interface FieldHandler { + + public void handleValue(String fieldContent, CalendarTime startTime, int timeWidth[]); + } + + /** + * must contain T or space to delimit date and time. + * @param exampleTime "1992-353T02:00" + * @return "%Y-%jT%H%M" etc. + */ + public static String iso8601String(String exampleTime) { + int i = exampleTime.indexOf("T"); + if (i == -1) { + i = exampleTime.indexOf(" "); + } + char dateTimeDelim = exampleTime.charAt(i); + + String date = null, time = null; + if (i != -1) { + String datePart = exampleTime.substring(0, i); + boolean hasDelim = !datePart.matches("\\d+"); + char delim = 0; + if (hasDelim) { + delim = datePart.charAt(4); + } + switch (datePart.length()) { + case 10: + date = "%Y" + delim + "%m" + delim + "%d"; + break; + case 9: + date = "%Y" + delim + "%j"; + break; + case 8: + date = hasDelim ? "%Y" + delim + "%j" : "%Y%m%d"; + break; + case 7: + date = "%Y%j"; + break; + default: + throw new IllegalArgumentException("unable to identify date format for " + exampleTime); + } + + String timePart = exampleTime.substring(i + 1); + if ( timePart.endsWith("Z") ) timePart= timePart.substring(0,timePart.length()-1); // see below + hasDelim = !timePart.matches("\\d+"); + delim = 0; + if (hasDelim) { + delim = timePart.charAt(2); + } + switch (timePart.length()) { + case 4: + time = "%H%M"; + break; + case 5: + time = "%H" + delim + "%M"; + break; + case 6: + time = "%H%M%S"; + break; + case 8: + time = "%H" + delim + "%M" + delim + "%S"; + break; + case 12: + time = "%H" + delim + "%M" + delim + "%S.%{milli}"; + break; + case 15: + time = "%H" + delim + "%M" + delim + "%S.%{milli}%{micro}"; + break; + default: + throw new IllegalArgumentException("unable to identify time format for " + exampleTime); + } + if ( timePart.endsWith("Z") ) time+= "Z"; + return date + dateTimeDelim + time; + + } else { + throw new IllegalArgumentException("example time must contain T or space."); + } + } + + private TimeParser(String formatString, Map/**/ fieldHandlers) { + time = new CalendarTime(); + this.fieldHandlers = fieldHandlers; + this.formatString = formatString; + + String[] ss = formatString.split("%"); + fc = new String[ss.length]; + String[] delim = new String[ss.length + 1]; + + ndigits = ss.length; + + StringBuffer regex = new StringBuffer(100); + regex.append(ss[0]); + + lengths = new int[ndigits]; + for (int i = 0; i < lengths.length; i++) { + lengths[i] = -1; // -1 indicates not known, but we'll figure out as many as we can. + + } + + delim[0] = ss[0]; + for (int i = 1; i < ndigits; i++) { + int pp = 0; + while (Character.isDigit(ss[i].charAt(pp)) || ss[i].charAt(pp)=='-' ) { + pp++; + } + if (pp > 0) { + lengths[i] = Integer.parseInt(ss[i].substring(0, pp)); + } else { + lengths[i] = 0; // determine later by field type + } + + if (ss[i].charAt(pp) != '{') { + fc[i] = ss[i].substring(pp, pp + 1); + delim[i] = ss[i].substring(pp + 1); + } else { + int endIndex = ss[i].indexOf('}', pp); + int comma= ss[i].indexOf(",",pp); + if ( comma!=-1 ) { + fc[i] = ss[i].substring(pp + 1, comma); + } else { + fc[i] = ss[i].substring(pp + 1, endIndex); + } + delim[i] = ss[i].substring(endIndex + 1); + } + } + + handlers = new int[ndigits]; + offsets = new int[ndigits]; + + int pos = 0; + offsets[0] = pos; + + lsd = -1; + for (int i = 1; i < ndigits; i++) { + if (pos != -1) { + pos += delim[i - 1].length(); + } + int handler = 9999; + + for (int j = 0; j < valid_formatCodes.length; j++) { + if (valid_formatCodes[j].equals(fc[i])) { + handler = j; + break; + } + } + + if (handler == 9999) { + if (fieldHandlers == null || !fieldHandlers.containsKey(fc[i])) { + throw new IllegalArgumentException("bad format code: \"" + fc[i] + "\""); + } else { + lsd = 100; + handler = 100; + handlers[i] = 100; + offsets[i] = pos; + if (lengths[i] < 1 || pos == -1) { // 0->indetermined as well, allows user to force indeterminate + + pos = -1; + lengths[i] = -1; + } else { + pos += lengths[i]; + } + } + } else { + handlers[i] = handler; + if (lengths[i] == 0) { + lengths[i] = formatCode_lengths[handler]; + } + offsets[i] = pos; + if (lengths[i] < 1 || pos == -1) { + pos = -1; + lengths[i] = -1; + } else { + pos += lengths[i]; + } + } + + if (handler < 100) { + if (precision[handler] > lsd) { + lsd = precision[handler]; + } + } + String dots = "........."; + if (lengths[i] == -1) { + regex.append("(.*)"); + } else { + regex.append("(" + dots.substring(0, lengths[i]) + ")"); + } + regex.append(delim[i]); + + } + + timeWidth = new int[]{0,0,0,0,0,0,0}; + switch (lsd) { + case 0: + timeWidth[0] = 1; + break; + case 1: + timeWidth[1] = 1; + break; + case 2: + timeWidth[2] = 1; + break; + case 3: + timeWidth[3] = 1; + break; + case 4: + timeWidth[4] = 1; + break; + case 5: + timeWidth[5] = 1; + break; + case 6: + timeWidth[6] = 1000000; + break; + case 7: + timeWidth[7] = 1000; + break; + case 100: /* do nothing */ break; + } + + this.delims = delim; + this.regex = regex.toString(); + } + + /** + *

    +     *  %[fieldLength]<1-char code>  or
    +     *  %[fieldLength]{}
    +     *
    +     *  fieldLength=0 --> makes field length indeterminate, deliminator must follow.
    +     *
    +     *  %Y   4-digit year
    +     *  %y    2-digit year
    +     *  %j     3-digit day of year
    +     *  %m   2-digit month
    +     *  %b   3-char month name
    +     *  %d    2-digit day
    +     *  %H    2-digit hour
    +     *  %M    2-digit minute
    +     *  %S     2-digit second
    +     *  %{milli}  3-digit milliseconds
    +     *  
    + * + */ + public static TimeParser create(String formatString) { + return new TimeParser(formatString, null); + } + + /** + * This version allows for extension by specifying an external handler. + * + * %3{fieldName} 2 characters are passed to the handler + * %Y 4-digit year + * %y 2-digit year + * %m month + * %2m 2-digit month + * %d 2-digit day + * %H 2-digit hour + * %M 2-digit minute + * %S 2-digit second + * %{milli} 3-digit milliseconds + */ + public static TimeParser create(String formatString, String fieldName, FieldHandler handler) { + HashMap map = new HashMap(); + map.put(fieldName, handler); + return new TimeParser(formatString, map); + } + + private double toUs2000(CalendarTime d) { + int year = d.year(); + int month = d.month(); + int day = d.day(); + int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - + 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + + 275 * month / 9 + day + 1721029; + int hour = d.hour(); + int minute = d.minute(); + double seconds = d.second() + hour * (float) 3600.0 + minute * (float) 60.0; + int mjd1958 = (jd - 2436205); + double us2000 = (mjd1958 - 15340) * 86400000000. + seconds * 1e6 + d.nanosecond() / 1000; + return us2000; + } + + private double toUs1980(CalendarTime d) { + int year = d.year(); + int month = d.month(); + int day = d.day(); + int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - + 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + + 275 * month / 9 + day + 1721029; + int hour = d.hour(); + int minute = d.minute(); + double seconds = d.second() + hour * (float) 3600.0 + minute * (float) 60.0; + double us1980 = (jd - 2436205 - 8035) * 86400000000. + seconds * 1e6 + d.nanosecond() / 1000; + return us1980; + } + + /** + * reset the seconds register. setDigit( String formatCode, double val ) accumulates + * fractional part in the seconds. + */ + public void resetSeconds() { + time.setSecond(0); + } + + /** + * force the parser to look for delimiters + */ + public void sloppyColumns() { + this.lengths[0]=-1; + for (int i=1; i 8) { + int idx= 5; + while ( idx<8 && st[idx].getClassName().contains("java.util.logging.Logger") ) idx++; + source = String.valueOf(st[idx].getClassName()); + if (source.startsWith("org.das2")) { + source = source.substring("org.das2".length()); + if (source.length() < spaces.length()) { + source = spaces.substring(source.length()) + source; + } + } + } + + long t = System.currentTimeMillis() - t0; + + String threadId= Thread.currentThread().getName(); + threadId = fixedColumn(threadId, 20); + + return nf.format(t) + ":" + fixedColumn(rec.getLoggerName(),20) + ": " + source + ": " + threadId+":" + rec.getLevel().getLocalizedName() + ": " + String.valueOf(message) + "\n"; + } + + public void setResetMessage( String msg ) { + this.resetMessage= msg; + } + + public TimerConsoleFormatter() { + t0 = System.currentTimeMillis(); + nf = new DecimalFormat("00000"); + } + + String spaces= " "; + private String fixedColumn(String threadId, int sp) { + try { + if (threadId.length() > sp) threadId = threadId.substring(threadId.length()-sp, threadId.length()); + if (threadId.length() < sp) threadId = spaces.substring(0, sp - threadId.length()) + threadId; + return threadId; + } catch ( StringIndexOutOfBoundsException ex ) { + return threadId; + } + } +} diff --git a/dasCore/src/main/java/org/das2/util/URLBuddy.java b/dasCore/src/main/java/org/das2/util/URLBuddy.java new file mode 100644 index 000000000..bff3b57c1 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/URLBuddy.java @@ -0,0 +1,141 @@ +/* File: URLBuddy.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on April 7, 2004, 11:34 AM + * by Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util; + +import java.util.*; +import java.util.regex.*; + +/** + * + * @author eew + */ +public class URLBuddy { + + private static final String ALPHA = "[A-Za-z]"; + private static final String DIGIT = "[0-9]"; + private static final String HEX = "[" + DIGIT + "A-F]"; + + public static final Pattern VALID_QUERY_NAME = Pattern.compile( + ALPHA + "[" + ALPHA + DIGIT + "-_:.]*" + ); + public static final Pattern VALID_QUERY_VALUE = Pattern.compile( + "(?:[" + ALPHA + DIGIT + "\\.\\-\\*\\_\\+]|\\%(" + HEX + HEX + "))*" + ); + + /** Creates a new instance of URLBuddy */ + public URLBuddy() { + } + + public static String encodeUTF8(String str) { + try { + return java.net.URLEncoder.encode(str, "UTF-8"); + } + catch (java.io.UnsupportedEncodingException uee) { + //All JVM's are required to support UTF-8 + throw new RuntimeException(uee); + } + } + + public static String decodeUTF8(String str) { + try { + return java.net.URLDecoder.decode(str, "UTF-8"); + } + catch (java.io.UnsupportedEncodingException uee) { + //All JVM's are required to support UTF-8 + throw new RuntimeException(uee); + } + } + + + /** Returns an unmodifiable map representing the query string passed in. + * each key is a name from the string and each value is the url encoded + * value for the key. + *@param str an URLEncoded query string + */ + public static Map parseQueryString(String str) { + HashMap map = new HashMap(); + String[] tokens = str.split("\\&"); + for (int i = 0; i < tokens.length; i++) { + int eqIndex = tokens[i].indexOf('='); + if (eqIndex == -1) { + throwUnexpectedToken(tokens[i], str, "name/value pair"); + } + String name = tokens[i].substring(0, eqIndex); + String value = tokens[i].substring(eqIndex + 1); + if (!validName(name)) { + throwUnexpectedToken(name, str, "valid name"); + } + if (!validValue(value)) { + throwUnexpectedToken(name, str, "url encoded value"); + } + value = decodeUTF8(value); + map.put(name, value); + } + return Collections.unmodifiableMap(map); + } + + private static final void throwUnexpectedToken(String token, String input, String expecting) { + int index = input.indexOf(token); + StringBuffer messageBuffer = new StringBuffer(); + messageBuffer.append("Error parsing query string: Expecting "); + messageBuffer.append(expecting).append(", found '"); + messageBuffer.append(token).append("'\n"); + messageBuffer.append("Input: ").append(input).append('\n'); + messageBuffer.append(" "); + for (int i = 0; i < index; i++) { + messageBuffer.append('.'); + } + messageBuffer.append('^'); + throw new IllegalArgumentException(messageBuffer.toString()); + } + + public static String formatQueryString(Map m) { + StringBuffer query = new StringBuffer(); + for (Iterator i = m.entrySet().iterator(); i.hasNext();) { + Map.Entry entry = (Map.Entry)i.next(); + String name = (String)entry.getKey(); + String value = (String)entry.getValue(); + if (!validName(name)) { + throw new IllegalArgumentException("'" + name + "' is not a valid query name."); + } + value = encodeUTF8(value); + query.append(name).append('=').append(value).append('&'); + } + if (query.charAt(query.length() - 1) == '&') { + query.deleteCharAt(query.length() - 1); + } + return query.toString(); + } + + private static boolean validName(String name) { + Matcher m = VALID_QUERY_NAME.matcher(name); + return m.matches(); + } + + private static boolean validValue(String value) { + Matcher m = VALID_QUERY_VALUE.matcher(value); + return m.matches(); + } + +} diff --git a/dasCore/src/main/java/org/das2/util/WeakListenerManager.java b/dasCore/src/main/java/org/das2/util/WeakListenerManager.java new file mode 100644 index 000000000..aeae4e3a0 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/WeakListenerManager.java @@ -0,0 +1,114 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.util; + +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.EventListener; + +/** + * + * @author eew + */ +public class WeakListenerManager { + + public static L weakListener( + Object target, + Class listenerClass, + L listener) + { + String removeMethod = "remove"+listenerClass.getSimpleName(); + return weakListener(target, removeMethod, listenerClass, listener); + } + + public static L weakListener( + Object target, + String removeMethod, + Class listenerClass, + L listener) + { + for (Method m : target.getClass().getMethods()) { + Class[] params = m.getParameterTypes(); + if (m.getName().equals(removeMethod) + && params.length == 1 && params[0] == listenerClass) + { + return weakListener(target, m, listenerClass, listener); + } + } + throw new IllegalArgumentException("No suitable remove method found"); + } + + public static L weakListener( + Object target, + Method removeMethod, + Class listenerClass, + L listener) + { + Handler h = new Handler(target, removeMethod, listener); + Class[] type = { listenerClass }; + Object proxy = Proxy.newProxyInstance(null, type, h); + return listenerClass.cast(proxy); + } + + private static class Handler implements InvocationHandler { + + private final Object target; + private final Method removeMethod; + private final WeakReference listener; + + private Handler(Object target, Method removeMethod, Object listener) { + this.target = target; + this.removeMethod = removeMethod; + this.listener = new WeakReference(listener); + } + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable + { + Object l = listener.get(); + + if (method.getDeclaringClass() == Object.class) { + String name = method.getName(); + if (name.equals("equals")) { + return proxy == args[0]; + } + else if (name.equals("toString")) { + return "Proxy@"+System.identityHashCode(proxy) + +"["+listener.get()+"]"; + } + else if (name.equals("hashCode")) { + return this.hashCode(); + } + else { + return null; + } + } + else if (l == null) { + try { + removeMethod.invoke(target, proxy); + } + catch (IllegalAccessException ex) { + ex.printStackTrace(); + } + catch (IllegalArgumentException ex) { + ex.printStackTrace(); + } + catch (InvocationTargetException ex) { + ex.printStackTrace(); + } + return null; + } + else { + return method.invoke(l, args); + } + } + + } + +} diff --git a/dasCore/src/main/java/org/das2/util/awt/EventQueueBlocker.java b/dasCore/src/main/java/org/das2/util/awt/EventQueueBlocker.java new file mode 100644 index 000000000..621c899ee --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/awt/EventQueueBlocker.java @@ -0,0 +1,122 @@ +/* + * EventQueueBlocker.java + * + * Created on May 25, 2006, 8:31 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.util.awt; + +import org.das2.event.DasUpdateEvent; +import org.das2.system.DasLogger; +import java.awt.AWTEvent; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.lang.reflect.InvocationTargetException; +import java.util.EmptyStackException; +import java.util.LinkedList; +import java.util.Queue; +import java.util.logging.Logger; +import javax.swing.SwingUtilities; + +/** + * + * @author Jeremy + */ +public final class EventQueueBlocker { + + private static final Logger logger= DasLogger.getLogger(DasLogger.SYSTEM_LOG); + + /** Creates a new instance of EventQueueBlocker */ + private EventQueueBlocker() { + } + + + static class MyEventQueue extends EventQueue { + protected void dispatchEvent(AWTEvent event) { + super.dispatchEvent(event); + if (myEventQueue.peekEvent(DasUpdateEvent.DAS_UPDATE_EVENT_ID) == null) { + synchronized (this) { + pop(); + this.notifyAll(); + } + } + } + /* + protected void pop() throws EmptyStackException { + super.pop(); + } + */ + } + + static MyEventQueue myEventQueue= new MyEventQueue(); + + private static void clearEventQueueImmediately() { + System.err.println( Thread.currentThread().getName() ); + + synchronized (myEventQueue) { + Toolkit.getDefaultToolkit().getSystemEventQueue().push( myEventQueue ); + try { + myEventQueue.wait(); + } catch (InterruptedException ie) { + throw new RuntimeException(ie); + } + } + + /* + while (myEventQueue.peekEvent(DasUpdateEvent.DAS_UPDATE_EVENT_ID) != null) { + try { + Thread.sleep(40); + } + catch (InterruptedException ie) { + throw new RuntimeException(ie); + } + } + + myEventQueue.pop(); + */ + logger.fine("no more pending events"); + } + + + private static DasUpdateEvent findUpdateEvent( EventQueue eventQueue ) { + Queue queue= new LinkedList(); + AWTEvent evt=null; + DasUpdateEvent result=null; + while ( eventQueue.peekEvent()!=null ) { + try { + evt= eventQueue.getNextEvent(); + if ( evt instanceof DasUpdateEvent ) { + result= (DasUpdateEvent)evt; + } + } catch (InterruptedException ex) { + } + queue.add(evt); + } + while ( queue.size() > 0 ) { + eventQueue.postEvent((AWTEvent)queue.remove()); + } + return result; + } + + public static synchronized void clearEventQueue() throws InterruptedException { + + if ( SwingUtilities.isEventDispatchThread() ) { + clearEventQueueImmediately(); + } else { + try { + EventQueue.invokeAndWait(new Runnable() { + public void run() { + clearEventQueueImmediately(); + } + }); + } catch (InvocationTargetException ex) { + throw new RuntimeException(ex); + } + logger.finer("waiting for lockObject to indicate eventQueue is clear"); + logger.finer("event queue task complete"); + } + } +} diff --git a/dasCore/src/main/java/org/das2/util/awt/EventQueueBlocker_1.java b/dasCore/src/main/java/org/das2/util/awt/EventQueueBlocker_1.java new file mode 100644 index 000000000..c6ea7a8a0 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/awt/EventQueueBlocker_1.java @@ -0,0 +1,95 @@ +/* + * EventQueueBlocker.java + * + * Created on May 25, 2006, 8:31 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.util.awt; + +import org.das2.event.DasUpdateEvent; +import org.das2.system.DasLogger; +import java.awt.AWTEvent; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.io.PrintStream; +import java.util.LinkedList; +import java.util.Queue; +import java.util.logging.Logger; +import javax.swing.SwingUtilities; + +/** + * + * @author Jeremy + */ +public final class EventQueueBlocker_1 { + + private static final Logger logger= DasLogger.getLogger(DasLogger.SYSTEM_LOG); + + private static Object lockObject= new String("EQB_1"); + + /** Creates a new instance of EventQueueBlocker */ + private EventQueueBlocker_1() { + } + + private static Runnable clearEventQueueImmediatelyRunnable= new Runnable() { + public void run() { + clearEventQueueImmediately(); + } + }; + + private static void clearEventQueueImmediately() { + DasUpdateEvent evt; + evt= (DasUpdateEvent) Toolkit.getDefaultToolkit().getSystemEventQueue().peekEvent(DasUpdateEvent.DAS_UPDATE_EVENT_ID) ; + if ( evt != null ) { + logger.finer("pending update event: "+evt); + EventQueue.invokeLater( clearEventQueueImmediatelyRunnable ); + } else { + logger.finer("no update events found "); + synchronized(lockObject) { + lockObject.notify(); + } + } + } + + public static void clearEventQueue() throws InterruptedException { + + if ( SwingUtilities.isEventDispatchThread() ) { + throw new IllegalStateException( "must not be called on the EventQueue"); + } + synchronized(lockObject) { + if ( Toolkit.getDefaultToolkit().getSystemEventQueue().peekEvent(DasUpdateEvent.DAS_UPDATE_EVENT_ID) != null ) { + EventQueue.invokeLater( clearEventQueueImmediatelyRunnable ); + logger.finer("waiting for lockObject to indicate eventQueue is clear"); + lockObject.wait(); + } else { + logger.finer("no update events found, no runnable submitted "); + } + } + } + + /** + * method for inspecting the event queue. + */ + public static void dumpEventQueue( PrintStream out ) { + + EventQueue eventQueue= Toolkit.getDefaultToolkit().getSystemEventQueue(); + + Queue queue= new LinkedList(); + AWTEvent evt=null; + DasUpdateEvent result=null; + while ( eventQueue.peekEvent()!=null ) { + try { + evt= eventQueue.getNextEvent(); + out.println(evt); + } catch (InterruptedException ex) { + } + queue.add(evt); + } + while ( queue.size() > 0 ) { + eventQueue.postEvent((AWTEvent)queue.remove()); + } + } +} diff --git a/dasCoreUtil/src/org/das2/util/awt/GraphicsOutput.java b/dasCore/src/main/java/org/das2/util/awt/GraphicsOutput.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/awt/GraphicsOutput.java rename to dasCore/src/main/java/org/das2/util/awt/GraphicsOutput.java diff --git a/dasCore/src/main/java/org/das2/util/awt/LoggingEventQueue.java b/dasCore/src/main/java/org/das2/util/awt/LoggingEventQueue.java new file mode 100644 index 000000000..104b24762 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/awt/LoggingEventQueue.java @@ -0,0 +1,73 @@ +/* + * LoggingEventQueue.java + * + * Created on October 31, 2005, 1:02 PM + * + * + */ + +package org.das2.util.awt; + +import org.das2.system.DasLogger; +import java.awt.AWTEvent; +import java.awt.EventQueue; +import java.awt.event.InvocationEvent; +import java.util.LinkedList; +import java.util.Queue; +import java.util.logging.Logger; + +/** + * Tool for debugging event queue stuff. This can be used to log the event + * queue, or insert breakpoints, etc. + * Toolkit.getDefaultToolkit().getSystemEventQueue().push(new LoggingEventQueue()); + * + * @author Jeremy + * + */ +public class LoggingEventQueue extends EventQueue { + + final static Logger logger= DasLogger.getLogger(DasLogger.GUI_LOG); + + Object lockObject= new String("lockObjectLEQ"); + + private LoggingEventQueue() { + + } + + public void postEvent(java.awt.AWTEvent theEvent) { + if (theEvent instanceof InvocationEvent) { + // logger.info("XXX: "+theEvent); + } else { + // logger.info(""+theEvent); + } + super.postEvent(theEvent); + } + + private static LoggingEventQueue instance; + public static LoggingEventQueue getInstance() { + if ( instance==null ) { + instance= new LoggingEventQueue(); + } + return instance; + } + + public static synchronized void dumpPendingEvents() { + System.err.println("---------------------------------------------------------------"); + Queue queue= new LinkedList(); + AWTEvent evt; + while ( instance.peekEvent()!=null ) { + try { + evt= instance.getNextEvent(); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + System.err.println("[ "+evt); + queue.add(evt); + } + System.err.println("-----e--n--d-----------------------------------------------------"); + while ( queue.size() > 0 ) { + instance.postEvent((AWTEvent)queue.remove()); + } + } + +} diff --git a/dasCore/src/main/java/org/das2/util/awt/PdfGraphicsOutput.java b/dasCore/src/main/java/org/das2/util/awt/PdfGraphicsOutput.java new file mode 100644 index 000000000..b26383b7b --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/awt/PdfGraphicsOutput.java @@ -0,0 +1,76 @@ +/* + * PDFGraphicsOutput.java + * + * Created on January 28, 2005, 5:27 PM + */ + +package org.das2.util.awt; + +import com.lowagie.text.Document; +import com.lowagie.text.DocumentException; +import com.lowagie.text.Rectangle; +import com.lowagie.text.pdf.PdfContentByte; +import com.lowagie.text.pdf.PdfWriter; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.io.IOException; +import java.io.OutputStream; + +/** + * + * @author eew + */ +public class PdfGraphicsOutput implements GraphicsOutput { + + private float width; + private float height; + private OutputStream out; + private Document doc; + private PdfWriter writer; + private PdfContentByte cb; + private Graphics2D graphics; + + + /** Creates a new instance of PDFGraphicsOutput */ + public PdfGraphicsOutput() {} + + public Graphics2D getGraphics2D() { + if (graphics != null) { + return graphics; + } + return (graphics = cb.createGraphics(width, height)); + } + + public void finish() throws IOException { + graphics.dispose(); + cb.restoreState(); + doc.close(); + } + + public Graphics getGraphics() { + return getGraphics2D(); + } + + public void setOutputStream(OutputStream out) { + this.out = out; + } + + public void setSize(int width, int height) { + this.width = (float)width; + this.height = (float)height; + } + public void start() { + try { + Rectangle rect = new Rectangle(width, height); + doc = new Document(rect, 0f, 0f, 0f, 0f); + writer = PdfWriter.getInstance(doc, out); + doc.open(); + cb = writer.getDirectContent(); + cb.saveState(); + } + catch (DocumentException de) { + throw new RuntimeException(de); + } + } + +} diff --git a/dasCoreUtil/src/org/das2/util/awt/PngGraphicsOutput.java b/dasCore/src/main/java/org/das2/util/awt/PngGraphicsOutput.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/awt/PngGraphicsOutput.java rename to dasCore/src/main/java/org/das2/util/awt/PngGraphicsOutput.java diff --git a/dasCoreUtil/src/org/das2/util/awt/SvgGraphicsOutput.java b/dasCore/src/main/java/org/das2/util/awt/SvgGraphicsOutput.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/awt/SvgGraphicsOutput.java rename to dasCore/src/main/java/org/das2/util/awt/SvgGraphicsOutput.java diff --git a/dasCoreUtil/src/org/das2/util/awt/package.html b/dasCore/src/main/java/org/das2/util/awt/package.html similarity index 100% rename from dasCoreUtil/src/org/das2/util/awt/package.html rename to dasCore/src/main/java/org/das2/util/awt/package.html diff --git a/dasCoreUtil/src/org/das2/util/fft/SimpleFFT.java b/dasCore/src/main/java/org/das2/util/fft/SimpleFFT.java similarity index 100% rename from dasCoreUtil/src/org/das2/util/fft/SimpleFFT.java rename to dasCore/src/main/java/org/das2/util/fft/SimpleFFT.java diff --git a/dasCoreUtil/src/org/das2/util/fft/package.html b/dasCore/src/main/java/org/das2/util/fft/package.html similarity index 100% rename from dasCoreUtil/src/org/das2/util/fft/package.html rename to dasCore/src/main/java/org/das2/util/fft/package.html diff --git a/dasCore/src/main/java/org/das2/util/filesystem/AppletHttpProtocol.java b/dasCore/src/main/java/org/das2/util/filesystem/AppletHttpProtocol.java new file mode 100755 index 000000000..f8ddbb56f --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/AppletHttpProtocol.java @@ -0,0 +1,71 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.util.filesystem; + +import org.das2.util.DasProgressMonitorInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * uses HTTP, and doesn't download resources to cache + * @author jbf + */ +public class AppletHttpProtocol implements WebProtocol { + + public InputStream getInputStream(WebFileObject fo, org.das2.util.monitor.ProgressMonitor mon) throws IOException { + HttpURLConnection connect = (HttpURLConnection) fo.wfs.getURL(fo.pathname).openConnection(); + connect.connect(); + int len = connect.getContentLength(); + DasProgressMonitorInputStream in = new DasProgressMonitorInputStream(connect.getInputStream(), mon); + if (len != -1) + in.setStreamLength(len); + return in; + } + + public Map getMetadata(WebFileObject fo) throws IOException { + String realName = fo.pathname; + boolean exists; + + URL ur = new URL(fo.wfs.root, realName); + HttpURLConnection connect = (HttpURLConnection) ur.openConnection(); + connect.setRequestMethod("HEAD"); + HttpURLConnection.setFollowRedirects(false); + connect.connect(); + HttpURLConnection.setFollowRedirects(true); + // check for rename, which means we'll do another request + if (connect.getResponseCode() == 303) { + String surl = connect.getHeaderField("Location"); + if (surl.startsWith(fo.wfs.root.toString())) { + realName = surl.substring(fo.wfs.root.toString().length()); + } + connect.disconnect(); + ur = new URL(fo.wfs.root, realName); + connect = (HttpURLConnection) ur.openConnection(); + connect.setRequestMethod("HEAD"); + connect.connect(); + } + exists = connect.getResponseCode() != 404; + + Map result = new HashMap(); + + Map> fields = connect.getHeaderFields(); + for (String key : fields.keySet()) { + List value = fields.get(key); + result.put(key, value.get(0)); + } + + result.put( META_EXIST, String.valueOf(exists) ); + + connect.disconnect(); + + return result; + + } +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/DefaultHttpProtocol.java b/dasCore/src/main/java/org/das2/util/filesystem/DefaultHttpProtocol.java new file mode 100644 index 000000000..f501037d3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/DefaultHttpProtocol.java @@ -0,0 +1,87 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.util.filesystem; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.das2.util.monitor.ProgressMonitor; + +/** + * This is the old way that uses subclasses of WebFileObject. This should + * go away. + * + * @author jbf + */ +public class DefaultHttpProtocol implements WebProtocol { + + public InputStream getInputStream(WebFileObject fo, ProgressMonitor mon) throws IOException { + if (fo.isFolder) { + throw new IllegalArgumentException("is a folder"); + } + if (!fo.localFile.exists()) { + File partFile = new File(fo.localFile.toString() + ".part"); + fo.wfs.downloadFile(fo.pathname, fo.localFile, partFile, mon); + } + return new FileInputStream(fo.localFile); + } + + public Map getMetadata(WebFileObject fo) throws IOException { + boolean exists; + + String realName = fo.pathname; + if ( realName.length()>0 && realName.charAt(0)=='/' ) { + realName= realName.substring(1); + } + URL ur = new URL(fo.wfs.root, realName); + HttpURLConnection connect = (HttpURLConnection) ur.openConnection(); + connect.setRequestMethod("HEAD"); + HttpURLConnection.setFollowRedirects(false); + connect.connect(); + HttpURLConnection.setFollowRedirects(true); + // check for rename, which means we'll do another request + if (connect.getResponseCode() == 303) { + String surl = connect.getHeaderField("Location"); + if (surl.startsWith(fo.wfs.root.toString())) { + realName = surl.substring(fo.wfs.root.toString().length()); + } + connect.disconnect(); + ur = new URL(fo.wfs.root, realName); + connect = (HttpURLConnection) ur.openConnection(); + connect.setRequestMethod("HEAD"); + connect.connect(); + } + exists = connect.getResponseCode() != 404; + + Map result = new HashMap(); + + Map> fields = connect.getHeaderFields(); + for (String key : fields.keySet()) { + List value = fields.get(key); + result.put(key, value.get(0)); + } + + + if ( result.get("Last-Modified")==null ) { + result.put( META_LAST_MODIFIED, new Date( ).toString() ); + } else { + result.put( META_LAST_MODIFIED, new Date( Date.parse( result.get("Last-Modified") ) ).toString() ); + } + + result.put(META_EXIST, String.valueOf(exists)); + + connect.disconnect(); + + return result; + + } +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/DefaultWebProtocol.java b/dasCore/src/main/java/org/das2/util/filesystem/DefaultWebProtocol.java new file mode 100755 index 000000000..031a666c7 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/DefaultWebProtocol.java @@ -0,0 +1,95 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.util.filesystem; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.das2.util.monitor.ProgressMonitor; + +/** + * This is the old way that uses subclasses of WebFileObject. This should + * go away. + * + * @author jbf + */ +public class DefaultWebProtocol implements WebProtocol { + + public InputStream getInputStream(WebFileObject fo, ProgressMonitor mon) throws IOException { + if (fo.isFolder) { + throw new IllegalArgumentException("is a folder"); + } + if (!fo.localFile.exists()) { + File partFile = new File(fo.localFile.toString() + ".part"); + fo.wfs.downloadFile(fo.pathname, fo.localFile, partFile, mon); + } + return new FileInputStream(fo.localFile); + } + + public Map getMetadata(WebFileObject fo) throws IOException { + if (!fo.wfs.getRootURL().getProtocol().equals("ftp")) { + String realName = fo.pathname; + boolean exists; + + URL ur = new URL(fo.wfs.root, realName); + HttpURLConnection connect = (HttpURLConnection) ur.openConnection(); + connect.setRequestMethod("HEAD"); + HttpURLConnection.setFollowRedirects(false); + connect.connect(); + HttpURLConnection.setFollowRedirects(true); + // check for rename, which means we'll do another request + if (connect.getResponseCode() == 303) { + String surl = connect.getHeaderField("Location"); + if (surl.startsWith(fo.wfs.root.toString())) { + realName = surl.substring(fo.wfs.root.toString().length()); + } + connect.disconnect(); + ur = new URL(fo.wfs.root, realName); + connect = (HttpURLConnection) ur.openConnection(); + connect.setRequestMethod("HEAD"); + connect.connect(); + } + exists = connect.getResponseCode() != 404; + + Map result = new HashMap(); + + Map> fields = connect.getHeaderFields(); + for (String key : fields.keySet()) { + List value = fields.get(key); + result.put(key, value.get(0)); + } + + result.put(META_EXIST, String.valueOf(exists)); + + connect.disconnect(); + + return result; + + } else { + + Map result = new HashMap(); + + URL url= new URL( fo.wfs.getRootURL(), fo.pathname ); + URLConnection urlc = url.openConnection(); + try { + urlc.connect(); + urlc.getInputStream().close(); + result.put( META_EXIST, "true" ); + + } catch ( IOException ex ) { + result.put( META_EXIST, "false" ); + } + return result; + + } + } +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/FTPFileSystem.java b/dasCore/src/main/java/org/das2/util/filesystem/FTPFileSystem.java new file mode 100644 index 000000000..5a9758873 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/FTPFileSystem.java @@ -0,0 +1,153 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * FTPFileSystem.java + * + * Created on August 17, 2005, 3:33 PM + * + */ + +package org.das2.util.filesystem; + +import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.monitor.NullProgressMonitor; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Jeremy + */ +public class FTPFileSystem extends WebFileSystem { + FTPFileSystem( URL root ) { + super( root, localRoot(root) ); + } + + public boolean isDirectory(String filename) { + return filename.endsWith("/"); + } + + private String[] parseLsl( String dir, File listing ) throws IOException { + InputStream in= new FileInputStream( listing ); + + BufferedReader reader= new BufferedReader( new InputStreamReader( in ) ); + + String aline= reader.readLine(); + + boolean done= aline==null; + + String types="d-"; + + long bytesRead= 0; + long totalSize; + long sumSize=0; + + List result= new ArrayList( 20 ); + while ( ! done ) { + + bytesRead= bytesRead+ aline.length() + 1; + + aline= aline.trim(); + + if ( aline.length() == 0 ) { + done=true; + } else { + + char type= aline.charAt(0); + if ( type == 't' ) { + if ( aline.indexOf( "total" ) == 0 ) { + //totalSize= Long.parseLong( aline.substring( 5 ).trim() ); + } + } + + if ( types.indexOf(type)!=-1 ) { + int i= aline.lastIndexOf( ' ' ); + String name= aline.substring( i+1 ); + //long size= Long.parseLong( aline.substring( 31, 31+12 ) ); // tested on: linux + boolean isFolder= type=='d'; + + result.add( name + ( isFolder ? "/" : "" ) ); + + //sumSize= sumSize + size; + + } + + aline= reader.readLine(); + done= aline==null; + + } // while + + } + return (String[])result.toArray(new String[result.size()]); + } + + public String[] listDirectory(String directory) { + directory= toCanonicalFolderName( directory ); + + try { + new File( localRoot, directory ).mkdirs(); + File listing= new File( localRoot, directory + ".listing" ); + if ( !listing.canRead() ) { + File partFile= listing; + downloadFile( directory, listing, partFile, new NullProgressMonitor() ); + } + listing.deleteOnExit(); + return parseLsl( directory, listing ); + } catch ( IOException e ) { + throw new RuntimeException(e); + } + } + + protected void downloadFile(String filename, java.io.File targetFile, File partFile, ProgressMonitor monitor ) throws java.io.IOException { + FileOutputStream out=null; + InputStream is= null; + try { + filename= toCanonicalFilename( filename ); + URL url= new URL( root + filename.substring(1) ); + + URLConnection urlc = url.openConnection(); + + int i= urlc.getContentLength(); + monitor.setTaskSize( i ); + out= new FileOutputStream( partFile ); + is = urlc.getInputStream(); // To download + monitor.started(); + copyStream(is, out, monitor ); + monitor.finished(); + out.close(); + is.close(); + partFile.renameTo( targetFile ); + } catch ( IOException e ) { + if ( out!=null ) out.close(); + if ( is!=null ) is.close(); + partFile.delete(); + throw e; + } + + } + +} \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/util/filesystem/FileObject.java b/dasCore/src/main/java/org/das2/util/filesystem/FileObject.java new file mode 100755 index 000000000..d06a24c2d --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/FileObject.java @@ -0,0 +1,196 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * WebFile.java + * + * Created on May 14, 2004, 10:06 AM + */ + +package org.das2.util.filesystem; + +import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.monitor.NullProgressMonitor; +import java.io.*; +import java.nio.channels.*; + +/** + *

    Class for describing and accessing files in file systems. This + * is similar to java.io.File, except that it can describe files on remote + * file systems like an ftp site.

    + * + *

    Note: this is modeled after the NetBeans fileSystem FileObject, with the + * thought that we might use it later.

    + * + *

    TODO: investigate using javax.tools.FileObject. + * + * @author Jeremy + */ +public abstract class FileObject { + + /** + * returns true if the file can be read by the client. + * @return true if the file can be read (see getInputStream) + */ + public abstract boolean canRead(); + + /** + * returns objects within a folder. + * @return an array of all FileObjects with the folder. + */ + public abstract FileObject[] getChildren() throws IOException; + + /** + * opens an inputStream, perhaps transferring the file to a + * cache first. + * @param monitor for monitoring the download. The monitor won't be used when the access + * is immediate, for example with local FileObjects. + * @throws FileNotFoundException if the file doesn't exist. + * @throws IOException + * @return an InputStream + */ + public abstract InputStream getInputStream( ProgressMonitor monitor ) throws FileNotFoundException, IOException; + + /** + * opens an inputStream, perhaps transferring the file to a + * cache first. Note no monitor is used but this may block at sub-interactive + * time until the file is downloaded. + * + * @throws FileNotFoundException if the file doesn't exist. + * @throws IOException + * @return an InputStream + */ + public InputStream getInputStream() throws FileNotFoundException, IOException { + return getInputStream( new NullProgressMonitor() ); + } + + /** + * opens a Channel, perhaps transferring the file to a local cache first. monitor + * is used to monitor the download. + * @param monitor for monitoring the download. The monitor won't be used when the access + * is immediate, for example with local FileObjects. + * @throws FileNotFoundException if the file doesn't exist. + * @throws IOException + * @return a java.nio.channels.Channel for fast IO reads. + */ + public abstract ReadableByteChannel getChannel( ProgressMonitor monitor ) throws FileNotFoundException, IOException; + + /** + * opens a Channel, but without a monitor. Note this may block at sub-interactive time + * if the FileObject needs to be downloaded before access. + * @throws FileNotFoundException if the file doesn't exist. + * @throws IOException + * @return a java.nio.channels.Channel for fast IO reads. + */ + public ReadableByteChannel getChannel() throws FileNotFoundException, IOException { + return getChannel( new NullProgressMonitor() ); + } + + /** + * gets a File object that can be opened by the client. This may download a remote file, so + * a progress monitor can be used to monitor the download. + * @return a reference to a File that can be opened. + * @param monitor for monitoring the download. The monitor won't be used when the access + * is immediate, for example with local FileObjects. + * @throws java.io.FileNotFoundException if the file doesn't exist. + * @throws IOException if the file cannot be made local + * @throws NullPointerException if the monitor is null. + */ + public abstract File getFile( ProgressMonitor monitor ) throws FileNotFoundException, IOException; + + /** + * gets a File object that can be opened by the client. Note this may block at sub-interactive time + * if the remote file needs to be downloaded before access. + * @return a reference to a File that can be opened. + * @throws java.io.FileNotFoundException if the file doesn't exist. + */ + public File getFile() throws FileNotFoundException, IOException { + return getFile( new NullProgressMonitor() ); + } + + /** + * returns the parent FileObject (a folder). + * If the fileObject is root, then null should be returned. + * @return the parent folder of this object. + */ + public abstract FileObject getParent(); + + /** + * returns the size of the file. + * @return the size in bytes of the file, and -1 if the size is unknown. + */ + public abstract long getSize(); + + /** + * returns true if the file is a data file that to be used + * reading or writing data. (And not a folder.) + * @return true if the file is a data file + */ + public abstract boolean isData(); + + /** + * indicates the type of FileObject + * @return true if the object is a folder (directory). + */ + public abstract boolean isFolder(); + + /** + * true is the file is read-only. + * @return true if the file is read-only + */ + public abstract boolean isReadOnly(); + + /** + * returns true if this is the root of the filesystem it came from. + * @return true if this is the root of the filesystem it came from. + */ + public abstract boolean isRoot(); + + /** + * returns true if the file is locally available, meaning clients can + * call getFile() and the readble File reference will be available in + * interactive time. Note that isLocal does not imply exists(). Also, + * This may result in side effects such as a website hit. + */ + public abstract boolean isLocal(); + + /** + * returns true if the file exists. This may have the side effect of + * downloading the file. + * @return true if the file exists + */ + public abstract boolean exists(); + + /** + * returns the canonical name of the file within the filesystem. For example, + * in the local filesystem /mnt/data/steven/, /mnt/data/steven/jan/01.dat + * would be /jan/01.dat. + * + * For example, /a/b/c.dat. + * @return the name of the file within the FileSystem. + */ + public abstract String getNameExt(); + + /** + * returns the Date when the file was last modified. or new Date(0L) if the date is + * not available. + * + * @return the last modified Date, or new Date(0) if it is not available. + */ + public abstract java.util.Date lastModified(); + +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/FileSystem.java b/dasCore/src/main/java/org/das2/util/filesystem/FileSystem.java new file mode 100755 index 000000000..6466ffeda --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/FileSystem.java @@ -0,0 +1,304 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * FileSystem.java + * + * Created on May 14, 2004, 12:43 PM + */ + +package org.das2.util.filesystem; +import java.io.*; +import java.net.*; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.das2.util.monitor.NullProgressMonitor; +import org.das2.util.monitor.ProgressMonitor; + +/** + * Filesystems provide an abstraction layer so that clients can access + * any heirarchy of files in a implementation-independent way. For example, + * remote filesystems accessible via http are accessible through the same + * interface as a local filesystem. + * + * @author Jeremy + */ + + +public abstract class FileSystem { + + URL root; + protected static Logger logger= Logger.getLogger( "das2.filesystem" ); + + public static class FileSystemOfflineException extends IOException { + public FileSystemOfflineException() { + super(); + } + public FileSystemOfflineException( String message ) { + super( message ); + } + public FileSystemOfflineException( IOException e ) { + super( e.getMessage() ); + initCause(e); + } + } + + public static FileSystem create(URL root) throws FileSystemOfflineException { + return create(root, new NullProgressMonitor()); + } + /** + * Creates a FileSystem by parsing the URL and creating the correct FS type. + * Presently, file, http, and ftp are supported. If the URL contains a folder + * ending in .zip and a FileSystemFactory is registered as handling .zip, then + * The zip file will be transferred and the zip file mounted. + */ + public static FileSystem create( URL root, ProgressMonitor mon ) throws FileSystemOfflineException { + logger.fine("create filesystem "+root); + FileSystemFactory factory; + if ( root.getPath().contains(".zip") && registry.containsKey("zip") ) { + try { + String surl= root.toString(); + int i= surl.indexOf(".zip"); + String[] ss= FileSystem.splitUrl( surl.substring(0,i+4) ); + URL parent = new URL(ss[2]); //getparent + String zipname = ss[3].substring(ss[2].length()); + String subdir = surl.substring(i+4); + FileSystem remote = FileSystem.create(parent); + File localZipFile = remote.getFileObject(zipname).getFile(mon); + factory = (FileSystemFactory) registry.get("zip"); + FileSystem zipfs = factory.createFileSystem(localZipFile.toURI().toURL()); + if ( subdir.equals("") || subdir.equals("/") ) { + return zipfs; + } else { + return new SubFileSystem(zipfs, subdir); + } + } catch (IOException ex) { + throw new FileSystemOfflineException(ex); + } + } else { + factory= (FileSystemFactory) registry.get(root.getProtocol()); + } + if ( factory==null ) { + throw new IllegalArgumentException( "unsupported protocol: "+root ); + } else { + return factory.createFileSystem(root); + } + } + + public static FileSystemSettings settings() { + return settings; + } + + private static FileSystemSettings settings= new FileSystemSettings(); + + static HashMap registry; + static { + registry= new HashMap(); + registry.put("file",new LocalFileSystemFactory() ); + registry.put("http",new HttpFileSystemFactory() ); + registry.put("https",new HttpFileSystemFactory() ); + registry.put("ftp",new FtpFileSystemFactory() ); + } + + public static void registerFileSystemFactory( String proto, FileSystemFactory factory ) { + registry.put( proto, factory ); + } + + protected FileSystem( URL root ) { + if ( !root.toString().endsWith("/" ) ) { + String s= root.toString(); + try { + root= new URL( s+"/" ); + } catch ( MalformedURLException e ) { + throw new RuntimeException(e); + } + } + this.root= root; + } + + public URL getRootURL() { + return root; + } + + private static String getRegexFromGlob( String glob ) { + final String regex= glob.replaceAll("\\.","\\\\.").replaceAll("\\*","\\.\\*").replaceAll("\\?","\\."); + return regex; + } + + + /** + * returns the canonical name /a/b/c.dat of a string that + * contains backslashes and might not have the leading / + * and trailing slashes. Also, double slashes (//) are + * removed. Note this is the name of the FileObject + * within the FileSystem. + */ + protected static String toCanonicalFilename( String filename ) { + filename= filename.replaceAll( "\\\\", "/" ); + if ( filename.length()==0 || filename.charAt(0)!='/' ) { + filename= "/"+filename; + } + filename= filename.replaceAll( "//", "/" ); + return filename; + } + + protected static String toCanonicalFolderName( String name ) { + name= toCanonicalFilename( name ); + if ( !name.endsWith("/") ) name= name + "/"; + return name; + } + + /** + * return the FileObject that corresponds to the name. + */ + abstract public FileObject getFileObject( String filename ); + + abstract public boolean isDirectory( String filename ) throws IOException; + + /** + * returns a list of the names of the files in a directory. Names ending + * in "/" are themselves directories, and the "/" is not part of the name. + * This is optional, and a directory may or may not be tagged with the trailing + * slash. + */ + abstract public String[] listDirectory( String directory ) throws IOException; + + /** + * returns a list of the names of the files in a directory that match regex. + * Trailing slashes on directory names are not part of the name and need + * not be part of the regex. + */ + abstract public String[] listDirectory( String directory, String regex ) throws IOException; + + /** + * Boolean.TRUE if the filesystem ignores case, such as Windows local filesystem. + */ + public static final String PROP_CASE_INSENSITIVE= "caseInsensitive"; + + protected HashMap properties= new HashMap(5); + + public Object getProperty( String name ) { + return properties.get(name); + } + + /** + * return the folder that is a local copy of the filesystem. + * For LocalFilesystem, this is the same as the filesystem. For remote + * filesystems, this is a folder within their home directory. + * Note File.getAbsolutePath() returns the string representation of this root. + * @return the folder that is a local copy of the filesystem. + */ + abstract public File getLocalRoot(); + + /** + * create a new filesystem that is a part of this filesystem, rooted at + * directory. + */ + public FileSystem createFileSystem( String directory ) { + try { + return new SubFileSystem( this, toCanonicalFolderName(directory) ); + } catch ( MalformedURLException e ) { + throw new IllegalArgumentException("invalid directory: "+directory); + } + } + + /** + * returns a String[5]: + * [0] is proto "http://" + * [1] will be the host + * [2] is proto + path + * [3] is proto + path + file + * [4] is file ext + * [5] is params, not including ?. + * @param surl an url string to parse. + */ + public static String[] splitUrl( String surl ) { + + if ( !( surl.startsWith("file:/") || surl.startsWith("ftp://") || surl.startsWith("http://") || surl.startsWith("https://") ) ) { + surl= "file://"+ ( ( surl.charAt(0)=='/' ) ? surl : ( '/' + surl ) ); // Windows c: + } + + int i; + + String params=null; + + int fileEnd; + // check for just one ? + i= surl.indexOf( "?" ); + if ( i != -1 ) { + fileEnd= i; + params= surl.substring(i+1); + i= surl.indexOf("?",i+1); + if ( i!=-1 ) { + throw new IllegalArgumentException("too many ??'s!"); + } + } else { + fileEnd= surl.length(); + } + + i= surl.lastIndexOf("/"); + String surlDir= surl.substring(0,i); + + String file= surl.substring(i,fileEnd); + i= file.lastIndexOf('.'); + String ext; + if ( i!=-1 ) { + ext= file.substring(i+1); + } else { + ext= ""; + } + + // let i2 be the end if the protocol and the beginning of the file. + int i2= surl.indexOf("://")+3; + if ( surl.indexOf("://")==-1 && surl.startsWith("file:/" ) ) i2=5; + int i3= surl.indexOf("/",i2+1); + if ( i3==-1 ) i3= i2; + String[] result= new String[6]; + result[0]= surl.substring(0,i2); + result[1]= surl.substring(0,i3); + result[2]= surlDir+"/"; + result[3]= surl.substring(0,fileEnd); + result[4]= ext; + result[5]= params; + + return result; + + } + + /** + * DirectoryEntry defines a structure for containing directory entry data. + */ + public class DirectoryEntry { + /** + * the name within the context of the directory. + */ + public String name; + /** + * the type of entry. d=directory, f=file + */ + public char type; + /** + * the length in bytes of the entry + */ + public long size; + /** + * modified date, in seconds since 1970. + */ + public long modified; + } +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/FileSystemFactory.java b/dasCore/src/main/java/org/das2/util/filesystem/FileSystemFactory.java new file mode 100644 index 000000000..0f413c693 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/FileSystemFactory.java @@ -0,0 +1,35 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * FileSystemFactory.java + * + * Created on November 15, 2007, 9:26 AM + */ + +package org.das2.util.filesystem; + +import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; +import java.net.URL; + +/** + * creates a new instance of a type of filesystem + * @author jbf + */ +public interface FileSystemFactory { + FileSystem createFileSystem( URL root ) throws FileSystemOfflineException; +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/FileSystemSettings.java b/dasCore/src/main/java/org/das2/util/filesystem/FileSystemSettings.java new file mode 100755 index 000000000..7ace8b20e --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/FileSystemSettings.java @@ -0,0 +1,110 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.util.filesystem; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.File; +import org.das2.DasApplication; + +/** + * controls for file systems. + * @author jbf + */ +public class FileSystemSettings { + + protected FileSystemSettings() { + File local; + if ( !DasApplication.hasAllPermission() ) { + local= new File("applet_mode"); // this should not be opened. + } else { + if (System.getProperty("user.name").equals("Web")) { + local = new File("/tmp"); + } else { + local = new File(System.getProperty("user.home")); + } + local = new File(local, ".das2/fsCache/wfs/"); + } + localCacheDir = local; + } + + public enum Persistence { + /** + * No persistence. No files are cached locally. + */ + NONE, + /** + * Within a session, files are cached locally. This is the default. + */ + SESSION, + /** + * Files persist until a new version is available on the remote cache. + */ + EXPIRES, + /** + * Files persist indefinately, and the server is only contacted when a file + * is not available locally. + */ + ALWAYS + } + + protected File localCacheDir = null; + /** + * setting for the location of where the local cache is kept. + */ + public static final String PROP_LOCALCACHEDIR = "localCacheDir"; + + public File getLocalCacheDir() { + return localCacheDir; + } + + public void setLocalCacheDir(File localCacheDir) { + File oldLocalCacheDir = this.localCacheDir; + this.localCacheDir = localCacheDir; + propertyChangeSupport.firePropertyChange(PROP_LOCALCACHEDIR, oldLocalCacheDir, localCacheDir); + } + protected Persistence persistence = Persistence.SESSION; + /** + * setting for how long files should be kept and using in the cache. + */ + public static final String PROP_PERSISTENCE = "persistence"; + + public Persistence getPersistence() { + return persistence; + } + + public void setPersistence(Persistence persistence) { + Persistence oldPersistence = this.persistence; + this.persistence = persistence; + propertyChangeSupport.firePropertyChange(PROP_PERSISTENCE, oldPersistence, persistence); + } + protected boolean allowOffline = false; + /** + * allow use of persistent, cached files when the file system is not accessible. + * FileSystem implementations will throw FileNotFound exception when remote + * resources are not available, and FileSystemOfflineExceptions are not + * thrown. + */ + public static final String PROP_ALLOWOFFLINE = "allowOffline"; + + public boolean isAllowOffline() { + return allowOffline; + } + + public void setAllowOffline(boolean allowOffline) { + boolean oldAllowOffline = allowOffline; + this.allowOffline = allowOffline; + propertyChangeSupport.firePropertyChange(PROP_ALLOWOFFLINE, oldAllowOffline, allowOffline); + } + private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); + + public void addPropertyChangeListener(PropertyChangeListener listener) { + propertyChangeSupport.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + propertyChangeSupport.removePropertyChangeListener(listener); + } +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/FileSystemUtil.java b/dasCore/src/main/java/org/das2/util/filesystem/FileSystemUtil.java new file mode 100644 index 000000000..4502dd6ea --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/FileSystemUtil.java @@ -0,0 +1,56 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.util.filesystem; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.channels.Channel; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; + +/** + * + * @author jbf + */ +public class FileSystemUtil { + + /** + * Dump the contents of the inputstream into a file. If the inputStream comes + * from a file, then java.nio is used to transfer the data quickly. + * @param in + * @param f + * @throws java.io.FileNotFoundException + * @throws java.io.IOException + */ + public static void dumpToFile( InputStream in, File f ) throws FileNotFoundException, IOException { + ReadableByteChannel ic = Channels.newChannel(in); + FileChannel oc = new FileOutputStream(f).getChannel(); + if ( ic instanceof FileChannel ) { + FileChannel fic= (FileChannel)ic; + fic.transferTo(0, fic.size(), oc); + fic.close(); + oc.close(); + } else { + ByteBuffer buf= ByteBuffer.allocateDirect( 16*1024 ); + while ( ic.read(buf) >= 0 || buf.position() != 0 ) { + buf.flip(); + oc.write(buf); + buf.compact(); + } + } + } + + public static void main( String[] args ) throws Exception { + InputStream in= new ByteArrayInputStream( "Hello there".getBytes() ); + dumpToFile( in, new File( "/home/jbf/text.txt" ) ); + } +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/FtpFileSystemFactory.java b/dasCore/src/main/java/org/das2/util/filesystem/FtpFileSystemFactory.java new file mode 100644 index 000000000..e31842b1f --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/FtpFileSystemFactory.java @@ -0,0 +1,42 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * FtpFileSystemFactory.java + * + * Created on November 15, 2007, 9:31 AM + */ + +package org.das2.util.filesystem; + +import java.net.URL; + +/** + * + * @author jbf + */ +public class FtpFileSystemFactory implements FileSystemFactory { + + /** Creates a new instance of FtpFileSystemFactory */ + public FtpFileSystemFactory() { + } + + public FileSystem createFileSystem(URL root) throws FileSystem.FileSystemOfflineException { + return FTPFileSystem.create( root ); + } + +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/Glob.java b/dasCore/src/main/java/org/das2/util/filesystem/Glob.java new file mode 100644 index 000000000..dc5c71fe6 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/Glob.java @@ -0,0 +1,149 @@ +/* File: Glob.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util.filesystem; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; +import javax.swing.filechooser.FileFilter; + +/** + * known bug: *.java matches ".java". The unix glob behavior is to + * require that a leading . must be explicitly matched. + * @author jbf + */ +public class Glob { + + /** + * + * @param regex + * @return + */ + private static String getParentDirectory( String regex ) { + String[] s= regex.split( "/" ); + String dirRegex; + if ( s.length>1 ) { + dirRegex= s[0]; + for ( int i=1; i .*\.dat + */ + public static Pattern getPattern( String glob ) { + final String regex= getRegex( glob ); + final Pattern absPattern= Pattern.compile(regex); + return absPattern; + } + + /** + * converts a glob into a regex. + */ + public static String getRegex( String glob ) { + return glob.replaceAll("\\.","\\\\.").replaceAll("\\*","\\.\\*").replaceAll("\\?","\\."); + } + + /** + * unglob the glob into an array of the matching FileObjects. + * @param glob + * @return an array of FileObjects that match the glob. + */ + public static FileObject[] unGlob( FileSystem fs, String glob ) throws IOException { + return unGlob( fs, glob, false ); + } + + private static FileObject[] unGlob( FileSystem fs, String glob, final boolean directoriesOnly ) throws IOException { + if ( File.separatorChar=='\\' ) glob= glob.replaceAll( "\\\\", "/" ); + String parentGlob= getParentDirectory( glob ); + FileObject[] files; + if ( parentGlob!=null ) { + if ( isRoot( parentGlob ) ) { + FileObject rootFile= fs.getFileObject(parentGlob); + if ( rootFile.exists() ) { + files= new FileObject[] { rootFile }; + } else { + throw new IllegalArgumentException("root does not exist: "+glob); + } + } else { + files= unGlob( fs, parentGlob, true ); + } + } else { + throw new IllegalArgumentException("absolute files only"); + } + + final String regex= getRegex( glob ); + final Pattern absPattern= Pattern.compile(regex); + List list= new ArrayList(); + for ( int i=0; i utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * HtmlUtil.java + * + * Created on May 14, 2004, 9:06 AM + */ + +package org.das2.util.filesystem; + +import org.das2.util.Base64; +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @author Jeremy + */ +public class HtmlUtil { + + public static boolean isDirectory( URL url ) { + String file= url.getFile(); + return file.charAt(file.length()-1) != '/'; + } + + public static URL[] getDirectoryListing( URL url ) throws IOException { + FileSystem.logger.finer("listing "+url); + + String file= url.getFile(); + if ( file.charAt(file.length()-1)!='/' ) { + url= new URL( url.toString()+'/' ); + } + + ArrayList urlList= new ArrayList(); + + long t0= System.currentTimeMillis(); + URLConnection urlConnection = url.openConnection(); + urlConnection.setAllowUserInteraction(false); + + int contentLength=10000; + + //System.err.println("connected in "+( System.currentTimeMillis() - t0 )+" millis" ); + if ( url.getUserInfo()!=null ) { + String encode= new String( Base64.encodeBytes(url.getUserInfo().getBytes()) ); + urlConnection.setRequestProperty("Authorization", "Basic " + encode); + } + InputStream urlStream = urlConnection.getInputStream(); + + // search the input stream for links + // first, read in the entire URL + byte b[] = new byte[10000]; + int numRead = urlStream.read(b); + StringBuffer contentBuffer = new StringBuffer( contentLength ); + contentBuffer.append( new String( b, 0, numRead ) ); + while (numRead != -1) { + FileSystem.logger.finest("download listing"); + numRead = urlStream.read(b); + if (numRead != -1) { + String newContent = new String(b, 0, numRead); + contentBuffer.append( newContent ); + } + } + urlStream.close(); + + // System.err.println("read listing data in "+( System.currentTimeMillis() - t0 )+" millis" ); + String content= contentBuffer.toString(); + + String hrefRegex= "(?i)href\\s*=\\s*([\"'])(.+?)\\1"; + Pattern hrefPattern= Pattern.compile( hrefRegex ); + + Matcher matcher= hrefPattern.matcher( content ); + + while ( matcher.find() ) { + FileSystem.logger.finest("parse listing"); + String strLink= matcher.group(2); + URL urlLink= null; + + try { + urlLink = new URL(url, strLink); + strLink = urlLink.toString(); + } catch (MalformedURLException e) { + System.err.println("bad URL: "+url+" "+strLink); + continue; + } + + if ( urlLink.toString().startsWith(url.toString()) && null==urlLink.getQuery() ) { + urlList.add( urlLink ); + } + } + + return (URL[]) urlList.toArray( new URL[urlList.size()] ); + } + +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/HttpFileSystem.java b/dasCore/src/main/java/org/das2/util/filesystem/HttpFileSystem.java new file mode 100755 index 000000000..5c512ce40 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/HttpFileSystem.java @@ -0,0 +1,350 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * WebFileSystem.java + * + * Created on May 13, 2004, 1:22 PM + * + * A WebFileSystem allows web files to be opened just as if they were + * local files, since it manages the transfer of the file to a local + * file system. + */ +package org.das2.util.filesystem; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import org.das2.DasApplication; +import org.das2.util.Base64; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; +import java.net.*; +import java.util.*; +import java.util.regex.*; + +/** + * + * @author Jeremy + */ +public class HttpFileSystem extends WebFileSystem { + + private HashMap listings; + private static HashMap instances = new HashMap(); + /** + * Keep track of active downloads. This handles, for example, the case + * where the same file is requested several times by different threads. + */ + private HashMap downloads = new HashMap(); + private String userPass; + + /** Creates a new instance of WebFileSystem */ + private HttpFileSystem(URL root, File localRoot) { + super(root, localRoot); + listings = new HashMap(); + } + + public static synchronized HttpFileSystem createHttpFileSystem( URL root ) throws FileSystemOfflineException { + if (instances.containsKey(root.toString())) { + logger.finer("reusing " + root); + return (HttpFileSystem) instances.get(root.toString()); + } else { + try { + // verify URL is valid and accessible + HttpURLConnection urlc = (HttpURLConnection) root.openConnection(); + urlc.setRequestMethod("HEAD"); + if (root.getUserInfo() != null) { + String encode = Base64.encodeBytes(root.getUserInfo().getBytes()); + // xerces String encode= new String( Base64.encode(root.getUserInfo().getBytes()) ); + urlc.setRequestProperty("Authorization", "Basic " + encode); + } + + boolean offline= true; + urlc.connect(); + if ( urlc.getResponseCode() != HttpURLConnection.HTTP_OK && urlc.getResponseCode()!=HttpURLConnection.HTTP_FORBIDDEN ) { + if ( FileSystem.settings().isAllowOffline() ) { + logger.fine("remote filesystem is offline, allowing access to local cache."); + } else { + throw new FileSystemOfflineException("" + urlc.getResponseCode() + ": " + urlc.getResponseMessage()); + } + } else { + offline= false; + } + + File local; + + if ( DasApplication.hasAllPermission() ) { + local = localRoot(root); + logger.finer("initializing httpfs " + root + " at " + local); + } else { + local= null; + logger.finer("initializing httpfs " + root + " in applet mode" ); + } + HttpFileSystem result = new HttpFileSystem(root, local); + result.offline= offline; + + instances.put(root.toString(), result); + return result; + + } catch (FileSystemOfflineException e) { + throw e; + } catch (IOException e) { + throw new FileSystemOfflineException(e); + } + } + } + + protected void downloadFile(String filename, File f, File partFile, ProgressMonitor monitor) throws IOException { + + logger.fine("downloadFile(" + filename + ")"); + + boolean waitForAnother; + synchronized (downloads) { + ProgressMonitor mon = (ProgressMonitor) downloads.get(filename); + if (mon != null) { // the httpFS is already loading this file, so wait. + + monitor.setProgressMessage("Waiting for file to download"); + while (mon != null) { + while (!mon.isStarted()) { + try { + downloads.wait(100); + } catch (InterruptedException e) { + } + } + monitor.setTaskSize(mon.getTaskSize()); + monitor.started(); + if (monitor.isCancelled()) { + mon.cancel(); + } + monitor.setTaskProgress(mon.getTaskProgress()); + try { + downloads.wait(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + mon = (ProgressMonitor) downloads.get(filename); + logger.finest("waiting for download"); + + } + monitor.finished(); + if (f.exists()) { + return; + } else { + throw new FileNotFoundException("expected to find " + f); + } + } else { + downloads.put(filename, monitor); + waitForAnother = false; + } + } + + try { + logger.fine("downloadFile " + filename); + + URL remoteURL = new URL(root.toString() + filename); + + URLConnection urlc = remoteURL.openConnection(); + if (root.getUserInfo() != null) { + String encode = new String(Base64.encodeBytes(root.getUserInfo().getBytes())); + urlc.setRequestProperty("Authorization", "Basic " + encode); + } + + HttpURLConnection hurlc = (HttpURLConnection) urlc; + if ( hurlc.getResponseCode()==404 ) { + logger.fine("" + hurlc.getResponseCode() + " URL: " + remoteURL); + throw new FileNotFoundException("not found: "+remoteURL ); + } else if (hurlc.getResponseCode() != 200) { + logger.fine("" + hurlc.getResponseCode() + " URL: " + remoteURL); + throw new IOException(hurlc.getResponseMessage()); + } + + monitor.setTaskSize(urlc.getContentLength()); + + if (!f.getParentFile().exists()) { + logger.fine("make dirs " + f.getParentFile()); + f.getParentFile().mkdirs(); + } + if (partFile.exists()) { + logger.fine("clobber file " + f); + if (!partFile.delete()) { + logger.fine("Unable to clobber file " + f + ", better use it for now."); + return; + } + } + + if (partFile.createNewFile()) { + InputStream in; + in = urlc.getInputStream(); + + logger.fine("transferring bytes of " + filename); + monitor.setLabel("downloading file"); + monitor.started(); + + FileOutputStream out = new FileOutputStream(partFile); + + try { + copyStream(in, out, monitor); + monitor.finished(); + out.close(); + in.close(); + partFile.renameTo(f); + } catch (IOException e) { + out.close(); + in.close(); + partFile.delete(); + throw e; + } + } else { + throw new IOException("couldn't create local file: " + f); + } + } finally { + synchronized (downloads) { + downloads.remove(filename); + downloads.notifyAll(); + } + } + } + + /** + * this is introduced to support checking if the symbol foo/bar is a folder by checking + * for a 303 redirect. + * EXIST->Boolean + * REAL_NAME->String + * @param f + * @throws java.io.IOException + */ + protected Map getHeadMeta( String f ) throws IOException { + String realName= f; + boolean exists; + try { + URL ur = new URL(this.root, f); + HttpURLConnection connect = (HttpURLConnection) ur.openConnection(); + connect.setRequestMethod("HEAD"); + HttpURLConnection.setFollowRedirects(false); + connect.connect(); + HttpURLConnection.setFollowRedirects(true); + // check for rename, which means we'll do another request + if ( connect.getResponseCode()==303 ) { + String surl= connect.getHeaderField("Location"); + if ( surl.startsWith(root.toString()) ) { + realName= surl.substring(root.toString().length()); + } + connect.disconnect(); + ur = new URL(this.root, realName); + connect = (HttpURLConnection) ur.openConnection(); + connect.setRequestMethod("HEAD"); + connect.connect(); + } + exists= connect.getResponseCode()!=404; + + Map result= new HashMap(); + result.putAll( connect.getHeaderFields() ); + connect.disconnect(); + + return result; + + } catch (MalformedURLException ex) { + throw new IllegalArgumentException(ex); + } + + } + + + /** dumb method looks for / in parent directory's listing. Since we have + * to list the parent, then IOException can be thrown. + * + */ + public boolean isDirectory(String filename) throws IOException { + + if ( localRoot==null ) return filename.endsWith("/"); + + File f = new File(localRoot, filename); + if (f.exists()) { + return f.isDirectory(); + } else { + if (filename.endsWith("/")) { + return true; + } else { + File parentFile = f.getParentFile(); + String parent = getLocalName(parentFile); + if (!parent.endsWith("/")) { + parent = parent + "/"; + } + String[] list = listDirectory(parent); + String lookFor; + if (filename.startsWith("/")) { + lookFor = filename.substring(1) + "/"; + } else { + lookFor = filename + "/"; + } + for (int i = 0; i < list.length; i++) { + if (list[i].equals(lookFor)) { + return true; + } + } + return false; + } + } + } + + public String[] listDirectory(String directory) throws IOException { + directory = this.toCanonicalFilename(directory); + if (!isDirectory(directory)) { + throw new IllegalArgumentException("is not a directory: " + directory); + } + + if (!directory.endsWith("/")) { + directory = directory + "/"; + } + synchronized (listings) { + if (listings.containsKey(directory)) { + return (String[]) listings.get(directory); + } else { + + URL[] list = HtmlUtil.getDirectoryListing(getURL(directory)); + String[] result = new String[list.length]; + int n = directory.length(); + for (int i = 0; i < list.length; i++) { + URL url = list[i]; + result[i] = getLocalName(url).substring(n); + } + listings.put(directory, result); + return result; + } + } + } + + public String[] listDirectory(String directory, String regex) throws IOException { + directory = toCanonicalFilename(directory); + if (!isDirectory(directory)) { + throw new IllegalArgumentException("is not a directory: " + directory); + } + + String[] listing = listDirectory(directory); + Pattern pattern = Pattern.compile(regex + "/?"); + ArrayList result = new ArrayList(); + for (int i = 0; i < listing.length; i++) { + if (pattern.matcher(listing[i]).matches()) { + result.add(listing[i]); + } + } + return (String[]) result.toArray(new String[result.size()]); + + } +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/HttpFileSystemFactory.java b/dasCore/src/main/java/org/das2/util/filesystem/HttpFileSystemFactory.java new file mode 100644 index 000000000..510eb61db --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/HttpFileSystemFactory.java @@ -0,0 +1,47 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * HttpFileSystemFactory.java + * + * Created on November 15, 2007, 9:28 AM + * + */ + +package org.das2.util.filesystem; + +import org.das2.DasApplication; +import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; +import java.net.URL; + +/** + * + * @author jbf + */ +public class HttpFileSystemFactory implements FileSystemFactory { + + /** Creates a new instance of HttpFileSystemFactory */ + public HttpFileSystemFactory() { + } + + public FileSystem createFileSystem(URL root) throws FileSystemOfflineException { + HttpFileSystem hfs= HttpFileSystem.createHttpFileSystem( root ); + if ( ! DasApplication.hasAllPermission() ) hfs.setAppletMode(true); + return hfs; + } + +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/LocalFileObject.java b/dasCore/src/main/java/org/das2/util/filesystem/LocalFileObject.java new file mode 100755 index 000000000..69e0d6cf4 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/LocalFileObject.java @@ -0,0 +1,125 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * LocalFileObject.java + * + * Created on May 25, 2004, 6:01 PM + */ + +package org.das2.util.filesystem; + +import org.das2.util.monitor.ProgressMonitor; +import java.io.*; +import org.das2.util.monitor.NullProgressMonitor; + +/** + * + * @author Jeremy + */ +public class LocalFileObject extends FileObject { + + File localFile; + File localRoot; + LocalFileSystem lfs; + + protected LocalFileObject( LocalFileSystem lfs, File localRoot, String filename ) { + this.lfs= lfs; + this.localFile= new File( localRoot, filename ); + this.localRoot= localRoot; + } + + public boolean canRead() { + return localFile.canRead(); + } + + public FileObject[] getChildren() { + File[] files= localFile.listFiles(); + LocalFileObject[] result= new LocalFileObject[files.length]; + for ( int i=0; i utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * FileWebFileSystem.java + * + * Created on May 14, 2004, 1:02 PM + */ + +package org.das2.util.filesystem; + +import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.regex.*; + +/** + * + * @author Jeremy + */ +public class LocalFileSystem extends FileSystem { + + File localRoot; + + /** + * Note the String used to create the URL should have either one or three slashes: + * file:/home/jbf or file:///home/jbf but not + * file://home/jbf + * Also, on Windows, /c:/documents and settings/jbf/ is okay. + */ + protected LocalFileSystem(URL root) throws FileSystemOfflineException { + super( root ); + if ( !("file".equals(root.getProtocol()) ) ) { + throw new IllegalArgumentException("protocol not file: "+root); + } + String surl= root.toString(); + if ( !surl.endsWith("/") ) surl+="/"; + String[] split= FileSystem.splitUrl( surl ); + + localRoot=new File( split[2].substring(split[0].length()) ); + if ( !localRoot.exists() ) { + File[] roots= File.listRoots(); + if ( Arrays.asList(roots).contains(localRoot) ) { + throw new FileSystemOfflineException(); + } else { + throw new IllegalArgumentException( "root does not exist: "+root ); + } + } + boolean b= new File("xxx").equals(new File("XXX")); + properties.put( PROP_CASE_INSENSITIVE, Boolean.valueOf( b ) ); + } + + public boolean isDirectory(String filename) { + return new File( localRoot, filename ).isDirectory(); + } + + String getLocalName( File file ) { + if ( !file.toString().startsWith(localRoot.toString() ) ) { + throw new IllegalArgumentException( "file \""+file+"\"is not of this web file system" ); + } + String filename= file.toString().substring(localRoot.toString().length() ); + filename= filename.replaceAll( "\\\\", "/" ); + return filename; + } + + public String[] listDirectory(String directory) { + File f= new File( localRoot, directory ); + File[] files= f.listFiles(); + String[] result= new String[files.length]; + for ( int i=0; i utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * LocalFileSystemFactory.java + * + * Created on November 15, 2007, 9:30 AM + */ + +package org.das2.util.filesystem; + +import java.net.URL; + +/** + * + * @author jbf + */ +public class LocalFileSystemFactory implements FileSystemFactory { + + /** Creates a new instance of LocalFileSystemFactory */ + public LocalFileSystemFactory() { + } + + public FileSystem createFileSystem(URL root) throws FileSystem.FileSystemOfflineException { + return new LocalFileSystem(root); + } + +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/SubFileSystem.java b/dasCore/src/main/java/org/das2/util/filesystem/SubFileSystem.java new file mode 100644 index 000000000..199579bc3 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/SubFileSystem.java @@ -0,0 +1,70 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * SubFileSystem.java + * + * Created on January 16, 2007, 2:19 PM + * + * + */ + +package org.das2.util.filesystem; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * + * @author Jeremy + */ +public class SubFileSystem extends FileSystem { + FileSystem parent; + String dir; + + protected SubFileSystem( FileSystem parent, String dir ) throws MalformedURLException { + super( new URL( parent.getRootURL(), dir ) ); + this.parent= parent; + this.dir= dir; + + } + + public FileObject getFileObject(String filename) { + return parent.getFileObject( dir + filename ); + } + + public boolean isDirectory(String filename) throws IOException { + return parent.isDirectory( dir + filename ); + } + + public String[] listDirectory(String directory) throws IOException { + return parent.listDirectory( dir + directory ); + } + + public String[] listDirectory(String directory, String regex) throws IOException { + return parent.listDirectory( dir + directory, regex ); + } + + @Override + public File getLocalRoot() { + return new File( parent.getLocalRoot(), dir ); + } + + +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/WebFileObject.java b/dasCore/src/main/java/org/das2/util/filesystem/WebFileObject.java new file mode 100644 index 000000000..262384b97 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/WebFileObject.java @@ -0,0 +1,324 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * WebFile.java + * + * Created on May 14, 2004, 10:06 AM + */ +package org.das2.util.filesystem; + +import java.text.ParseException; +import java.util.logging.Level; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.monitor.NullProgressMonitor; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.channels.Channels; +import java.text.DateFormat; +import java.util.*; +import java.util.logging.Logger; + +/** + * + * @author Jeremy + * + * This is a refactoring of the HttpFileObject, generalized for use with FTP and HTTP file objects. Note that + * the HttpFileObject has not been refactored to use this. + * + */ +public class WebFileObject extends FileObject { + + WebFileSystem wfs; + String pathname; + File localFile; + Date modifiedDate; + boolean isRoot; + boolean isFolder; + Map metadata; + + /** + * true if we know if it's a folder or not. + */ + boolean isFolderResolved = false; + + public boolean canRead() { + return true; + } + + synchronized void maybeLoadMetadata() throws IOException { + if ( metadata==null ) { + metadata= wfs.protocol.getMetadata( this ); + } + } + + public FileObject[] getChildren() throws IOException { + if (!isFolder) { + throw new IllegalArgumentException(toString() + "is not a folder"); + } + String[] list = wfs.listDirectory(pathname); + FileObject[] result = new FileObject[list.length]; + for (int i = 0; i < result.length; i++) { + result[i] = new WebFileObject(wfs, list[i], new Date(System.currentTimeMillis())); + } + return result; + } + + public InputStream getInputStream(ProgressMonitor monitor) throws FileNotFoundException, IOException { + if ( wfs.protocol !=null ) { + return wfs.protocol.getInputStream(this, monitor); + } + if (isFolder) { + throw new IllegalArgumentException("is a folder"); + } + if (!localFile.exists()) { + File partFile = new File(localFile.toString() + ".part"); + wfs.downloadFile(pathname, localFile, partFile, monitor); + } + return new FileInputStream(localFile); + } + + /** + * + * @return a WebFileObject referencing the parent directory. + */ + public FileObject getParent() { + return new WebFileObject(wfs, wfs.getLocalName(localFile.getParentFile()), new Date(System.currentTimeMillis())); + } + + public long getSize() { + if (isFolder) { + throw new IllegalArgumentException("is a folder"); + } + return localFile.length(); + } + + public boolean isData() { + return !this.isFolder; + } + + public boolean isFolder() { + if ( this.isFolderResolved ) { + return this.isFolder; + } else { + //TODO: make HttpFileObject that does HEAD requests to properly answer these questions. See HttpFileSystem.getHeadMeta() + throw new RuntimeException("IOException in constructor prevented us from resolving"); + } + } + + public boolean isReadOnly() { + return true; + } + + public boolean isRoot() { + return this.isRoot; + } + + public java.util.Date lastModified() { + if (modifiedDate == null) { + try { + Map meta = wfs.protocol.getMetadata(this); + String stime = meta.get(WebProtocol.META_LAST_MODIFIED); + if (stime == null) { + modifiedDate = new Date(); + } else { + modifiedDate = new Date(Date.parse(stime)); + return modifiedDate; + } + } catch (IOException ex) { + Logger.getLogger(WebFileObject.class.getName()).log(Level.SEVERE, null, ex); + return new Date(); + } + + } + return modifiedDate; + } + + /** + * returns the File that corresponds to the remote file. This may or may + * not exist, depending on whether it's been downloaded yet. + */ + protected File getLocalFile() { + return this.localFile; + } + + public boolean exists() { + if (localFile.exists()) { + return true; + } else { + try { + if ( wfs.protocol!=null ) { + maybeLoadMetadata(); + return "true".equals( metadata.get( WebProtocol.META_EXIST ) ); + } else { + // TODO: use HTTP HEAD, etc + Logger.getLogger("das2.filesystem").fine("This implementation of WebFileObject.exists() is not optimal"); + File partFile = new File(localFile.toString() + ".part"); + wfs.downloadFile(pathname, localFile, partFile, new NullProgressMonitor()); + return localFile.exists(); + } + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + // I'm going to assume that it's because the file was not found. 404's from pw's server end up here + return false; + } + } + } + + protected WebFileObject( WebFileSystem wfs, String pathname, Date modifiedDate ) { + + this.modifiedDate = modifiedDate; + + this.wfs = wfs; + this.pathname = pathname; + this.isFolderResolved = false; + + if ( ! wfs.isAppletMode() ) { + this.localFile = new File(wfs.getLocalRoot(), pathname); + + if ( FileSystem.settings().getPersistence()==FileSystemSettings.Persistence.SESSION ) this.localFile.deleteOnExit(); + + try { + if (!localFile.canRead()) { + if (wfs.isDirectory(pathname)) { + localFile.mkdirs(); + this.isFolder = true; + if ("".equals(pathname)) { + this.isRoot = true; + } + } else { + this.isFolder = false; + } + } else { + this.isFolder = localFile.isDirectory(); + } + this.isFolderResolved= true; + } catch (IOException ex) { + this.isFolderResolved = false; + } + } + } + + public String toString() { + return "[" + wfs + "]" + getNameExt(); + } + + public String getNameExt() { + return pathname; + } + + /** + * return a Channel for the resource. If the resource can be made locally available, a FileChannel is returned. + * @param monitor + * @return + * @throws java.io.FileNotFoundException + * @throws java.io.IOException + */ + public java.nio.channels.ReadableByteChannel getChannel(ProgressMonitor monitor) throws FileNotFoundException, IOException { + InputStream in= getInputStream(monitor); + return Channels.newChannel(in); + } + + public File getFile(ProgressMonitor monitor) throws FileNotFoundException, IOException { + + if ( wfs.isAppletMode() ) throw new SecurityException("getFile cannot be used with applets."); + + boolean download = false; + + if ( monitor==null ) throw new NullPointerException("monitor may not be null"); + Date remoteDate; + if (wfs instanceof HttpFileSystem) { + URL url = wfs.getURL(this.getNameExt()); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("HEAD"); + connection.connect(); + remoteDate = new Date(connection.getLastModified()); + } else { + // this is the old logic + remoteDate = new Date(localFile.lastModified()); + } + + if (localFile.exists()) { + Date localFileLastModified = new Date(localFile.lastModified()); + if (remoteDate.after(localFileLastModified)) { + FileSystem.logger.fine("remote file is newer than local copy of " + this.getNameExt() + ", download."); + download = true; + } + } else { + download = true; + } + + if (download) { + try { + if (!localFile.getParentFile().exists()) { + localFile.getParentFile().mkdirs(); + } + File partFile = new File(localFile.toString() + ".part"); + wfs.downloadFile(pathname, localFile, partFile, monitor); + } catch (FileNotFoundException e) { + // TODO: do something with part file. + throw e; + } + } + + return localFile; + + } + + /** + * returns true is the file is locally available, meaning clients can + * call getFile() and the readble File reference will be available in + * interactive time. For FileObjects from HttpFileSystem, a HEAD request + * is made to ensure that the local file is as new as the website one. + */ + public boolean isLocal() { + if ( wfs.isAppletMode() ) return false; + try { + boolean download = false; + + if (localFile.exists()) { + Date remoteDate; + if (wfs instanceof HttpFileSystem) { + URL url = wfs.getURL(this.getNameExt()); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("HEAD"); + connection.connect(); + remoteDate = new Date(connection.getLastModified()); + } else { + // this is the old logic + remoteDate = new Date(localFile.lastModified()); + } + + Date localFileLastModified = new Date(localFile.lastModified()); + if (remoteDate.after(localFileLastModified)) { + FileSystem.logger.fine("remote file is newer than local copy of " + this.getNameExt() + ", download."); + download = true; + } + + } else { + download = true; + } + + return !download; + + } catch (IOException e) { + return false; + } + } +} \ No newline at end of file diff --git a/dasCore/src/main/java/org/das2/util/filesystem/WebFileSystem.java b/dasCore/src/main/java/org/das2/util/filesystem/WebFileSystem.java new file mode 100644 index 000000000..47cdd38d6 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/WebFileSystem.java @@ -0,0 +1,228 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * WebFileSystem.java + * + * Created on May 13, 2004, 1:22 PM + * + * A WebFileSystem allows web files to be opened just as if they were + * local files, since it manages the transfer of the file to a local + * file system. + */ + +package org.das2.util.filesystem; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import org.das2.util.monitor.ProgressMonitor; +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.regex.*; + +/** + * Base class for HTTP and FTP-based filesystems. A local cache is kept of + * the files. + * + * @author Jeremy + */ +public abstract class WebFileSystem extends FileSystem { + + public static File getDownloadDirectory() { + File local; + if (System.getProperty("user.name").equals("Web")) { + local = new File("/tmp"); + } else { + local = new File(System.getProperty("user.home")); + } + local = new File(local, ".das2/fsCache/wfs/"); + + return local; + } + + protected final File localRoot; + + /** + * if true, then don't download to local cache. Instead, provide inputStream + * and getFile throws exception. + */ + private boolean applet; + + /** + * plug-in template for implementation. if non-null, use this. + */ + protected WebProtocol protocol; + + protected boolean offline = true; + + /** + * if true, then the remote filesystem is not accessible, but local cache + * copies may be accessed. See FileSystemSettings.allowOffline + */ + public static final String PROP_OFFLINE = "offline"; + + public boolean isOffline() { + return offline; + } + + public void setOffline(boolean offline) { + boolean oldOffline = offline; + this.offline = offline; + propertyChangeSupport.firePropertyChange(PROP_OFFLINE, oldOffline, offline); + } + + private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); + + public void addPropertyChangeListener(PropertyChangeListener listener) { + propertyChangeSupport.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + propertyChangeSupport.removePropertyChangeListener(listener); + } + + /** Creates a new instance of WebFileSystem */ + protected WebFileSystem(URL root, File localRoot) { + super( root ); + this.localRoot= localRoot; + if ( localRoot==null ) { + if ( root.getProtocol().equals("http") ) { + this.protocol= new AppletHttpProtocol(); + } + } else { + if ( root.getProtocol().equals("http") ) { + this.protocol= new DefaultHttpProtocol(); + } + } + } + + static protected File localRoot( URL root ) { + + File local= FileSystem.settings().getLocalCacheDir(); + + String s= root.getProtocol() + "/"+ root.getHost() + "/" + root.getFile(); + + local= new File( local, s ); + + local.mkdirs(); + return local; + } + + /** + * Transfers the file from the remote store to a local copy f. This should only be + * used within the class and subclasses, clients should use getFileObject( String ).getFile(). + * Subclasses implementing this should download data to partfile, then rename partfile to + * f after the download is complete. + * + * @param partfile the temporary file during download. + */ + protected abstract void downloadFile( String filename, File f, File partfile, ProgressMonitor monitor ) throws IOException; + + /** Get the root of the local file cache + * @deprecated use getLocalRoot().getAbsolutePath() + */ + public String getLocalRootAbsPath(){ return this.localRoot.getAbsolutePath(); } + + public File getLocalRoot() { + return this.localRoot; + } + + abstract public boolean isDirectory( String filename ) throws IOException; + + abstract public String[] listDirectory( String directory ) throws IOException; + + public String[] listDirectory( String directory, String regex ) throws IOException { + String[] names= listDirectory( directory ); + Pattern pattern= Pattern.compile(regex); + ArrayList result= new ArrayList(); + for ( int i=0; i-1 ) { + if ( monitor.isCancelled() ) throw new InterruptedIOException( ); + monitor.setTaskProgress( totalBytesRead ); + out.write( buffer, 0, bytesRead ); + bytesRead= is.read( buffer, 0, 2048 ); + totalBytesRead+= bytesRead; + logger.finest( "transferring data" ); + } + } + + public String toString() { + return "wfs "+root; + } + + public boolean isAppletMode() { + return applet; + } + + public void setAppletMode( boolean applet ) { + this.applet= applet; + } + + +} diff --git a/dasCore/src/main/java/org/das2/util/filesystem/WebProtocol.java b/dasCore/src/main/java/org/das2/util/filesystem/WebProtocol.java new file mode 100755 index 000000000..a6d020377 --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/filesystem/WebProtocol.java @@ -0,0 +1,31 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.util.filesystem; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * template for web-based protocols to implement FileSystems + * @author jbf + */ +public interface WebProtocol { + public static final String META_EXIST="exist"; + public static final String META_LAST_MODIFIED="Last-Modified"; + + + public InputStream getInputStream( WebFileObject fo, org.das2.util.monitor.ProgressMonitor mon ) throws IOException; + + /** + * returns metadata for the object. For property names, see META_LAST_MODIFIED, META_EXIST, etc. + * @param fo + * @return + * @throws java.io.IOException + */ + public Map getMetadata( WebFileObject fo ) throws IOException; + +} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/package.html b/dasCore/src/main/java/org/das2/util/filesystem/package.html similarity index 100% rename from dasCoreUtil/src/org/das2/util/filesystem/package.html rename to dasCore/src/main/java/org/das2/util/filesystem/package.html diff --git a/dasCore/src/main/java/org/das2/util/monitor/NullProgressMonitor.java b/dasCore/src/main/java/org/das2/util/monitor/NullProgressMonitor.java new file mode 100644 index 000000000..e6b2c95cf --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/monitor/NullProgressMonitor.java @@ -0,0 +1,111 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * NullProgressMonitor.java + * + * Created on October 23, 2007, 10:11 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.util.monitor; + +/** + * This is a progress monitor to use when we don't care about the progress. + * This replaces ProgressMonitor.NULL, which has the problem that it was + * stateless and there assume that progress monitors have state. + * + * Further, this can act as a base class for other monitor types. + * + * @author jbf + */ +public class NullProgressMonitor implements ProgressMonitor { + + public NullProgressMonitor() { + } + + private long taskSize=-1 ; + + public void setTaskSize(long taskSize) { + this.taskSize= taskSize; + } + + public long getTaskSize( ) { + return taskSize; + } + + public void setProgressMessage( String message ) {} ; + + private long position=0; + + public void setTaskProgress(long position) throws IllegalArgumentException { + this.position= position; + } + + public long getTaskProgress() { + return position; + } + + private boolean started= false; + + public void started() { + this.started= false; + } + + public boolean isStarted() { + return started; + } + + private boolean finished= false; + + public void finished() { + finished= true; + } + + public boolean isFinished() { + return finished; + } + + private boolean cancelled= false; + + public void cancel() { + cancelled= true; + } + + public boolean isCancelled() { + return cancelled; + } + + @Deprecated + public void setAdditionalInfo(String s) { }; + + private String label; + + public void setLabel( String s ) { + this.label= label; + } + + public String getLabel() { + return label; + } + + public String toString() { + return "" + this.position + " of "+ this.taskSize; + } +} diff --git a/dasCore/src/main/java/org/das2/util/monitor/ProgressMonitor.java b/dasCore/src/main/java/org/das2/util/monitor/ProgressMonitor.java new file mode 100755 index 000000000..1c5d8b0ea --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/monitor/ProgressMonitor.java @@ -0,0 +1,179 @@ +/* File: ProgressMonitor.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.util.monitor; + +/** ProgressMonitor defines a set of methods that are useful for + * keeping track of the progress of an operation. This interface also allows + * the operation being tracked to be notified if the user wishes to cancel the + * operation. Code using this interface to track progress should call + * {@link #isCancelled()} prior to calling {@link #setTaskProgress(long)}. + * Implementations of this interface should throw an + * IllegalArgumentException when setTaskProgress(int) + * is called after the operation has been cancelled. + *

    + * Code using the ProgressMonitor should call {@link #started()} + * before setTaskProgress(long) is called for the first time. + * setTaskProgress() should not be called after + * cancel() or finished() has been called. Therefore, + * monitored processes should check isCancelled() before setTaskProgress(long) + * is called. An + * implementation may throw an IllegalArgumentException if + * setTaskProgress(int) is called before started() or + * after finished() is called. + * + *

    A client codes receiving a monitor must do one of two things. + * It should either call setTaskSize(long), started(), setTaskProgress(long) zero or more times, then + * finished(); or it should do nothing with the monitor, possibly passing the + * monitor to a subprocess. This is to ensure that it's easy to see that + * the monitor lifecycle is properly performed.

    + * + * TODO: check this, I think it's legal now for a process to ignore cancelled, + * and the monitor should disable the client's ability to cancel in this case. + * + * TODO: what about exceptions and the monitor lifecycle? + * + * @author jbf + */ +public interface ProgressMonitor { + + /** + * Use NULL when you do not need or wish to use a progressMonitor. It simply + * ignores the progress messages. + * @deprecated this should not be used. Instead use new NullProgressMonitor(); + */ + public static final ProgressMonitor NULL= new ProgressMonitor() { + public void setTaskSize(long taskSize) {} ; + public long getTaskSize( ) { return 1; } + public void setTaskProgress(long position) throws IllegalArgumentException {}; + public void setProgressMessage( String message ) {} ; + public long getTaskProgress() { return 0; }; + public void started() { }; + public boolean isStarted() { + return true; + } + public void finished() {}; + public boolean isFinished() { + return false; + } + public void cancel() {}; + public boolean isCancelled() { return false; }; + public void setAdditionalInfo(String s) { }; + public void setLabel( String s ) { }; + public String getLabel() { return ""; } + }; + + public final static long SIZE_INDETERMINATE= -1; + + /** Sets the maximum value for the task progress of this + * ProgressMonitor. + * @param taskSize maximum value for the task progress. A taskSize of -1 indicates the taskSize is indeterminate. + */ + void setTaskSize(long taskSize); + + /** Notifies the ProgressMonitor of a change in the progress + * of the task. + * @param position the current task position + * @throws IllegalArgumentException if {@link #isCancelled()} returns true or, + * possibly if started() has not been called or + * finished() has been called. + */ + void setTaskProgress(long position) throws IllegalArgumentException; + + /** + * Provides additional feedback as to what's going on in the process. + * This message should be set by the service provider, not the client, + * and refer to the implementation of the task. e.g. "Reading file myData.dat" + * @param message the message describing the state of progress. + */ + void setProgressMessage( String message ); + + /** Returns the current progress of the monitored task. + * @return the current progress of the monitored task. + */ + long getTaskProgress(); + + /** + * Set a consise string that describes the task being performed. Monitors + * don't necessarily need to display this label, and this request may be + * ignored. It is only provided so a process can describe the task that + * is going on. This is usually set by the client of the process to indicate + * what service we are waiting for. e.g. "Loading Data" + */ + public void setLabel( String label ); + + /** + * Return the label string displayed. This is primarily to aid in debugging, + * and this method need not return the string set by setLabel. + */ + public String getLabel(); + + long getTaskSize(); + + /** Notifies the ProgressMonitor that the task + * being monitored has started. If the ProgressMonitor + * is in a cancelled state when this method is called, that + * ProgressMonitor should be 'uncancelled'. + */ + void started(); + + /** Notifies the ProgressMonitor that the task + * being monitored has finished. + */ + void finished(); + + /** Notifies the ProgressMonitor that the task + * being monitored should be canceled. After this method is + * called, implementations should return true on + * any subsequent calls to {@link #isCancelled()} and should + * throw an IllegalStateException on any subsequent calls to + * {@link #setTaskProgress(long)}. + */ + void cancel(); + + /** Returns true if the operation being tracked + * should be cancelled. + * @return true if the operation being tracked + * should be cancelled. + */ + boolean isCancelled(); + + /** additional information to be displayed alongside the progress. That + * might be of interest. + * "85 of 100 (50KB/s)" + * @deprecated setProgressMessage should be used by the service provider + * to indicate how the process is being implemented. + */ + public void setAdditionalInfo(String s); + + /** + * true if the process has indicated that it has started. + */ + boolean isStarted(); + + /** + * true if the process has indicated that it is finished + */ + boolean isFinished(); + +} diff --git a/dasCore/src/main/java/org/das2/util/monitor/SubTaskMonitor.java b/dasCore/src/main/java/org/das2/util/monitor/SubTaskMonitor.java new file mode 100644 index 000000000..379de59bb --- /dev/null +++ b/dasCore/src/main/java/org/das2/util/monitor/SubTaskMonitor.java @@ -0,0 +1,123 @@ +/* Copyright (C) 2003-2008 The University of Iowa + * + * This file is part of the Das2 utilities library. + * + * Das2 utilities are free software: you can redistribute and/or modify them + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Das2 utilities are distributed in the hope that they 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 + * as well as the GNU General Public License along with Das2 utilities. If + * not, see . + * + * SubTaskMonitor.java + * + * Created on August 18, 2005, 4:01 PM + */ + +package org.das2.util.monitor; + + +/** + * creates a ProgressMonitor that maps its progress to a parent's progress. + * For example, if a process takes a progress monitor, but is implemented in + * two steps that each take a progress monitor, then two subtask monitors can + * be created to monitor each step, and the client who passed in the monitor + * will see the whole deal as one process. + * + * @author Jeremy + */ +public class SubTaskMonitor implements ProgressMonitor { + + ProgressMonitor parent; + long min, max, progress, size; + String label; + + private SubTaskMonitor( ProgressMonitor parent, long min, long max ) { + this.parent= parent; + this.min= min; + this.max= max; + this.size= -1; + } + + public static SubTaskMonitor create( ProgressMonitor parent, long min, long max ) { + return new SubTaskMonitor( parent, min, max ); + } + + public void cancel() { + parent.cancel(); + } + + private boolean finished= false; + + public void finished() { + this.finished= true; // only to support the bean property + } + + public boolean isFinished() { + return this.finished; + } + + public long getTaskProgress() { + return progress; + } + + public boolean isCancelled() { + return parent.isCancelled(); + } + + @Deprecated + public void setAdditionalInfo(String s) { + // ignore + } + + public void setTaskProgress(long position) throws IllegalArgumentException { + this.progress= position; + if ( size==-1 ) { + parent.setTaskProgress( min ); + } else { + parent.setTaskProgress( min + ( max - min ) * position / size ); + } + } + + public void setTaskSize(long taskSize) { + this.size= taskSize; + } + + public long getTaskSize() { + return this.size; + } + + boolean started= false; + + public void started() { + this.started= true; + if ( parent.isStarted()==false ) parent.started(); + } + + public boolean isStarted() { + return started; + } + + public void setLabel(String label) { + this.label= label; + } + + public String getLabel() { + return label; + } + + public String toString() { + return parent.toString()+">"+label; + } + + public void setProgressMessage(String message) { + //parent.setProgressMessage(message); + } +} diff --git a/dasCoreUtil/src/org/das2/util/package.html b/dasCore/src/main/java/org/das2/util/package.html similarity index 100% rename from dasCoreUtil/src/org/das2/util/package.html rename to dasCore/src/main/java/org/das2/util/package.html diff --git a/dasCore/src/main/java/org/das2/v20161010a.java b/dasCore/src/main/java/org/das2/v20161010a.java new file mode 100644 index 000000000..4893a713c --- /dev/null +++ b/dasCore/src/main/java/org/das2/v20161010a.java @@ -0,0 +1,16 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2; + +/** + * + * @author jbf + */ +public class v20161010a { + public static void main(String[] args) { + System.err.println("v20161010a"); + } +} diff --git a/dasCore/src/main/resources/images/das2logo-130.png b/dasCore/src/main/resources/images/das2logo-130.png new file mode 100644 index 000000000..010564be3 Binary files /dev/null and b/dasCore/src/main/resources/images/das2logo-130.png differ diff --git a/dasCore/src/main/resources/images/das2logo-64.png b/dasCore/src/main/resources/images/das2logo-64.png new file mode 100644 index 000000000..3bfb7ab9f Binary files /dev/null and b/dasCore/src/main/resources/images/das2logo-64.png differ diff --git a/dasCore/src/main/resources/images/dasSplash.gif b/dasCore/src/main/resources/images/dasSplash.gif new file mode 100644 index 000000000..4ec74d2ca Binary files /dev/null and b/dasCore/src/main/resources/images/dasSplash.gif differ diff --git a/dasCore/src/main/resources/images/dasSplash.png b/dasCore/src/main/resources/images/dasSplash.png new file mode 100644 index 000000000..86928d3c4 Binary files /dev/null and b/dasCore/src/main/resources/images/dasSplash.png differ diff --git a/dasCore/src/images/cancel14.png b/dasCore/src/main/resources/images/icons/cancel14.png old mode 100644 new mode 100755 similarity index 100% rename from dasCore/src/images/cancel14.png rename to dasCore/src/main/resources/images/icons/cancel14.png diff --git a/dasCore/src/images/cancelGrey14.png b/dasCore/src/main/resources/images/icons/cancelGrey14.png old mode 100644 new mode 100755 similarity index 100% rename from dasCore/src/images/cancelGrey14.png rename to dasCore/src/main/resources/images/icons/cancelGrey14.png diff --git a/dasCore/src/main/resources/images/icons/contoursRenderer.png b/dasCore/src/main/resources/images/icons/contoursRenderer.png new file mode 100755 index 000000000..3a6dd4b37 Binary files /dev/null and b/dasCore/src/main/resources/images/icons/contoursRenderer.png differ diff --git a/dasCore/src/main/resources/images/icons/rebin.binAverage.png b/dasCore/src/main/resources/images/icons/rebin.binAverage.png new file mode 100644 index 000000000..5555f911e Binary files /dev/null and b/dasCore/src/main/resources/images/icons/rebin.binAverage.png differ diff --git a/dasCore/src/images/icons/rebin.binXinterpY.png b/dasCore/src/main/resources/images/icons/rebin.nearestNeighbor.png similarity index 100% rename from dasCore/src/images/icons/rebin.binXinterpY.png rename to dasCore/src/main/resources/images/icons/rebin.nearestNeighbor.png diff --git a/dasCore/src/main/resources/images/icons/rebin.noInterpolate.png b/dasCore/src/main/resources/images/icons/rebin.noInterpolate.png new file mode 100644 index 000000000..b40644c41 Binary files /dev/null and b/dasCore/src/main/resources/images/icons/rebin.noInterpolate.png differ diff --git a/dasCore/src/main/resources/images/icons/rebin.noInterpolateNoEnlarge.png b/dasCore/src/main/resources/images/icons/rebin.noInterpolateNoEnlarge.png new file mode 100644 index 000000000..ce1a7ab7d Binary files /dev/null and b/dasCore/src/main/resources/images/icons/rebin.noInterpolateNoEnlarge.png differ diff --git a/dasCore/src/main/resources/images/icons/rebinnerBinAverage.png b/dasCore/src/main/resources/images/icons/rebinnerBinAverage.png new file mode 100644 index 000000000..cd9c4fd58 Binary files /dev/null and b/dasCore/src/main/resources/images/icons/rebinnerBinAverage.png differ diff --git a/dasCore/src/main/resources/images/icons/showDataMouseModule.png b/dasCore/src/main/resources/images/icons/showDataMouseModule.png new file mode 100644 index 000000000..fd8074470 Binary files /dev/null and b/dasCore/src/main/resources/images/icons/showDataMouseModule.png differ diff --git a/dasCore/src/main/resources/images/toolbar/button.png b/dasCore/src/main/resources/images/toolbar/button.png new file mode 100644 index 000000000..94f163e3d Binary files /dev/null and b/dasCore/src/main/resources/images/toolbar/button.png differ diff --git a/dasCore/src/main/resources/images/toolbar/checkbox.png b/dasCore/src/main/resources/images/toolbar/checkbox.png new file mode 100644 index 000000000..27c9aa4eb Binary files /dev/null and b/dasCore/src/main/resources/images/toolbar/checkbox.png differ diff --git a/dasCore/src/main/resources/images/toolbar/choice.png b/dasCore/src/main/resources/images/toolbar/choice.png new file mode 100644 index 000000000..ad1d71642 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbar/choice.png differ diff --git a/dasCore/src/main/resources/images/toolbar/new_column.png b/dasCore/src/main/resources/images/toolbar/new_column.png new file mode 100644 index 000000000..f88d6812b Binary files /dev/null and b/dasCore/src/main/resources/images/toolbar/new_column.png differ diff --git a/dasCore/src/main/resources/images/toolbar/new_row.png b/dasCore/src/main/resources/images/toolbar/new_row.png new file mode 100644 index 000000000..8cce202ab Binary files /dev/null and b/dasCore/src/main/resources/images/toolbar/new_row.png differ diff --git a/dasCore/src/main/resources/images/toolbar/radiobutton.png b/dasCore/src/main/resources/images/toolbar/radiobutton.png new file mode 100644 index 000000000..9659ad056 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbar/radiobutton.png differ diff --git a/dasCore/src/main/resources/images/toolbar/row_column_select.png b/dasCore/src/main/resources/images/toolbar/row_column_select.png new file mode 100644 index 000000000..064db81b4 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbar/row_column_select.png differ diff --git a/dasCore/src/main/resources/images/toolbar/select.png b/dasCore/src/main/resources/images/toolbar/select.png new file mode 100644 index 000000000..103c00609 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbar/select.png differ diff --git a/dasCore/src/main/resources/images/toolbar/text.png b/dasCore/src/main/resources/images/toolbar/text.png new file mode 100644 index 000000000..44ae25d47 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbar/text.png differ diff --git a/dasCore/src/main/resources/images/toolbar/textfield.png b/dasCore/src/main/resources/images/toolbar/textfield.png new file mode 100644 index 000000000..5ca3a5dfb Binary files /dev/null and b/dasCore/src/main/resources/images/toolbar/textfield.png differ diff --git a/dasCore/src/main/resources/images/toolbox/axis.gif b/dasCore/src/main/resources/images/toolbox/axis.gif new file mode 100644 index 000000000..42df9db1a Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/axis.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/button.gif b/dasCore/src/main/resources/images/toolbox/button.gif new file mode 100644 index 000000000..2d8b3f6fb Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/button.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/buttongroup.gif b/dasCore/src/main/resources/images/toolbox/buttongroup.gif new file mode 100644 index 000000000..9ac4e2eb8 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/buttongroup.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/canvas.gif b/dasCore/src/main/resources/images/toolbox/canvas.gif new file mode 100644 index 000000000..792179bc7 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/canvas.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/checkbox.gif b/dasCore/src/main/resources/images/toolbox/checkbox.gif new file mode 100644 index 000000000..c3d9753b7 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/checkbox.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/checkbox.png b/dasCore/src/main/resources/images/toolbox/checkbox.png new file mode 100644 index 000000000..27c9aa4eb Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/checkbox.png differ diff --git a/dasCore/src/main/resources/images/toolbox/choice.gif b/dasCore/src/main/resources/images/toolbox/choice.gif new file mode 100644 index 000000000..cc677c5a9 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/choice.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/colorbar.png b/dasCore/src/main/resources/images/toolbox/colorbar.png new file mode 100644 index 000000000..55416ab3c Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/colorbar.png differ diff --git a/dasCore/src/main/resources/images/toolbox/dragpointer.gif b/dasCore/src/main/resources/images/toolbox/dragpointer.gif new file mode 100644 index 000000000..f28c16b4e Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/dragpointer.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/line.gif b/dasCore/src/main/resources/images/toolbox/line.gif new file mode 100644 index 000000000..41ed0ae63 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/line.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/panel.gif b/dasCore/src/main/resources/images/toolbox/panel.gif new file mode 100644 index 000000000..4009cae0c Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/panel.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/plot.gif b/dasCore/src/main/resources/images/toolbox/plot.gif new file mode 100644 index 000000000..83c18fe72 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/plot.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/radiobutton.gif b/dasCore/src/main/resources/images/toolbox/radiobutton.gif new file mode 100644 index 000000000..b5f2bec7a Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/radiobutton.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/spectrogram.gif b/dasCore/src/main/resources/images/toolbox/spectrogram.gif new file mode 100644 index 000000000..9cf5bd101 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/spectrogram.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/spectrogram_plot.gif b/dasCore/src/main/resources/images/toolbox/spectrogram_plot.gif new file mode 100644 index 000000000..1bddbb090 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/spectrogram_plot.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/tab.gif b/dasCore/src/main/resources/images/toolbox/tab.gif new file mode 100644 index 000000000..d328d63f2 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/tab.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/taxis.gif b/dasCore/src/main/resources/images/toolbox/taxis.gif new file mode 100644 index 000000000..d222133ce Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/taxis.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/text.gif b/dasCore/src/main/resources/images/toolbox/text.gif new file mode 100644 index 000000000..2c926b89c Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/text.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/textfield.gif b/dasCore/src/main/resources/images/toolbox/textfield.gif new file mode 100644 index 000000000..35a68ab5d Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/textfield.gif differ diff --git a/dasCore/src/main/resources/images/toolbox/window.gif b/dasCore/src/main/resources/images/toolbox/window.gif new file mode 100644 index 000000000..88fa24c09 Binary files /dev/null and b/dasCore/src/main/resources/images/toolbox/window.gif differ diff --git a/dasCore/src/org/das2/CancelledOperationException.java b/dasCore/src/org/das2/CancelledOperationException.java index 882d4a9bb..9c2b306c2 100644 --- a/dasCore/src/org/das2/CancelledOperationException.java +++ b/dasCore/src/org/das2/CancelledOperationException.java @@ -26,10 +26,7 @@ import java.io.InterruptedIOException; /** - * Exception used to indicate the operation was cancelled. - * Note there is a CancelledOperationException within the util.filesystem - * package that is used to decouple the util from the rest of das2. - * + * * @author eew */ public class CancelledOperationException extends DasException { diff --git a/dasCore/src/org/das2/DasApplication.java b/dasCore/src/org/das2/DasApplication.java index 3ab43c633..ac7c8a79e 100755 --- a/dasCore/src/org/das2/DasApplication.java +++ b/dasCore/src/org/das2/DasApplication.java @@ -25,14 +25,14 @@ import org.das2.dataset.LimitSizeBytesDataSetCache; import org.das2.dataset.NullDataSetCache; -import org.das2.util.DefaultExceptionHandler; +import org.das2.system.DefaultExceptionHandler; import org.das2.system.DasLogger; -import org.das2.util.ExceptionHandler; +import org.das2.system.ExceptionHandler; import org.das2.system.LoggerId; import org.das2.system.NullMonitorFactory; import org.das2.system.MonitorFactory; import org.das2.system.DefaultMonitorFactory; -import org.das2.util.ThrowRuntimeExceptionHandler; +import org.das2.system.ThrowRuntimeExceptionHandler; import org.das2.util.Splash; import org.das2.util.DasExceptionHandler; import org.das2.util.ClassMap; @@ -53,7 +53,6 @@ import java.util.logging.*; import java.util.prefs.*; import javax.swing.*; -import org.das2.util.filesystem.FileSystemSettings; /** * DasApplication object manages per-application resources, like object name space, @@ -93,8 +92,6 @@ public Logger getLogger() { /** * @deprecated use DasLogger.getLogger( LoggerId ) - * @param id the id. - * @return the logger. */ public Logger getLogger( LoggerId id ) { return DasLogger.getLogger(id); @@ -135,7 +132,7 @@ public String suggestNameFor( Object c ) { } else { ihits= (hits.intValue())+1; } - hitsMap.put( type, ihits); + hitsMap.put( type, new Integer(ihits)); return type+"_"+ihits; } @@ -165,12 +162,10 @@ public final boolean isApplet() { /** * check the security manager to see if all permissions are allowed, * True indicates is not an applet running in a sandbox. - * See FileSystemSettings, which has a copy of this code * @return true if all permissions are allowed */ public static boolean hasAllPermission() { try { - if ( restrictPermission==true ) return false; SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new java.security.AllPermission()); @@ -180,19 +175,7 @@ public static boolean hasAllPermission() { return false; } } - - private static boolean restrictPermission= false; - - /** - * true means don't attempt to gain access to applet-restricted functions. - * @param v true means don't attempt to gain access to applet-restricted functions. - * @see FileSystemSettings#setRestrictPermission(boolean) - */ - public static void setRestrictPermission( boolean v ) { - restrictPermission= v; - FileSystemSettings.setRestrictPermission(v); - } - + /** * support restricted security environment by checking permissions before * checking property. @@ -257,12 +240,7 @@ public static File getDas2UserDirectory() { return local; } - /** - * return true if this doesn't have to be a headless application. - * This should not be used. - * @return - */ - private static boolean isHeadAvailable() { + public static boolean isHeadAvailable() { return true; /* return ( System.getProperty( "awt.toolkit" ) != null ); @@ -363,10 +341,6 @@ public void windowClosing( WindowEvent e ) { public JFrame getMainFrame() { return this.mainFrame; } - - public void setMainFrame( JFrame frame ) { - this.mainFrame= frame; - } public void quit() { final Preferences prefs= Preferences.userNodeForPackage(DasApplication.class); @@ -426,11 +400,7 @@ public DataSetCache getDataSetCache() { } ExceptionHandler exceptionHandler; - - /** - * warning: this code is repeated in FileSystem to avoid dependence. - * @return - */ + public ExceptionHandler getExceptionHandler() { if ( exceptionHandler==null ) { if ( isHeadless() ) { @@ -441,12 +411,5 @@ public ExceptionHandler getExceptionHandler() { } return exceptionHandler; } - - /** - * explicitly set the ExceptionHandler that will handle runtime exceptions - * @param h - */ - public void setExceptionHandler( ExceptionHandler h ) { - this.exceptionHandler= h; - } + } diff --git a/dasCore/src/org/das2/DasIOException.java b/dasCore/src/org/das2/DasIOException.java index 2f67d06c8..7b3746092 100644 --- a/dasCore/src/org/das2/DasIOException.java +++ b/dasCore/src/org/das2/DasIOException.java @@ -43,6 +43,10 @@ public DasIOException() { public DasIOException(String msg) { super(msg); } + + public DasIOException(String msg, Throwable t) { + super(msg,t); + } public DasIOException(java.io.IOException ex) { super( ex.getMessage() ); diff --git a/dasCore/src/org/das2/DasProperties.java b/dasCore/src/org/das2/DasProperties.java index 617ec9a5b..baf86e0fe 100755 --- a/dasCore/src/org/das2/DasProperties.java +++ b/dasCore/src/org/das2/DasProperties.java @@ -36,17 +36,16 @@ import java.util.Iterator; import java.util.Properties; import java.util.logging.*; -import org.das2.util.filesystem.FileSystemSettings; -public final class DasProperties extends Properties { +public class DasProperties extends Properties { // Contains the global user-configurable parameters that are // persistent between sessions. - private static final long serialVersionUID = 1L; - - private transient RenderingHints hints; - private static final Logger logger= Logger.getLogger("das2.system"); + private RenderingHints hints; + private boolean antiAlias; + private boolean visualCues; + private Logger logger; private static ArrayList propertyOrder; private static Editor editor; private static JFrame jframe; @@ -58,10 +57,8 @@ private DasProperties() { hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); setDefaults(); propertyOrder= new ArrayList(); - if ( DasApplication.hasAllPermission()!=FileSystemSettings.hasAllPermission() ) { - throw new RuntimeException("DasApplication.hasAllPermission()!=FileSystemSettings.hasAllPermission()"); - } if ( DasApplication.hasAllPermission() ) readPersistentProperties(); + logger= Logger.getLogger("das2"); setPropertyOrder(); } @@ -134,6 +131,7 @@ public void setValueAt(Object value, int row, int col) { Logger.getLogger("das2").setLevel(Level.FINE); } else instance.logger.setLevel(Level.parse(debugLevel)); + org.das2.util.DasDie.setDebugVerbosityLevel(value.toString()); } instance.setProperty(propertyName,value.toString()); editor.setDirty(true); @@ -264,39 +262,22 @@ public void readPersistentProperties() { try { String file= System.getProperty("user.home")+System.getProperty("file.separator")+".das2rc"; File f= new File(file); - + if (f.canRead()) { - InputStream in=null; try { - in= new FileInputStream(f); + InputStream in= new FileInputStream(f); load(in); - } catch (IOException ex) { - logger.log( Level.WARNING, ex.getMessage(), ex ); - org.das2.util.DasExceptionHandler.handle(ex); - } finally { - try { - if ( in!=null ) in.close(); - } catch ( IOException ex ) { - - } + in.close(); + } catch (IOException e) { + org.das2.util.DasExceptionHandler.handle(e); } } else { - if ( !f.exists() && f.canWrite() ) { - OutputStream out=null; - try { - out= new FileOutputStream(f); - store(out,""); - } catch (IOException e) { - logger.log( Level.WARNING, e.getMessage(), e ); - org.das2.util.DasExceptionHandler.handle(e); - } finally { - try { - if ( out!=null ) out.close(); - } catch ( IOException ex ) { - } - } - } else { - logger.log(Level.FINE, "Unable to read or write {0}. Using defaults.", file); + try { + OutputStream out= new FileOutputStream(f); + store(out,""); + out.close(); + } catch (IOException e) { + org.das2.util.DasExceptionHandler.handle(e); } } } catch ( SecurityException ex ) { @@ -310,38 +291,18 @@ public void writePersistentProperties() { File f= new File(file); if (f.canWrite()) { - logger.finer("Attempt to write .das2rc..."); - OutputStream out=null; + org.das2.util.DasDie.println("Attempt to write .das2rc..."); try { - out= new FileOutputStream(f); + OutputStream out= new FileOutputStream(f); store(out,""); + out.close(); } catch (IOException e) { org.das2.util.DasExceptionHandler.handle(e); - } finally { - try { - if ( out!=null ) out.close(); - } catch ( IOException ex ) { - } } } else { DasException e= new org.das2.DasIOException("Can't write to file "+f); org.das2.util.DasExceptionHandler.handle(e); } } - - /** define for Findbugs. Since this is a singleton class, this is fine. - * - * @param o - * @return - */ - @Override - public synchronized boolean equals(Object o) { - return this==o; - } - - @Override - public int hashCode() { - return super.hashCode(); - } } diff --git a/dasCore/src/org/das2/DebuggerVars.java b/dasCore/src/org/das2/DebuggerVars.java deleted file mode 100644 index ac8735102..000000000 --- a/dasCore/src/org/das2/DebuggerVars.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.das2; - - -import java.util.HashMap; -import java.util.Map; - -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -/** - * - * @author jbf - */ -public class DebuggerVars { - static Map vars= new HashMap(); - public static void put(String name,Object val) { vars.put(name, val); } - public static Object get( String name ) { return vars.get(name); } - public static Object remove( String name ) { return vars.remove(name); } -} diff --git a/dasCore/src/org/das2/NameContext.java b/dasCore/src/org/das2/NameContext.java index 77a906609..11851c3a1 100644 --- a/dasCore/src/org/das2/NameContext.java +++ b/dasCore/src/org/das2/NameContext.java @@ -23,7 +23,11 @@ package org.das2; +import org.das2.datum.Datum; +import org.das2.datum.TimeUtil; import org.das2.beans.BeansUtil; +import org.das2.dasml.ParsedExpression; +import org.das2.dasml.ParsedExpressionException; import java.beans.*; import java.lang.ref.WeakReference; @@ -31,6 +35,7 @@ import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; +import java.util.WeakHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -52,6 +57,10 @@ public class NameContext { public static final Pattern SIMPLE_NAME = Pattern.compile(SIMPLE_NAME_STRING); public static final Pattern INDEXED_NAME = Pattern.compile(INDEXED_NAME_STRING); public static final Pattern QUALIFIED_NAME = Pattern.compile(QUALIFIED_NAME_STRING); + public static final Pattern refPattern = Pattern.compile("\\$\\{([^\\}]+)\\}"); + public static final Pattern intPattern = Pattern.compile("-?(0|[1-9][0-9]*)"); + public static final Pattern floatPattern = Pattern.compile("-?[0-9]*(\\.[0-9]*)?([eE]-?[0-9]+)?"); + private Map nameMap; private Map propertyMap; @@ -67,7 +76,6 @@ public class NameContext { * characters and '_'. * @param name the name for the value to be associated with * @param value the value being named - * @throws org.das2.DasNameException when the name is not a valid name ("[A-Za-z][A-Za-z0-9_]*") */ public void put(String name, Object value) throws DasNameException { Matcher m = SIMPLE_NAME.matcher(name); @@ -101,7 +109,7 @@ public Object get(String name) throws DasPropertyException, InvocationTargetExce } } - public void set(String name, Object value) throws InvocationTargetException, DasPropertyException, DasNameException { + public void set(String name, Object value) throws InvocationTargetException, ParsedExpressionException, DasPropertyException, DasNameException { if (name == null) { throw new NullPointerException("name"); } @@ -154,14 +162,14 @@ public Object getIndexedPropertyValue(Object obj, String property, int index) th } IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor)pd; Method readMethod = ipd.getIndexedReadMethod(); - return readMethod.invoke(obj, new Object[] { index }); + return readMethod.invoke(obj, new Object[] { new Integer(index) }); } catch (IllegalAccessException iae) { throw new RuntimeException(iae); } } - public void setPropertyValue(Object obj, String property, Object value) throws InvocationTargetException, DasPropertyException { + public void setPropertyValue(Object obj, String property, Object value) throws InvocationTargetException, ParsedExpressionException, DasPropertyException { try { Class type = obj.getClass(); maybeLoadPropertiesForClass(type); @@ -176,9 +184,7 @@ public void setPropertyValue(Object obj, String property, Object value) throws I } Class propertyType = pd.getPropertyType(); if (value instanceof String) { - throw new RuntimeException("not implemented"); - //Class.forName("org.das2.dasml.Processor").getMethod("parseValue", NameContext.class, String.class, Class.class ); - //value = Processor.parseValue(this,(String)value, propertyType); + value = parseValue((String)value, propertyType); } if (!propertyType.isInstance(value) && !(propertyType == boolean.class && value instanceof Boolean) @@ -198,7 +204,7 @@ public void setPropertyValue(Object obj, String property, Object value) throws I } } - public void setIndexedPropertyValue(Object obj, String property, int index, Object value) throws InvocationTargetException, DasPropertyException { + public void setIndexedPropertyValue(Object obj, String property, int index, Object value) throws InvocationTargetException, ParsedExpressionException, DasPropertyException { try { Class type = obj.getClass(); maybeLoadPropertiesForClass(type); @@ -217,8 +223,7 @@ public void setIndexedPropertyValue(Object obj, String property, int index, Obje } Class propertyType = pd.getPropertyType(); if (value instanceof String) { - throw new RuntimeException("not implemented"); - //value = Processor.parseValue(this,(String)value, propertyType); + value = parseValue((String)value, propertyType); } if (!propertyType.isInstance(value) && !(propertyType == boolean.class && value instanceof Boolean) @@ -231,7 +236,7 @@ public void setIndexedPropertyValue(Object obj, String property, int index, Obje && !(propertyType == long.class && value instanceof Long)) { throw new DasPropertyException(DasPropertyException.TYPE_MISMATCH, null, property); } - writeMethod.invoke(obj, new Object[] { index, value }); + writeMethod.invoke(obj, new Object[] { new Integer(index), value }); } catch (IllegalAccessException iae) { throw new RuntimeException(iae); @@ -267,7 +272,93 @@ public void remove(String name) { nameMap.remove(name); } - + /** + * Parses the given String object in an attempt to + * produce the an object of the given type. + * + * @param valueString the given String + * @param type the given type + */ + public Object parseValue(String valueString, Class type) throws org.das2.dasml.ParsedExpressionException, InvocationTargetException, DasPropertyException { + Object parsedValue; + valueString = replaceReferences(valueString); + if (type == String.class) { + return valueString; + } + else if (type == boolean.class || type == Boolean.class) { + if (valueString.equals("true")) return Boolean.TRUE; + if (valueString.equals("false")) return Boolean.FALSE; + ParsedExpression exp = new ParsedExpression(valueString); + Object o = exp.evaluate(this); + if (!(o instanceof Boolean)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a boolean value"); + return o; + } + else if (type == int.class || type == Integer.class) { + if (intPattern.matcher(valueString).matches()) { + return new Integer(valueString); + } + ParsedExpression exp = new ParsedExpression(valueString); + Object o = exp.evaluate(this); + if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); + return (o instanceof Integer ? (Integer)o : new Integer(((Number)o).intValue())); + } + else if (type == long.class || type == Long.class) { + if (intPattern.matcher(valueString).matches()) { + parsedValue = new Long(valueString); + } + ParsedExpression exp = new ParsedExpression(valueString); + Object o = exp.evaluate(this); + if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); + return new Long(((Number)o).longValue()); + } + else if (type == float.class || type == Float.class) { + if (floatPattern.matcher(valueString).matches()) { + parsedValue = new Float(valueString); + } + ParsedExpression exp = new ParsedExpression(valueString); + Object o = exp.evaluate(this); + if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); + return new Float(((Number)o).floatValue()); + } + else if (type == double.class || type == Double.class) { + if (floatPattern.matcher(valueString).matches()) { + parsedValue = new Double(valueString); + } + ParsedExpression exp = new ParsedExpression(valueString); + Object o = exp.evaluate(this); + if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); + return (o instanceof Double ? (Double)o : new Double(((Number)o).doubleValue())); + } + else if (type == Datum.class) { + try { + return TimeUtil.create(valueString); + } + catch ( java.text.ParseException ex ) { + try { + return Datum.create(Double.parseDouble(valueString)); + } + catch (NumberFormatException iae) { + throw new ParsedExpressionException(valueString + " cannot be parsed as a Datum"); + } + } + + } + else { + throw new IllegalStateException(type.getName() + " is not a recognized type"); + } + } + + protected String replaceReferences(String str) throws DasPropertyException, InvocationTargetException { + Matcher matcher = refPattern.matcher(str); + while (matcher.find()) { + String name = matcher.group(1).trim(); + Object value = get(name); + str = matcher.replaceFirst(value.toString()); + matcher.reset(str); + } + return str; + } + public String toString() { return getClass().getName() + nameMap.keySet().toString(); } diff --git a/dasCore/src/org/das2/beans/AccessLevelBeanInfo.java b/dasCore/src/org/das2/beans/AccessLevelBeanInfo.java index 5c04f5d1f..d048e1f38 100644 --- a/dasCore/src/org/das2/beans/AccessLevelBeanInfo.java +++ b/dasCore/src/org/das2/beans/AccessLevelBeanInfo.java @@ -27,9 +27,6 @@ import java.beans.*; import java.util.ArrayList; import java.util.Arrays; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.das2.util.LoggerManager; /** * This class is designed to implement access levels for bean properties. @@ -41,14 +38,12 @@ */ public abstract class AccessLevelBeanInfo extends SimpleBeanInfo { - private static final Logger logger= LoggerManager.getLogger("das2.system"); - /** * Type-safe enumeration class used to specify access levels * for bean properties. * * **NOTE TO DEVELOPERS** - * The order parameter for the constructor should not be specified as a negative number + * The order parameter for the constructor should not be specifed as a negative number */ public static class AccessLevel implements Comparable { public static final AccessLevel ALL = new AccessLevel("ALL", 0); @@ -62,16 +57,6 @@ private AccessLevel(String str, int order) { public int compareTo(Object o) { return order - ((AccessLevel)o).order; } - @Override - public boolean equals(Object o) { - if ( !( o instanceof AccessLevel ) ) return false; - return order==((AccessLevel)o).order; - } - @Override - public int hashCode() { - return this.order; - } - @Override public String toString() { return str; } @@ -93,16 +78,6 @@ private PersistenceLevel(String str, int order) { public int compareTo(Object o) { return order - ((PersistenceLevel)o).order; } - @Override - public boolean equals(Object o) { - if ( !( o instanceof PersistenceLevel ) ) return false; - return order==((PersistenceLevel)o).order; - } - @Override - public int hashCode() { - return this.order; - } - @Override public String toString() { return str; } @@ -111,7 +86,7 @@ public String toString() { private Property[] properties; private Class beanClass; private static AccessLevel accessLevel; - private static final Object lockObject = new Object(); + private static Object lockObject = new Object(); static { String level = DasApplication.getProperty("edu.uiowa.physics.das.beans.AccessLevelBeanInfo.AccessLevel",null); @@ -177,6 +152,7 @@ public PropertyDescriptor[] getPropertyDescriptors( PersistenceLevel persistence synchronized (lockObject) { try { ArrayList result= new ArrayList(); + int propertyIndex = 0; for (int index = 0; index < properties.length; index++) { if (persistenceLevel.compareTo(properties[index].getPersistenceLevel()) <= 0) { result.add( properties[index].getPropertyDescriptor(beanClass) ); @@ -196,11 +172,6 @@ public PropertyDescriptor[] getPropertyDescriptors( PersistenceLevel persistence } } - /** - * get the property descriptors for the object. - * @return - */ - @Override public PropertyDescriptor[] getPropertyDescriptors() { synchronized (lockObject) { try { @@ -214,7 +185,6 @@ public PropertyDescriptor[] getPropertyDescriptors() { int propertyIndex = 0; for (int index = 0; index < properties.length; index++) { if (accessLevel.compareTo(properties[index].getLevel()) <= 0) { - logger.log(Level.FINE, "property: {0}", properties[index].getPropertyDescriptor(beanClass).getDisplayName()); descriptors[propertyIndex] = properties[index].getPropertyDescriptor(beanClass); propertyIndex++; } @@ -226,15 +196,10 @@ public PropertyDescriptor[] getPropertyDescriptors() { } } - /** - * get the Property for the PropertyDescriptor. - * @param pd - * @return - */ public Property getProperty( PropertyDescriptor pd ) { String name= pd.getName(); for ( int i=0; i 0) { BeanInfo aBeanInfo = (BeanInfo) additionalBeanInfo.remove(0); pdthis = aBeanInfo.getPropertyDescriptors(); - for (PropertyDescriptor pdthi : pdthis) { - String name = pdthi.getName(); - boolean isUseable = pdthi.getReadMethod() != null && !excludePropertyNames.contains(name); - if (isUseable) { - propertyList.add(pdthi); + for (int i = 0; i < pdthis.length; i++) { + String name = pdthis[i].getName(); + boolean isWriteable = pdthis[i].getWriteMethod() != null; + boolean isUseable = pdthis[i].getReadMethod() != null && !excludePropertyNames.contains(name); + if (isUseable || (pdthis[i] instanceof IndexedPropertyDescriptor)) { + propertyList.add(pdthis[i]); } } - /* This is commented to mimic the behavior of the Introspector, which doesn't climb up the bean inheritance - tree unless the additional are specified via the Introspector. */ - // if ( aBeanInfo.getAdditionalBeanInfo()!=null ) { - // additionalBeanInfo.addAll( Arrays.asList( aBeanInfo.getAdditionalBeanInfo() ) ); - // } + /* This is commented to mimic the behavior of the Introspector, which doesn't climb up the bean inheritance + tree unless the additional are specified via the Introspector. */ + // if ( aBeanInfo.getAdditionalBeanInfo()!=null ) { + // additionalBeanInfo.addAll( Arrays.asList( aBeanInfo.getAdditionalBeanInfo() ) ); + // } } } @@ -222,24 +200,15 @@ public static PropertyDescriptor[] getPropertyDescriptors(Class c) { } } - //private static final Map beanInfoCache= new ConcurrentHashMap(); - public static BeanInfo getBeanInfo(final Class c) throws IntrospectionException { - long t0= System.currentTimeMillis(); - // goal: get BeanInfo for the class, or the AccessLevelBeanInfo if it exists. - - BeanInfo beanInfo = null; // beanInfoCache.get(c); - //if ( beanInfo!=null ) { - // logger.log(Level.FINER, "class {0} found in cache in {1} millis", new Object[] { c.getName(), System.currentTimeMillis()-t0 } ); - // return beanInfo; - //} - + BeanInfo beanInfo = null; + if (c.getPackage() == null) { // e.g. String array beanInfo = Introspector.getBeanInfo(c); - logger.log(Level.FINER, "using BeanInfo {0} for {1}", new Object[]{beanInfo.getClass().getName(), c.getName()}); + log.finer("using BeanInfo " + beanInfo.getClass().getName() + " for " + c.getName()); } else { String s; @@ -250,32 +219,22 @@ public static BeanInfo getBeanInfo(final Class c) throws IntrospectionException } Class maybeClass = null; - String beanInfoClassName; + String beanInfoClassName = null; try { beanInfoClassName = c.getPackage() + "." + s + "BeanInfo"; maybeClass = Class.forName(beanInfoClassName); - logger.log(Level.FINER, "class found in {0} millis", ( System.currentTimeMillis()-t0 ) ); } catch (ClassNotFoundException e) { try { beanInfoClassName = "org.das2.beans." + s + "BeanInfo"; maybeClass = Class.forName(beanInfoClassName); - logger.log(Level.FINER, "org.das2.beans class found in {0} millis", ( System.currentTimeMillis()-t0 ) ); } catch (ClassNotFoundException e2) { beanInfo = Introspector.getBeanInfo(c); beanInfoClassName = beanInfo.getClass().getName(); - logger.log(Level.FINER, "introspector found class {0} found in {1} millis", new Object[] { c.getName(), System.currentTimeMillis()-t0 } ); } } - long dt= System.currentTimeMillis()-t0; - - if ( dt>100 ) { - logger.log(Level.INFO, "class {0} found in {1} millis", new Object[] { c.getName(), dt } ); - // weird case where suddenly it's taking forever to resolve these. - } - - logger.log(Level.FINER, "using BeanInfo {0} for {1}", new Object[]{beanInfoClassName, c.getName()}); + log.finer("using BeanInfo " + beanInfoClassName + " for " + c.getName()); if (beanInfo == null) { try { @@ -287,9 +246,6 @@ public static BeanInfo getBeanInfo(final Class c) throws IntrospectionException } } } - - //beanInfoCache.put(c,beanInfo); - return beanInfo; } @@ -304,7 +260,7 @@ public static String[] getPropertyNames(PropertyDescriptor[] propertyList) { /** * Use reflection to get a list of all the property names for the class. * The properties are returned in the order specified, and put inherited properties - * at the end of the list. This is motivated by the arbitrary order that the + * at the end of the list. This is motivated by the arbitary order that the * Introspector presents the properties, which is in conflict with our desire to control * the property order. */ diff --git a/dasCore/src/org/das2/beans/ColumnColumnConnectorBeanInfo.java b/dasCore/src/org/das2/beans/ColumnColumnConnectorBeanInfo.java index 4d6dabc51..b046bf109 100755 --- a/dasCore/src/org/das2/beans/ColumnColumnConnectorBeanInfo.java +++ b/dasCore/src/org/das2/beans/ColumnColumnConnectorBeanInfo.java @@ -25,17 +25,19 @@ import org.das2.beans.AccessLevelBeanInfo.AccessLevel; import org.das2.beans.AccessLevelBeanInfo.Property; +import org.das2.graph.ColumnColumnConnector; /** - * BeanInfo class for ColumnColumnConnector + * BeanInfo class for DasColorBar * * @author Edward West */ public class ColumnColumnConnectorBeanInfo extends AccessLevelBeanInfo { - private static final Property[] properties = { + ColumnColumnConnector c; + + protected static final Property[] properties = { new Property("bottomCurtain", AccessLevel.DASML, "isBottomCurtain", "setBottomCurtain", null), - new Property("showYPosition", AccessLevel.DASML, "isShowYPosition", "setShowYPosition", null), new Property("curtainOpacityPercent", AccessLevel.DASML, "getCurtainOpacityPercent", "setCurtainOpacityPercent", null), new Property("fill", AccessLevel.DASML, "isFill", "setFill", null), new Property("fillColor", AccessLevel.DASML, "getFillColor", "setFillColor", null), diff --git a/dasCore/src/org/das2/beans/CrossHairRendererBeanInfo.java b/dasCore/src/org/das2/beans/CrossHairRendererBeanInfo.java index 313692bfa..1270c9730 100644 --- a/dasCore/src/org/das2/beans/CrossHairRendererBeanInfo.java +++ b/dasCore/src/org/das2/beans/CrossHairRendererBeanInfo.java @@ -38,7 +38,6 @@ public CrossHairRendererBeanInfo() { super(properties, org.das2.event.CrossHairRenderer.class); } - @Override public java.beans.BeanInfo[] getAdditionalBeanInfo() { BeanInfo[] additional = { new LabelDragRendererBeanInfo(), diff --git a/dasCore/src/org/das2/beans/DasApplicationBeanInfo.java b/dasCore/src/org/das2/beans/DasApplicationBeanInfo.java index 9dc238d3b..285141d04 100644 --- a/dasCore/src/org/das2/beans/DasApplicationBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasApplicationBeanInfo.java @@ -23,6 +23,7 @@ package org.das2.beans; +import org.das2.DasApplication; import org.das2.beans.AccessLevelBeanInfo.AccessLevel; import org.das2.beans.AccessLevelBeanInfo.Property; diff --git a/dasCore/src/org/das2/beans/DasAxisBeanInfo.java b/dasCore/src/org/das2/beans/DasAxisBeanInfo.java index 01116e5dc..fab7111db 100644 --- a/dasCore/src/org/das2/beans/DasAxisBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasAxisBeanInfo.java @@ -43,21 +43,17 @@ public class DasAxisBeanInfo extends AccessLevelBeanInfo { new Property("units", AccessLevel.DASML, "getUnits", null, null), new Property("format", AccessLevel.DASML, "getFormat", "setFormat", null), new Property("tickLabelsVisible", AccessLevel.DASML, "isTickLabelsVisible", "setTickLabelsVisible", null), - new Property("tickLength",AccessLevel.DASML, "getTickLength", "setTickLength", null), new Property("oppositeAxisVisible", AccessLevel.DASML, "isOppositeAxisVisible", "setOppositeAxisVisible", null), - new Property("useDomainDivider", AccessLevel.DASML, "isUseDomainDivider", "setUseDomainDivider", null), new Property("flipLabel", AccessLevel.DASML, "isFlipLabel", "setFlipLabel", null), new Property("animated", AccessLevel.DASML, "isAnimated", "setAnimated", null), new Property("dataPath", AccessLevel.DASML, "getDataPath", "setDataPath", null), new Property("showTca", AccessLevel.DASML, "getDrawTca", "setDrawTca", null), - new Property("scanRange", AccessLevel.DASML, "getScanRange", "setScanRange", null), }; public DasAxisBeanInfo() { super(properties, org.das2.graph.DasAxis.class); } - @Override public BeanInfo[] getAdditionalBeanInfo() { java.beans.BeanInfo[] additional = { new DasCanvasComponentBeanInfo() diff --git a/dasCore/src/org/das2/beans/DasCanvasBeanInfo.java b/dasCore/src/org/das2/beans/DasCanvasBeanInfo.java index 30eefd16e..c68965ec7 100644 --- a/dasCore/src/org/das2/beans/DasCanvasBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasCanvasBeanInfo.java @@ -24,7 +24,6 @@ package org.das2.beans; import java.beans.MethodDescriptor; -import java.util.Arrays; /** * @@ -55,7 +54,7 @@ public class DasCanvasBeanInfo extends AccessLevelBeanInfo { methods[0] = new MethodDescriptor(org.das2.graph.DasCanvas.class.getMethod("writeToPng", writeToPngParams)); } catch (NoSuchMethodException nsme) { - IllegalStateException ise = new IllegalStateException(nsme); + IllegalStateException ise = new IllegalStateException(nsme.getMessage()); ise.initCause(nsme); throw ise; } @@ -65,9 +64,8 @@ public DasCanvasBeanInfo() { super(properties, org.das2.graph.DasCanvas.class); } - @Override public MethodDescriptor[] getMethodDescriptors() { - return Arrays.copyOf( methods,methods.length ); + return methods; } } diff --git a/dasCore/src/org/das2/beans/DasCanvasComponentBeanInfo.java b/dasCore/src/org/das2/beans/DasCanvasComponentBeanInfo.java index ac4bf298f..44d44b004 100644 --- a/dasCore/src/org/das2/beans/DasCanvasComponentBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasCanvasComponentBeanInfo.java @@ -26,7 +26,6 @@ import org.das2.graph.DasCanvasComponent; import java.beans.MethodDescriptor; -import java.util.Arrays; public class DasCanvasComponentBeanInfo extends AccessLevelBeanInfo { @@ -34,7 +33,7 @@ public class DasCanvasComponentBeanInfo extends AccessLevelBeanInfo { new Property("name", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getDasName", "setDasName", null), new Property("row", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getRow", "setRow", null), new Property("column", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getColumn", "setColumn", null), - new Property("mouseAdapter", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getDasMouseInputAdapter", null, null) + new Property("mouseAdapter", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getDasMouseInputAdapter", "setDasMouseInputAdapter", null) }; private static MethodDescriptor[] methods; @@ -44,7 +43,7 @@ public class DasCanvasComponentBeanInfo extends AccessLevelBeanInfo { methods[0] = new MethodDescriptor(DasCanvasComponent.class.getMethod("update")); } catch (NoSuchMethodException nsme) { - IllegalStateException ise = new IllegalStateException(nsme); + IllegalStateException ise = new IllegalStateException(nsme.getMessage()); ise.initCause(nsme); throw ise; } @@ -54,9 +53,8 @@ public DasCanvasComponentBeanInfo() { super(properties, org.das2.graph.DasCanvasComponent.class); } - @Override public MethodDescriptor[] getMethodDescriptors() { - return Arrays.copyOf( methods,methods.length ); + return methods; } } diff --git a/dasCore/src/org/das2/beans/DasColorBarBeanInfo.java b/dasCore/src/org/das2/beans/DasColorBarBeanInfo.java index a23d4158d..082360274 100644 --- a/dasCore/src/org/das2/beans/DasColorBarBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasColorBarBeanInfo.java @@ -25,7 +25,10 @@ import org.das2.beans.AccessLevelBeanInfo.AccessLevel; import org.das2.beans.AccessLevelBeanInfo.Property; +import org.das2.graph.DasAxis; import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; import org.das2.components.propertyeditor.EnumerationEditor; /** @@ -35,7 +38,7 @@ */ public class DasColorBarBeanInfo extends AccessLevelBeanInfo { - private static final Property[] properties = { + protected static final Property[] properties = { new Property("type", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getType", "setType", EnumerationEditor.class), new Property("fillColor", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getFillColor", "setFillColor", null ) }; @@ -44,7 +47,6 @@ public DasColorBarBeanInfo() { super(properties, org.das2.graph.DasColorBar.class); } - @Override public BeanInfo[] getAdditionalBeanInfo() { BeanInfo[] additional = { new DasAxisBeanInfo(), diff --git a/dasCore/src/org/das2/beans/DasColumnBeanInfo.java b/dasCore/src/org/das2/beans/DasColumnBeanInfo.java index 97f03908f..0f2645be4 100644 --- a/dasCore/src/org/das2/beans/DasColumnBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasColumnBeanInfo.java @@ -32,7 +32,6 @@ public class DasColumnBeanInfo extends AccessLevelBeanInfo { private static Property[] properties = { new Property("name", AccessLevel.ALL, PersistenceLevel.PERSISTENT, "getDasName", "setDasName", null), - new Property("parent", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getParent", null, null), new Property("minimum", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getMinimum", "setMinimum", null), new Property("maximum", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getMaximum", "setMaximum", null), new Property("dminimum", AccessLevel.DASML, "getDMinimum", "setDMinimum", null), diff --git a/dasCore/src/org/das2/beans/DasLabelAxisBeanInfo.java b/dasCore/src/org/das2/beans/DasLabelAxisBeanInfo.java index 29a2784ac..4dfb88a20 100644 --- a/dasCore/src/org/das2/beans/DasLabelAxisBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasLabelAxisBeanInfo.java @@ -45,7 +45,6 @@ public DasLabelAxisBeanInfo() { super(properties, org.das2.graph.DasLabelAxis.class); } - @Override public BeanInfo[] getAdditionalBeanInfo() { java.beans.BeanInfo[] additional = { new DasCanvasComponentBeanInfo() diff --git a/dasCore/src/org/das2/beans/DasMouseInputAdapterBeanInfo.java b/dasCore/src/org/das2/beans/DasMouseInputAdapterBeanInfo.java index 9dd43a199..4ab9c6f5d 100644 --- a/dasCore/src/org/das2/beans/DasMouseInputAdapterBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasMouseInputAdapterBeanInfo.java @@ -23,10 +23,14 @@ package org.das2.beans; +import java.beans.BeanInfo; + public class DasMouseInputAdapterBeanInfo extends AccessLevelBeanInfo { private static final Property[] properties = { - new Property("mouseModules", AccessLevel.DASML, "getMouseModules", null, "getMouseModule", null, null), + new Property("mouseModules", AccessLevel.DASML, "getMouseModules", null, "getMouseModule", null, null), + //new Property("hoverHighlite", AccessLevel.DASML, "isHoverHighlite", "setHoverHighlite", null ), + new Property("primaryModule", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getPrimaryModule", "setPrimaryModule", null ), new Property("secondaryModule", AccessLevel.DASML, PersistenceLevel.PERSISTENT, diff --git a/dasCore/src/org/das2/beans/DasPlotBeanInfo.java b/dasCore/src/org/das2/beans/DasPlotBeanInfo.java index 6596e5eb7..c66c4befb 100644 --- a/dasCore/src/org/das2/beans/DasPlotBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasPlotBeanInfo.java @@ -30,18 +30,10 @@ public class DasPlotBeanInfo extends AccessLevelBeanInfo { private static final Property[] properties = { new Property("title", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getTitle", "setTitle", null), new Property("drawGrid", AccessLevel.DASML, "isDrawGrid", "setDrawGrid", null), - new Property("drawGridOver", AccessLevel.DASML, "isDrawGridOver", "setDrawGridOver", null), - new Property("drawGridColor", AccessLevel.DASML, "getDrawGridColor", "setDrawGridColor", null), new Property("drawMinorGrid", AccessLevel.DASML, "isDrawMinorGrid", "setDrawMinorGrid", null), - new Property("drawBackground", AccessLevel.DASML, "getDrawBackground", "setDrawBackground", null), new Property("preview", AccessLevel.DASML, "isPreviewEnabled", "setPreviewEnabled", null ), new Property("oversize", AccessLevel.DASML, "isOverSize", "setOverSize", null ), - new Property("legendPosition", AccessLevel.DASML, "getLegendPosition", "setLegendPosition", null ), - new Property("legendRelativeFontSize", AccessLevel.DASML, "getLegendRelativeFontSize", "setLegendRelativeFontSize", null ), - new Property("logLevel", AccessLevel.DASML, "getLogLevel", "setLogLevel", null ), - new Property("logTimeoutSec", AccessLevel.DASML, "getLogTimeoutSec", "setLogTimeoutSec", null ), - new Property("isotropic", AccessLevel.DASML, "isIsotropic", "setIsotropic", null ), - new Property("renderers", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getRenderers", null, "getRenderer", null, null), + new Property("renderers", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getRenderers", null, "getRenderer", null, null), new Property("xAxis", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getXAxis", "setXAxis", null), new Property("yAxis", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getYAxis", "setYAxis", null), }; diff --git a/dasCore/src/org/das2/beans/DasRowBeanInfo.java b/dasCore/src/org/das2/beans/DasRowBeanInfo.java index a6c2214e5..57effa40d 100644 --- a/dasCore/src/org/das2/beans/DasRowBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasRowBeanInfo.java @@ -32,7 +32,6 @@ public class DasRowBeanInfo extends AccessLevelBeanInfo { private static Property[] properties = { new Property("name", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getDasName", "setDasName", null), - new Property("parent", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getParent", null, null), new Property("minimum", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getMinimum", "setMinimum", null), new Property("maximum", AccessLevel.DASML, PersistenceLevel.PERSISTENT, "getMaximum", "setMaximum", null), new Property("dminimum", AccessLevel.DASML, "getDMinimum", "setDMinimum", null), diff --git a/dasCore/src/org/das2/beans/DasServerBeanInfo.java b/dasCore/src/org/das2/beans/DasServerBeanInfo.java index e21693fd7..b5e289a89 100644 --- a/dasCore/src/org/das2/beans/DasServerBeanInfo.java +++ b/dasCore/src/org/das2/beans/DasServerBeanInfo.java @@ -25,6 +25,7 @@ import org.das2.beans.AccessLevelBeanInfo.AccessLevel; import org.das2.beans.AccessLevelBeanInfo.Property; +import org.das2.client.DasServer; /** * Bean Info implementation for DasDevicePosition @@ -32,7 +33,7 @@ * @author Edward West */ public class DasServerBeanInfo extends AccessLevelBeanInfo { - + DasServer me; private static Property[] properties = { new Property("name", AccessLevel.DASML, "getName", null, null), }; diff --git a/dasCore/src/org/das2/beans/DataPointRecorderBeanInfo.java b/dasCore/src/org/das2/beans/DataPointRecorderBeanInfo.java index 2180d3f4a..dead015d0 100644 --- a/dasCore/src/org/das2/beans/DataPointRecorderBeanInfo.java +++ b/dasCore/src/org/das2/beans/DataPointRecorderBeanInfo.java @@ -23,12 +23,13 @@ package org.das2.beans; + + public class DataPointRecorderBeanInfo extends AccessLevelBeanInfo { private static final Property[] properties = { new Property("xTagWidth", AccessLevel.DASML, "getXTagWidth", "setXTagWidth", null), - new Property("snapToGrid", AccessLevel.DASML, "isSnapToGrid", "setSnapToGrid", null), - new Property("sorted", AccessLevel.DASML, "isSorted", "setSorted", null), + new Property("snapToGrid", AccessLevel.DASML, "isSnapToGrid", "setSnapToGrid", null), }; public DataPointRecorderBeanInfo() { diff --git a/dasCore/src/org/das2/beans/ImplicitAccessLevelBeanInfo.java b/dasCore/src/org/das2/beans/ImplicitAccessLevelBeanInfo.java index 838397a61..2c9d3b1c5 100644 --- a/dasCore/src/org/das2/beans/ImplicitAccessLevelBeanInfo.java +++ b/dasCore/src/org/das2/beans/ImplicitAccessLevelBeanInfo.java @@ -24,12 +24,12 @@ */ public class ImplicitAccessLevelBeanInfo extends AccessLevelBeanInfo { - //BeanInfo beanInfo; + BeanInfo beanInfo; /** Creates a new instance of ImplicitAccessLevelBeanInfo */ private ImplicitAccessLevelBeanInfo( BeanInfo beanInfo, Class beanClass, Property[] properties ) { super( properties, beanClass ); - //this.beanInfo= beanInfo; + this.beanInfo= beanInfo; } public static ImplicitAccessLevelBeanInfo create( BeanInfo beanInfo, Class beanClass ) { diff --git a/dasCore/src/org/das2/beans/LabelDragRendererBeanInfo.java b/dasCore/src/org/das2/beans/LabelDragRendererBeanInfo.java index c8e8861f4..4e89d9dc2 100644 --- a/dasCore/src/org/das2/beans/LabelDragRendererBeanInfo.java +++ b/dasCore/src/org/das2/beans/LabelDragRendererBeanInfo.java @@ -23,6 +23,8 @@ package org.das2.beans; + + public class LabelDragRendererBeanInfo extends AccessLevelBeanInfo { private static final Property[] properties = { diff --git a/dasCore/src/org/das2/beans/MouseModuleBeanInfo.java b/dasCore/src/org/das2/beans/MouseModuleBeanInfo.java index 7716bda42..c714d1cb2 100644 --- a/dasCore/src/org/das2/beans/MouseModuleBeanInfo.java +++ b/dasCore/src/org/das2/beans/MouseModuleBeanInfo.java @@ -23,6 +23,8 @@ package org.das2.beans; +import java.beans.BeanInfo; + public class MouseModuleBeanInfo extends AccessLevelBeanInfo { private static final Property[] properties = { diff --git a/dasCore/src/org/das2/beans/RectangleEditor.java b/dasCore/src/org/das2/beans/RectangleEditor.java index 9d6a1f3b6..8846e50d9 100644 --- a/dasCore/src/org/das2/beans/RectangleEditor.java +++ b/dasCore/src/org/das2/beans/RectangleEditor.java @@ -11,7 +11,9 @@ package org.das2.beans; import org.das2.datum.Units; +import java.awt.Rectangle; import java.beans.PropertyEditorSupport; +import java.beans.XMLEncoder; /** * @@ -19,12 +21,10 @@ */ public class RectangleEditor extends PropertyEditorSupport { - @Override public void setAsText(String str) throws IllegalArgumentException { setValue( Units.getByName(str) ); } - @Override public String getAsText() { return String.valueOf( getValue() ); } diff --git a/dasCore/src/org/das2/beans/RendererBeanInfo.java b/dasCore/src/org/das2/beans/RendererBeanInfo.java index 197afb7cc..0f166fa7a 100755 --- a/dasCore/src/org/das2/beans/RendererBeanInfo.java +++ b/dasCore/src/org/das2/beans/RendererBeanInfo.java @@ -33,8 +33,7 @@ public class RendererBeanInfo extends AccessLevelBeanInfo { new Property("dumpDataSet", AccessLevel.DASML, "isDumpDataSet", "setDumpDataSet", null), new Property("dataSet", AccessLevel.DASML, "getDataSet", null, null), new Property("lastException", AccessLevel.DASML, "getLastException", null, null), - new Property("dataLoader", AccessLevel.DASML, "getDataLoader", null, null), - new Property("lengendLabel", AccessLevel.DASML, "getLegendLabel", "setLegendLabel", null), + new Property("dataLoader", AccessLevel.DASML, "getDataLoader", null, null), //new Property("overLoading", AccessLevel.DASML, "isOverloading", "setOverloading", null), }; diff --git a/dasCore/src/org/das2/beans/RowRowConnectorBeanInfo.java b/dasCore/src/org/das2/beans/RowRowConnectorBeanInfo.java index c24d25bbc..41366ee4f 100644 --- a/dasCore/src/org/das2/beans/RowRowConnectorBeanInfo.java +++ b/dasCore/src/org/das2/beans/RowRowConnectorBeanInfo.java @@ -23,6 +23,9 @@ package org.das2.beans; +import org.das2.components.propertyeditor.EnumerationEditor; +import java.beans.BeanInfo; + /** * BeanInfo class for DasColorBar * diff --git a/dasCore/src/org/das2/beans/SpectrogramRendererBeanInfo.java b/dasCore/src/org/das2/beans/SpectrogramRendererBeanInfo.java index beab804ee..5a597f306 100755 --- a/dasCore/src/org/das2/beans/SpectrogramRendererBeanInfo.java +++ b/dasCore/src/org/das2/beans/SpectrogramRendererBeanInfo.java @@ -39,7 +39,6 @@ public SpectrogramRendererBeanInfo() { super(properties, org.das2.graph.SpectrogramRenderer.class); } - @Override public java.beans.BeanInfo[] getAdditionalBeanInfo() { BeanInfo[] additional = { new RendererBeanInfo() diff --git a/dasCore/src/org/das2/beans/StackedHistogramRendererBeanInfo.java b/dasCore/src/org/das2/beans/StackedHistogramRendererBeanInfo.java index 6c89d2bb3..2b08eb15b 100755 --- a/dasCore/src/org/das2/beans/StackedHistogramRendererBeanInfo.java +++ b/dasCore/src/org/das2/beans/StackedHistogramRendererBeanInfo.java @@ -37,13 +37,13 @@ public class StackedHistogramRendererBeanInfo extends AccessLevelBeanInfo { new Property("ZAxis", AccessLevel.DASML, "getZAxis", "setZAxis", null), new Property("PeaksIndicator", AccessLevel.DASML, "getPeaksIndicator", "setPeaksIndicator", EnumerationEditor.class ), new Property("sliceRebinnedData", AccessLevel.DASML, "isSliceRebinnedData", "setSliceRebinnedData", null), + new Property("transparentBackground", AccessLevel.DASML, "isTransparentBackground", "setTransparentBackground", null) }; public StackedHistogramRendererBeanInfo() { super(properties, org.das2.graph.StackedHistogramRenderer.class); } - @Override public BeanInfo[] getAdditionalBeanInfo() { BeanInfo[] additional = { new RendererBeanInfo() diff --git a/dasCore/src/org/das2/beans/StreamDataSetDescriptorBeanInfo.java b/dasCore/src/org/das2/beans/StreamDataSetDescriptorBeanInfo.java index bdf1436f5..1e878088f 100644 --- a/dasCore/src/org/das2/beans/StreamDataSetDescriptorBeanInfo.java +++ b/dasCore/src/org/das2/beans/StreamDataSetDescriptorBeanInfo.java @@ -38,7 +38,6 @@ public class StreamDataSetDescriptorBeanInfo extends AccessLevelBeanInfo { new Property("serverSideReduction", AccessLevel.DASML, "isServerSideReduction", null, null), }; - @Override public BeanInfo[] getAdditionalBeanInfo() { BeanInfo[] additional = { new DataSetDescriptorBeanInfo() diff --git a/dasCore/src/org/das2/beans/SymbolLineRendererBeanInfo.java b/dasCore/src/org/das2/beans/SymbolLineRendererBeanInfo.java index c5a54b57f..446e3934b 100755 --- a/dasCore/src/org/das2/beans/SymbolLineRendererBeanInfo.java +++ b/dasCore/src/org/das2/beans/SymbolLineRendererBeanInfo.java @@ -42,7 +42,6 @@ public SymbolLineRendererBeanInfo() { super(properties, org.das2.graph.SymbolLineRenderer.class); } - @Override public BeanInfo[] getAdditionalBeanInfo() { BeanInfo[] additional = { new RendererBeanInfo() diff --git a/dasCore/src/org/das2/beans/UnitsEditor.java b/dasCore/src/org/das2/beans/UnitsEditor.java index 4acb617cf..23eebf689 100644 --- a/dasCore/src/org/das2/beans/UnitsEditor.java +++ b/dasCore/src/org/das2/beans/UnitsEditor.java @@ -19,7 +19,6 @@ */ public class UnitsEditor extends PropertyEditorSupport { - @Override public void setAsText(String str) throws IllegalArgumentException { setValue( Units.getByName(str) ); } diff --git a/dasCore/src/org/das2/client/Authenticator.java b/dasCore/src/org/das2/client/Authenticator.java index 2dfd6eaaf..972446950 100755 --- a/dasCore/src/org/das2/client/Authenticator.java +++ b/dasCore/src/org/das2/client/Authenticator.java @@ -33,7 +33,6 @@ import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.List; -import org.das2.system.NullPreferences; public class Authenticator extends JPanel { @@ -49,14 +48,7 @@ public class Authenticator extends JPanel { final String KEY_AUTOLOGIN= "autoLogin"; final String KEY_SAVECREDENTIALS= "saveCredentials"; - Preferences prefs; - { - try { - prefs= Preferences.userNodeForPackage( Authenticator.class ); - } catch ( NullPointerException ex ) { - prefs= new NullPreferences(); - } - } + Preferences prefs= Preferences.userNodeForPackage( Authenticator.class ); public Authenticator(DasServer dasServer) { this( dasServer, "" ); @@ -161,14 +153,9 @@ public Key authenticate() { feedbackLabel.setText("stored credentials rejected by server"); } } - - if ( DasApplication.getDefaultApplication().isHeadless() ) { - System.err.println("head is not available to query for credentials"); - return null; - } Component parent=DasApplication.getDefaultApplication().getMainFrame(); - + while ( okayCancel==JOptionPane.OK_OPTION && result==null ) { okayCancel= JOptionPane.showConfirmDialog(parent,this,"Authenticator", diff --git a/dasCore/src/org/das2/client/DasServer.java b/dasCore/src/org/das2/client/DasServer.java index 2888f9d6c..63b036690 100755 --- a/dasCore/src/org/das2/client/DasServer.java +++ b/dasCore/src/org/das2/client/DasServer.java @@ -23,7 +23,6 @@ package org.das2.client; -import java.util.logging.Level; import org.das2.stream.StreamDescriptor; import org.das2.stream.StreamException; import org.das2.stream.DasStreamFormatException; @@ -45,15 +44,12 @@ import org.w3c.dom.Element; /** Represents a remote Das 2.1 compliant server. - * + * * Use the create() method to instantiate new Das 2 server objects. Each call to * create() will only allocate a new server instance if no server matching the given URL * has already been created. * * @author jbf - * A DasServer is the object that holds the methods of a remote das server. - * These include, for example, getLogo() which returns a graphical mnemonic - * for the server, authenticate() and setPassword(). */ public class DasServer { @@ -62,9 +58,9 @@ public class DasServer { private String host; private String path; private int port; - + @Deprecated - private final HashMap keys; // Holds a list of all non-http auth das2 server keys + private HashMap keys; // Holds a list of all non-http auth das2 server keys //Probably dead code, let's see //private Key key; @@ -74,58 +70,21 @@ public class DasServer { /* Holds the global list of Das2 Server objects */ private static HashMap instanceHashMap= new HashMap(); -// @Deprecated -// public static DasServer plasmaWaveGroup; -// -// @Deprecated -// public static DasServer sarahandjeremy; -// -// static { -// try { -// plasmaWaveGroup= DasServer.create(new URL("http://www-pw.physics.uiowa.edu/das/das2Server")); -// } catch ( java.net.MalformedURLException e ) { -// throw new IllegalArgumentException(e); -// } -// try { -// sarahandjeremy= DasServer.create(new URL("http://www.sarahandjeremy.net/das/dasServer.cgi")); -// } catch ( java.net.MalformedURLException e ) { -// throw new IllegalArgumentException(e); -// } -// } - - /** - * return one DasServer to serve as an example. - * @return - */ - public static DasServer createPlasmaWaveGroup() { + @Deprecated + public static DasServer plasmaWaveGroup; + + @Deprecated + public static DasServer sarahandjeremy; + + static { try { - return DasServer.create(new URL("http://www-pw.physics.uiowa.edu/das/das2Server")); + plasmaWaveGroup= DasServer.create(new URL("http://www-pw.physics.uiowa.edu/das/das2Server")); + sarahandjeremy= DasServer.create(new URL("http://www.sarahandjeremy.net/das/dasServer.cgi")); } catch ( java.net.MalformedURLException e ) { - throw new IllegalArgumentException(e); + org.das2.util.DasExceptionHandler.handle(e); } } - - /** Class to represent know information about an item in a list of data sources. - * @author cwp - */ - public class DataSrcListItem { - private boolean bDirectory; - private String sName; - private String sDesc; - /** Create a data source item with a description string */ - public DataSrcListItem(boolean bDirectory, String sName, String sDesc){ - this.bDirectory = bDirectory; - this.sName = sName; - this.sDesc = sDesc; - } - public boolean isDirectory(){return bDirectory;} - public boolean isDataSource(){return !bDirectory;} - public String name(){return sName;} - public String description(){return sDesc;} - @Override - public String toString(){return sName;} - } - + private DasServer(String sProto, String host, String path) { String[] s= host.split(":"); if ( s.length>1 ) { @@ -139,7 +98,7 @@ private DasServer(String sProto, String host, String path) { this.path= path; this.keys= new HashMap(); } - + /** Provide the Das2 Server location. * Note that more than one Das2 server may be present on a single host web-site. * @return A URL string containing protocol, host and path information. @@ -169,17 +128,17 @@ public static DasServer create( URL url ) { } String key= proto+"://" + host + url.getPath(); if ( instanceHashMap.containsKey( key ) ) { - logger.log( Level.FINE, "Using existing DasServer for {0}", url); + logger.fine( "Using existing DasServer for "+url); return (DasServer) instanceHashMap.get( key ); } else { String path= url.getPath(); - logger.log( Level.FINE, "Creating DasServer for {0}", url); + logger.fine( "Creating DasServer for "+url); DasServer result= new DasServer(proto, host, path); instanceHashMap.put(key,result); return result; } } - + /** Query the remote DasServer for it's id string. * For Das 2.1 servers this is handled by sending the GET query ?server=id. * @@ -187,181 +146,93 @@ public static DasServer create( URL url ) { */ public String getName() { String formData= "server=id"; - InputStream in=null; + try { URL server= new URL("http",host,port,path+"?"+formData); - logger.log( Level.FINE, "connecting to {0}", server); + logger.fine( "connecting to "+server); URLConnection urlConnection = server.openConnection(); urlConnection.connect(); - in= urlConnection.getInputStream(); + String contentType = urlConnection.getContentType(); + InputStream in= urlConnection.getInputStream(); String result= new String( read(in) ); - logger.log( Level.FINE, "response={0}", result); + logger.fine( "response="+result); return result; } catch (IOException e) { - return ""; - } finally { - if ( in!=null ) try { - in.close(); - } catch ( IOException ex ) { - logger.log( Level.WARNING, ex.toString(), ex ); - } } - } + /** Query the remote DasServer for it's image icon. + * For Das 2.1 servers this is handled by sending the GET query ?server=logo. + * + * @return An ImageIcon with the server logo, or an empty ImageIcon if the request + * failed + */ public ImageIcon getLogo() { - String formData= "server=logo"; - InputStream in=null; + try { URL server= new URL("http",host,port,path+"?"+formData); - logger.log( Level.FINE, "connecting to {0}", server); + logger.fine( "connecting to "+server); URLConnection urlConnection = server.openConnection(); urlConnection.connect(); - in= urlConnection.getInputStream(); + String contentType = urlConnection.getContentType(); + InputStream in= urlConnection.getInputStream(); byte[] data= read(in); - logger.log( Level.FINE, "response={0} bytes", data.length); + logger.fine( "response="+data.length+" bytes"); return new ImageIcon(data); } catch (IOException e) { - return new ImageIcon(); - } finally { - if ( in!=null ) try { - in.close(); - } catch ( IOException ex ) { - logger.log( Level.WARNING, ex.toString(), ex ); - } - } - - } - - public TreeModel getDataSetListWithDiscovery() throws org.das2.DasException { - String formData= "server=discovery"; - - InputStream in=null; - try { - URL server= new URL(sProto,host,port,path+"?"+formData); - - logger.log( Level.FINE, "connecting to {0}", server); - - URLConnection urlConnection = server.openConnection(); - urlConnection.connect(); - - in= urlConnection.getInputStream(); - - TreeModel result= createModel(in); - logger.log( Level.FINE, "response->{0}", result); - return result; - - } catch (IOException e) { - throw new DasIOException( e.getMessage() ); - } finally { - if ( in!=null ) try { - in.close(); - } catch ( IOException ex ) { - logger.log( Level.WARNING, ex.toString(), ex ); - } } } + /** Query the remote DasServer for a hierarchical tree of all data sources on + * the server. + * For Das 2.1 servers this is handled by sending the GET query ?server=list. + * + * @return + */ public TreeModel getDataSetList() throws org.das2.DasException { String formData= "server=list"; - InputStream in=null; + try { - URL server= new URL(sProto,host,port,path+"?"+formData); + URL server= new URL("http",host,port,path+"?"+formData); - logger.log( Level.FINE, "connecting to {0}", server); + logger.fine( "connecting to "+server); URLConnection urlConnection = server.openConnection(); urlConnection.connect(); - in= urlConnection.getInputStream(); + String contentType = urlConnection.getContentType(); + InputStream in= urlConnection.getInputStream(); TreeModel result= createModel(in); - logger.log( Level.FINE, "response->{0}", result); + logger.fine( "response->"+result); return result; } catch (IOException e) { throw new DasIOException( e.getMessage() ); - } finally { - if ( in!=null ) try { - in.close(); - } catch ( IOException ex ) { - logger.log( Level.WARNING, ex.toString(), ex ); - } } - } - /** - * sort the DefaultMutableTreeNodes so the directories are listed before - * the files. - * @param tn - */ - private void sortDirectories( DefaultMutableTreeNode tn ) { - DefaultMutableTreeNode[] children= new DefaultMutableTreeNode[tn.getChildCount()]; - int ichild=0; - for ( int i=0; i-1 ) { - sDesc = line.substring(ipipe+1).trim(); - if(sDesc.isEmpty()) sDesc = null; - line = line.substring(0,ipipe).trim(); - } - DefaultMutableTreeNode current = root; StringTokenizer tokenizer = new StringTokenizer(line, "/"); token: while (tokenizer.hasMoreTokens()) { @@ -369,19 +240,21 @@ private TreeModel createModel(InputStream uin) throws IOException { for (int index = 0; index < current.getChildCount(); index++) { String str = current.getChildAt(index).toString(); if (str.equals(tok)) { - current = (DefaultMutableTreeNode)current.getChildAt(index); + current = + (DefaultMutableTreeNode)current.getChildAt(index); continue token; } } - boolean bDir = tokenizer.hasMoreElements() ? true : line.endsWith("/"); - DataSrcListItem dsItem = new DataSrcListItem(bDir, tok, sDesc); - DefaultMutableTreeNode node = new DefaultMutableTreeNode(dsItem, bDir); + DefaultMutableTreeNode node = + new DefaultMutableTreeNode(tok, + (tokenizer.hasMoreElements() + ? true + : line.endsWith("/"))); current.add(node); current = node; } line = in.readLine(); } - sortDirectories( root ); return model; } @@ -394,13 +267,13 @@ public StreamDescriptor getStreamDescriptor( URL dataSetID ) throws DasException String dsdf = dataSetID.getQuery().split("&")[0]; URL url = new URL(sProto, host, port, path+"?server=dsdf&dataset=" + dsdf); - logger.log( Level.FINE, "connecting to {0}", url); + logger.fine( "connecting to "+url); URLConnection connection = url.openConnection(); connection.connect(); String contentType = connection.getContentType(); String[] s1= contentType.split(";"); // dump charset info contentType= s1[0]; - + InputStream inStream = null; if(connection instanceof HttpURLConnection){ HttpURLConnection httpConn = (HttpURLConnection) connection; @@ -414,31 +287,32 @@ public StreamDescriptor getStreamDescriptor( URL dataSetID ) throws DasException contentType.equalsIgnoreCase("text/vnd.das2.das2stream") ) { PushbackReader reader = new PushbackReader(new InputStreamReader(inStream), 4); char[] four = new char[4]; - int count= reader.read(four); - if ( count!=4 ) throw new IllegalArgumentException("failed to read four characters"); + reader.read(four); if (new String(four).equals("[00]")) { logger.fine("response is a das2Stream"); - if ( reader.skip(6)!=6 ) throw new IllegalArgumentException("expected to skip six characters"); + reader.skip(6); Document header = StreamDescriptor.parseHeader(reader); Element root = header.getDocumentElement(); - if (root.getTagName().equals("stream")) { - return new StreamDescriptor(root); - } - else if ( root.getTagName().equals("exception") ) { - logger.info("response is an exception"); - String type= root.getAttribute("type"); - StreamException se= new StreamException( "stream exception: "+type ); - throw new DasException("stream exception: " + type, se); - } - else if (root.getTagName().equals("")) { - throw new DasStreamFormatException(); - } - else { - throw new DasStreamFormatException(); - } - } + switch(root.getTagName()){ + case "stream": + return new StreamDescriptor(root); + case "exception": + logger.fine("response is an exception"); + String type= root.getAttribute("type"); + String sMsg = root.getAttribute("message"); + String sExceptMsg = "stream exception: "+type; + if(!sMsg.isEmpty()) + sExceptMsg = sExceptMsg + ", " + sMsg; + StreamException se= new StreamException( sExceptMsg ); + throw new DasException(sExceptMsg, se); + case "": + throw new DasStreamFormatException(); + default: + throw new DasStreamFormatException(); + } + } else { - logger.info("response is a legacy descriptor"); + logger.fine("response is a legacy descriptor"); reader.unread(four); BufferedReader in = new BufferedReader(reader); StreamDescriptor result = StreamDescriptor.createLegacyDescriptor(in); @@ -446,7 +320,7 @@ else if (root.getTagName().equals("")) { } } else { - BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + BufferedReader in = new BufferedReader(new InputStreamReader(inStream)); StringBuilder message = new StringBuilder(); for (String line = in.readLine(); line != null; line = in.readLine()) { message.append(line).append('\n'); @@ -460,10 +334,8 @@ else if (root.getTagName().equals("")) { } catch ( IOException e ) { throw new DasIOException(e.toString()); } - - } - + /** Handles key based authentication */ @Deprecated public Key authenticate( String user, String passCrypt) { @@ -476,7 +348,7 @@ public Key authenticate( String user, String passCrypt) { URL server= new URL("http",host,port,path+"?"+formData); - logger.log( Level.FINE, "connecting to {0}", server); + logger.fine( "connecting to "+server); InputStream in= server.openStream(); BufferedInputStream bin= new BufferedInputStream(in); @@ -501,12 +373,8 @@ public Key authenticate( String user, String passCrypt) { } } - /** - * returns a {@code List} of resource Id's available with this key - * @param key the key - * @return a list of the groups. - * @deprecated this is not used. - */ + /** returns a List of resource Id's available with this key */ + @Deprecated public List groups( Key key ) { try { String formData= "server=groups"; @@ -514,7 +382,7 @@ public List groups( Key key ) { URL server= new URL("http",host,port,path+"?"+formData); - logger.log( Level.FINE, "connecting to {0}", server); + logger.fine( "connecting to "+server); InputStream in= server.openStream(); BufferedInputStream bin= new BufferedInputStream(in); @@ -534,14 +402,7 @@ public List groups( Key key ) { } } - /** - * old code for changing password - * @param user - * @param oldPass - * @param newPass - * @throws DasServerException - * @deprecated - */ + @Deprecated public void changePassword( String user, String oldPass, String newPass ) throws DasServerException { try { String formData= "server=changePassword"; @@ -552,7 +413,7 @@ public void changePassword( String user, String oldPass, String newPass ) throws formData+= "&newPasswd="+URLBuddy.encodeUTF8(cryptNewPass); URL server= new URL("http",host,port,path+"?"+formData); - logger.log( Level.FINE, "connecting to {0}", server); + logger.fine( "connecting to "+server); InputStream in= server.openStream(); BufferedInputStream bin= new BufferedInputStream(in); @@ -578,12 +439,7 @@ public void changePassword( String user, String oldPass, String newPass ) throws } - /** - * - * @param in - * @return - * @deprecated - */ + @Deprecated public String readServerResponse( BufferedInputStream in ) { // Read ..., leaving the InputStream immediately after // @@ -593,9 +449,9 @@ public String readServerResponse( BufferedInputStream in ) { byte[] data = new byte[4096]; - //int lastBytesRead = -1; + int lastBytesRead = -1; - //String s; + String s; int offset=0; @@ -621,14 +477,10 @@ public String readServerResponse( BufferedInputStream in ) { das2Response= new String(data,14,index-14); - logger.log(Level.FINER, "das2Response={0}", das2Response); + org.das2.util.DasDie.println("das2Response="+das2Response); in.reset(); - long n= das2Response.length() + 2 * das2ResponseTag.length() + 5; - while ( n>0 ) { - long k= in.skip( n ); - n-= k; - } + in.skip( das2Response.length() + 2 * das2ResponseTag.length() + 5 ); } else { in.reset(); @@ -639,29 +491,23 @@ public String readServerResponse( BufferedInputStream in ) { das2Response= ""; } - logger.log( Level.FINE, "response={0}", das2Response); + logger.fine( "response="+das2Response); return das2Response; } - /** - * Utility function to handle reading data off the HTTP stream. Used by functions - * such as getName and getLogo that don't expect to receive a Das2 Stream - * - * @param uin - * @return - * @throws IOException - */ + // Utility function to handle reading data off the HTTP stream. Used by functions + // such as getName and getLogo that don't expect to receive a Das2 Stream private byte[] read(InputStream uin) throws IOException { - LinkedList list = new LinkedList(); + LinkedList list = new LinkedList(); byte[] data; int bytesRead=0; - //int totalBytesRead=0; + int totalBytesRead=0; //BufferedInputStream in= new BufferedInputStream(uin,4096*2); InputStream in= uin; - //long time = System.currentTimeMillis(); + long time = System.currentTimeMillis(); // fireReaderStarted(); //FileOutputStream out= new FileOutputStream("x."+time+".dat"); @@ -670,6 +516,8 @@ private byte[] read(InputStream uin) throws IOException { int lastBytesRead = -1; + String s; + int offset=0; // if (requestor != null) { @@ -680,7 +528,7 @@ private byte[] read(InputStream uin) throws IOException { while (bytesRead != -1) { - //int bytesSoFar = totalBytesRead; + int bytesSoFar = totalBytesRead; // fireReaderUpdate(bytesSoFar); // if (requestor != null) { // requestor.currentByteCount(bytesSoFar); @@ -695,7 +543,7 @@ private byte[] read(InputStream uin) throws IOException { offset=0; } - //totalBytesRead+= bytesRead; + totalBytesRead+= bytesRead; bytesRead= in.read(data,offset,4096-offset); @@ -713,7 +561,7 @@ private byte[] read(InputStream uin) throws IOException { data = new byte[dataLength]; - Iterator iterator = list.iterator(); + Iterator iterator = list.iterator(); int i; for (i = 0; i < list.size()-1; i++) { System.arraycopy(iterator.next(), 0, data, i*4096, 4096); @@ -749,18 +597,19 @@ public Key getKey( String resource ) { if ( keys.get(resource)==null ) { Authenticator authenticator; authenticator= new Authenticator(this,resource); - Key key1= authenticator.authenticate(); - if ( key1!=null ) keys.put( resource, key1); + Key key= authenticator.authenticate(); + if ( key!=null ) keys.put( resource, key); } } return (Key)keys.get(resource); } - public void setKey( Key key ) { - logger.info("this key is ignored"); - } + //Let's see if this is actually used anywhere + //public void setKey( Key key ) { + // this.key= key; + //} - @Override + @Override public String toString() { return this.getURL(); } diff --git a/dasCore/src/org/das2/client/DataSetStreamHandler.java b/dasCore/src/org/das2/client/DataSetStreamHandler.java index c4fcb1a5e..f7194cf53 100755 --- a/dasCore/src/org/das2/client/DataSetStreamHandler.java +++ b/dasCore/src/org/das2/client/DataSetStreamHandler.java @@ -23,7 +23,6 @@ package org.das2.client; -import java.beans.PropertyDescriptor; import org.das2.stream.StreamYScanDescriptor; import org.das2.stream.StreamComment; import org.das2.stream.StreamMultiYDescriptor; @@ -34,7 +33,7 @@ import org.das2.stream.PacketDescriptor; import org.das2.util.monitor.NullProgressMonitor; import org.das2.util.monitor.ProgressMonitor; -import org.das2.datum.CacheTag; +import org.das2.dataset.CacheTag; import org.das2.dataset.DataSet; import org.das2.dataset.TableDataSetBuilder; import org.das2.dataset.VectorDataSetBuilder; @@ -103,36 +102,6 @@ public void streamDescriptor(StreamDescriptor sd) throws StreamException { e.printStackTrace(); } } - else{ - // Get the cacheTag via the new parameters, *CacheRange *CacheResolution. - // Don't use X_RANGE and X_TAG_WIDTH because these are ment for display - // purposes and don't represent the coverage period and resolution directly. - Object oRng = null; - Object oRes = null; - if((oRng = sd.getProperty(DataSet.PROPERTY_X_CACHE_RNG)) != null){ - try{ - DatumRange rng = null; - if(!(oRng instanceof DatumRange)) - rng = DatumRangeUtil.parseDatumRange((String)oRng); - else - rng = (DatumRange)oRng; - - Datum res = null; // intrinsic resolution - if((oRes = sd.getProperty(DataSet.PROPERTY_X_CACHE_RES)) != null){ - if(!(oRes instanceof Datum)) - res = DatumUtil.parse((String) oRes); - else - res = (Datum)oRes; - } - - extraProperties.put(DataSet.PROPERTY_CACHE_TAG, - new CacheTag(rng.min(), rng.max(), res)); - } - catch(ParseException e){ - e.printStackTrace(); - } - } - } if ( ( o=sd.getProperty( DataSet.PROPERTY_X_MONOTONIC ) )!=null ) { extraProperties.put( DataSet.PROPERTY_X_MONOTONIC, Boolean.valueOf((String)o) ); @@ -142,6 +111,7 @@ public void streamDescriptor(StreamDescriptor sd) throws StreamException { } } + @Override public void packetDescriptor(PacketDescriptor pd) throws StreamException { logger.finest("got packet descriptor"); if (delegate == null) { @@ -166,6 +136,7 @@ public boolean getReadPackets(){ return bReadPkts; } + @Override public void packet(PacketDescriptor pd, Datum xTag, DatumVector[] vectors) throws StreamException { if(!bReadPkts) return; @@ -183,6 +154,7 @@ public void packet(PacketDescriptor pd, Datum xTag, DatumVector[] vectors) throw } } + @Override public void streamClosed(StreamDescriptor sd) throws StreamException { logger.finest("got streamClosed"); if (delegate != null) { @@ -190,10 +162,12 @@ public void streamClosed(StreamDescriptor sd) throws StreamException { } } + @Override public void streamException(StreamException se) throws StreamException { logger.finest("got stream exception"); } + @Override public void streamComment(StreamComment sc) throws StreamException { logger.log(Level.FINEST, "got stream comment: {0}", sc); @@ -263,10 +237,13 @@ private VectorDataSetStreamHandler(PacketDescriptor pd) throws StreamException { Units xUnits = base == null ? pd.getXDescriptor().getUnits() : base.getUnits(); Units yUnits = y.getUnits(); builder = new VectorDataSetBuilder(xUnits,yUnits); - builder.addProperties( Collections.singletonMap( DataSet.PROPERTY_Y_LABEL, y.getProperty("name") )); + builder.addProperties( Collections.singletonMap( DataSet.PROPERTY_Y_LABEL, + y.getProperty("name") )); for ( int i=1; i speedLimit ) { long targetMillis= (long) ( ( totalBytesRead ) / ( speedLimit / 1000. ) ); long waitMs= Math.min( 1000, targetMillis - calcMillisElapsed() ); - DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG).log(Level.FINE, "limiting speed by waiting {0} ms", waitMs); + DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG).fine("limiting speed by waiting "+waitMs+" ms"); try { Thread.sleep(waitMs); } catch ( InterruptedException ex ) { } } } @@ -146,7 +143,7 @@ private double calcTransmitSpeed() { if ( millis==0 ) { return Units.bytesPerSecond.getFillDouble(); } else { - return 1000. * totalBytesRead / millis; + return 1000 * totalBytesRead / millis; } } diff --git a/dasCore/src/org/das2/client/StandardDataStreamSource.java b/dasCore/src/org/das2/client/StandardDataStreamSource.java index 5205dc13a..645c949bb 100644 --- a/dasCore/src/org/das2/client/StandardDataStreamSource.java +++ b/dasCore/src/org/das2/client/StandardDataStreamSource.java @@ -25,7 +25,6 @@ import org.das2.datum.Datum; import org.das2.DasException; -import org.das2.dataset.DataSetDescriptor; import java.io.InputStream; diff --git a/dasCore/src/org/das2/client/StreamDataSetDescriptor.java b/dasCore/src/org/das2/client/StreamDataSetDescriptor.java index 08731eaa2..1f4770133 100755 --- a/dasCore/src/org/das2/client/StreamDataSetDescriptor.java +++ b/dasCore/src/org/das2/client/StreamDataSetDescriptor.java @@ -22,20 +22,6 @@ */ package org.das2.client; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InterruptedIOException; -import java.io.PushbackInputStream; -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; -import java.util.logging.Level; import org.das2.dataset.VectorDataSetBuilder; import org.das2.dataset.DataSetDescriptor; import org.das2.dataset.TableDataSet; @@ -44,6 +30,7 @@ import org.das2.dataset.TableDataSetBuilder; import org.das2.datum.Units; import org.das2.datum.Datum; +import org.das2.datum.UnitsConverter; import org.das2.stream.StreamYScanDescriptor; import org.das2.stream.StreamMultiYDescriptor; import org.das2.stream.StreamDescriptor; @@ -57,15 +44,17 @@ import org.das2.util.monitor.ProgressMonitor; import org.das2.datum.DatumVector; import org.das2.system.DasLogger; -import org.das2.stream.StreamTool; +import org.das2.util.StreamTool; +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; import java.util.logging.Logger; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.*; +import org.xml.sax.*; +import org.w3c.dom.*; import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.xml.sax.SAXException; public class StreamDataSetDescriptor extends DataSetDescriptor { @@ -75,7 +64,6 @@ public class StreamDataSetDescriptor extends DataSetDescriptor { private static final Logger logger= DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG); - @Override public Units getXUnits() { return Units.us2000; } @@ -83,7 +71,6 @@ public Units getXUnits() { /** * Creates a new instance of StreamDataSetDescriptor * from the specified file - * @param properties the properties */ protected StreamDataSetDescriptor(Map properties) { setProperties(properties); @@ -106,7 +93,7 @@ public StandardDataStreamSource getStandardDataStreamSource() { return this.standardDataStreamSource; } - protected final void setProperties(Map properties, boolean legacy) { + protected void setProperties(Map properties, boolean legacy) { super.setProperties(properties); if (properties.containsKey("form") && properties.get("form").equals("x_multi_y") && properties.containsKey("items")) { @@ -117,8 +104,8 @@ protected final void setProperties(Map properties, boolean legacy) { } } - @Override - protected final void setProperties( Map properties ) { + @Override + protected void setProperties( Map properties ) { setProperties(properties, false); } @@ -134,23 +121,21 @@ private ByteBuffer getByteBuffer(InputStream in) throws DasException { * Read data for the given start and end dates and returns an array of bytes * * @author eew - * @param in the source - * @return the bytes. - * @throws DasException If there is an error getting data from the reader + * @throws java.io.IOException If there is an error getting data from the reader, and IOException is thrown */ protected byte[] readBytes(InputStream in) throws DasException { - LinkedList list = new LinkedList(); + LinkedList list = new LinkedList(); byte[] data = new byte[4096]; int bytesRead=0; - //int totalBytesRead=0; + int totalBytesRead=0; int lastBytesRead = -1; int offset=0; - //long time = System.currentTimeMillis(); + long time = System.currentTimeMillis(); try { bytesRead= in.read(data,offset,4096-offset); while (bytesRead != -1) { - //int bytesSoFar = totalBytesRead; + int bytesSoFar = totalBytesRead; offset+=bytesRead; lastBytesRead= offset; if (offset==4096) { @@ -158,7 +143,7 @@ protected byte[] readBytes(InputStream in) throws DasException { data = new byte[4096]; offset=0; } - //totalBytesRead+= bytesRead; + totalBytesRead+= bytesRead; bytesRead= in.read(data,offset,4096-offset); } } catch ( IOException e ) { @@ -174,6 +159,7 @@ protected byte[] readBytes(InputStream in) throws DasException { } int dataLength = (list.size()-1)*4096 + lastBytesRead; data = new byte[dataLength]; + Iterator iterator = list.iterator(); for (int i = 0; i < list.size()-1; i++) { System.arraycopy(list.get(i), 0, data, i*4096, 4096); } @@ -181,24 +167,24 @@ protected byte[] readBytes(InputStream in) throws DasException { return data; } - @Override + @Override public String toString() { return "dsd "+getDataSetID(); } - @Override + @Override protected DataSet getDataSetImpl( Datum start, Datum end, Datum resolution, ProgressMonitor monitor ) throws DasException { if ( resolution != null && !resolution.isFinite() ) throw new IllegalArgumentException( "resolution is not finite" ); InputStream in; DataSet result; if ( serverSideReduction ) { - logger.info("getting stream from standard data stream source"); + logger.fine("getting stream from standard data stream source"); in= standardDataStreamSource.getReducedInputStream( this, start, end, resolution); } else { in= standardDataStreamSource.getInputStream( this, start, end ); } - logger.info("reading stream"); + logger.fine("reading stream"); result = getDataSetFromStream( in, start, end, monitor ); return result; } @@ -210,9 +196,9 @@ protected DataSet getDataSetFromStream(InputStream in, Datum start, Datum end, P try { byte[] four = new byte[4]; int bytesRead= pin.read(four); - logger.log(Level.FINER, "read first four bytes bytesRead={0}", bytesRead); + logger.finer("read first four bytes bytesRead="+bytesRead); if ( bytesRead!=4 ) { - logger.info("no data returned from server"); + logger.fine("no data returned from server"); throw new DasIOException( "No data returned from server" ); } @@ -232,7 +218,6 @@ protected DataSet getDataSetFromStream(InputStream in, Datum start, Datum end, P ReadableByteChannel channel = Channels.newChannel(mpin); DataSetStreamHandler handler = new DataSetStreamHandler( properties, monitor ) { - @Override public void streamDescriptor(StreamDescriptor sd) throws StreamException { super.streamDescriptor( sd ); if ( super.taskSize!=-1 ) { @@ -289,11 +274,7 @@ else if (getProperty("form").equals("x_multi_y")) { } } finally { - try { - pin.close(); - } catch (IOException ioe) { - logger.log( Level.WARNING, null, ioe ); - } + try { pin.close(); } catch (IOException ioe) {} } } @@ -337,7 +318,7 @@ private DataSet getLegacyVectorDataSet(InputStream in0, Datum start) throws DasE ByteBuffer data = getByteBuffer(in); double timeBaseValue = start.doubleValue(start.getUnits()); Units offsetUnits = start.getUnits().getOffsetUnits(); - //UnitsConverter uc = sd.getXDescriptor().getUnits().getConverter(offsetUnits); + UnitsConverter uc = sd.getXDescriptor().getUnits().getConverter(offsetUnits); while (data.remaining() > recordSize) { DatumVector vector = sd.getXDescriptor().read(data); double xTag = timeBaseValue + vector.doubleValue(0, offsetUnits); @@ -371,7 +352,7 @@ private DataSet getLegacyTableDataSet(InputStream in0, Datum start) throws DasEx PacketDescriptor sd = getPacketDescriptor(in); TableDataSetBuilder builder = new TableDataSetBuilder(start.getUnits(),Units.dimensionless,Units.dimensionless); Units yUnits = Units.dimensionless; - //Units zUnits= Units.dimensionless; + Units zUnits= Units.dimensionless; for (Iterator i = sd.getYDescriptors().iterator(); i.hasNext();) { Object o = i.next(); @@ -395,9 +376,11 @@ private DataSet getLegacyTableDataSet(InputStream in0, Datum start) throws DasEx recordSize += yScans[planeIndex].getSizeBytes(); } ByteBuffer data = getByteBuffer(in); - //double timeBaseValue= start.doubleValue(start.getUnits()); - //Units offsetUnits = start.getUnits().getOffsetUnits(); - //UnitsConverter uc = sd.getXDescriptor().getUnits().getConverter(offsetUnits); + double timeBaseValue= start.doubleValue(start.getUnits()); + Units offsetUnits = start.getUnits().getOffsetUnits(); + Units xdescUnits= sd.getXDescriptor().getUnits(); + if ( xdescUnits==null ) xdescUnits= Units.seconds; + //UnitsConverter uc = xdescUnits.getConverter(offsetUnits); double[] yCoordinates = yScans[0].getYTags(); DatumVector y = DatumVector.newDatumVector(yCoordinates, yUnits); @@ -423,11 +406,13 @@ private DataSet getLegacyTableDataSet(InputStream in0, Datum start) throws DasEx private static final byte[] HEADER = { (byte)'d', (byte)'a', (byte)'s', (byte)'2', (byte)0177, (byte)0177 }; - private PacketDescriptor getPacketDescriptor(PushbackInputStream in) throws DasIOException { + private PacketDescriptor getPacketDescriptor(PushbackInputStream in) + throws DasIOException, StreamException + { try { byte[] four = new byte[HEADER.length]; - int bytesRead; + int bytesRead = 0; int totalBytesRead = 0; do { bytesRead = in.read(four, totalBytesRead, HEADER.length - totalBytesRead); @@ -455,11 +440,6 @@ private PacketDescriptor getPacketDescriptor(PushbackInputStream in) throws DasI dioe.initCause(dioe); throw dioe; } - catch ( StreamException dnfe) { - DasIOException dioe = new DasIOException(dnfe.getMessage()); - dioe.initCause(dioe); - throw dioe; - } catch ( SAXException ex ) { DasIOException e= new DasIOException(ex.getMessage()); e.initCause(ex); diff --git a/dasCore/src/org/das2/client/WebStandardDataStreamSource.java b/dasCore/src/org/das2/client/WebStandardDataStreamSource.java index 59bc11879..35fa94ebb 100755 --- a/dasCore/src/org/das2/client/WebStandardDataStreamSource.java +++ b/dasCore/src/org/das2/client/WebStandardDataStreamSource.java @@ -21,11 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/** - * - * @author jbf - */ - package org.das2.client; import org.das2.dataset.NoDataInIntervalException; @@ -35,52 +30,52 @@ import org.das2.stream.StreamYScanDescriptor; import org.das2.DasApplication; import org.das2.util.URLBuddy; +/** + * + * @author jbf + */ import org.das2.DasException; import org.das2.DasIOException; import org.das2.datum.format.DatumFormatter; import java.io.*; import java.net.*; -import java.util.logging.Level; import java.util.logging.Logger; -import org.das2.datum.LoggerManager; - -/* - * Web standard data stream source */ - -/** +import javax.swing.ImageIcon; +import org.das2.CancelledOperationException; +import org.das2.system.LogCategory; +import org.das2.util.CredentialsManager; + +/** Web standard data stream source + * + * This class handles Das 1 and Das 2 streams served from a server implementing the + * Das 2.1 client - server protocol, including HTTP authentication. The Das 2.2 + * client - server protocol is not handled at this time. */ public class WebStandardDataStreamSource implements StandardDataStreamSource { - private static final Logger logger= LoggerManager.getLogger("das2.dataTransfer"); private DasServer server; - private boolean legacyStream = true; + protected String m_sHost; + protected String m_sDataSet; private String extraParameters; - - /** - * Holds value of property compress. - */ + + /* Holds value of property compress. */ private boolean compress; - /** - * Holds value of property lastRequestURL. - */ + /* Holds value of property lastRequestURL. */ private String lastRequestURL; public WebStandardDataStreamSource(DasServer server, URL url) { this.server = server; + m_sHost = url.getHost(); String[] query = url.getQuery() == null ? new String[0] : url.getQuery().split("&"); - if (query.length >= 2) { - extraParameters = query[1]; - } + if (query.length > 0) m_sDataSet = query[0]; + if (query.length > 1) extraParameters = query[1]; } - public boolean isLegacyStream() { - return legacyStream; - } - - - public InputStream getInputStream(StreamDataSetDescriptor dsd, Datum start, Datum end) throws DasException { + @Override + public InputStream getInputStream(StreamDataSetDescriptor dsd, Datum start, Datum end) + throws DasException { String serverType="dataset"; StringBuffer formData = new StringBuffer(); @@ -89,10 +84,9 @@ public InputStream getInputStream(StreamDataSetDescriptor dsd, Datum start, Datu InputStream in= openURLConnection( dsd, start, end, formData ); in= DasApplication.getDefaultApplication().getInputStreamMeter().meterInputStream(in); return in; - } - + @Override public InputStream getReducedInputStream( StreamDataSetDescriptor dsd, Datum start, Datum end, Datum timeResolution) throws DasException { StringBuffer formData = new StringBuffer(); @@ -129,20 +123,14 @@ else if( "1".equals(dsd.getProperty("requiresInterval")) //} //compress= true; - if (compress) { - formData.append("&compress=true"); - } - - if ( !devel.equals("") ) { - formData.append("&devel=").append(devel); - } + if (compress) formData.append("&compress=true"); + + if ( !devel.equals("") ) formData.append("&devel=").append(devel); InputStream in= openURLConnection( dsd, start, end, formData ); - + in= DasApplication.getDefaultApplication().getInputStreamMeter().meterInputStream(in); - return in; - } private String createFormDataString(String dataSetID, Datum start, Datum end, StringBuffer additionalFormData) throws UnsupportedEncodingException { @@ -156,61 +144,135 @@ private String createFormDataString(String dataSetID, Datum start, Datum end, St formData.append("&").append(additionalFormData); return formData.toString(); } - - protected synchronized InputStream openURLConnection( StreamDataSetDescriptor dsd, Datum start, Datum end, StringBuffer additionalFormData ) throws DasException { - - String[] tokens = dsd.getDataSetID().split("\\?|\\&"); - String dataSetID = tokens[1]; - - try { - String formData = createFormDataString(dataSetID, start, end, additionalFormData); - if ( dsd.isRestrictedAccess() ) { - key= server.getKey(""); - if (key!=null) { - formData+= "&key="+URLEncoder.encode(key.toString(),"UTF-8"); - } - } - - if ( redirect ) { - formData+= "&redirect=1"; - } - - URL serverURL= this.server.getURL(formData); - - this.lastRequestURL= String.valueOf( serverURL ); - - logger.log(Level.FINE, "opening {0}", serverURL.toString()); - - URLConnection urlConnection = serverURL.openConnection(); - urlConnection.connect(); - - String contentType = urlConnection.getContentType(); - - if (!contentType.equalsIgnoreCase("application/octet-stream")) { - BufferedReader bin = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); - String line = bin.readLine(); - String message = ""; - while (line != null) { - message = message.concat(line); - line = bin.readLine(); - } - throw new DasIOException(message); - } - - InputStream in= urlConnection.getInputStream(); - - if (isLegacyStream()) { - return processLegacyStream(in); - } else { - throw new UnsupportedOperationException(); - } - } catch (IOException e) { - throw new DasIOException(e); - } + + // Get the location ID associated with the dataSetId + protected String getLocId(){ + if((m_sHost != null)&&(m_sDataSet != null)) + return String.format("%s|%s", m_sHost, m_sDataSet); + else + return null; + } + + // Check / Set location information + protected void checkSetLocDescription(CredentialsManager cm){ + String sLocId = getLocId(); + if(sLocId == null) return; + + if(cm.hasDescription(sLocId)) return; + + String sSvrName = server.getName(); + String sDesc = String.format("

    %s


    Server: %s
    " + + "Data Set: %s", sSvrName, m_sHost, m_sDataSet); + + ImageIcon icon = server.getLogo(); + cm.setDescription(sLocId, sDesc, icon); + } + + @SuppressWarnings("null") + protected synchronized InputStream openURLConnection( + StreamDataSetDescriptor dsd, Datum start, Datum end, StringBuffer additionalFormData ) + throws DasException { + + String[] tokens = dsd.getDataSetID().split("\\?|\\&"); + String dataSetID = tokens[1]; + + URL serverURL; + InputStream inStream = null; + String sContentType = null; + try{ + //Construct the GET string + String formData = createFormDataString(dataSetID, start, end, additionalFormData); + + //Handle old-style das2 authentication + if(dsd.isRestrictedAccess()){ + key = server.getKey(""); + if(key != null){ + formData += "&key=" + URLEncoder.encode(key.toString(), "UTF-8"); + } + } + + if(redirect){ + formData += "&redirect=1"; + } + serverURL = server.getURL(formData); + + // Loop handling authentication if needed + Logger.getLogger(LogCategory.DATA_TRANSFER_LOG).fine("opening " + serverURL.toString()); + String sLocId = getLocId(); + + while(inStream == null){ + HttpURLConnection httpConn = null; + URLConnection conn = serverURL.openConnection(); + if(serverURL.getProtocol().startsWith("http")){ + httpConn = (HttpURLConnection) conn; + } + else{ + throw new DasException("Das2 Server Protocol " + serverURL.getProtocol() + + "is not supported."); + } + + CredentialsManager cm = CredentialsManager.getMannager(); + if(cm.hasCredentials(sLocId)){ + httpConn.setRequestProperty("Authorization", + "Basic " + cm.getHttpBasicHash(sLocId)); + } + + httpConn.connect(); + sContentType = httpConn.getContentType(); + + int nStatus = httpConn.getResponseCode(); + + if(nStatus == HttpURLConnection.HTTP_UNAUTHORIZED){ + checkSetLocDescription(cm); + // If the cm had credentials for this location, they obviously didn't + // work, so invalidate them. + cm.invalidate(sLocId); + if(cm.getHttpBasicHash(sLocId) == null){ + throw new CancelledOperationException("Failed to gather credentials for "+sLocId); + } + + // Try again... + httpConn.disconnect(); + continue; + } + + if(nStatus >= 400){ + inStream = httpConn.getErrorStream(); + } + else{ + inStream = httpConn.getInputStream(); + } + } + } + catch(IOException ex){ + throw new DasIOException(ex); + } + + this.lastRequestURL = String.valueOf(serverURL); + + try{ + + //if (!contentType.equalsIgnoreCase("application/octet-stream")) { + if (sContentType.equalsIgnoreCase("text/plain")) { + BufferedReader bin = new BufferedReader(new InputStreamReader(inStream)); + String line = bin.readLine(); + String message = ""; + while (line != null) { + message = message.concat(line); + line = bin.readLine(); + } + throw new DasIOException(message); + } + + return processStream(inStream); + } + catch(IOException ex){ + throw new DasIOException(ex); + } } - private InputStream processLegacyStream(InputStream in) throws IOException, DasException { - /* advances the inputStream past the old das2server tags */ + private InputStream processStream(InputStream in) throws IOException, DasException { + /* advances the inputStream past the old das2server tags if needed */ BufferedInputStream bin= new BufferedInputStream(in); bin.mark(Integer.MAX_VALUE); @@ -227,21 +289,22 @@ private InputStream processLegacyStream(InputStream in) throws IOException, DasE String errorTag= "error"; if (serverResponse.startsWith("<"+errorTag+">")) { + int index2= serverResponse.indexOf(""); String error= serverResponse.substring( errorTag.length()+2, serverResponse.length()-(errorTag.length()+3)); - logger.log(Level.FINER, "error={0}", error); + org.das2.util.DasDie.println("error="+error); /* presume that the endUser has opted out */ if (error.equals("")) { - throw new NoKeyProvidedException(""); + throw new NoKeyProvidedException(""); } if (error.equals("")) { /* the server says the key used does not have access to the resouce requested. Allow the user to reauthenticate */ - server.setKey(null); - server.getKey(""); + //server.setKey(null); + //server.getKey(""); throw new AccessDeniedException(""); } @@ -270,6 +333,10 @@ private String readServerResponse(InputStream in) { byte[] data = new byte[4096]; + int lastBytesRead = -1; + + String s; + int offset=0; try { @@ -284,7 +351,7 @@ private String readServerResponse(InputStream in) { } if ( new String(data,0,14,"UTF-8").equals("<"+das2ResponseTag+">")) { - while ( new String( data,0,offset,"UTF-8" ).indexOf("")==-1 && + while (! new String( data,0,offset,"UTF-8" ).contains("") && offset<4096 ) { offset+= bytesRead; bytesRead= in.read(data,offset,4096-offset); @@ -294,15 +361,10 @@ private String readServerResponse(InputStream in) { das2Response= new String(data,14,index-14); - logger.log(Level.FINER, "das2Response={0}", das2Response); + org.das2.util.DasDie.println("das2Response="+das2Response); in.reset(); - - long toSkip= das2Response.length() + 2 * das2ResponseTag.length() + 5; - long nskip= in.skip( toSkip ); - if ( nskip/readers/ instead. * - * @param devel string identifying the developer. */ public void setDevel(String devel) { this.devel = devel; diff --git a/dasCore/src/org/das2/components/AngleSpectrogramSlicer.java b/dasCore/src/org/das2/components/AngleSpectrogramSlicer.java index 1f5220c4f..2c83e5b5a 100644 --- a/dasCore/src/org/das2/components/AngleSpectrogramSlicer.java +++ b/dasCore/src/org/das2/components/AngleSpectrogramSlicer.java @@ -23,6 +23,7 @@ package org.das2.components; +import org.das2.graph.SymbolLineRenderer; import org.das2.graph.DasColumn; import org.das2.graph.DasCanvas; import org.das2.graph.GraphUtil; @@ -30,6 +31,11 @@ import org.das2.graph.DasPlot; import org.das2.graph.DasAxis; import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.DataSetUtil; import org.das2.datum.DatumRange; import org.das2.datum.Units; import org.das2.datum.Datum; @@ -39,32 +45,28 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; -import org.das2.graph.SeriesRenderer; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dsutil.DataSetBuilder; public class AngleSpectrogramSlicer extends DasPlot implements BoxSelectionListener { private JDialog popupWindow; - private final SeriesRenderer renderer; - private final DasPlot parentPlot; - private final TableDataSetConsumer consumer; + private SymbolLineRenderer renderer; + private DasPlot parentPlot; + private TableDataSetConsumer consumer; private Datum xstart; private Datum ystart; private DatumRange xrange; private DatumRange yrange; private int sliceDir; - private static final int SLICEDIR_HORIZ=0; // we are showing the Xs along the X axis. + private final int SLICEDIR_HORIZ=0; + private final int SLICEDIR_VERTICAL=1; private AngleSpectrogramSlicer(DasPlot plot, DasAxis xAxis, DasAxis yAxis, TableDataSetConsumer consumer ) { super(xAxis, yAxis); parentPlot = plot; - renderer= new SeriesRenderer(); + renderer= new SymbolLineRenderer(); this.consumer= consumer; addRenderer(renderer); } @@ -102,7 +104,7 @@ private void showPopupImpl() { private void createPopup() { int width = parentPlot.getCanvas().getWidth() / 2; int height = parentPlot.getCanvas().getHeight() / 2; - final DasCanvas canvas = new DasCanvas(width, height); + DasCanvas canvas = new DasCanvas(width, height); DasRow row = new DasRow(canvas, 0.1, 0.9); DasColumn column = new DasColumn(canvas, 0.1, 0.9); canvas.add(this, row, column); @@ -111,33 +113,29 @@ private void createPopup() { JPanel buttonPanel = new JPanel(); BoxLayout buttonLayout = new BoxLayout(buttonPanel, BoxLayout.X_AXIS); - buttonPanel.setLayout(buttonLayout); - - buttonPanel.add(Box.createHorizontalGlue()); - - JButton printButton= new JButton( new AbstractAction("Print...") { - public void actionPerformed( ActionEvent e ) { - canvas.makeCurrent(); - canvas.PRINT_ACTION.actionPerformed(e); - } - }); - buttonPanel.add( printButton ); - JButton close = new JButton("Hide Window"); close.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { popupWindow.setVisible(false); } }); - + buttonPanel.setLayout(buttonLayout); + buttonPanel.add(Box.createHorizontalGlue()); buttonPanel.add(close); content.add(canvas, BorderLayout.CENTER); content.add(buttonPanel, BorderLayout.SOUTH); Window parentWindow = SwingUtilities.getWindowAncestor(parentPlot); - popupWindow = new JDialog(parentWindow); - + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } + else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } + else { + popupWindow = new JDialog(); + } popupWindow.setTitle("Angle Slicer"); popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); popupWindow.setContentPane(content); @@ -145,72 +143,43 @@ public void actionPerformed(ActionEvent e) { Point parentLocation = new Point(); SwingUtilities.convertPointToScreen(parentLocation, parentPlot.getCanvas()); - - //make sure some portion of the slice window is visible on the screen. - int xx= parentLocation.x + parentPlot.getCanvas().getWidth(); - int yy= parentLocation.y; - - GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); - int totalwidth = gd.getDisplayMode().getWidth(); - int totalheight = gd.getDisplayMode().getHeight(); - - if ( xx>totalwidth-100 ) { - xx= totalwidth-100; - } - if ( yy>totalheight-100 ) { - yy= totalheight-100; - } - popupWindow.setLocation(xx,yy); + popupWindow.setLocation(parentLocation.x + parentPlot.getCanvas().getWidth(),parentLocation.y + height); } - private QDataSet angleSliceHoriz( QDataSet tds, DatumRange xlimit, Datum xbase, Datum ybase, Datum slope ) { - - DataSetBuilder builder= new DataSetBuilder(1,100); - DataSetBuilder xbuilder= new DataSetBuilder(1,100); - - QDataSet xds= SemanticOps.xtagsDataSet(tds); - QDataSet yds= SemanticOps.ytagsDataSet(tds); - - int i0= DataSetUtil.closestIndex( xds, xlimit.min() ); - int i1= DataSetUtil.closestIndex( xds, xlimit.max() ); + private VectorDataSet angleSliceHoriz( TableDataSet tds, DatumRange xlimit, Datum xbase, Datum ybase, Datum slope ) { + VectorDataSetBuilder builder= new VectorDataSetBuilder( tds.getXUnits(), tds.getZUnits() ); + int i0= DataSetUtil.closestColumn( tds, xlimit.min() ); + int i1= DataSetUtil.closestColumn( tds, xlimit.max() ); int irow0=0; - int irow1; + int irow1=0; - Units zunits= SemanticOps.getUnits(tds); - Units yunits= SemanticOps.getUnits(yds); - Units xunits= SemanticOps.getUnits(xds); + Units zunits= tds.getZUnits(); + Units yunits= tds.getYUnits(); for ( int i=i0; i0 && yds.value( irow0 ) > y ) irow0--; + Datum x= tds.getXTagDatum(i); + Datum y= ybase.add( slope.multiply(x.subtract(xbase) ) ); + int itable= tds.tableOfIndex(i); + while ( irow0>0 && tds.getYTagDatum( itable, irow0 ).gt( y ) ) irow0--; irow1= irow0+1; - while ( (irow1+1)< yds.length() && yds.value(irow1)< y ) irow1++; + while ( (irow1+1)0 ) { - double z0= tds.value( i, irow0 ); - double z1= tds.value( i, irow1 ); - double y0= yds.value( irow0 ); - double y1= yds.value( irow1 ); - double yy= y; + double z0= tds.getDouble( i, irow0, zunits ); + double z1= tds.getDouble( i, irow1, zunits ); + double y0= tds.getYTagDouble( itable, irow0, yunits ); + double y1= tds.getYTagDouble( itable, irow1, yunits ); + double yy= y.doubleValue(yunits); double alpha= ( yy-y0 ) / ( y1-y0 ); double zinterp= z0 + (z1-z0) * alpha; - builder.putValue( -1, zinterp ); - xbuilder.putValue( -1, x ); - builder.nextRecord(); - xbuilder.nextRecord(); + builder.insertY( x, Datum.create(zinterp,zunits) ); } } - - xbuilder.putProperty( QDataSet.UNITS, xunits ); - builder.putProperty( QDataSet.UNITS, zunits ); - - builder.putProperty( QDataSet.DEPEND_0, xbuilder.getDataSet() ); - return builder.getDataSet(); - + + return builder.toVectorDataSet(); } @Override @@ -238,7 +207,6 @@ public void drawContent(Graphics2D g) { } - @Override protected void processDasUpdateEvent(org.das2.event.DasUpdateEvent e) { if (isDisplayable()) { updateImmediately(); @@ -258,14 +226,14 @@ public void BoxSelected(BoxSelectionEvent e) { Datum slope= e.getFinishY().subtract(ybase) .divide( e.getFinishX().subtract(xbase) ); - QDataSet ds = consumer.getConsumedDataSet(); + DataSet ds = consumer.getConsumedDataSet(); - if (ds==null || !( SemanticOps.isSimpleTableDataSet(ds) )) { + if (ds==null || !(ds instanceof TableDataSet)) { return; } - QDataSet tds = (QDataSet)ds; - QDataSet sliceDataSet; + TableDataSet tds = (TableDataSet)ds; + VectorDataSet sliceDataSet; sliceDataSet= angleSliceHoriz( tds, getXAxis().getDatumRange(), xbase, ybase, slope ); sliceDir= SLICEDIR_HORIZ; diff --git a/dasCore/src/org/das2/components/AsciiFileParser.form b/dasCore/src/org/das2/components/AsciiFileParser.form new file mode 100644 index 000000000..9da4f7151 --- /dev/null +++ b/dasCore/src/org/das2/components/AsciiFileParser.form @@ -0,0 +1,182 @@ + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dasCore/src/org/das2/components/AsciiFileParser.java b/dasCore/src/org/das2/components/AsciiFileParser.java new file mode 100644 index 000000000..15982d0c3 --- /dev/null +++ b/dasCore/src/org/das2/components/AsciiFileParser.java @@ -0,0 +1,434 @@ +/* + * AsciiFileParser.java + * + * Created on July 13, 2006, 3:26 PM + */ + +package org.das2.components; + +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.util.DasExceptionHandler; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.MouseEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import javax.swing.JFileChooser; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; +import javax.swing.event.MouseInputAdapter; + +/** + * Interactive GUI for parsing ascii tables into VectorDataSets + * + * @author Jeremy + */ +public class AsciiFileParser extends javax.swing.JPanel { + + /** Creates new form AsciiFileParser */ + public AsciiFileParser() { + initComponents(); + jTextField1.addFocusListener( new FocusAdapter() { + public void focusLost(FocusEvent e) { + updateSkipLines(); + } + }); + model= new AsciiTableModel(); + } + + class AsciiTableModel { + int columnCount=0; + int[] columnOffsets= new int[50]; + int[] columnWidths= new int[50]; + Units[] units= new Units[50]; + String[] names= new String[50]; + int skipLines=0; + } + + AsciiTableModel model; + File file; + + public DataSet parse() throws FileNotFoundException, IOException, ParseException { + + ArrayList usableColumns= new ArrayList(); + for ( int i=0; i//GEN-BEGIN:initComponents + private void initComponents() { + jLabel1 = new javax.swing.JLabel(); + jLabel2 = new javax.swing.JLabel(); + delimSelector = new javax.swing.JComboBox(); + jLabel3 = new javax.swing.JLabel(); + jButton1 = new javax.swing.JButton(); + firstColumnTimeCheckBox = new javax.swing.JCheckBox(); + jScrollPane1 = new javax.swing.JScrollPane(); + jPanel1 = new javax.swing.JPanel(); + jTextArea1 = new FixedColumnTextArea(); + jTextArea2 = new FixedColumnTextArea(); + jLabel4 = new javax.swing.JLabel(); + jTextField1 = new javax.swing.JTextField(); + + jLabel1.setText("File:"); + + jLabel2.setText("f:\\myfile.txt"); + + delimSelector.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "WhiteSpace", "Comma", "RegExp (\\s\\s+)" })); + delimSelector.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + delimSelectorActionPerformed(evt); + } + }); + + jLabel3.setText("Delimiter:"); + + jButton1.setText("choose"); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); + + firstColumnTimeCheckBox.setText("FirstColumnTime"); + firstColumnTimeCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + firstColumnTimeCheckBox.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + jPanel1.setLayout(new java.awt.BorderLayout()); + + jTextArea1.setColumns(20); + jTextArea1.setRows(5); + jPanel1.add(jTextArea1, java.awt.BorderLayout.CENTER); + + jTextArea2.setBackground(new java.awt.Color(238, 238, 255)); + jTextArea2.setColumns(20); + jTextArea2.setRows(1); + MouseInputAdapter mia= new ColumnMouseInputAdapter((FixedColumnTextArea)jTextArea2); + jTextArea2.addMouseListener(mia); + jPanel1.add(jTextArea2, java.awt.BorderLayout.NORTH); + + jScrollPane1.setViewportView(jPanel1); + + jLabel4.setText("SkipLines:"); + + jTextField1.setText("0"); + jTextField1.addPropertyChangeListener(new java.beans.PropertyChangeListener() { + public void propertyChange(java.beans.PropertyChangeEvent evt) { + jTextField1PropertyChange(evt); + } + }); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(org.jdesktop.layout.GroupLayout.TRAILING, jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 453, Short.MAX_VALUE) + .add(layout.createSequentialGroup() + .add(jLabel1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 45, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jButton1) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jLabel2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 264, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(layout.createSequentialGroup() + .add(jLabel3) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(delimSelector, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(layout.createSequentialGroup() + .add(firstColumnTimeCheckBox) + .add(77, 77, 77) + .add(jLabel4) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jTextField1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 47, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(jLabel2) + .add(jLabel1) + .add(jButton1)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(delimSelector, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(jLabel3)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(firstColumnTimeCheckBox) + .add(jLabel4) + .add(jTextField1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 335, Short.MAX_VALUE) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + private void jTextField1PropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_jTextField1PropertyChange + updateSkipLines(); + }//GEN-LAST:event_jTextField1PropertyChange + + private void delimSelectorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_delimSelectorActionPerformed + try { + resetDelims(); + } catch ( IOException e ) { + handleException(e); + } + }//GEN-LAST:event_delimSelectorActionPerformed + + private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + JFileChooser chooser= new JFileChooser(); + int retVal= chooser.showOpenDialog(this); + if ( retVal==chooser.APPROVE_OPTION ) { + try { + setFile( chooser.getSelectedFile() ); + } catch (IOException ex) { + handleException(ex); + } + } + }//GEN-LAST:event_jButton1ActionPerformed + + private void handleException( Throwable t ) { + DasExceptionHandler.handle(t); + } + + private void setFile(File file) throws IOException { + this.file= file; + resetDelims(); + } + + public String getDelimRegex() { + String result; + switch ( delimSelector.getSelectedIndex() ) { + case 0: result="\\s+"; break; + case 1: result=","; break; + case 2: result="\\s\\s+"; break; + default: throw new IllegalStateException("not implemented"); + } + return result; + } + + public void resetDelims() throws IOException { + String regex= getDelimRegex(); + BufferedReader reader= new BufferedReader( new FileReader( file ) ); + + for ( int i=0; i=taskList.size() ) { if ( exit ) System.exit(0); } else { - DasLogger.getLogger(DasLogger.SYSTEM_LOG).log(Level.INFO, "itask={0}", taskList.get(itask)); + DasLogger.getLogger(DasLogger.SYSTEM_LOG).fine( "itask="+taskList.get(itask) ); DataRangeSelectionEvent ev= (DataRangeSelectionEvent) taskList.get(itask++); fireDataRangeSelectionListenerDataRangeSelected( ev ); - canvas.waitUntilIdle(); + try { + canvas.waitUntilIdle(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } tod.completeTask( ev.getDatumRange() ); submitNextTask(); } diff --git a/dasCore/src/org/das2/components/ColorBarComponent.java b/dasCore/src/org/das2/components/ColorBarComponent.java index 7b8c7f6c7..c96d59e01 100644 --- a/dasCore/src/org/das2/components/ColorBarComponent.java +++ b/dasCore/src/org/das2/components/ColorBarComponent.java @@ -14,33 +14,23 @@ import javax.swing.*; /** - * ColorBarComponent wraps a DasColorBar and DasCanvas to make a component. - * @deprecated This is not terribly useful and probably should be removed. + * * @author Owner */ public class ColorBarComponent extends JPanel { DasColorBar colorBar; DasCanvas canvas; - /** - * create a new ColorBarComponent - * @param min the minimum - * @param max the maximum - * @param isLog true if the colorbar should be log initially. - */ + /** Creates a new instance of ColorBarComponent */ public ColorBarComponent(Datum min, Datum max, boolean isLog) { canvas= new DasCanvas(100, 500); - DasRow row= new DasRow(canvas,0.1,0.9); + DasRow row= DasRow.create(canvas); DasColumn column= DasColumn.create(canvas); colorBar= new DasColorBar( min, max, isLog ); canvas.add(colorBar,row, column); this.add(canvas); } - /** - * get the colorbar - * @return the colorbar - */ public DasColorBar getColorBar() { return colorBar; } diff --git a/dasCore/src/org/das2/components/ComponentsUtil.java b/dasCore/src/org/das2/components/ComponentsUtil.java index 1e211a6f4..29764af39 100644 --- a/dasCore/src/org/das2/components/ComponentsUtil.java +++ b/dasCore/src/org/das2/components/ComponentsUtil.java @@ -48,7 +48,13 @@ public static DasCanvas createPopupCanvas( Component parent, String title, int w final JDialog popupWindow; Window parentWindow = SwingUtilities.getWindowAncestor(parent); - popupWindow = new JDialog(parentWindow); + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } else { + popupWindow = new JDialog(); + } popupWindow.setTitle(title); popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); diff --git a/dasCore/src/org/das2/components/DasAxisSelector.java b/dasCore/src/org/das2/components/DasAxisSelector.java index 1b2908dd9..a65c008c3 100644 --- a/dasCore/src/org/das2/components/DasAxisSelector.java +++ b/dasCore/src/org/das2/components/DasAxisSelector.java @@ -34,14 +34,9 @@ import java.awt.*; import java.awt.event.ActionListener; import java.text.DecimalFormat; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.das2.util.LoggerManager; public class DasAxisSelector extends javax.swing.JPanel implements ActionListener { - private static final Logger logger= LoggerManager.getLogger("das2.gui"); - private DasAxis axis= null; JTextField idStart= null; @@ -103,14 +98,14 @@ public void actionPerformed(java.awt.event.ActionEvent actionEvent) { axis.setDataRange(Datum.create(Double.valueOf(idStart.getText()).doubleValue(),axis.getUnits()), axis.getDataMaximum()); } catch (NumberFormatException e) { - logger.log(Level.WARNING,e.getMessage(),e); + org.das2.util.DasDie.println(e); } } else if (command.equals("setMaximum")) { try { axis.setDataRange(axis.getDataMinimum(), Datum.create(Double.valueOf(idStop.getText()).doubleValue(), axis.getUnits() )); } catch (NumberFormatException e) { - logger.log(Level.WARNING,e.getMessage(),e); + org.das2.util.DasDie.println(e); } } } diff --git a/dasCore/src/org/das2/components/DasProgressLabel.java b/dasCore/src/org/das2/components/DasProgressLabel.java deleted file mode 100644 index 316f6de56..000000000 --- a/dasCore/src/org/das2/components/DasProgressLabel.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package org.das2.components; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.JLabel; -import javax.swing.SwingUtilities; -import javax.swing.Timer; -import org.das2.util.monitor.NullProgressMonitor; - -/** - * One-line Label component prints the update progress to a - * JLabel component. This is either created by the client and set with - * setLabelComponent, or this class will also create one with getLabelComponent. - * - * @author jbf - */ -public class DasProgressLabel extends NullProgressMonitor { - - private int ndot= 2; - private String taskLabel= ""; - private JLabel label=null; - - Timer repaintTimer= new Timer( 333,new ActionListener() { - @Override - public void actionPerformed( ActionEvent e ) { - String p; - if ( getTaskSize()==-1 ) { - p= ""; - } else { - p= "" + getTaskProgress()+"/"+getTaskSize(); - } - ndot++; - if ( ndot==4 ) ndot=1; - if ( label!=null ) { - if ( isFinished() ) { - label.setText( " " + taskLabel + "...finished" ); - } else { - label.setText( " " + taskLabel + "...".substring(0,ndot) + " ".substring(ndot,3)+p+"" ); - } - } - } - } ); - - /** - * create the DasProgessLabel with the given label for the task. - * @param taskLabel the label for the task. - */ - public DasProgressLabel( String taskLabel ) { - repaintTimer.setRepeats(true); - repaintTimer.start(); - this.taskLabel= taskLabel; - } - - /** - * get the assigned label component, or create one if one has not been assigned. - * @return the assigned label component - */ - public synchronized JLabel getLabelComponent() { - if ( label==null ) { - label= new JLabel(); - } - return label; - } - - /** - * set the label to use for the progress monitor. - * @param label the assigned label component - */ - public synchronized void setLabelComponent( JLabel label ) { - this.label= label; - } - - - @Override - public void finished() { - super.finished(); - repaintTimer.setRepeats(false); - repaintTimer.stop(); - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - if ( label!=null ) label.setText( " " + taskLabel + "...finished" ); - } - }); - } - -} diff --git a/dasCore/src/org/das2/components/DasProgressPanel.java b/dasCore/src/org/das2/components/DasProgressPanel.java index 08b66798b..1f7a04887 100755 --- a/dasCore/src/org/das2/components/DasProgressPanel.java +++ b/dasCore/src/org/das2/components/DasProgressPanel.java @@ -22,73 +22,28 @@ */ package org.das2.components; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Insets; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.Window; import org.das2.graph.DasCanvasComponent; import org.das2.system.DasLogger; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.InvocationTargetException; import java.text.DecimalFormat; import java.util.logging.Level; +import javax.swing.*; +import javax.swing.border.*; import org.das2.util.monitor.ProgressMonitor; import org.das2.util.NumberFormatUtil; +import java.util.*; import java.util.logging.Logger; -import javax.swing.BoxLayout; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; import org.das2.graph.DasCanvas; -import org.das2.util.monitor.NullProgressMonitor; -import org.das2.util.monitor.SubTaskMonitor; /** - * ProgressMonitor component used throughout das2. - * - * Here's an Autoplot script demoing its operation: - *
    {@code
    - *monitor.setTaskSize( 100 )
    - *monitor.started()
    - *
    - *for i in range(100):
    - *  if ( i>50 and monitor.isCancelled() ):
    - *    raise Exception('cancel pressed')
    - *  print i
    - *  java.lang.Thread.sleep(100)
    - *  monitor.setTaskProgress(i)
    - *
    - *monitor.finished()
    - *}
    * * @author eew */ public class DasProgressPanel implements ProgressMonitor { - private static final Logger logger = DasLogger.getLogger(DasLogger.SYSTEM_LOG); - - public static final String MSG_CANCEL_TASK = "cancel task"; - public static final String MSG_TASK_CANNOT_BE_CANCELED = "task cannot be cancelled"; - - private static final int PROGRESS_MESSAGE_LEN_LIMIT = 40; - private static final int LABEL_LEN_LIMIT = 34; - private long taskStartedTime; private long currentTaskPosition; private long maximumTaskPosition; @@ -101,58 +56,29 @@ public class DasProgressPanel implements ProgressMonitor { private boolean progressMessageDirty = false; private JLabel kbLabel; private JProgressBar progressBar; - private Window jframe = null; // created when createFramed() is used. + private JFrame jframe = null; // created when createFramed() is used. private boolean isCancelled = false; private JButton cancelButton; - private int cancelCheckFailures = 2; // number of times client codes failed to check cancelled before setTaskProgress. Start with disabled. + private int cancelCheckFailures = 0; // number of times client codes failed to check cancelled before setTaskProgress. private boolean cancelChecked = false; private String label; private static final int hideInitiallyMilliSeconds = 300; private static final int refreshPeriodMilliSeconds = 500; private boolean running = false; private boolean finished = false; + private long lastRefreshTime; + private ArrayList refreshTimeQueue; private Thread updateThread; + private Logger logger = DasLogger.getLogger(DasLogger.SYSTEM_LOG); private boolean showProgressRate; private JPanel thePanel; private boolean componentsInitialized; private DasCanvasComponent parentComponent; private DasCanvas parentCanvas; - private Container removeFromComponent= null; // this is the parent we need to remove the monitor from when finished. private static int createComponentCount = 0; - /** - * string representing the units, such as "M" or "K" - */ - private String units; + class MyPanel extends JPanel { - /** - * factor for units (e.g. 1000,1000000) - */ - private int unitsf=1; - - @Override - public ProgressMonitor getSubtaskMonitor(int start, int end, String label) { - if ( label!=null ) setProgressMessage(label); - if ( this.isFinished() ) { - logger.info("getSubtaskMonitor called after finished"); - return new NullProgressMonitor(); - } - return SubTaskMonitor.create( this, start, end, cancelCheckFailures < 2 ); - } - - @Override - public ProgressMonitor getSubtaskMonitor( String label ) { - if ( label!=null ) setProgressMessage(label); - if ( this.isFinished() ) { - logger.info("getSubtaskMonitor called after finished"); - return new NullProgressMonitor(); - } - return SubTaskMonitor.create( this, cancelCheckFailures < 2 ); - } - - private static class MyPanel extends JPanel { - - @Override protected void paintComponent(Graphics g1) { Graphics2D g2 = (Graphics2D) g1; @@ -167,39 +93,48 @@ protected void paintComponent(Graphics g1) { } super.paintComponent(g1); } - - @Override - public void print(Graphics g) { - //System.err.println("this ought not be printed"); I think this is printed when Autoplot gets thumbnails. - //super.print(g); //To change body of generated methods, choose Tools | Templates. - } - - } // provides details button, which shows who creates and who consumes the ProgressPanel + final static boolean useDetails = false; Exception source; Exception consumer; - ImageIcon cancel= new ImageIcon( DasProgressPanel.class.getResource("/images/icons/cancel14.png") ); - ImageIcon cancelGrey= new ImageIcon( DasProgressPanel.class.getResource("/images/icons/cancelGrey14.png") ); - - protected DasProgressPanel(String label) { + public DasProgressPanel(String label) { + if (useDetails) + source = new Exception(); componentsInitialized = false; - label= abbrevateStringEllipsis( label, LABEL_LEN_LIMIT); this.label = label; transferRateFormat = NumberFormatUtil.getDecimalFormat(); transferRateFormat.setMaximumFractionDigits(2); maximumTaskPosition = -1; - //lastRefreshTime = Integer.MIN_VALUE; + lastRefreshTime = Integer.MIN_VALUE; showProgressRate = true; isCancelled = false; running = false; } + private void details() { + System.err.println("Source: "); + source.printStackTrace(); + System.err.println("Consumer: "); + consumer.printStackTrace(); + String stateString; + if (finished) { + stateString = "finished"; + } else if (running) { + stateString = "running"; + } else if (isCancelled) { + stateString = "cancelled"; + } + System.err.println("State: "); + System.err.println(" running: " + running); + System.err.println(" cancelled: " + isCancelled); + System.err.println(" finished: " + finished); + } + /** * returns the JPanel component. - * @return the JPanel component. */ public Component getComponent() { if (!componentsInitialized) @@ -222,102 +157,52 @@ public static DasProgressPanel createComponentPanel(DasCanvas canvas, String ini /** Returning true here keeps the progress bar from forcing the whole canvas * to repaint when the label of the progress bar changes. - * @return this always returns true */ public boolean isValidateRoot() { return true; } - /** - * this may be called from off the event thread, but it does assume that the - * event thread is free. - * @param label label describing the task. - * @return a DasProgressPanel. - */ - public static DasProgressPanel createFramed( final String label) { - final DasProgressPanel result; + public static DasProgressPanel createFramed(String label) { + DasProgressPanel result; result = new DasProgressPanel(label); - Runnable run= new Runnable() { - @Override - public void run() { - JFrame fr= new JFrame("Progress Monitor"); - result.jframe = fr; - result.initComponents(); - fr.getContentPane().add(result.thePanel); - fr.pack(); - fr.setVisible(false); - fr.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - } - }; - if ( SwingUtilities.isEventDispatchThread() ) { - run.run(); - } else { - SwingUtilities.invokeLater(run); - } - return result; - } - - /** - * creates a dialog object that follows a parent - * @param parent the Window that owns this popup gui. - * @param label label describing the task. - * @return a DasProgressPanel. - */ - public static DasProgressPanel createFramed( final Window parent, String label) { - final DasProgressPanel result; - result = new DasProgressPanel(label); - Runnable run= new Runnable() { - @Override - public void run() { - result.jframe = new JDialog(parent,"Progress Monitor"); - result.initComponents(); - JPanel lthePanel= result.thePanel; - result.jframe.add(lthePanel); - result.jframe.pack(); - result.jframe.setLocationRelativeTo(parent); - result.jframe.setVisible(false); - } - }; - if ( SwingUtilities.isEventDispatchThread() ) { - run.run(); - } else { - SwingUtilities.invokeLater(run); - } + result.jframe = new JFrame("Das Progress Monitor"); + result.initComponents(); + result.jframe.getContentPane().add(result.thePanel); + result.jframe.pack(); + result.jframe.setVisible(false); + result.jframe.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); return result; } - @Override public void setLabel(String label) { - label= abbrevateStringEllipsis( label, LABEL_LEN_LIMIT); this.label = label; this.labelDirty = true; if (thePanel != null) thePanel.repaint(); } - @Override public String getLabel() { return label; } private void initComponents() { // get a stack trace so we can see what caused this. + if (useDetails) + consumer = new Exception(); createComponentCount++; - logger.log(Level.FINER, "createComponentCount={0}", createComponentCount); - JPanel mainPanel; + //System.err.println("createComponentCount="+createComponentCount ); + JPanel mainPanel, buttonPanel; taskLabel = new JLabel(); taskLabel.setOpaque(false); - taskLabel.setFont(new Font("Dialog", 1, 14)); + taskLabel.setFont(new Font("Dialog", 1, 18)); taskLabel.setHorizontalAlignment(JLabel.CENTER); taskLabel.setText(label); - logger.log( Level.FINE, "taskLabel: {0}", label); taskLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT); progressMessageLabel = new JLabel() { - @Override public void paint(Graphics g) { ((java.awt.Graphics2D) g).setRenderingHint( java.awt.RenderingHints.KEY_TEXT_ANTIALIASING, @@ -333,7 +218,6 @@ public void paint(Graphics g) { progressBar = new JProgressBar(); progressBar.setOpaque(false); - progressBar.setPreferredSize( new Dimension(220,14) ); progressBar.setMaximumSize(progressBar.getPreferredSize()); progressBar.setMinimumSize(progressBar.getPreferredSize()); progressBar.setAlignmentX(JComponent.CENTER_ALIGNMENT); @@ -348,124 +232,97 @@ public void paint(Graphics g) { kbLabel.setPreferredSize(progressBar.getPreferredSize()); kbLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT); - JPanel progressBarPanel= new JPanel( new BorderLayout() ); + mainPanel = new JPanel(); + mainPanel.setOpaque(false); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + mainPanel.add(taskLabel); + mainPanel.add(progressMessageLabel); + mainPanel.add(progressBar); + mainPanel.add(kbLabel); + + Border lineBorder = new LineBorder(Color.BLACK, 2); + Border emptyBorder = new EmptyBorder(2, 2, 2, 2); + CompoundBorder border = new CompoundBorder(lineBorder, emptyBorder); + + JButton detailsButton; + if (useDetails) { + detailsButton = new JButton("details"); + detailsButton.setOpaque(false); + detailsButton.setBorder(border); + detailsButton.setFocusPainted(false); + detailsButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + details(); + } + }); + } - cancelButton = new JButton( cancelGrey ); - Dimension cs= new Dimension(20,20); - cancelButton.setMaximumSize( cs ); - cancelButton.setMinimumSize( cs ); - cancelButton.setPreferredSize( cs ); - cancelButton.setMargin( new Insets( 2,2,2,2 ) ); - cancelButton.setToolTipText(MSG_TASK_CANNOT_BE_CANCELED); + cancelButton = new JButton("cancel"); cancelButton.setEnabled(false); - cancelButton.setIcon( cancelGrey ); - cancelButton.setVerticalAlignment( SwingConstants.CENTER ); cancelButton.setOpaque(false); - //cancelButton.setBorder(border); + cancelButton.setBorder(border); cancelButton.setFocusPainted(false); cancelButton.addActionListener(new ActionListener() { - @Override + public void actionPerformed(ActionEvent e) { cancel(); } }); - mainPanel = new JPanel(); - mainPanel.setOpaque(false); - mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); - mainPanel.add(taskLabel); - mainPanel.add(progressMessageLabel); - progressBarPanel.add(progressBar,BorderLayout.CENTER); - progressBarPanel.add(cancelButton,BorderLayout.EAST); - mainPanel.add(progressBarPanel); - mainPanel.add(kbLabel); + buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel.setOpaque(false); + + if (useDetails) + buttonPanel.add(detailsButton); + buttonPanel.add(cancelButton); thePanel = new MyPanel(); thePanel.setOpaque(false); thePanel.setLayout(new BorderLayout()); thePanel.add(mainPanel, BorderLayout.CENTER); - //thePanel.add(buttonPanel, BorderLayout.EAST); + thePanel.add(buttonPanel, BorderLayout.SOUTH); if (parentComponent != null) { thePanel.setSize(thePanel.getPreferredSize()); int x = parentComponent.getColumn().getDMiddle(); int y = parentComponent.getRow().getDMiddle(); thePanel.setLocation(x - thePanel.getWidth() / 2, y - thePanel.getHeight() / 2); - removeFromComponent= ((Container) (parentComponent.getCanvas().getGlassPane())); - removeFromComponent.add(thePanel); + ((Container) (parentComponent.getCanvas().getGlassPane())).add(thePanel); thePanel.setVisible(false); } else if ( parentCanvas!=null ) { thePanel.setSize(thePanel.getPreferredSize()); int x = parentCanvas.getWidth()/2; int y = parentCanvas.getHeight()/2; thePanel.setLocation(x - thePanel.getWidth() / 2, y - thePanel.getHeight() / 2); - removeFromComponent= ((Container) (parentCanvas.getGlassPane())); - removeFromComponent.add(thePanel); - + ((Container) (parentCanvas.getGlassPane())).add(thePanel); thePanel.setVisible(false); - } else { - thePanel.setSize(thePanel.getPreferredSize()); } componentsInitialized = true; } - StackTraceElement[] stackTrace= null; - - @Override public synchronized void finished() { - if ( finished==true ) { - logger.warning("monitor finished was called twice!"); - logger.warning("here was the first call:"); - new Exception().printStackTrace(); - } else { - logger.fine("enter monitor finished"); - stackTrace= Thread.currentThread().getStackTrace(); - } running = false; finished = true; - Runnable run= new Runnable() { - @Override - public void run() { - if ( removeFromComponent!=null ) { - removeFromComponent.remove(thePanel); - removeFromComponent.invalidate(); - removeFromComponent.validate(); - Rectangle b= thePanel.getBounds(); - removeFromComponent.repaint(b.x,b.y,b.width,b.height); - } - if (jframe == null) { - setVisible(false); - } else { - jframe.setVisible(false); - } - - } - }; - SwingUtilities.invokeLater(run); + if (jframe == null) { + setVisible(false); + } else { + jframe.dispose(); + } } /* ProgressMonitor interface */ - @Override public void setTaskProgress(long position) throws IllegalStateException { if (logger.isLoggable(Level.FINEST)) { - logger.log(Level.FINEST, "progressPosition={0}", position); + logger.finest("progressPosition=" + position); } if (isCancelled) { // note that the monitored process should check isCancelled before setTaskProgress, but this is no longer required. // If this is not done, we throw a IllegalStateException to kill the thread, and the monitored process is killed that way. logger.fine("setTaskProgress called when isCancelled true. consider checking isCancelled before calling setTaskProgress."); - boolean cancelEnabled = cancelCheckFailures < 2; - if ( !cancelEnabled ) { - // this was introduced as a quick-n-dirty way to provide cancel, that messes up applications like - // autoplot. We need to be more careful about how this is used. - throw new IllegalStateException("Operation cancelled: developers: consider checking isCancelled before calling setTaskProgress."); - } else { - logger.fine("setTaskProgress but isCancelled, assuming its okay to ignore."); - // just ignore the progress update. This should be a transitional state. - return; - } + throw new IllegalStateException("Operation cancelled: developers: consider checking isCancelled before calling setTaskProgress."); } if (!running) { @@ -473,14 +330,13 @@ public void setTaskProgress(long position) throws IllegalStateException { } if (position != 0 && position < currentTaskPosition) { - logger.finest("progress position goes backwards, this is allowed."); + logger.finest("progress position goes backwards"); } if (!cancelChecked) { // cancelCheckFailures is used to detect when if the monitored process is not checking cancelled. If it is not, then we // disable the cancel button. Note the cancel() method can still be called from elsewhere, killing the process. cancelCheckFailures++; - logger.log(Level.FINER, "cancelCheckFailures={0}", cancelCheckFailures); } cancelChecked = false; // reset for next time, isCancelled will set true. @@ -494,35 +350,36 @@ public void setTaskProgress(long position) throws IllegalStateException { if (elapsedTimeMs > hideInitiallyMilliSeconds && !isVisible()) { setVisible(true); } + /* long tnow; + if ( (tnow=System.currentTimeMillis()) - lastRefreshTime > 30 ) { + updateUIComponents(); + if (Toolkit.getDefaultToolkit().getSystemEventQueue().isDispatchThread()) { + paintImmediately(0, 0, getWidth(), getHeight()); } - - @Override - public boolean canBeCancelled() { - return cancelChecked; + else { + repaint(); + } + lastRefreshTime= tnow; + } */ } - private void startUpdateThread() { Runnable run = new Runnable() { - @Override public void run() { while (!DasProgressPanel.this.finished) { try { SwingUtilities.invokeAndWait(new Runnable() { - @Override public void run() { - if ( progressBar!=null ) { - updateUIComponents(); - thePanel.repaint(); - } + updateUIComponents(); + thePanel.repaint(); } }); Thread.sleep(refreshPeriodMilliSeconds); } catch (InterruptedException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); + Logger.getLogger(DasProgressPanel.class.getName()).log(Level.SEVERE, null, ex); } catch (InvocationTargetException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); + Logger.getLogger(DasProgressPanel.class.getName()).log(Level.SEVERE, null, ex); } } } @@ -531,29 +388,11 @@ public void run() { updateThread.start(); } - /** - * return a string of no longer than s long. Currently this just takes a bit of the front and end, but - * a future version of this might look for the bit that's changing. - * @param s the unabbreviated string. This is any length, and contains a message - * @param lenLimit - * @return a string of no more that lenLimit+2 chars. (why +2? because the ellipse used is short.) - */ - private String abbrevateStringEllipsis( String s, int lenLimit ) { - if (s.length() > lenLimit ) { - int n = s.length(); - s = s.substring(0, 10) + "..." + s.substring( n - (lenLimit-11), n ); - } - return s; - } - - /** - * this must be run on the event thread - */ private void updateUIComponents() { long elapsedTimeMs = System.currentTimeMillis() - taskStartedTime; long kb = currentTaskPosition; - + if (maximumTaskPosition == -1) { progressBar.setIndeterminate(true); } else { @@ -567,15 +406,17 @@ private void updateUIComponents() { String bytesReadLabel; if (maximumTaskPosition > 0) { - bytesReadLabel = "" + (kb/unitsf) + units + "/" + (maximumTaskPosition/unitsf) + "" + units; + bytesReadLabel = "" + kb + "/" + maximumTaskPosition + ""; } else { bytesReadLabel = "" + kb + ""; } if (progressMessageDirty) { - progressMessageLabel.setToolTipText( progressMessageString ); - String s= abbrevateStringEllipsis( progressMessageString, PROGRESS_MESSAGE_LEN_LIMIT); - progressMessageLabel.setText(s); + if (progressMessageString.length() > 33) { + int n = progressMessageString.length(); + progressMessageString = progressMessageString.substring(0, 10) + "..." + progressMessageString.substring(n - 22, n); + } + progressMessageLabel.setText(progressMessageString); progressMessageDirty = false; } @@ -585,96 +426,53 @@ private void updateUIComponents() { } if (showProgressRate && elapsedTimeMs > 1000 && transferRateString != null) { + double transferRate = ((double) currentTaskPosition * 1000) / (elapsedTimeMs); kbLabel.setText(bytesReadLabel + " " + transferRateString); } else { kbLabel.setText(bytesReadLabel); } - kbLabel.setToolTipText(""+kb+"/"+maximumTaskPosition); boolean cancelEnabled = cancelCheckFailures < 2; if (cancelEnabled != cancelButton.isEnabled()) { cancelButton.setEnabled(cancelEnabled); - if ( cancelEnabled ) { - logger.finer("cancel enabled"); - cancelButton.setIcon( cancel ); - cancelButton.setToolTipText(MSG_CANCEL_TASK); - } else { - logger.finer("cancel disabled"); - cancelButton.setIcon( cancelGrey ); - cancelButton.setToolTipText(MSG_TASK_CANNOT_BE_CANCELED); - } } } @Deprecated - @Override public void setAdditionalInfo(String s) { transferRateString = s; } - @Override public long getTaskProgress() { return currentTaskPosition; } - @Override public long getTaskSize() { return maximumTaskPosition; } - @Override public void setTaskSize(long taskSize) { if (taskSize < -1) { throw new IllegalArgumentException("taskSize must be positive, -1, or 0, not " + taskSize); } else { - if (componentsInitialized) { - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - progressBar.setIndeterminate(false); - } - } ); - } - } - if ( taskSize>100000000 ) { - units= "M"; - unitsf= 1000000; - } else if ( taskSize>100000 ) { - units= "K"; - unitsf= 1000; - } else { - units= ""; - unitsf= 1; + if (componentsInitialized) + progressBar.setIndeterminate(false); } maximumTaskPosition = taskSize; } - /** - * make the progressPanel visible or hide it. This - * @param visible true if the progressPanel should be visible. - */ - public synchronized void setVisible(final boolean visible) { + public synchronized void setVisible(boolean visible) { if (!componentsInitialized && !visible) return; - Runnable run= new Runnable() { - @Override - public void run() { - if (!componentsInitialized && !finished) - initComponents(); - if (thePanel != null) - thePanel.setVisible(visible); - if (DasProgressPanel.this.jframe != null) - DasProgressPanel.this.jframe.setVisible(visible); - } - }; - if ( SwingUtilities.isEventDispatchThread() ) { - run.run(); - } else { - SwingUtilities.invokeLater(run); - } + if (!componentsInitialized && !finished) + initComponents(); + if (thePanel != null) + thePanel.setVisible(visible); + if (this.jframe != null) + this.jframe.setVisible(visible); - if ( visible ) { - if ( updateThread==null ) startUpdateThread(); + if (visible) { + startUpdateThread(); } } @@ -682,7 +480,6 @@ public boolean isVisible() { return (!componentsInitialized || thePanel.isVisible()); } - @Override public void started() { taskStartedTime = System.currentTimeMillis(); running = true; @@ -690,14 +487,15 @@ public void started() { if (hideInitiallyMilliSeconds > 0) { setVisible(false); new Thread(new Runnable() { - @Override + public void run() { try { Thread.sleep(hideInitiallyMilliSeconds); } catch (InterruptedException e) { } + ; if (running) { - logger.log(Level.FINE, "hide time={0}", (System.currentTimeMillis() - taskStartedTime)); + logger.fine("hide time=" + (System.currentTimeMillis() - taskStartedTime)); setVisible(true); } } @@ -713,16 +511,13 @@ public void run() { setTaskProgress(0); } - @Override public void cancel() { isCancelled = true; finished(); } - @Override public boolean isCancelled() { cancelCheckFailures = 0; - logger.log(Level.FINER, "cancelCheckFailures={0}", cancelCheckFailures); cancelChecked = true; return isCancelled; } @@ -743,7 +538,6 @@ public void setShowProgressRate(boolean showProgressRate) { this.showProgressRate = showProgressRate; } - @Override public String toString() { if (isCancelled) { return "cancelled"; @@ -756,18 +550,15 @@ public String toString() { } } - @Override public void setProgressMessage(String message) { this.progressMessageString = message; this.progressMessageDirty = true; } - @Override public boolean isStarted() { return running; } - @Override public boolean isFinished() { return finished; } diff --git a/dasCore/src/org/das2/components/DasProgressWheel.java b/dasCore/src/org/das2/components/DasProgressWheel.java deleted file mode 100644 index 1ee81e2ad..000000000 --- a/dasCore/src/org/das2/components/DasProgressWheel.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.components; - -import java.awt.Color; -import java.awt.Container; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.geom.GeneralPath; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JComponent; -import javax.swing.SwingUtilities; -import javax.swing.Timer; -import org.das2.DasApplication; -import org.das2.util.LoggerManager; -import org.das2.util.monitor.AbstractProgressMonitor; -//import org.das2.util.monitor.ProgressMonitor; - -/** - * Small 16x16 pixel progress wheel, designed intentionally for loading TCAs with X axis. - * @author jbf - */ -public class DasProgressWheel extends AbstractProgressMonitor { - - //Used to keep track of who is starting but not finishing. - //private static final Map mons= new HashMap(); - - private static final Logger logger= LoggerManager.getLogger("das2.graphics.progress"); - private static final int SIZE= 16; - private static final int HIDE_MS= 300; - - public DasProgressWheel() { - //mons.put( this, Thread.currentThread().getStackTrace() ); - } - - class MyPanel extends JComponent { - - @Override - protected void paintComponent(Graphics g1) { - - if ( DasApplication.getDefaultApplication().isHeadless() ) { - logger.info("suppressing paintComponent because graphics is headless."); - return; - } - - Graphics2D g2 = (Graphics2D) g1; - - String txt= ""+DasProgressWheel.this.getTaskProgress()+" of "+DasProgressWheel.this.getTaskSize(); - g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, - RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - - - logger.log(Level.FINEST, "painting {0}", txt); - - Color c= Color.BLUE; - g2.setColor(c); - g2.getClip(); - - int r= SIZE/2; - - if ( System.currentTimeMillis()-t0 < HIDE_MS ) { - return; - } - - double a= ( System.currentTimeMillis()-t0 )/5000. * 2 * Math.PI; - double da= 30*Math.PI/180; - - GeneralPath gp= new GeneralPath(); - gp.moveTo( (float)(r-r*Math.cos(a+da)), (float)(r-r*Math.sin(a+da)) ); - gp.lineTo( (float)(r+r*Math.cos(a+da)), (float)(r+r*(Math.sin(a+da))) ); - gp.lineTo( (float)(r+r*Math.cos(a-da)), (float)(r+r*(Math.sin(a-da))) ); - gp.lineTo( (float)(r-r*Math.cos(a-da)), (float)(r-r*Math.sin(a-da)) ); - gp.lineTo( (float)(r-r*Math.cos(a+da)), (float)(r-r*Math.sin(a+da)) ); - g2.fill( gp ); - this.setToolTipText( "" + txt + "
    " + DasProgressWheel.this.getLabel() + "
    " + DasProgressWheel.this.getProgressMessage() ); - super.paintComponent(g1); - - } - - } // provides details button, which shows who creates and who consumes the ProgressPanel - - private void init() { - Container parentComponent= thePanel.getParent(); - if (parentComponent != null) { - int x = 0; - int y = 0; - thePanel.setLocation( x, y ); - } - timer= new Timer( 100, new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - c++; - thePanel.repaint(); - logger.finest("repaint"); - } - } ); - timer.setRepeats(true); - timer.start(); - - } - - @Override - public void finished() { - super.finished(); - if ( timer!=null ) { - timer.stop(); - } - Runnable run= new Runnable() { - @Override - public void run() { - if ( thePanel!=null ) thePanel.setVisible(false); - if ( theParent!=null ) { - theParent.remove(thePanel); - theParent.repaint(); - } - } - }; - SwingUtilities.invokeLater(run); - //mons.remove( this ); - } - - @Override - public void cancel() { - super.cancel(); - timer.stop(); - finished(); - } - - JComponent thePanel; - - /** - * this is the component containing the monitor, generally a glass pane. - */ - JComponent theParent; - int c= 0; - Timer timer; - long t0= System.currentTimeMillis(); - - /** - * return the small component that visually shows the wheel. - * @param parent - * @return - */ - public JComponent getPanel( final JComponent parent ) { - if ( thePanel==null && !( isFinished() || isCancelled() ) ) { - thePanel= new MyPanel(); - thePanel.setBounds( new Rectangle(0,0,SIZE,SIZE) ); - parent.add(thePanel); - theParent= parent; - init(); - } - return thePanel; - } - - -} diff --git a/dasCore/src/org/das2/components/DasTimeRangeSelector.java b/dasCore/src/org/das2/components/DasTimeRangeSelector.java index de145b250..5a5f173b4 100755 --- a/dasCore/src/org/das2/components/DasTimeRangeSelector.java +++ b/dasCore/src/org/das2/components/DasTimeRangeSelector.java @@ -50,9 +50,9 @@ import java.util.List; import java.util.prefs.*; import javax.swing.*; -import org.das2.datum.DatumUtil; public class DasTimeRangeSelector extends JPanel implements TimeRangeSelectionListener { + public static final String PROP_RANGE = "range"; private DatumRange range= null; @@ -108,21 +108,25 @@ public void actionPerformed(ActionEvent e) { private JComboBox rangeComboBox; + private boolean pref; + /** Creates a new instance of DasTimeRangeSelector */ public DasTimeRangeSelector() { super(); - updateRangeString= Preferences.userNodeForPackage(this.getClass()).getBoolean("updateRangeString", false); + pref= !org.das2.DasApplication.getDefaultApplication().isApplet(); + if ( pref ) { + updateRangeString= Preferences.userNodeForPackage(this.getClass()).getBoolean("updateRangeString", false); + } else { + updateRangeString= false; + } buildComponents(); - Datum tnow= TimeUtil.prevMidnight( TimeUtil.now().convertTo(Units.us2000) ); - this.range= new DatumRange( tnow, TimeUtil.next( TimeUtil.DAY, tnow ) ); - update(); } private Action getModeAction() { return new AbstractAction("mode") { public void actionPerformed( ActionEvent e ) { updateRangeString= !updateRangeString; - Preferences.userNodeForPackage(this.getClass()).putBoolean("updateRangeString", updateRangeString ); + if ( pref ) Preferences.userNodeForPackage(this.getClass()).putBoolean("updateRangeString", updateRangeString ); revalidateUpdateMode(); update(); } @@ -175,7 +179,7 @@ private void buildComponents() { startStopModePane.add( timesPane ); favoritesButton= new JButton("v"); - favoritesButton.setToolTipText("recent entry times"); + favoritesButton.setToolTipText("recently entries times"); favoritesButton.setPreferredSize(new Dimension( 20,20 ) ); favoritesButton.setVisible(false); startStopModePane.add(favoritesButton); @@ -219,7 +223,8 @@ private void parseRange() { DatumRange oldRange= range; range= dr; updateRangeString= true; - firePropertyChange( "range", oldRange, range ); + firePropertyChange( PROP_RANGE, oldRange, range ); + } catch ( ParseException e ) { DasExceptionHandler.handle(e); } @@ -230,14 +235,14 @@ private void parseRange() { Datum s2= TimeUtil.create(idStop.getText()); DatumRange oldRange= range; range= new DatumRange(s1,s2); - firePropertyChange( "range", oldRange, range ); + firePropertyChange( PROP_RANGE, oldRange ,range); } catch ( ParseException e ) { DasExceptionHandler.handle(e); } } - if ( updateRangeString!=updateRangeString0 ) - Preferences.userNodeForPackage(getClass()).putBoolean("updateRangeString", updateRangeString ); - + if ( updateRangeString!=updateRangeString0 ) { + if (pref) Preferences.userNodeForPackage(getClass()).putBoolean("updateRangeString", updateRangeString ); + } return; } @@ -257,7 +262,8 @@ public void actionPerformed( ActionEvent e ) { } private void buildFavorites( ) { - String favorites= Preferences.userNodeForPackage(getClass()).get( "timeRangeSelector.favorites."+favoritesGroup, "" ); + String favorites=""; + if ( pref ) favorites= Preferences.userNodeForPackage(getClass()).get( "timeRangeSelector.favorites."+favoritesGroup, "" ); String[] ss= favorites.split("\\|\\|"); favoritesList= new ArrayList(); for ( int i=0; i100 ) { - return 1; - } else { - return 0; - } - } - return diff.lt(xt) ? -1 : myt.gt(xt) ? 1 : 0; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 97 * hash + Arrays.deepHashCode(this.data); - hash = 97 * hash + (this.planes != null ? this.planes.hashCode() : 0); - return hash; + return this.data[0].lt(that.data[0]) ? -1 : this.data[0].gt(that.data[0]) ? 1 : 0; } - @Override - public boolean equals( Object o ) { - if ( !( o instanceof DataPoint ) ) return false; - return compareTo(o)==0; - } - - @Override public String toString() { - StringBuilder result = new StringBuilder("" + data[0] + " " + data[1]); + StringBuffer result = new StringBuffer("" + data[0] + " " + data[1]); if (planes != null) { for (Iterator i = planes.keySet().iterator(); i.hasNext();) { Object key = i.next(); - result.append(" ").append(planes.get(key)); + result.append(" " + planes.get(key)); } } return result.toString(); } } - private final Object planesArrayLock; - private class MyTableModel extends AbstractTableModel { - @Override + public int getColumnCount() { - //TODO: System.err.println("===> " + Thread.currentThread().getName()); - synchronized (planesArrayLock) { - if (unitsArray == null) { - return 2; - } else { - return planesArray.length; - } + if (unitsArray == null) { + return 2; + } else { + return planesArray.length; } } - @Override public String getColumnName(int j) { - //System.err.println("===> " + Thread.currentThread().getName()); - synchronized (planesArrayLock) { - String result = planesArray[j]; - if (unitsArray[j] != null) { - if ( unitsArray[j] instanceof EnumerationUnits ) { - result += "(ordinal)"; - } else { - result += "(" + unitsArray[j] + ")"; - } - } - return result; + String result = planesArray[j]; + if (unitsArray[j] != null) { + result += "(" + unitsArray[j] + ")"; } + return result; } - @Override - public Class getColumnClass(int columnIndex) { - return Datum.class; - } - - - @Override public int getRowCount() { int nrow = dataPoints.size(); - return nrow; + nrow = nrow > 0 ? nrow : 1; + return dataPoints.size(); } - - @Override public Object getValueAt(int i, int j) { - DataPoint x; - synchronized (dataPoints) { - x = (DataPoint) dataPoints.get(i); - } + DataPoint x = (DataPoint) dataPoints.get(i); if (j < x.data.length) { Datum d = x.get(j); DatumFormatter format = d.getFormatter(); @@ -274,78 +157,19 @@ public Object getValueAt(int i, int j) { } else { return (String) o; } - } - } - } - - /** - * delete all the points within the interval. This was introduced to support the - * case where we are going to reprocess an interval, as with the - * RBSP digitizer. - * - * @param range range to delete, end time is exclusive. - */ - public void deleteInterval( DatumRange range ) { - if ( !sorted ) { - throw new IllegalArgumentException("data must be sorted"); - } else { - synchronized ( dataPoints ) { - Comparator comp= new Comparator() { - @Override - public int compare(Object o1, Object o2) { - return ((DataPoint)o1).get(0).compareTo((Datum)o2); - } - }; - int index1= Collections.binarySearch( dataPoints, range.min(), comp ); - if ( index1<0 ) index1= ~index1; - int index2= Collections.binarySearch( dataPoints, range.max(), comp ); - if ( index2<0 ) index2= ~index2; - if ( index1==index2 ) return; - int[] arr= new int[ index2-index1 ]; - for ( int i=0; i=0; i-- ) { - dataPoints.remove(selectedRows[i]); - } - modified = true; - } + dataPoints.remove(row); + modified = true; updateClients(); updateStatus(); - if ( active ) { - fireDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); - } - myTableModel.fireTableDataChanged(); + fireDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); } - - private class MyDataSetDescriptor extends DataSetDescriptor { + class MyDataSetDescriptor extends DataSetDescriptor { MyDataSetDescriptor() { super(null); @@ -355,258 +179,192 @@ public void fireUpdate() { fireDataSetUpdateEvent(new DataSetUpdateEvent((Object) this)); } - @Override protected DataSet getDataSetImpl(Datum s1, Datum s2, Datum s3, ProgressMonitor monitor) throws DasException { - synchronized ( dataPoints ) { - if (dataPoints.isEmpty()) { - return null; - } else { - VectorDataSetBuilder builder = new VectorDataSetBuilder(unitsArray[0], unitsArray[1]); - for (int irow = 0; irow < dataPoints.size(); irow++) { - DataPoint dp = (DataPoint) dataPoints.get(irow); - builder.insertY(dp.get(0), dp.get(1)); - } - return builder.toVectorDataSet(); + if (dataPoints.size() == 0) { + return null; + } else { + VectorDataSetBuilder builder = new VectorDataSetBuilder(unitsArray[0], unitsArray[1]); + for (int irow = 0; irow < dataPoints.size(); irow++) { + DataPoint dp = (DataPoint) dataPoints.get(irow); + builder.insertY(dp.get(0), dp.get(1)); } + return builder.toVectorDataSet(); } } - @Override public Units getXUnits() { return unitsArray[0]; } } private MyDataSetDescriptor dataSetDescriptor; + /** + * @deprecated use getDataSet() and getSelectedDataSet() instead + */ + public DataSetDescriptor getDataSetDescriptor() { + if (dataSetDescriptor == null) { + dataSetDescriptor = new MyDataSetDescriptor(); + } + return dataSetDescriptor; + } + /** * returns a data set of the table data. - * @return a data set of the table data. */ - public QDataSet getDataSet() { - if ( unitsArray[0]==null ) return null; - VectorDataSetBuilder builder = new VectorDataSetBuilder(unitsArray[0], unitsArray[1]); - synchronized ( dataPoints ) { - if (dataPoints.isEmpty()) { - return null; - } else { + public VectorDataSet getDataSet() { + if (dataPoints.size() == 0) { + return null; + } else { + VectorDataSetBuilder builder = new VectorDataSetBuilder(unitsArray[0], unitsArray[1]); + for (int i = 2; i < planesArray.length; i++) { + if (unitsArray[i] != null) { + builder.addPlane(planesArray[i], unitsArray[i]); + } + } + for (int irow = 0; irow < dataPoints.size(); irow++) { + DataPoint dp = (DataPoint) dataPoints.get(irow); + builder.insertY(dp.get(0), dp.get(1)); for (int i = 2; i < planesArray.length; i++) { if (unitsArray[i] != null) { - builder.addPlane(planesArray[i], unitsArray[i]); - } - } - for (int irow = 0; irow < dataPoints.size(); irow++) { - DataPoint dp = (DataPoint) dataPoints.get(irow); - builder.insertY(dp.get(0), dp.get(1)); - for (int i = 2; i < planesArray.length; i++) { - if (unitsArray[i] != null) { - builder.insertY(dp.get(0), (Datum) dp.getPlane(planesArray[i]), planesArray[i]); + Object s= dp.getPlane(planesArray[i]); + if ( s instanceof Datum ) { + builder.insertY(dp.get(0), (Datum)s, planesArray[i]); + } else { + builder.insertY(dp.get(0), ((EnumerationUnits)unitsArray[i]).createDatum(s),planesArray[i]); } } } - if ( xTagWidth != null && xTagWidth.value()>0 && !xTagWidth.isFill() ) { - builder.setProperty("xTagWidth", xTagWidth); - } } + if (this.xTagWidth != null) { + builder.setProperty("xTagWidth", xTagWidth); + } + return builder.toVectorDataSet(); } - return DataSetAdapter.create( builder.toVectorDataSet() ); } - + /** - * returns a data set of the selected table data. Warning: this used to - * return a bundle dataset with Y,plane1,plane2,etc that had DEPEND_0 for X. - * This now returns a bundle ds[n,m] where m is the number of columns and - * n is the number of records. - * @return a data set of the selected table data. - * @see #select(org.das2.datum.DatumRange, org.das2.datum.DatumRange) which selects part of the dataset. + * returns a data set of the selected table data */ - public synchronized QDataSet getSelectedDataSet() { - int[] selectedRows = getSelectedRowsInModel(); - + public VectorDataSet getSelectedDataSet() { + int[] selectedRows = table.getSelectedRows(); if (selectedRows.length == 0) { return null; } else { VectorDataSetBuilder builder = new VectorDataSetBuilder(unitsArray[0], unitsArray[1]); - synchronized (dataPoints) { + for (int j = 2; j < planesArray.length; j++) { + builder.addPlane(planesArray[j], unitsArray[j]); + } + for (int i = 0; i < selectedRows.length; i++) { + int irow = selectedRows[i]; + DataPoint dp = (DataPoint) dataPoints.get(irow); + builder.insertY(dp.get(0), dp.get(1)); + Map map = dp.planes; for (int j = 2; j < planesArray.length; j++) { - builder.addPlane(planesArray[j], unitsArray[j]); - } - for (int i = 0; i < selectedRows.length; i++) { - int irow = selectedRows[i]; - if ( irow0 && !xTagWidth.isFill() ) { - builder.setProperty("xTagWidth", xTagWidth); - } } - return DataSetAdapter.create( builder.toVectorDataSet() ); + if (this.xTagWidth != null) { + builder.setProperty("xTagWidth", xTagWidth); + } + return builder.toVectorDataSet(); } } /** - * Selects all the points where the first column is within xrange and - * the second column is within yrange. Returns the selected index, or -1 - * if no elements are found. - * @param xrange the range constraint (non-null). - * @param yrange the range constraint (non-null). - * @return the selected index, or -1 if no elements are found. + * Selects all the points within the DatumRange */ - public int select( DatumRange xrange, DatumRange yrange ) { - return select(xrange,yrange,false); - } - - /** - * Selects all the points within the DatumRange. Returns the selected - * index, or -1 if no elements are found. - * @param xrange the x range - * @param yrange the y range or null if no constraint - * @param xOrY if true, then match if either yrange or xrange - * @return the selected index, or -1 if no elements are found. - */ - public int select(DatumRange xrange, DatumRange yrange, boolean xOrY ) { - if ( xOrY && yrange==null ) throw new IllegalArgumentException("yrange is null with or condition--this would select all points."); - Datum mid= xrange.rescale( 0.5,0.5 ).min(); - synchronized (dataPoints) { - List selectMe = new ArrayList(); - int iclosest= -1; - Datum closestDist=null; - for (int i = 0; i < dataPoints.size(); i++) { - DataPoint p = (DataPoint) dataPoints.get(i); - if ( xOrY ) { - assert yrange!=null; - if ( xrange.contains(p.data[0]) || ( yrange.contains(p.data[1]) ) ) { - selectMe.add( i ); - } - } else { - if ( xrange.contains(p.data[0]) && ( yrange==null || yrange.contains(p.data[1]) ) ) { - selectMe.add( i ); - } - } - if ( closestDist==null || p.data[0].subtract(mid).abs().lt( closestDist ) ) { - iclosest= i; - closestDist= p.data[0].subtract(mid).abs(); - } - } - if ( iclosest!=-1 && selectMe.isEmpty() ) { - assert closestDist!=null; - if ( closestDist.gt( xrange.width() ) ) { - return -1; - } else { - selectMe= Collections.singletonList(iclosest); - } - } - table.getSelectionModel().clearSelection(); - for ( Integer selectMe1 : selectMe ) { - int iselect = selectMe1; - table.getSelectionModel().addSelectionInterval(iselect, iselect); - } - - if ( selectMe.size()>0 ) { - int iselect= selectMe.get(0); - table.scrollRectToVisible(new Rectangle(table.getCellRect( iselect, 0, true)) ); - return iselect; - } else { - return -1; + public void select(DatumRange xrange, DatumRange yrange) { + List selectMe = new ArrayList(); + for (int i = 0; i < dataPoints.size(); i++) { + DataPoint p = (DataPoint) dataPoints.get(i); + if (xrange.contains(p.data[0]) && yrange.contains(p.data[1])) { + selectMe.add(new Integer(i)); } } + table.getSelectionModel().clearSelection(); + for (int i = 0; i < selectMe.size(); i++) { + int iselect = ((Integer) selectMe.get(i)).intValue(); + table.getSelectionModel().addSelectionInterval(iselect, iselect); + } } public void saveToFile(File file) throws IOException { - List dataPoints1; - synchronized (this.dataPoints) { - dataPoints1= new ArrayList(this.dataPoints); - } FileOutputStream out = new FileOutputStream(file); BufferedWriter r = new BufferedWriter(new OutputStreamWriter(out)); - try { - StringBuilder header = new StringBuilder(); - //header.append("## "); // don't use comment characters so that labels and units are used in Autoplot's ascii parser. - for (int j = 0; j < planesArray.length; j++) { - header.append(myTableModel.getColumnName(j)).append("\t"); + StringBuffer header = new StringBuffer(); + header.append("## "); + for (int j = 0; j < planesArray.length; j++) { + header.append(myTableModel.getColumnName(j) + "\t"); + } + r.write(header.toString()); + r.newLine(); + for (int i = 0; i < dataPoints.size(); i++) { + DataPoint x = (DataPoint) dataPoints.get(i); + StringBuffer s = new StringBuffer(); + for (int j = 0; j < 2; j++) { + DatumFormatter formatter = x.get(j).getFormatter(); + s.append(formatter.format(x.get(j), unitsArray[j]) + "\t"); } - r.write(header.toString()); - r.newLine(); - for (int i = 0; i < dataPoints1.size(); i++) { - DataPoint x = (DataPoint) dataPoints1.get(i); - StringBuilder s = new StringBuilder(); - for (int j = 0; j < 2; j++) { - DatumFormatter formatter = x.get(j).getFormatter(); - s.append(formatter.format(x.get(j), unitsArray[j])).append("\t"); - } - for (int j = 2; j < planesArray.length; j++) { - Object o = x.getPlane(planesArray[j]); - if ( o==null ) { - x.getPlane(planesArray[j]); // for debugging - throw new IllegalArgumentException("unable to find plane: "+planesArray[j]); + for (int j = 2; j < planesArray.length; j++) { + Object o = x.getPlane(planesArray[j]); + if (unitsArray[j] == null) { + if (o == null) { + o = ""; } - if (unitsArray[j] == null) { - s.append("\"").append(o).append("\"\t"); - } else { + s.append("\"" + o + "\"\t"); + } else { + if ( o instanceof Datum ) { Datum d = (Datum) o; DatumFormatter f = d.getFormatter(); - s.append(f.format(d, unitsArray[j])).append("\t"); + s.append(f.format(d, unitsArray[j]) + "\t"); + } else { + s.append( o.toString() + "\t" ); } } - r.write(s.toString()); - r.newLine(); - prefs.put("components.DataPointRecorder.lastFileSave", file.toString()); - prefs.put("components.DataPointRecorder.lastFileLoad", file.toString()); } - } finally { - r.close(); + r.write(s.toString()); + r.newLine(); + prefs.put("components.DataPointRecorder.lastFileSave", file.toString()); + prefs.put("components.DataPointRecorder.lastFileLoad", file.toString()); } + r.close(); modified = false; updateStatus(); } - private int lineCount( File file ) throws IOException { - BufferedReader r=null; - int lineCount = 0; - try { - FileInputStream in = new FileInputStream(file); - r = new BufferedReader(new InputStreamReader(in)); - - - for (String line = r.readLine(); line != null; line = r.readLine()) { - lineCount++; - } - } catch ( IOException ex ) { - throw ex; - - } finally { - if ( r!=null ) r.close(); - } - return lineCount; - - } - public void loadFromFile(File file) throws IOException { ProgressMonitor mon= new NullProgressMonitor(); - BufferedReader r=null; - - boolean active0= active; - try { active = false; - int lineCount= lineCount( file ); + FileInputStream in = new FileInputStream(file); + BufferedReader r = new BufferedReader(new InputStreamReader(in)); + + int lineCount = 0; + for (String line = r.readLine(); line != null; line = r.readLine()) { + lineCount++; + } + r.close(); - r = new BufferedReader( new FileReader( file ) ); + in = new FileInputStream(file); + r = new BufferedReader(new InputStreamReader(in)); dataPoints.clear(); - String[] planesArray1 = null; - Units[] unitsArray1 = null; + String[] planesArray = null; + Units[] unitsArray = null; Datum x; Datum y; @@ -614,9 +372,12 @@ public void loadFromFile(File file) throws IOException { if (lineCount > 500) { mon = DasProgressPanel.createFramed("reading file"); + } else { + mon = new NullProgressMonitor(); } // tabs detected in file. + boolean hasTabs= false; String delim= "\t"; mon.setTaskSize(lineCount); @@ -627,99 +388,90 @@ public void loadFromFile(File file) throws IOException { if (mon.isCancelled()) { break; } - line= line.trim(); - if ( line.length()==0 ) { - continue; - } mon.setTaskProgress(linenum); - if (line.startsWith("## ") || line.length()>0 && Character.isJavaIdentifierStart( line.charAt(0) ) ) { - if ( unitsArray1!=null ) continue; - while ( line.startsWith("#") ) line = line.substring(1); + if (line.startsWith("## ")) { + if ( unitsArray!=null ) continue; + line = line.substring(3); if ( line.indexOf("\t")==-1 ) delim= "\\s+"; - String[] s = line.split(delim); - for ( int i=0; i0) { - File lastFile= new File(lastFileString); - jj.setSelectedFile(lastFile); - } + if (saveFile == null) { + getSaveAsAction().actionPerformed(e); + } else { + try { + saveToFile(saveFile); + } catch (IOException ex) { + DasExceptionHandler.handle(ex); + } - int status = jj.showSaveDialog(DataPointRecorder.this); - if (status == JFileChooser.APPROVE_OPTION) { - try { - File pathname= jj.getSelectedFile(); - if ( !( pathname.toString().endsWith(".dat") || pathname.toString().endsWith(".txt") ) ) { - pathname= new File( pathname.getAbsolutePath() + ".dat" ); } - DataPointRecorder.this.saveFile = pathname; - saveToFile(saveFile); - //messageLabel.setText("saved data to "+saveFile); - } catch (IOException e1) { - DasExceptionHandler.handle(e1); - return false; - } - } else if ( status == JFileChooser.CANCEL_OPTION ) { - return false; - } - return true; - } - - public boolean save() { - if (saveFile == null) { - return saveAs(); - } else { - try { - saveToFile(saveFile); - return true; - } catch (IOException ex) { - DasExceptionHandler.handle(ex); - return false; } - } - } - - /** - * shows the current name for the file. - * @return the current name for the file. - */ - public File getCurrentFile() { - return this.saveFile; - } - - - /** - * return true if the file was saved or "don't save" was pressed by the user. - * @return true if the file was saved or "don't save" was pressed by the user. - */ - public boolean saveBeforeExit( ) { - if ( this.modified ) { - int i= JOptionPane.showConfirmDialog( this, "Save changes before exiting?"); - if ( i==JOptionPane.OK_OPTION ) { - return save(); - } else if ( i==JOptionPane.CANCEL_OPTION ) { - return false; - } else { - return true; - } - } else { - return true; - } + }; } private Action getLoadAction() { return new AbstractAction("Open...") { - @Override + public void actionPerformed(ActionEvent e) { if (checkModified(e)) { JFileChooser jj = new JFileChooser(); String lastFileString = prefs.get("components.DataPointRecorder.lastFileLoad", ""); - if ( lastFileString.length()>0 ) { - File lastFile; + File lastFile = null; + if (lastFileString != "") { lastFile = new File(lastFileString); jj.setSelectedFile(lastFile); } @@ -940,12 +605,15 @@ public void actionPerformed(ActionEvent e) { final File loadFile = jj.getSelectedFile(); prefs.put("components.DataPointRecorder.lastFileLoad", loadFile.toString()); Runnable run = new Runnable() { - @Override + public void run() { try { loadFromFile(loadFile); + saveFile = + loadFile; updateStatus(); } catch (IOException e) { + DasExceptionHandler.handle(e); } @@ -983,14 +651,14 @@ private boolean checkModified(ActionEvent e) { private Action getNewAction() { return new AbstractAction("New") { - @Override + public void actionPerformed(ActionEvent e) { if (checkModified(e)) { - dataPoints.clear(); - saveFile = null; + dataPoints.removeAll(dataPoints); + saveFile = + null; updateStatus(); updateClients(); - table.repaint(); } } @@ -999,7 +667,7 @@ public void actionPerformed(ActionEvent e) { private Action getPropertiesAction() { return new AbstractAction("Properties") { - @Override + public void actionPerformed(ActionEvent e) { new PropertyEditor(DataPointRecorder.this).showDialog(DataPointRecorder.this); } @@ -1008,42 +676,26 @@ public void actionPerformed(ActionEvent e) { private Action getUpdateAction() { return new AbstractAction("Update") { - @Override + public void actionPerformed(ActionEvent e) { - update(); + // what if no one is listening? + if (dataSetDescriptor != null) { + dataSetDescriptor.fireUpdate(); + } + + fireDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); + fireSelectedDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); } }; } - private Action getDeleteSelectedAction() { - return new AbstractAction("Delete Selected") { - @Override - public void actionPerformed(ActionEvent e) { - int[] selectedRows = getSelectedRowsInModel(); - deleteRows(selectedRows); - } - }; - } - - /** - * Notify listeners that the dataset has updated. Pressing the "Update" - * button calls this. - */ - public void update() { - if (dataSetDescriptor != null) { - dataSetDescriptor.fireUpdate(); - } - - fireDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); - fireSelectedDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); - } - /** Creates a new instance of DataPointRecorder */ public DataPointRecorder() { super(); - this.planesArrayLock = new Object(); - dataPoints = new ArrayList(); - myTableModel = new MyTableModel(); + dataPoints = + new ArrayList(); + myTableModel = + new MyTableModel(); this.setLayout(new BorderLayout()); JMenuBar menuBar = new JMenuBar(); @@ -1056,44 +708,24 @@ public DataPointRecorder() { JMenu editMenu = new JMenu("Edit"); editMenu.add(new JMenuItem(getPropertiesAction())); - editMenu.add( new JMenuItem( new AbstractAction("Clear Table Sorting") { - public void actionPerformed(ActionEvent e) { - table.setAutoCreateRowSorter(false); - table.setAutoCreateRowSorter(true); - } - } ) ); menuBar.add(editMenu); this.add(menuBar, BorderLayout.NORTH); - planesArray = new String[]{"X", "Y"}; - unitsArray = new Units[]{null, null}; + planesArray = + new String[]{"X", "Y"}; + unitsArray = + new Units[]{null, null}; - table = new JTable(myTableModel); - table.setAutoCreateRowSorter(true); // Java 1.6 + table = + new JTable(myTableModel); table.getTableHeader().setReorderingAllowed(true); - table.setColumnModel( new DefaultTableColumnModel() { - @Override - public int getColumnCount() { - synchronized ( planesArrayLock ) { - return super.getColumnCount(); //To change body of generated methods, choose Tools | Templates. - } - } - - @Override - public TableColumn getColumn(int columnIndex) { - synchronized ( planesArrayLock ) { - return super.getColumn(columnIndex); //To change body of generated methods, choose Tools | Templates. - } - } - - }); table.setRowSelectionAllowed(true); table.addMouseListener(new DataPointRecorder.MyMouseAdapter(table)); table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override + public void valueChanged(ListSelectionEvent e) { fireSelectedDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(DataPointRecorder.this)); int selected = table.getSelectedRow(); // we could do a better job here @@ -1102,12 +734,12 @@ public void valueChanged(ListSelectionEvent e) { DataPointSelectionEvent e2 = new DataPointSelectionEvent(DataPointRecorder.this, dp.get(0), dp.get(1)); fireDataPointSelectionListenerDataPointSelected(e2); } - deleteSelectionButton.setEnabled( table.getSelectedRowCount()>0 ); - clearSelectionButton.setEnabled( table.getSelectedRowCount()>0 ); + } }); - scrollPane = new JScrollPane(table); + scrollPane = + new JScrollPane(table); this.add(scrollPane, BorderLayout.CENTER); JPanel controlStatusPanel = new JPanel(); @@ -1116,23 +748,12 @@ public void valueChanged(ListSelectionEvent e) { final JPanel controlPanel = new JPanel(); controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.X_AXIS)); - updateButton = new JButton(getUpdateAction()); - updateButton.setVisible(false); - updateButton.setEnabled(false); + JButton updateButton = new JButton(getUpdateAction()); controlPanel.add(updateButton); - clearSelectionButton = new JButton( getClearSelectionAction() ); - controlPanel.add( clearSelectionButton ); - - deleteSelectionButton = new JButton( getDeleteSelectedAction() ); - controlPanel.add( deleteSelectionButton ); - - controlPanel.add( Box.createGlue() ); - accessoryPanel= new JPanel( new BorderLayout() ); - controlPanel.add( accessoryPanel ); - - messageLabel = new JLabel("ready"); + messageLabel = + new JLabel("ready"); messageLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT); controlStatusPanel.add(messageLabel); @@ -1146,7 +767,8 @@ public void valueChanged(ListSelectionEvent e) { public static DataPointRecorder createFramed() { DataPointRecorder result; JFrame frame = new JFrame("Data Point Recorder"); - result = new DataPointRecorder(); + result = + new DataPointRecorder(); frame.getContentPane().add(result); frame.pack(); frame.setVisible(true); @@ -1154,15 +776,6 @@ public static DataPointRecorder createFramed() { return result; } - /** - * add a small component to the lower-right portion of the DataPointRecorder. It should be roughly the size of a - * button. - * @param c the component. - */ - public void addAccessory( JComponent c ) { - accessoryPanel.add(c); - } - /** * update fires off the TableDataChanged, and sets the current selected * row if necessary. @@ -1173,214 +786,103 @@ private void updateClients() { if (selectRow != -1 && table.getRowCount()>selectRow ) { table.setRowSelectionInterval(selectRow, selectRow); table.scrollRectToVisible(table.getCellRect(selectRow, 0, true)); - selectRow = -1; + selectRow = + -1; } - table.repaint(); + } } - /** - * update the status label "(modified)" - */ private void updateStatus() { - String statusString = (saveFile == null ? "" : (String.valueOf(saveFile) + " ")) + + Runnable run= () -> { + String statusString = (saveFile == null ? "" : (String.valueOf(saveFile) + " ")) + (modified ? "(modified)" : ""); - String t= messageLabel.getText(); - if ( !statusString.equals(t) ) { messageLabel.setText(statusString); - } + }; + SwingUtilities.invokeLater(run); } - - /** - * insert the point into the data points. If the dataset is sorted, then we - * replace any point that is within 10milliseconds of the point. - * @param newPoint - */ + private void insertInternal(DataPoint newPoint) { int newSelect; - synchronized ( dataPoints ) { - Set keys= newPoint.planes.keySet(); - int ikey=2; - for ( String key: keys ) { - Object o= newPoint.planes.get(key); - if ( o instanceof QDataSet ) { - QDataSet qds= (QDataSet)o; - if ( qds.rank()>0 ) { - throw new IllegalArgumentException("QDataSet rank must be zero: "+key+"="+o ); - } else { - Datum d= DataSetUtil.asDatum((QDataSet)o); - if ( !d.getUnits().isConvertibleTo( unitsArray[ikey]) ) { - throw new IllegalArgumentException("Units are not convertible: "+key+"="+d + ", expected "+unitsArray[ikey]); - } - newPoint.planes.put( key, d ); - } - } else if ( o instanceof Datum ) { - Datum d= (Datum)o; - if ( !d.getUnits().isConvertibleTo( unitsArray[ikey]) ) { - throw new IllegalArgumentException("Units are not convertible: "+key+"="+d+ ", expected "+unitsArray[ikey]); - } - // do nothing - } else if ( o instanceof String ) { - newPoint.planes.put( key, ((EnumerationUnits)unitsArray[ikey]).createDatum(o) ); - } else if ( o instanceof Number ) { - newPoint.planes.put( key, (unitsArray[ikey]).createDatum(((Number)o) ) ); - } - ikey++; - } - if (sorted) { - if ( dataPoints.size()>2 ) { // this checks out, it is sorted... - Units off= ((DataPoint)(dataPoints.get(0))).data[0].getUnits().getOffsetUnits(); - Datum doff= off.createDatum(0); - for ( int i=1; i0 ) { - throw new IllegalArgumentException("qdatasets in planes must be rank 0"); - } else { - unitsArray[index] = SemanticOps.getUnits((QDataSet)value); - } - } else if ( value instanceof Number ) { - unitsArray[index]= Units.dimensionless; - } else if ( value instanceof String ) { - unitsArray[index]= EnumerationUnits.create("default"); //TODO: we already checked for this. - } else { - throw new IllegalArgumentException("values must be rank 0 Datum or QDataSet, not " + value); - } - } - index++; - } - myTableModel.fireTableStructureChanged(); - for ( int i=0; i<1; i++ ) { //i0 && !xTagWidth.isFill() && dataPoints.size() > 0) { - QDataSet ds = getDataSet(); - QDataSet xds= (QDataSet)ds.property(QDataSet.DEPEND_0); - Units xunits= SemanticOps.getUnits(xds); - int i = DataSetUtil.closestIndex( xds, x ); - Datum diff = e.getX().subtract( xunits.createDatum(xds.value(i)) ); - if (Math.abs( diff.divide(xTagWidth).doubleValue(Units.dimensionless)) < 0.5 ) { - x = xunits.createDatum(xds.value(i)); - } - + // if a point exists within xTagWidth of the point, then have this point replace + Datum x = e.getX(); + if (snapToGrid && xTagWidth != null && dataPoints.size() > 0) { + DataSet ds = getDataSet(); + int i = DataSetUtil.closestColumn(ds, e.getX()); + Datum diff = e.getX().subtract(ds.getXTagDatum(i)); + if (Math.abs(diff.divide(xTagWidth).doubleValue(Units.dimensionless)) < 0.5) { + x = ds.getXTagDatum(i); } - addDataPoint(x, e.getY(), planesMap); + } + addDataPoint(x, e.getY(), planesMap); updateClients(); } + private javax.swing.event.EventListenerList listenerList = null; - /** - * hide the update button if no one is listening. - * @return true if it is now visible - */ - boolean checkUpdateEnable() { - int listenerList1Count; - int selectedListenerListCount; - synchronized (this) { - listenerList1Count= listenerList1==null ? 0 : listenerList1.getListenerCount(); - selectedListenerListCount= selectedListenerList==null ? 0 : selectedListenerList.getListenerCount(); + public synchronized void addDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + if (listenerList == null) { + listenerList = new javax.swing.event.EventListenerList(); } - if ( listenerList1Count>0 || selectedListenerListCount>0 ) { - updateButton.setEnabled(true); - updateButton.setVisible(true); - updateButton.setToolTipText(null); - return true; - } else { - updateButton.setEnabled(false); - updateButton.setToolTipText("no listeners. See File->Save to save table."); - updateButton.setVisible(false); - return false; - } - } - private javax.swing.event.EventListenerList listenerList1 = new javax.swing.event.EventListenerList(); - public void addDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { - listenerList1.add(org.das2.dataset.DataSetUpdateListener.class, listener); - checkUpdateEnable(); + + + + listenerList.add(org.das2.dataset.DataSetUpdateListener.class, listener); } - public void removeDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { - listenerList1.remove(org.das2.dataset.DataSetUpdateListener.class, listener); - checkUpdateEnable(); + public synchronized void removeDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + listenerList.remove(org.das2.dataset.DataSetUpdateListener.class, listener); } private void fireDataSetUpdateListenerDataSetUpdated(org.das2.dataset.DataSetUpdateEvent event) { - Object[] listeners= listenerList1.getListenerList(); - for (int i = listeners.length - 2; i >=0; i-= 2) { + if (listenerList == null) { + return; + } + + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= + 0; i -= + 2) { if (listeners[i] == org.das2.dataset.DataSetUpdateListener.class) { ((org.das2.dataset.DataSetUpdateListener) listeners[i + 1]).dataSetUpdated(event); } } } - - - /** - * the selection are the highlighted points in the table. Listeners can grab this data and do something with the - * dataset. - */ - private javax.swing.event.EventListenerList selectedListenerList = new javax.swing.event.EventListenerList(); + private javax.swing.event.EventListenerList selectedListenerList = null; + + public synchronized void addSelectedDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + if (selectedListenerList == null) { + selectedListenerList = new javax.swing.event.EventListenerList(); + } + + + - public void addSelectedDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { selectedListenerList.add(org.das2.dataset.DataSetUpdateListener.class, listener); - checkUpdateEnable(); } - public void removeSelectedDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + public synchronized void removeSelectedDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { selectedListenerList.remove(org.das2.dataset.DataSetUpdateListener.class, listener); - checkUpdateEnable(); } - private void fireSelectedDataSetUpdateListenerDataSetUpdated(org.das2.dataset.DataSetUpdateEvent event) { - Object[] listeners= selectedListenerList.getListenerList(); - for ( int i = listeners.length - 2; i >=0; i-=2 ) { + if (selectedListenerList == null) { + return; + } + + Object[] listeners = selectedListenerList.getListenerList(); + for (int i = listeners.length - 2; i >= + 0; i -= + 2) { if (listeners[i] == org.das2.dataset.DataSetUpdateListener.class) { ((org.das2.dataset.DataSetUpdateListener) listeners[i + 1]).dataSetUpdated(event); } } } - /** * Holds value of property sorted. */ @@ -1570,19 +1056,25 @@ public void setSorted(boolean sorted) { * Registers DataPointSelectionListener to receive events. * @param listener The listener to register. */ - public void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { - if (listenerList1 == null) { - listenerList1 = new javax.swing.event.EventListenerList(); + public synchronized void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + + if (listenerList == null) { + listenerList = new javax.swing.event.EventListenerList(); } - listenerList1.add(org.das2.event.DataPointSelectionListener.class, listener); + + + + + listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); } /** * Removes DataPointSelectionListener from the list of listeners. * @param listener The listener to remove. */ - public void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { - listenerList1.remove(org.das2.event.DataPointSelectionListener.class, listener); + public synchronized void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + + listenerList.remove(org.das2.event.DataPointSelectionListener.class, listener); } /** @@ -1591,24 +1083,28 @@ public void removeDataPointSelectionListener(org.das2.event.DataPointSelectionLi * @param event The event to be fired */ private void fireDataPointSelectionListenerDataPointSelected(org.das2.event.DataPointSelectionEvent event) { - Object[] listeners= listenerList1.getListenerList(); + if (listenerList == null) { + return; + } + logger.fine("firing data point selection event"); - for (int i = listeners.length - 2; i >= 0; i -= 2 ) { + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= + 0; i -= + 2) { if (listeners[i] == org.das2.event.DataPointSelectionListener.class) { ((org.das2.event.DataPointSelectionListener) listeners[i + 1]).dataPointSelected(event); } } } - /** * Holds value of property xTagWidth. */ - private Datum xTagWidth = Datum.create(0); + private Datum xTagWidth = null; /** - * Getter for property xTagWidth. When xTagWidth is zero, - * this implies there is no binning. + * Getter for property xTagWidth. * @return Value of property xTagWidth. */ public Datum getXTagWidth() { @@ -1616,7 +1112,7 @@ public Datum getXTagWidth() { } /** - * bins for the data, when xTagWidth is non-zero. + * Setter for property xTagWidth. * @param xTagWidth New value of property xTagWidth. */ public void setXTagWidth(Datum xTagWidth) { @@ -1645,13 +1141,4 @@ public void setSnapToGrid(boolean snapToGrid) { this.snapToGrid = snapToGrid; } - - /** - * return true when the data point recorder has been modified. - * @return true when the data point recorder has been modified. - */ - public boolean isModified() { - return modified; - } - } diff --git a/dasCore/src/org/das2/components/DataPointRecorderNew.java b/dasCore/src/org/das2/components/DataPointRecorderNew.java deleted file mode 100755 index 07d9d6720..000000000 --- a/dasCore/src/org/das2/components/DataPointRecorderNew.java +++ /dev/null @@ -1,1685 +0,0 @@ -/* - * DataPointRecorderNew.java - * - * Created on Apr 18, 2014 5:57am - */ -package org.das2.components; - -import org.das2.dataset.DataSetUpdateEvent; -import org.das2.dataset.VectorDataSet; -import org.das2.dataset.DataSetUpdateListener; -import org.das2.datum.DatumRange; -import org.das2.datum.Units; -import org.das2.datum.Datum; -import org.das2.datum.DatumUtil; -import org.das2.datum.TimeUtil; -import org.das2.util.DasExceptionHandler; -import org.das2.util.monitor.NullProgressMonitor; -import org.das2.util.monitor.ProgressMonitor; -import org.das2.components.propertyeditor.PropertyEditor; -import org.das2.datum.format.DatumFormatter; -import org.das2.system.DasLogger; -import java.awt.BorderLayout; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.lang.reflect.InvocationTargetException; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.prefs.Preferences; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.SwingUtilities; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.filechooser.FileFilter; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.DefaultTableColumnModel; -import javax.swing.table.TableColumn; -import org.das2.datum.EnumerationUnits; -import org.das2.datum.InconvertibleUnitsException; -import org.das2.datum.UnitsUtil; -import org.das2.event.DataPointSelectionEvent; -import org.virbo.dataset.AbstractDataSet; -import org.virbo.dataset.ArrayDataSet; -import org.virbo.dataset.DDataSet; -import org.virbo.dataset.DataSetOps; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dataset.SparseDataSetBuilder; -import org.virbo.dsops.Ops; -import org.virbo.dsutil.DataSetBuilder; - -/** - * DataPointRecorderNew is a GUI for storing data points selected by the user. - * This is the old recorder but: - * 1. uses QDataSet to handle the data. No more strange internal object. - * 2. allows the columns to be declared explicitly by code, and data is merged in by name. - * @deprecated use DataPointRecorder, which is this code. DataPointRecorderNew is left because of use in Jython scripts. - * @author jbf - */ -public class DataPointRecorderNew extends JPanel { - - /** - * width of time column - */ - private static final int TIME_WIDTH = 180; - - protected JTable table; - protected JScrollPane scrollPane; - protected JButton updateButton; - final protected List dataPoints; - private int selectRow; // this row needs to be selected after the update. - - /** - * units[index]==null if HashMap contains non-datum object. - */ - protected Units[] unitsArray; - - protected Units[] defaultUnitsArray; - - /** - * array of names that are also the column headers. - */ - protected String[] namesArray; - - protected String[] defaultNamesArray; - - private double[] defaultsArray; - - /** - * bundleDescriptor for the dataset. - */ - private QDataSet bundleDescriptor; - - protected AbstractTableModel myTableModel; - private File saveFile; - private boolean modified; - private JLabel messageLabel; - private boolean active = true; // false means don't fire updates - Preferences prefs = Preferences.userNodeForPackage(this.getClass()); - private static final Logger logger = DasLogger.getLogger(DasLogger.GUI_LOG); - private final JButton clearSelectionButton; - - - private final Object namesArrayLock; - - private class MyTableModel extends AbstractTableModel { - @Override - public int getColumnCount() { - synchronized (namesArrayLock) { - return namesArray==null ? 0 : namesArray.length; - } - } - - @Override - public String getColumnName(int j) { - synchronized (namesArrayLock) { - String result = namesArray[j]; - if (unitsArray[j] != null) { - if ( unitsArray[j] instanceof EnumerationUnits ) { - result += "(ordinal)"; - } else if ( UnitsUtil.isTimeLocation( unitsArray[j] ) ) { - result += "(UTC)"; - } else if ( unitsArray[j]==Units.dimensionless ) { - // add nothing. - } else { - result += "(" + unitsArray[j] + ")"; - } - } - return result; - } - } - - @Override - public Class getColumnClass(int columnIndex) { - return Datum.class; - } - - - @Override - public int getRowCount() { - int nrow = dataPoints.size(); - return nrow; - } - - - @Override - public Object getValueAt(int i, int j) { - QDataSet x; - synchronized (dataPoints) { - x= (QDataSet) dataPoints.get(i); - } - if (j < x.length()) { - Datum d = unitsArray[j].createDatum(x.value(j)); - DatumFormatter format = d.getFormatter(); - return format.format(d, unitsArray[j]); - } else { - throw new IndexOutOfBoundsException("no such column"); - } - } - } - - /** - * delete all the points within the interval. This was introduced to support the - * case where we are going to reprocess an interval, as with the - * RBSP digitizer. - * - * @param range range to delete, end time is exclusive. - */ - public void deleteInterval( DatumRange range ) { - if ( !sorted ) { - throw new IllegalArgumentException("data must be sorted"); - } else { - synchronized ( dataPoints ) { - Comparator comp= new Comparator() { - @Override - public int compare(Object o1, Object o2) { - Datum d1; - if ( o1 instanceof QDataSet ) { - d1= DataSetUtil.asDatum(((QDataSet)o1).slice(0)); - } else if ( o1 instanceof Datum ) { - d1= (Datum)o1; - } else { - throw new IllegalArgumentException("expected Datum or QDataSet"); - } - Datum d2; - if ( o2 instanceof QDataSet ) { - d2= DataSetUtil.asDatum(((QDataSet)o2).slice(0)); - } else if ( o2 instanceof Datum ) { - d2= (Datum)o2; - } else { - throw new IllegalArgumentException("expected Datum or QDataSet"); - } - return d1.compareTo(d2); - } - }; - int index1= Collections.binarySearch( dataPoints, range.min(), comp ); - if ( index1<0 ) index1= ~index1; - int index2= Collections.binarySearch( dataPoints, range.max(), comp ); - if ( index2<0 ) index2= ~index2; - if ( index1==index2 ) return; - int[] arr= new int[ index2-index1 ]; - for ( int i=0; i=0; i-- ) { - dataPoints.remove(selectedRows[i]); - } - modified = true; - } - updateClients(); - updateStatus(); - if ( active ) { - fireDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this,getDataSet())); - } - myTableModel.fireTableDataChanged(); - } - - - /** - * returns a data set of the table data. - * @return a data set of the table data. - */ - public QDataSet getDataSet() { - DataSetBuilder b; - synchronized ( dataPoints ) { - if (dataPoints.isEmpty()) { - return null; - } else { - b= new DataSetBuilder(2,dataPoints.size(),bundleDescriptor.length()); - b.putProperty( QDataSet.BUNDLE_1, bundleDescriptor ); - for (int irow = 0; irow < dataPoints.size(); irow++) { - QDataSet dp = dataPoints.get(irow); - b.putValues( -1, dp, dp.length() ); - b.nextRecord(); - } - } - } - return b.getDataSet(); - } - - /** - * returns a data set of the selected table data. Warning: this used to - * return a bundle dataset with Y,plane1,plane2,etc that had DEPEND_0 for X. - * This now returns a bundle ds[n,m] where m is the number of columns and - * n is the number of records. - * @return a data set of the selected table data. - * @see #select(org.das2.datum.DatumRange, org.das2.datum.DatumRange) which selects part of the dataset. - */ - public QDataSet getSelectedDataSet() { - int[] selectedRows; - List ldataPoints; - QDataSet lbundleDescriptor; - synchronized (this) { - selectedRows= getSelectedRowsInModel(); - ldataPoints= new ArrayList( dataPoints ); - lbundleDescriptor= bundleDescriptor; - } - DataSetBuilder b; - if (selectedRows.length == 0) { - return null; - } else { - b= new DataSetBuilder(2,selectedRows.length,lbundleDescriptor.length()); - b.putProperty( QDataSet.BUNDLE_1, lbundleDescriptor ); - for (int i = 0; i < selectedRows.length; i++) { - int irow = selectedRows[i]; - if ( irow0 ) { - int iselect= (Integer)selectMe.get(0); - table.scrollRectToVisible(new Rectangle(table.getCellRect( iselect, 0, true)) ); - } - } - } - - /** - * - * @param file - * @throws IOException - */ - public void saveToFile(File file) throws IOException { - List dataPoints1; - String[] lnamesArray; - Units[] lunitsArray; - synchronized (this) { - lnamesArray= Arrays.copyOf(namesArray,namesArray.length); - lunitsArray= Arrays.copyOf(unitsArray,unitsArray.length); - dataPoints1= new ArrayList( dataPoints ); - } - FileOutputStream out = new FileOutputStream(file); - BufferedWriter r = new BufferedWriter(new OutputStreamWriter(out)); - - try { - StringBuilder header = new StringBuilder(); - //header.append("## "); // don't use comment characters so that labels and units are used in Autoplot's ascii parser. - for (int j = 0; j < lnamesArray.length; j++) { - Units units= lunitsArray[j]; - String sunits; - if ( UnitsUtil.isTimeLocation(units) ) { - sunits= "(UTC)"; - } else if ( UnitsUtil.isOrdinalMeasurement(units) ) { - sunits= "(ordinal)"; - } else if ( units==Units.dimensionless ) { - sunits= ""; - } else { - sunits= "("+units+")"; - } - header.append( lnamesArray[j] ).append(sunits); - if ( j 500) { - mon = DasProgressPanel.createFramed("reading file"); - } - - // tabs detected in file. - String delim= ","; - - mon.setTaskSize(lineCount); - mon.started(); - int linenum = 0; - - final List records= new ArrayList<>(1440); - - for (String line = r.readLine(); line != null; line = r.readLine()) { - linenum++; - if (mon.isCancelled()) { - break; - } - line= line.trim(); - if ( line.length()==0 ) { - continue; - } - mon.setTaskProgress(linenum); - if (line.startsWith("## ") || line.length()>0 && Character.isJavaIdentifierStart( line.charAt(0) ) ) { - if ( unitsArray1!=null ) continue; - while ( line.startsWith("#") ) line = line.substring(1); - if ( !line.contains(delim) ) delim= "\t"; - if ( !line.contains(delim) ) delim= "\\s+"; - String[] s = line.split(delim); - for ( int i=0; i0) { - File lastFile= new File(lastFileString); - jj.setSelectedFile(lastFile); - } - - int status = jj.showSaveDialog(DataPointRecorderNew.this); - if (status == JFileChooser.APPROVE_OPTION) { - try { - File pathname= jj.getSelectedFile(); - if ( !( pathname.toString().endsWith(".dat") || pathname.toString().endsWith(".txt") ) ) { - pathname= new File( pathname.getAbsolutePath() + ".dat" ); - } - DataPointRecorderNew.this.saveFile = pathname; - saveToFile(saveFile); - //messageLabel.setText("saved data to "+saveFile); - } catch (IOException e1) { - DasExceptionHandler.handle(e1); - return false; - } - } else if ( status == JFileChooser.CANCEL_OPTION ) { - return false; - } - return true; - } - - public boolean save() { - if (saveFile == null) { - return saveAs(); - } else { - try { - saveToFile(saveFile); - return true; - } catch (IOException ex) { - DasExceptionHandler.handle(ex); - return false; - } - } - } - - /** - * shows the current name for the file. - * @return the current name for the file. - */ - public File getCurrentFile() { - return this.saveFile; - } - - - /** - * return true if the file was saved or "don't save" was pressed by the user. - * @return true if the file was saved or "don't save" was pressed by the user. - */ - public boolean saveBeforeExit( ) { - if ( this.modified ) { - int i= JOptionPane.showConfirmDialog( this, "Save changes before exiting?"); - if ( i==JOptionPane.OK_OPTION ) { - return save(); - } else if ( i==JOptionPane.CANCEL_OPTION ) { - return false; - } else { - return true; - } - } else { - return true; - } - } - - private Action getLoadAction() { - return new AbstractAction("Open...") { - @Override - public void actionPerformed(ActionEvent e) { - if (checkModified(e)) { - JFileChooser jj = new JFileChooser(); - String lastFileString = prefs.get("components.DataPointRecorder.lastFileLoad", ""); - if ( lastFileString.length()>0 ) { - File lastFile; - lastFile = new File(lastFileString); - jj.setSelectedFile(lastFile); - } - - int status = jj.showOpenDialog(DataPointRecorderNew.this); - if (status == JFileChooser.APPROVE_OPTION) { - final File loadFile = jj.getSelectedFile(); - prefs.put("components.DataPointRecorder.lastFileLoad", loadFile.toString()); - Runnable run = new Runnable() { - @Override - public void run() { - try { - loadFromFile(loadFile); - updateStatus(); - } catch (IOException e) { - DasExceptionHandler.handle(e); - } - - } - }; - new Thread(run).start(); - } - - } - } - }; - } - - /** - * returns true if the operation should continue, false - * if not, meaning the user pressed cancel. - */ - private boolean checkModified(ActionEvent e) { - if (modified) { - int n = JOptionPane.showConfirmDialog( - DataPointRecorderNew.this, - "Current work has not been saved.\n Save first?", - "Save work first", - JOptionPane.YES_NO_CANCEL_OPTION); - if (n == JOptionPane.YES_OPTION) { - getSaveAction().actionPerformed(e); - } - - return (n != JOptionPane.CANCEL_OPTION); - } else { - return true; - } - - } - - private Action getNewAction() { - return new AbstractAction("New") { - @Override - public void actionPerformed(ActionEvent e) { - if (checkModified(e)) { - dataPoints.clear(); - saveFile = null; - updateStatus(); - updateClients(); - table.repaint(); - } - - } - }; - } - - private Action getPropertiesAction() { - return new AbstractAction("Properties") { - @Override - public void actionPerformed(ActionEvent e) { - new PropertyEditor(DataPointRecorderNew.this).showDialog(DataPointRecorderNew.this); - } - }; - } - - private Action getUpdateAction() { - return new AbstractAction("Update") { - @Override - public void actionPerformed(ActionEvent e) { - update(); - } - }; - } - - /** - * Notify listeners that the dataset has updated. Pressing the "Update" - * button calls this. - */ - public void update() { - fireDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); - fireSelectedDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(this)); - } - - /** Creates a new instance of DataPointRecorder */ - public DataPointRecorderNew() { - super(); - this.namesArrayLock = new Object(); - dataPoints = new ArrayList(); - myTableModel = new MyTableModel(); - this.setLayout(new BorderLayout()); - - JMenuBar menuBar = new JMenuBar(); - JMenu fileMenu = new JMenu("File"); - fileMenu.add(new JMenuItem(getNewAction())); - fileMenu.add(new JMenuItem(getLoadAction())); - fileMenu.add(new JMenuItem(getSaveAction())); - fileMenu.add(new JMenuItem(getSaveAsAction())); - menuBar.add(fileMenu); - - JMenu editMenu = new JMenu("Edit"); - editMenu.add(new JMenuItem(getPropertiesAction())); - editMenu.add( new JMenuItem( new AbstractAction("Clear Table Sorting") { - public void actionPerformed(ActionEvent e) { - table.setAutoCreateRowSorter(false); - table.setAutoCreateRowSorter(true); - } - } ) ); - JMenuItem mi; - mi= new JMenuItem( new AbstractAction("Delete Selected Items") { - @Override - public void actionPerformed(ActionEvent e) { - int[] selectedRows = getSelectedRowsInModel(); - deleteRows(selectedRows); - } - } ); - editMenu.add( mi ); - menuBar.add(editMenu); - - this.add(menuBar, BorderLayout.NORTH); - - table = new JTable(myTableModel); - table.setAutoCreateRowSorter(true); // Java 1.6 - - table.getTableHeader().setReorderingAllowed(true); - table.setColumnModel( new DefaultTableColumnModel() { - - @Override - public int getColumnCount() { - synchronized ( namesArrayLock ) { - return super.getColumnCount(); //To change body of generated methods, choose Tools | Templates. - } - } - - @Override - public TableColumn getColumn(int columnIndex) { - synchronized ( namesArrayLock ) { - return super.getColumn(columnIndex); //To change body of generated methods, choose Tools | Templates. - } - } - - }); - table.setRowSelectionAllowed(true); - table.addMouseListener(new DataPointRecorderNew.MyMouseAdapter(table)); - table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - fireSelectedDataSetUpdateListenerDataSetUpdated(new DataSetUpdateEvent(DataPointRecorderNew.this)); - int selected = table.getSelectedRow(); // we could do a better job here - if (selected > -1) { - QDataSet dp = dataPoints.get(selected); - //System.err.println(dp); - Datum x= DataSetUtil.asDatum( dp.slice(0) ); - Datum y= DataSetUtil.asDatum( dp.slice(1) ); - DataPointSelectionEvent e2 = new DataPointSelectionEvent(DataPointRecorderNew.this, x, y ); - e2.setDataSet(dp); - fireDataPointSelectionListenerDataPointSelected(e2); - } - - } - }); - - scrollPane = new JScrollPane(table); - this.add(scrollPane, BorderLayout.CENTER); - - JPanel controlStatusPanel = new JPanel(); - controlStatusPanel.setLayout(new BoxLayout(controlStatusPanel, BoxLayout.Y_AXIS)); - - final JPanel controlPanel = new JPanel(); - controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.X_AXIS)); - - updateButton = new JButton(getUpdateAction()); - updateButton.setVisible(false); - updateButton.setEnabled(false); - - controlPanel.add(updateButton); - - clearSelectionButton = new JButton( getClearSelectionAction() ); - controlPanel.add( clearSelectionButton ); - - messageLabel = new JLabel("ready"); - messageLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT); - - controlStatusPanel.add(messageLabel); - - controlPanel.setAlignmentX(JLabel.LEFT_ALIGNMENT); - controlStatusPanel.add(controlPanel); - - this.add(controlStatusPanel, BorderLayout.SOUTH); - } - - public static DataPointRecorderNew createFramed() { - DataPointRecorderNew result; - JFrame frame = new JFrame("Data Point Recorder"); - result = new DataPointRecorderNew(); - frame.getContentPane().add(result); - frame.pack(); - frame.setVisible(true); - frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); - return result; - } - - /** - * update fires off the TableDataChanged, and sets the current selected - * row if necessary. - */ - private void updateClients() { - if (active) { - myTableModel.fireTableDataChanged(); - if (selectRow != -1 && table.getRowCount()>selectRow ) { - table.setRowSelectionInterval(selectRow, selectRow); - table.scrollRectToVisible(table.getCellRect(selectRow, 0, true)); - selectRow = -1; - } - table.repaint(); - } - } - - /** - * update the status label "(modified)" - */ - private void updateStatus() { - String statusString = (saveFile == null ? "" : (String.valueOf(saveFile) + " ")) + - (modified ? "(modified)" : ""); - String t= messageLabel.getText(); - if ( !statusString.equals(t) ) { - messageLabel.setText(statusString); - } - } - - private transient Comparator comparator= new Comparator() { - public int compare(Object o1, Object o2) { - if ( o1 instanceof QDataSet && o2 instanceof QDataSet ) { - QDataSet qds1= (QDataSet)o1; - QDataSet qds2= (QDataSet)o2; - return DataSetUtil.asDatum(qds1.slice(0)).gt( DataSetUtil.asDatum( qds2.slice(0) ) ) ? 1 : -1; - } else { - throw new IllegalArgumentException("expected qdatasets"); - } - } - }; - - /** - * explicitly declare the number of columns. Call this and then - * setColumn to define each column. - * @param count the number of columns. - */ - public void setColumnCount( int count ) { - namesArray= new String[count]; - unitsArray= new Units[count]; - defaultsArray= new double[count]; - for ( int i=0; i=namesArray.length ) { - throw new IndexOutOfBoundsException("column index is out of bounds (and 0 is the first column)"); - } - namesArray[i]= name; - unitsArray[i]= units; - defaultsArray[i]= deft.doubleValue(units); - } - - /** - * identify the name and unit for each column. - * @param i the column number - * @param name a Java identifier for the column, e.g. "StartTime" - * @param units units units for the column, or null for dimensionless. - * @param deft default value to use when data is not provided, which must be parseable by units. - */ - public void setColumn( int i, String name, Units units, String deft ) throws ParseException { - if ( units==null ) units= Units.dimensionless; - if ( units instanceof EnumerationUnits ) { - setColumn( i, name, units, ((EnumerationUnits)units).createDatum(deft) ); - } else { - setColumn( i, name, units, units.parse(deft) ); - } - } - - /** - * identify the name and unit for each column. - * @param i the column number - * @param name a Java identifier for the column, e.g. "StartTime" - * @param units units units for the column, or null for dimensionless. - * @param deft default value to use when data is not provided. - */ - public void setColumn( int i, String name, Units units, double deft ) throws ParseException { - if ( units==null ) units= Units.dimensionless; - setColumn( i, name, units, units.createDatum(deft) ); - } - - /** - * insert the point into the data points. If the dataset is sorted, then we - * replace any point that is within X_LIMIT of the point. - * @param newPoint - */ - private void insertInternal( QDataSet newPoint ) { - int newSelect; - if ( newPoint.rank()==2 && newPoint.length()==1 ) { - newPoint= newPoint.slice(0); - } - - // make sure all the units are correct by converting them as they come in. - ArrayDataSet mnp; - if ( defaultsArray!=null ) { - mnp= DDataSet.wrap( Arrays.copyOf(defaultsArray,defaultsArray.length) ); - } else { - mnp= DDataSet.createRank1(namesArray.length); - } - - QDataSet bds= (QDataSet) newPoint.property(QDataSet.BUNDLE_0); - - for ( int i=0; i planes ) { - - if ( planes==null ) planes= Collections.emptyMap(); - - DDataSet rec= DDataSet.createRank1( 2 + planes.size() ); - SparseDataSetBuilder bdsb= new SparseDataSetBuilder(2); - - int ii= 0; - - bdsb.putProperty( QDataSet.NAME, ii, "x" ); - bdsb.putProperty( QDataSet.UNITS, ii, x.getUnits() ); - rec.putValue( ii, x.doubleValue( x.getUnits() )); - ii++; - - bdsb.putProperty( QDataSet.NAME, ii, "y" ); - bdsb.putProperty( QDataSet.UNITS, ii, y.getUnits() ); - rec.putValue( ii, y.doubleValue( y.getUnits() )); - ii++; - - for ( Entry e : planes.entrySet() ) { - bdsb.putProperty( QDataSet.NAME, ii, e.getKey() ); - Object o= e.getValue(); - Units theu; - if ( o instanceof String ) { - Units eu= EnumerationUnits.create("default"); - theu= eu; - try { - rec.putValue( ii, theu.parse((String)o).doubleValue(theu) ); - } catch (ParseException ex) { - rec.putValue( ii, -1 ); // fill - } - } else if ( o instanceof Datum ) { - theu= ((Datum)o).getUnits(); - rec.putValue( ii, ((Datum)o).doubleValue(theu) ); - } else if ( o instanceof Number ) { - theu= Units.dimensionless; - rec.putValue( ii, ((Number)o).doubleValue() ); - } else if ( o instanceof QDataSet ) { - theu= SemanticOps.getUnits((QDataSet)o); - rec.putValue( ii, ((QDataSet)o).value() ); - } else { - throw new IllegalArgumentException("value must be String, Datum, DataSet or Number"); - } - bdsb.putProperty( QDataSet.UNITS, ii, theu ); - ii++; - } - - bdsb.setLength(ii); - - QDataSet bds= bdsb.getDataSet(); - rec.putProperty(QDataSet.BUNDLE_0,bds); - - addDataPoint( rec ); - } - - /** - * add the record to the collection of records. This should be a - * rank 1 bundle or 1-record rank 2 bundle. - *
    {@code
    -     *dpr=DataPointRecorder()
    -     *dpr.addDataPoint( createEvent( '2014-04-23/P1D', 0xFF0000, 'alert' ) )
    -     *}
    - * - * @param rec rank 1 qdataset, or 1-record rank 2 dataset (ds[1,n]) - */ - public void addDataPoint( QDataSet rec ) { - - if ( rec.rank()==2 && rec.length()==1 ) { - rec= rec.slice(0); // Jython createEvent produces rank 2 dataset. - } - - synchronized (dataPoints) { - if (dataPoints.isEmpty()) { - QDataSet bds= (QDataSet) rec.property( QDataSet.BUNDLE_0 ); - - if ( bds==null ) { - SparseDataSetBuilder bdsb= new SparseDataSetBuilder(2); - Units u= SemanticOps.getUnits(rec); - for ( int i=0;i0 ) { // || selectedListenerListCount>0 ) { - updateButton.setEnabled(true); - updateButton.setVisible(true); - updateButton.setToolTipText(null); - return true; - } else { - updateButton.setEnabled(false); - updateButton.setToolTipText("no listeners. See File->Save to save table."); - updateButton.setVisible(false); - return false; - } - } - private javax.swing.event.EventListenerList listenerList1 = new javax.swing.event.EventListenerList(); - - public void addDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { - listenerList1.add(org.das2.dataset.DataSetUpdateListener.class, listener); - checkUpdateEnable(); - } - - public void removeDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { - listenerList1.remove(org.das2.dataset.DataSetUpdateListener.class, listener); - checkUpdateEnable(); - } - - private void fireDataSetUpdateListenerDataSetUpdated(org.das2.dataset.DataSetUpdateEvent event) { - Object[] listeners= listenerList1.getListenerList(); - for (int i = listeners.length - 2; i >=0; i-= 2) { - if (listeners[i] == org.das2.dataset.DataSetUpdateListener.class) { - ((org.das2.dataset.DataSetUpdateListener) listeners[i + 1]).dataSetUpdated(event); - } - } - } - - - /** - * the selection are the highlighted points in the table. Listeners can grab this data and do something with the - * dataset. - */ - private javax.swing.event.EventListenerList selectedListenerList = new javax.swing.event.EventListenerList(); - - public void addSelectedDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { - selectedListenerList.add(org.das2.dataset.DataSetUpdateListener.class, listener); - checkUpdateEnable(); - } - - public void removeSelectedDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { - selectedListenerList.remove(org.das2.dataset.DataSetUpdateListener.class, listener); - checkUpdateEnable(); - } - - - private void fireSelectedDataSetUpdateListenerDataSetUpdated(org.das2.dataset.DataSetUpdateEvent event) { - Object[] listeners= selectedListenerList.getListenerList(); - for ( int i = listeners.length - 2; i >=0; i-=2 ) { - if (listeners[i] == org.das2.dataset.DataSetUpdateListener.class) { - ((org.das2.dataset.DataSetUpdateListener) listeners[i + 1]).dataSetUpdated(event); - } - } - - } - - /** - * Holds value of property sorted. - */ - private boolean sorted = true; - - /** - * Getter for property sorted. - * @return Value of property sorted. - */ - public boolean isSorted() { - - return this.sorted; - } - - /** - * Setter for property sorted. - * @param sorted New value of property sorted. - */ - public void setSorted(boolean sorted) { - - this.sorted = sorted; - } - - /** - * Registers DataPointSelectionListener to receive events. - * @param listener The listener to register. - */ - public synchronized void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { - if (listenerList1 == null) { - listenerList1 = new javax.swing.event.EventListenerList(); - } - listenerList1.add(org.das2.event.DataPointSelectionListener.class, listener); - } - - /** - * Removes DataPointSelectionListener from the list of listeners. - * @param listener The listener to remove. - */ - public synchronized void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { - listenerList1.remove(org.das2.event.DataPointSelectionListener.class, listener); - } - - /** - * Notifies all registered listeners about the event. - * - * @param event The event to be fired - */ - private void fireDataPointSelectionListenerDataPointSelected(org.das2.event.DataPointSelectionEvent event) { - Object[] listeners; - synchronized (this) { - if (listenerList1 == null) { - return; - } - listeners = listenerList1.getListenerList(); - } - - logger.fine("firing data point selection event"); - for (int i = listeners.length - 2; i >= 0; i -= 2 ) { - if (listeners[i] == org.das2.event.DataPointSelectionListener.class) { - ((org.das2.event.DataPointSelectionListener) listeners[i + 1]).dataPointSelected(event); - } - } - - } - - /** - * Holds value of property xTagWidth. - */ - private Datum xTagWidth = Datum.create(0); - - /** - * Getter for property xTagWidth. When xTagWidth is zero, - * this implies there is no binning. - * @return Value of property xTagWidth. - */ - public Datum getXTagWidth() { - return this.xTagWidth; - } - - /** - * bins for the data, when xTagWidth is non-zero. - * @param xTagWidth New value of property xTagWidth. - */ - public void setXTagWidth(Datum xTagWidth) { - this.xTagWidth = xTagWidth; - } - /** - * Holds value of property snapToGrid. - */ - private boolean snapToGrid = false; - - /** - * Getter for property snapToGrid. - * @return Value of property snapToGrid. - */ - public boolean isSnapToGrid() { - - return this.snapToGrid; - } - - /** - * Setter for property snapToGrid. true indicates the xtag will be reset - * so that the tags are equally spaced, each xTagWidth apart. - * @param snapToGrid New value of property snapToGrid. - */ - public void setSnapToGrid(boolean snapToGrid) { - - this.snapToGrid = snapToGrid; - } - - /** - * return true when the data point recorder has been modified. - */ - public boolean isModified() { - return modified; - } - -} diff --git a/dasCore/src/org/das2/components/DataSetBrowser.java b/dasCore/src/org/das2/components/DataSetBrowser.java new file mode 100644 index 000000000..c76615227 --- /dev/null +++ b/dasCore/src/org/das2/components/DataSetBrowser.java @@ -0,0 +1,108 @@ +/* File: DataSetBrowser.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.DasException; +import org.das2.DasIOException; +import org.das2.client.DasServer; + +import javax.swing.*; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.*; + +/** + * JTree veiw of a Das2Server's available data sets, with drag-n-drop of data set id's. + */ +public class DataSetBrowser extends JPanel implements DragSourceListener, DragGestureListener { + + DasServer dasServer; + JTree tree; + TreeModel dataSetListTreeModel; + + /** Creates a new instance of DataSetBrowser */ + public DataSetBrowser(DasServer dasServer) { + super(); + this.dasServer= dasServer; + try { + dataSetListTreeModel= dasServer.getDataSetList(); + } catch ( DasIOException e ) { + System.out.println(e); + } catch ( DasException e ) { + System.out.println(e); + } + + this.setLayout(new java.awt.BorderLayout()); + + tree= new JTree(dataSetListTreeModel); + + DragSource ds= DragSource.getDefaultDragSource(); + DragGestureRecognizer dgr= + ds.createDefaultDragGestureRecognizer(tree,DnDConstants.ACTION_COPY_OR_MOVE, + this ); + + this.add(new JScrollPane(tree),java.awt.BorderLayout.CENTER); + + } + + public String getSelectedDataSetId() { + TreePath tp= tree.getLeadSelectionPath(); + TreeNode tn = (TreeNode)tp.getLastPathComponent(); + if (tn.isLeaf()) { + String s = dasServer.getURL()+"?"+tp.getPathComponent(1); + for (int index = 2; index < tp.getPathCount(); index++) + s = s + "/" + tp.getPathComponent(index); + return s; + } else { + return null; + } + } + + public void dragDropEnd(java.awt.dnd.DragSourceDropEvent dragSourceDropEvent) { + } + + public void dragEnter(java.awt.dnd.DragSourceDragEvent dragSourceDragEvent) { + } + + public void dragExit(java.awt.dnd.DragSourceEvent dragSourceEvent) { + } + + public void dragGestureRecognized(java.awt.dnd.DragGestureEvent dragGestureEvent) { + String s= getSelectedDataSetId(); + if ( s!=null ) { + Transferable t= new StringSelection(s); + dragGestureEvent.startDrag(null,t,this); + } + } + + public void dragOver(java.awt.dnd.DragSourceDragEvent dragSourceDragEvent) { + } + + public void dropActionChanged(java.awt.dnd.DragSourceDragEvent dragSourceDragEvent) { + } + +} diff --git a/dasCore/src/org/das2/components/DatumEditor.java b/dasCore/src/org/das2/components/DatumEditor.java index bebf52091..a1fa576e1 100644 --- a/dasCore/src/org/das2/components/DatumEditor.java +++ b/dasCore/src/org/das2/components/DatumEditor.java @@ -40,7 +40,7 @@ import javax.swing.event.EventListenerList; import javax.swing.table.TableCellEditor; -/** +/**maybeInitGui * * @author Edward West */ @@ -59,7 +59,6 @@ public synchronized void removePropertyChangeListener(String propertyName, Prope pcs.removePropertyChangeListener(propertyName, listener); } - @Override public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { pcs.removePropertyChangeListener(listener); } @@ -68,7 +67,6 @@ public synchronized void addPropertyChangeListener(String propertyName, Property pcs.addPropertyChangeListener(propertyName, listener); } - @Override public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); } @@ -77,15 +75,14 @@ public synchronized void addPropertyChangeListener(PropertyChangeListener listen public DatumEditor() { } - private void initGui() { + private synchronized void initGui() { initComponents(); installListeners(); initToolTips(); - unitsButton.setVisible(false); panel.setFocusable(true); } - private void maybeInitGui() { + private synchronized void maybeInitGui() { if (panel == null) { initGui(); } @@ -129,7 +126,6 @@ public void setColumns(int columns) { * @param value the Datum object to be editted. * @throws IllegalArgumentException if value is not a Datum */ - @Override public void setValue(Object value) { if (value instanceof Datum) { setDatum((Datum) value); @@ -158,17 +154,16 @@ private void setDatum(Datum datum) { } } - @Override public void setAsText(String text) throws IllegalArgumentException { try { setDatum(units.parse(text)); } catch (ParseException pe) { - IllegalArgumentException iae = new IllegalArgumentException(pe); + IllegalArgumentException iae = new IllegalArgumentException(pe.getMessage()); + iae.initCause(pe); throw iae; } } - @Override public Object getValue() { return getDatum(); } @@ -196,7 +191,6 @@ public Datum getDatum() { } } - @Override public String getAsText() { Datum v = getDatum(); if (v == null) { @@ -208,15 +202,13 @@ public String getAsText() { public void setUnits(Units units) { if (unitsButton != null) { -// if (units instanceof TimeLocationUnits) { + if (units instanceof TimeLocationUnits) { unitsButton.setVisible(false); -// } else { -// unitsButton.setVisible(true); -// String unitsStr= units.toString(); -// if ( unitsStr.length()>10 ) unitsStr= unitsStr.substring(0,9)+"..."; -// unitsButton.setText(unitsStr); -// unitsButton.setToolTipText(units.toString()); // don't abbreviate -// } + } else { + unitsButton.setVisible(true); + unitsButton.setText(units.toString()); + unitsButton.setToolTipText("units selection"); + } } this.units = units; } @@ -251,7 +243,6 @@ private class UniversalListener implements MouseListener, KeyListener, FocusList /** Listens for focus events on the DatumEditor and sets the editor * caret visible when focus is gained. */ - @Override public void focusGained(FocusEvent e) { editor.getCaret().setVisible(true); editor.getCaret().setSelectionVisible(true); @@ -260,7 +251,6 @@ public void focusGained(FocusEvent e) { /** Listens for focus events on the DatumEditor and sets the editor * caret invisible when focus is lost. */ - @Override public void focusLost(FocusEvent e) { editor.getCaret().setVisible(false); editor.getCaret().setSelectionVisible(false); @@ -269,7 +259,6 @@ public void focusLost(FocusEvent e) { /** All key events are forwarded to the editor. Except for keyPresses * for VK_ENTER */ - @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { fireActionPerformed(); @@ -278,12 +267,10 @@ public void keyPressed(KeyEvent e) { } } - @Override public void keyReleased(KeyEvent e) { forwardKeyEvent(e); } - @Override public void keyTyped(KeyEvent e) { forwardKeyEvent(e); } @@ -294,25 +281,20 @@ private void forwardKeyEvent(KeyEvent e) { } /** Request focus when sub-components are clicked */ - @Override public void mousePressed(MouseEvent e) { panel.requestFocusInWindow(); } /** Unused */ - @Override public void mouseEntered(MouseEvent e) { } - @Override public void mouseExited(MouseEvent e) { } - @Override public void mouseClicked(MouseEvent e) { } - @Override public void mouseReleased(MouseEvent e) { } } @@ -328,7 +310,6 @@ public String getToolTipText(MouseEvent event) { /** @see java.beans.PropertyEditor#supportsCustomEditor() * @return true */ - @Override public boolean supportsCustomEditor() { return true; } @@ -336,7 +317,6 @@ public boolean supportsCustomEditor() { /** @see java.beans.PropertyEditor#getCustomEditor() * @return this */ - @Override public Component getCustomEditor() { maybeInitGui(); return panel; @@ -347,7 +327,6 @@ public Component getCustomEditor() { * code. * @return The string "???" */ - @Override public String getJavaInitializationString() { return "???"; } @@ -357,27 +336,33 @@ public String getJavaInitializationString() { * values. * @return null */ - @Override public String[] getTags() { return null; } - @Override + /** @see java.beans.PropertyEditor#isPaintable() + * @return false + */ public boolean isPaintable() { return false; } - @Override + /** Does nothing. + * @param g + * @param r + */ public void paintValue(Graphics g, Rectangle r) { } - @Override + /* CellEditor stuff */ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { setValue(value); + maybeInitGui(); return panel; } - @Override + /** + */ public void addCellEditorListener(CellEditorListener l) { if (listeners == null) { listeners = new EventListenerList(); @@ -385,7 +370,8 @@ public void addCellEditorListener(CellEditorListener l) { listeners.add(CellEditorListener.class, l); } - @Override + /** + */ public void removeCellEditorListener(CellEditorListener l) { if (listeners != null) { listeners.remove(CellEditorListener.class, l); @@ -395,7 +381,6 @@ public void removeCellEditorListener(CellEditorListener l) { /** @see javax.swing.CellEditor#isCellEditable(java.util.EventObject) * @return true */ - @Override public boolean isCellEditable(EventObject anEvent) { return true; } @@ -403,7 +388,6 @@ public boolean isCellEditable(EventObject anEvent) { /** @see javax.swing.CellEditor#shouldSelectCell(java.util.EventObject) * @return true */ - @Override public boolean shouldSelectCell(EventObject anEvent) { return true; } @@ -411,12 +395,10 @@ public boolean shouldSelectCell(EventObject anEvent) { /** Returns the value stored in this editor. * @return the current value being edited */ - @Override public Object getCellEditorValue() { return getDatum(); } - @Override public boolean stopCellEditing() { if (getDatum() == null) { return false; @@ -425,14 +407,12 @@ public boolean stopCellEditing() { return true; } - @Override public void cancelCellEditing() { fireEditingCanceled(); } private ChangeEvent evt; private void fireEditingStopped() { - if ( listeners==null ) return; Object[] l = listeners.getListenerList(); for (int i = 0; i < l.length; i += 2) { if (l[i] == CellEditorListener.class) { @@ -446,7 +426,6 @@ private void fireEditingStopped() { } private void fireEditingCanceled() { - if ( listeners==null ) return; Object[] l = listeners.getListenerList(); for (int i = 0; i < l.length; i += 2) { if (l[i] == CellEditorListener.class) { diff --git a/dasCore/src/org/das2/components/DatumRangeEditor.java b/dasCore/src/org/das2/components/DatumRangeEditor.java index 985e02e27..0c2d8c0e0 100644 --- a/dasCore/src/org/das2/components/DatumRangeEditor.java +++ b/dasCore/src/org/das2/components/DatumRangeEditor.java @@ -36,10 +36,10 @@ import javax.swing.*; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import javax.swing.event.EventListenerList; import javax.swing.table.TableCellEditor; -import org.das2.DasApplication; -import org.das2.datum.UnitsUtil; /** @@ -60,7 +60,6 @@ public DatumRangeEditor() { initComponents(); installListeners(); initToolTips(); - unitsButton.setVisible(false); setFocusable(true); } @@ -110,28 +109,9 @@ public void setValue(Object value) { */ private void setDatumRange(DatumRange datumRange ) { DatumRange oldValue = value; -// if ( datumRange.min().gt( datumRange.max() ) ) { -// System.err.println("how did we get here!"); -// } value = datumRange; Units u= datumRange.getUnits(); - String text= datumRange.toString(); - if ( !UnitsUtil.isTimeLocation(units) ) { - if ( text.endsWith( u.toString() ) && u.toString().length()>7 ) { - int n= text.length(); - text= text.substring(0,n-u.toString().length()); - } - } -// System.err.println("thetext="+text); -// try { -// DatumRangeUtil.parseTimeRange(text); -// } catch ( ParseException ex ) { -// ex.printStackTrace();; -// } catch ( IllegalArgumentException ex ) { -// ex.printStackTrace();; -// datumRange.toString(); -// } - editor.setText( text ); + editor.setText( datumRange.toString() ); setUnits(u); if (oldValue != value && oldValue != null && !oldValue.equals(value)) { firePropertyChange("value", oldValue, value); @@ -142,7 +122,8 @@ public void setAsText(String text) throws IllegalArgumentException { try { setDatumRange( parseText( text ) ); } catch (ParseException pe) { - IllegalArgumentException iae = new IllegalArgumentException(pe); + IllegalArgumentException iae = new IllegalArgumentException(pe.getMessage()); + iae.initCause(pe); throw iae; } } @@ -160,37 +141,10 @@ private DatumRange parseText( String text ) throws ParseException { } return result; } - - /** - * prevent displaying same message so many times... - */ - private String lastErrorText= null; - private long lastErrorTime= 0; - - private void showErrorUsage( String text, String why ) { - if ( !DasApplication.getDefaultApplication().isHeadless() ) { - if ( text!=null && text.equals(lastErrorText) - && (System.currentTimeMillis()-lastErrorTime)<5000 ) { - return; - } - if ( why!=null ) { - JOptionPane.showMessageDialog( this, "Unable to accept \""+text+"\"
    "+why+"" ); - } else { - JOptionPane.showMessageDialog( this, "Unable to accept \""+text+"\"" ); - } - lastErrorText= text; - lastErrorTime= System.currentTimeMillis(); - } - } - - /** - * attempt to read the datum range in the TextField. If it is not - * parseable, then display an error message and return to the old value. - * @return - */ + public DatumRange getDatumRange() { - String text = editor.getText(); try { + String text = editor.getText(); DatumRange dr = parseText(text); if (!dr.equals(value)) { DatumRange oldValue = value; @@ -201,23 +155,6 @@ public DatumRange getDatumRange() { } catch (ParseException e) { if ( value!=null ) { setDatumRange( value ); // cause reformat of old Datum - if ( UnitsUtil.isTimeLocation(value.getUnits()) ) { - showErrorUsage( text, "String cannot be parsed to time range" ); - } else { - showErrorUsage( text, "String cannot be parsed to range with units \""+value.getUnits()+"\"" ); - } - return value; - } else { - return null; - } - } catch ( IllegalArgumentException e ) { - if ( value!=null ) { - setDatumRange( value ); // cause reformat of old Datum - if ( e.getMessage().contains("min > max") ) { - showErrorUsage( text, "Min cannot be greater than max" ); - } else { - showErrorUsage( text, e.getMessage() ); - } return value; } else { return null; @@ -226,7 +163,7 @@ public DatumRange getDatumRange() { } public String getAsText() { - //String text= editor.getText(); + String text; Object value = getDatumRange(); if (value == null) { return null; @@ -235,15 +172,13 @@ public String getAsText() { } public void setUnits(Units units) { -// if (units instanceof TimeLocationUnits) { + if (units instanceof TimeLocationUnits) { unitsButton.setVisible(false); -// } else { -// unitsButton.setVisible(true); -// String unitsStr= units.toString(); -// if ( unitsStr.length()>10 ) unitsStr= unitsStr.substring(0,9)+"..."; -// unitsButton.setText(unitsStr); -// unitsButton.setToolTipText(units.toString()); // don't abbreviate -// } + } else { + unitsButton.setVisible(true); + unitsButton.setText(units.toString()); + unitsButton.setToolTipText("units selection"); + } this.units = units; } diff --git a/dasCore/src/org/das2/components/HistogramSlicer.java b/dasCore/src/org/das2/components/HistogramSlicer.java new file mode 100644 index 000000000..20091658e --- /dev/null +++ b/dasCore/src/org/das2/components/HistogramSlicer.java @@ -0,0 +1,292 @@ +/* File: HistogramSlicer.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.components; + +import org.das2.graph.SymbolLineRenderer; +import org.das2.graph.DasColorBar; +import org.das2.graph.SpectrogramRenderer; +import org.das2.graph.DasColumn; +import org.das2.graph.Psym; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasRow; +import org.das2.graph.DasPlot; +import org.das2.graph.DasAxis; +import org.das2.dataset.DefaultVectorDataSet; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.Units; +import org.das2.datum.format.TimeDatumFormatter; +import org.das2.datum.TimeLocationUnits; +import org.das2.util.DasMath; +import org.das2.datum.Datum; +import org.das2.event.DataPointSelectionEvent; +import org.das2.event.DataPointSelectionListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; +import java.util.List; +import javax.swing.*; + + +public class HistogramSlicer extends DasPlot implements DataPointSelectionListener { + + private JDialog popupWindow; + private Datum xValue; + private SpectrogramRenderer renderer; + + private HistogramSlicer(SpectrogramRenderer parentRenderer, DasAxis xAxis, DasAxis yAxis) { + super(xAxis, yAxis); + renderer = parentRenderer; + SymbolLineRenderer symLineRenderer= new SymbolLineRenderer(); + symLineRenderer.setHistogram(true); + symLineRenderer.setLineWidth(1.0f); + symLineRenderer.setPsym(Psym.CROSS); + addRenderer(symLineRenderer); + } + + public static HistogramSlicer createSlicer(SpectrogramRenderer renderer) { + DasAxis sourceZAxis = renderer.getColorBar(); + DasAxis xAxis = new DasAxis(sourceZAxis.getDataMinimum(), sourceZAxis.getDataMaximum(), DasAxis.HORIZONTAL, sourceZAxis.isLog()); + DasAxis yAxis = new DasAxis(Datum.create(0.0), Datum.create(1.0), DasAxis.VERTICAL); + return new HistogramSlicer(renderer, xAxis, yAxis); + } + + public void showPopup() { + if (SwingUtilities.isEventDispatchThread()) { + showPopupImpl(); + } + else { + Runnable r = new Runnable() { + public void run() { + showPopupImpl(); + } + }; + } + } + + /** This method should ONLY be called by the AWT event thread */ + private void showPopupImpl() { + if (popupWindow == null) { + createPopup(); + } + popupWindow.setVisible(true); + } + + /** This method should ONLY be called by the AWT event thread */ + private void createPopup() { + int width = renderer.getParent().getCanvas().getWidth() / 2; + int height = renderer.getParent().getCanvas().getHeight() / 2; + DasCanvas canvas = new DasCanvas(width, height); + DasRow row = new DasRow(canvas, 0.1, 0.9); + DasColumn column = new DasColumn(canvas, 0.1, 0.9); + canvas.add(this, row, column); + + JPanel content = new JPanel(new BorderLayout()); + + JPanel buttonPanel = new JPanel(); + BoxLayout buttonLayout = new BoxLayout(buttonPanel, BoxLayout.X_AXIS); + JButton close = new JButton("Hide Window"); + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + popupWindow.setVisible(false); + } + }); + buttonPanel.setLayout(buttonLayout); + buttonPanel.add(Box.createHorizontalGlue()); + buttonPanel.add(close); + + content.add(canvas, BorderLayout.CENTER); + content.add(buttonPanel, BorderLayout.SOUTH); + + Window parentWindow = SwingUtilities.getWindowAncestor(renderer.getParent()); + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } + else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } + else { + popupWindow = new JDialog(); + } + popupWindow.setTitle("Histogram Slicer"); + popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + popupWindow.setContentPane(content); + popupWindow.pack(); + + Point parentLocation = new Point(); + SwingUtilities.convertPointToScreen(parentLocation, renderer.getParent().getCanvas()); + popupWindow.setLocation(parentLocation.x + renderer.getParent().getCanvas().getWidth(),parentLocation.y + height); + } + + public void dataPointSelected(DataPointSelectionEvent e) { + + DataSet ds = e.getDataSet(); + if (ds==null || !(ds instanceof TableDataSet)) { + return; + } + + Datum yValue = e.getY(); + xValue = e.getX(); + + TableDataSet tds = (TableDataSet)ds; + //TableDataSet tds = (TableDataSet)renderer.getDataSet(); + + int itable= TableUtil.tableIndexAt( tds, DataSetUtil.closestColumn( tds, e.getX() ) ); + VectorDataSet sliceDataSet= tds.getYSlice( TableUtil.closestRow( tds, itable, e.getY() ), itable ); + + DasColorBar cb = renderer.getColorBar(); + DasAxis xAxis = getXAxis(); + if (!xAxis.getUnits().equals(cb.getUnits())) { + xAxis.setUnits(cb.getUnits()); + xAxis.setDataRange(cb.getDataMinimum(), cb.getDataMaximum()); + xAxis.setLog(cb.isLog()); + } + + VectorDataSet hist = getHistogram(sliceDataSet); + + getRenderer(0).setDataSet(hist); + + DatumFormatter formatter; + if ( xValue.getUnits() instanceof TimeLocationUnits ) { + formatter= TimeDatumFormatter.DEFAULT; + } else { + formatter= xValue.getFormatter(); + } + + if (!(popupWindow == null || popupWindow.isVisible()) || getCanvas() == null) { + showPopup(); + } + else { + repaint(); + } + } + + /** This should handle non-log data too probably. */ + public VectorDataSet getHistogram(VectorDataSet vds) { + final int BINS_PER_DECADE = 8; + DasAxis zAxis = renderer.getColorBar(); + Units yUnits = zAxis.getUnits(); + double min = getXAxis().getDataMinimum(yUnits); + double max = getXAxis().getDataMaximum(yUnits); + int sampleCount = vds.getXLength(); + if (zAxis.isLog()) { + double minLog = Math.floor(DasMath.log10(min)); + double maxLog = Math.ceil(DasMath.log10(max)); + int binCount = (int)(maxLog - minLog) * BINS_PER_DECADE; //4 bins per decade + double[] bins = new double[binCount]; + for (int i = 0; i < vds.getXLength(); i++) { + double y = vds.getDouble(i, yUnits); + if (yUnits.isFill(y) || Double.isNaN(y)) { + sampleCount--; + continue; + } + double yLog = DasMath.log10(y); + if (yLog < minLog) { + double newMinLog = Math.floor(yLog); + int binCountDelta = ((int)Math.floor(minLog - newMinLog)) * BINS_PER_DECADE; + binCount += binCountDelta; + double[] newBins = new double[binCount]; + System.arraycopy(bins, 0, newBins, binCountDelta, bins.length); + minLog = newMinLog; + bins = newBins; + } + else if (yLog >= maxLog) { + double newMaxLog = Math.ceil(yLog+0.001); + int binCountDelta = (int)(newMaxLog - maxLog) * BINS_PER_DECADE; + binCount += binCountDelta; + double[] newBins = new double[binCount]; + System.arraycopy(bins, 0, newBins, 0, bins.length); + maxLog = newMaxLog; + bins = newBins; + } + int index = (int)Math.floor((yLog - minLog) * (double)BINS_PER_DECADE); + if (index >= 0 && index < bins.length) { + bins[index] += 1.0; + } + } + double[] x = new double[binCount]; + for (int index = 0; index < binCount; index++) { + x[index] = DasMath.exp10(minLog + (index / (double)BINS_PER_DECADE)); + bins[index] = bins[index] / (double)sampleCount; + } + return new DefaultVectorDataSet(x, yUnits, bins, Units.dimensionless, Collections.EMPTY_MAP); + } + else { + //Should add logic that determine bin size instead of just using 1.0 + min = Math.floor(min); + max = Math.ceil(max); + int binCount = (int)(max - min); + double[] bins = new double[binCount]; + for (int i = 0; i < vds.getXLength(); i++) { + double y = vds.getDouble(i, yUnits); + if (yUnits.isFill(y) || Double.isNaN(y)) { + sampleCount--; + continue; + } + if (y < min) { + double newMin = Math.floor(y); + int binCountDelta = (int)(min - newMin); + binCount += binCountDelta; + double[] newBins = new double[binCount]; + System.arraycopy(bins, 0, newBins, binCountDelta, bins.length); + min = newMin; + bins = newBins; + } + else if (y >= max) { + double newMax = Math.ceil(y+0.001); + int binCountDelta = (int)(newMax - max); + binCount += binCountDelta; + double[] newBins = new double[binCount]; + System.arraycopy(bins, 0, newBins, 0, bins.length); + max = newMax; + bins = newBins; + } + int index = (int)Math.floor(y - min); + if (index >= 0 && index < bins.length) { + bins[index] += 1.0; + } + } + double[] x = new double[binCount]; + for (int index = 0; index < binCount; index++) { + x[index] = min + (double)index + 0.5; + bins[index] = bins[index] / (double)sampleCount; + } + return new DefaultVectorDataSet(x, yUnits, bins, Units.dimensionless, Collections.EMPTY_MAP); + } + } + + protected void uninstallComponent() { + super.uninstallComponent(); + } + + protected void installComponent() { + super.installComponent(); + } + +} diff --git a/dasCore/src/org/das2/components/HorizontalSpectrogramSlicer.java b/dasCore/src/org/das2/components/HorizontalSpectrogramSlicer.java index 1d0d22f06..68467f576 100755 --- a/dasCore/src/org/das2/components/HorizontalSpectrogramSlicer.java +++ b/dasCore/src/org/das2/components/HorizontalSpectrogramSlicer.java @@ -23,15 +23,6 @@ package org.das2.components; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dialog; -import java.awt.Frame; -import java.awt.Graphics; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.Point; -import java.awt.Window; import org.das2.graph.SymbolLineRenderer; import org.das2.graph.DasColumn; import org.das2.graph.DasCanvas; @@ -39,131 +30,42 @@ import org.das2.graph.DasPlot; import org.das2.graph.DasAxis; import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.VectorDataSet; import org.das2.datum.format.DatumFormatter; import org.das2.datum.format.TimeDatumFormatter; import org.das2.datum.TimeLocationUnits; -import org.das2.system.DasLogger; import org.das2.datum.Datum; import org.das2.event.DataPointSelectionEvent; import org.das2.event.DataPointSelectionListener; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; -import org.das2.components.propertyeditor.PropertyEditor; -import org.das2.datum.InconvertibleUnitsException; -import org.das2.datum.Units; -import org.das2.event.MouseModule; -import org.das2.event.PointSlopeDragRenderer; -import org.das2.graph.Renderer; -import org.das2.graph.SpectrogramRenderer; -import org.das2.util.monitor.ProgressMonitor; -import org.virbo.dataset.ArrayDataSet; -import org.virbo.dataset.DataSetOps; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.IDataSet; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dsutil.DataSetBuilder; +import javax.swing.*; -public class HorizontalSpectrogramSlicer implements DataPointSelectionListener { +public class HorizontalSpectrogramSlicer extends DasPlot implements DataPointSelectionListener { private JDialog popupWindow; - private final DasPlot parentPlot; - private DasPlot myPlot; - private final DasAxis sourceZAxis; - private final DasAxis sourceXAxis; - - protected Datum xValue; - protected Datum yValue; - - protected Datum ySlice; - - JPanel buttonPanel; - Action additionalAction= null; - - //private long eventBirthMilli; + private Datum xValue; private SymbolLineRenderer renderer; - private Color markColor = new Color(230,230,230); + private DasPlot parentPlot; - private HorizontalSpectrogramSlicer(DasPlot parent, DasAxis sourceXAxis, DasAxis sourceZAxis) { - this.sourceZAxis= sourceZAxis; - this.sourceXAxis= sourceXAxis; - this.parentPlot= parent; - } - - private void initPlot() { - DasAxis xAxis= sourceXAxis.createAttachedAxis( DasAxis.HORIZONTAL ); - DasAxis yAxis = sourceZAxis.createAttachedAxis(DasAxis.VERTICAL); - myPlot= new DasPlot( xAxis, yAxis); + private HorizontalSpectrogramSlicer(DasPlot plot, DasAxis xAxis, DasAxis yAxis) { + super(xAxis, yAxis); + parentPlot = plot; renderer= new SymbolLineRenderer(); - renderer.setAntiAliased(true); - myPlot.addRenderer(renderer); - myPlot.addRenderer( new Renderer() { - @Override - public void render(Graphics g, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { - int ix= (int)myPlot.getXAxis().transform(xValue); - DasRow row= myPlot.getRow(); - int iy0= (int)row.getDMinimum(); - int iy1= (int)row.getDMaximum(); - g.drawLine(ix+3,iy0,ix,iy0+3); - g.drawLine(ix-3,iy0,ix,iy0+3); - g.drawLine(ix+3,iy1,ix,iy1-3); - g.drawLine(ix-3,iy1,ix,iy1-3); - - g.setColor(markColor); - g.drawLine( ix, iy0+4, ix, iy1-4 ); - } - } ); - myPlot.getDasMouseInputAdapter().addMouseModule(new MouseModule(myPlot, new PointSlopeDragRenderer(myPlot, myPlot.getXAxis(), myPlot.getYAxis()), "Slope")); - - } - - /** - * add a button - * @param a the action for the button. - */ - public void addAction( Action a ) { - additionalAction= a; - if ( buttonPanel!=null ) { - JButton b= new JButton(a); - buttonPanel.add(b,0); - } - } - - protected void setDataSet( QDataSet ds ) { - renderer.setDataSet(ds); - } - - /** - * provide access to the dataset - * @return the dataset. - */ - public QDataSet getDataSet() { - return renderer.getDataSet(); + addRenderer(renderer); } - /** - * provide the Y position of the data. Note this may be different - * than where the user requested because the nearest channel is provided. - * @return the slice position. - */ - public Datum getSliceY() { - return ySlice; - } - - public static HorizontalSpectrogramSlicer createSlicer( DasPlot plot, TableDataSetConsumer dataSetConsumer) { + public static HorizontalSpectrogramSlicer createSlicer(DasPlot plot, TableDataSetConsumer dataSetConsumer) { DasAxis sourceXAxis = plot.getXAxis(); - DasAxis sourceZAxis = dataSetConsumer.getZAxis(); - return new HorizontalSpectrogramSlicer(plot, sourceXAxis, sourceZAxis ); + DasAxis xAxis = sourceXAxis.createAttachedAxis(DasAxis.HORIZONTAL); + DasAxis yAxis = dataSetConsumer.getZAxis().createAttachedAxis(DasAxis.VERTICAL); + return new HorizontalSpectrogramSlicer(plot, xAxis, yAxis); } public void showPopup() { @@ -172,12 +74,10 @@ public void showPopup() { } else { Runnable r = new Runnable() { - @Override public void run() { showPopupImpl(); } }; - SwingUtilities.invokeLater(r); } } @@ -189,282 +89,110 @@ private void showPopupImpl() { popupWindow.setVisible(true); } - /** - * dispose of the popup slicer. - */ - public void dispose() { - if ( popupWindow!=null ) { - popupWindow.setVisible(false); - popupWindow.dispose(); - } - } - - /** - * clear the current dataset to avoid units errors. If the - * new dataset can be used, use it. - * @param tds the new dataset - */ - public void clear( QDataSet tds ) { - if ( renderer!=null ) { - if ( tds==null ) { - this.renderer.setDataSet(null); - } else { - try { - if ( this.isPopupVisible() ) { - showSlice( tds, xValue, yValue ); - } - } catch ( InconvertibleUnitsException ex ) { - this.renderer.setDataSet(null); - } - } - } - } - /** This method should ONLY be called by the AWT event thread */ private void createPopup() { - if ( myPlot==null ) { - initPlot(); - } int width = parentPlot.getCanvas().getWidth() / 2; int height = parentPlot.getCanvas().getHeight() / 2; - final DasCanvas canvas = new DasCanvas(width, height); - DasRow row = new DasRow(canvas, null, 0, 1.0, 3, -5, 0, 0 ); - DasColumn column = new DasColumn(canvas, null, 0, 1.0, 7, -3, 0, 0 ); - canvas.add( myPlot, row, column); + DasCanvas canvas = new DasCanvas(width, height); + DasRow row = new DasRow(canvas, 0.1, 0.9); + DasColumn column = new DasColumn(canvas, 0.1, 0.9); + canvas.add(this, row, column); JPanel content = new JPanel(new BorderLayout()); - buttonPanel = new JPanel(); + JPanel buttonPanel = new JPanel(); BoxLayout buttonLayout = new BoxLayout(buttonPanel, BoxLayout.X_AXIS); - buttonPanel.setLayout(buttonLayout); - - if ( additionalAction!=null ) { - buttonPanel.add( new JButton(additionalAction) ); - } - - buttonPanel.add(Box.createHorizontalGlue()); - - JButton printButton= new JButton( new AbstractAction("Print...") { - @Override - public void actionPerformed( ActionEvent e ) { - canvas.makeCurrent(); - DasCanvas.PRINT_ACTION.actionPerformed(e); - } - }); - buttonPanel.add( printButton ); - - JButton settingsButton= new JButton( new AbstractAction("Settings...") { - @Override - public void actionPerformed( ActionEvent e ) { - SpectrogramRenderer rend=null; - for ( Renderer r : parentPlot.getRenderers() ) { - if ( r instanceof SpectrogramRenderer ) { - rend= (SpectrogramRenderer) r; - break; - } - } - if ( rend==null ) { - JOptionPane.showMessageDialog( null, "Unable to find associated Spectrogram" ); - return; - } - SliceSettings settings= new SliceSettings(); - settings.setSliceRebinnedData( rend.isSliceRebinnedData() ); - new PropertyEditor(settings).showModalDialog(canvas); - rend.setSliceRebinnedData(settings.isSliceRebinnedData()); - - QDataSet ds= rend.getConsumedDataSet(); - showSlice( ds, xValue, yValue ); - - } - }); - buttonPanel.add( settingsButton ); - JButton close = new JButton("Hide Window"); close.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { popupWindow.setVisible(false); } }); - + buttonPanel.setLayout(buttonLayout); + buttonPanel.add(Box.createHorizontalGlue()); buttonPanel.add(close); content.add(canvas, BorderLayout.CENTER); content.add(buttonPanel, BorderLayout.SOUTH); Window parentWindow = SwingUtilities.getWindowAncestor(parentPlot); - popupWindow = new JDialog(parentWindow); + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } + else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } + else { + popupWindow = new JDialog(); + } popupWindow.setTitle("Horizontal Slicer"); popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); popupWindow.setContentPane(content); popupWindow.pack(); - Point parentLocation = new Point( 0, parentPlot.getY() ); - parentLocation.translate( parentPlot.getX()/20, -1 * myPlot.getRow().getDMinimum() ); + Point parentLocation = new Point(); SwingUtilities.convertPointToScreen(parentLocation, parentPlot.getCanvas()); - - //make sure some portion of the slice window is visible on the screen. - int xx= parentLocation.x + parentPlot.getCanvas().getWidth(); - int yy= parentLocation.y; - - GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); - int totalwidth = gd.getDisplayMode().getWidth(); - int totalheight = gd.getDisplayMode().getHeight(); - - if ( xx>totalwidth-100 ) { - xx= totalwidth-100; - } - if ( yy>totalheight-100 ) { - yy= totalheight-100; - } - popupWindow.setLocation(xx,yy); - + popupWindow.setLocation(parentLocation.x + parentPlot.getCanvas().getWidth(),parentLocation.y + height); } - protected boolean isPopupVisible() { - return ( popupWindow != null && popupWindow.isVisible()) && myPlot.getCanvas() != null; - } - public void dataPointSelected(DataPointSelectionEvent e) { - long xxx[]= { 0,0,0,0 }; - xxx[0] = System.currentTimeMillis()-e.birthMilli; - yValue = e.getY(); - xValue = e.getX(); - - QDataSet ds = e.getDataSet(); - if (ds==null || ! SemanticOps.isTableDataSet(ds) ) + DataSet ds = e.getDataSet(); + if (ds==null || !(ds instanceof TableDataSet)) { return; - - QDataSet tds = (QDataSet)ds; - - showSlice( tds, xValue, yValue ); - - } - - private boolean showSlice( QDataSet tds, Datum xValue, Datum yValue ) { - QDataSet tds1 = null; - if (tds.rank() == 3) { - // slice to get the correct table; - for (int i = 0; i < tds.length(); i++) { - QDataSet bounds = DataSetOps.dependBounds(tds.slice(i)); - if (DataSetOps.boundsContains(bounds, xValue, yValue)) { - tds1 = tds.slice(i); - break; - } - } - } else { - QDataSet bounds = DataSetOps.dependBounds(tds); - if (DataSetOps.boundsContains(bounds, xValue, yValue)) { - tds1 = tds; - } - } - if (tds1 == null) { - return false; } - - QDataSet yds= SemanticOps.ytagsDataSet(tds1); - int iy; - Datum yy; - QDataSet sliceDataSet; + Datum yValue = e.getY(); + xValue = e.getX(); + + TableDataSet tds = (TableDataSet)ds; + + int itable= TableUtil.tableIndexAt( tds, DataSetUtil.closestColumn( tds, e.getX() ) ); + VectorDataSet sliceDataSet= tds.getYSlice( TableUtil.closestRow( tds, itable, e.getY() ), itable ); - if ( yds.rank()==2 ) { - QDataSet xds= SemanticOps.xtagsDataSet(tds1); - int ix= org.virbo.dataset.DataSetUtil.closestIndex( xds, xValue ); - QDataSet yds1= yds.slice(ix); - iy= org.virbo.dataset.DataSetUtil.closestIndex( yds1, yValue ); - yy= DataSetUtil.asDatum(yds.slice(ix).slice(iy)); - IDataSet eqdep1= IDataSet.createRank1(yds.length()); - eqdep1.putValue(ix,1); - - DataSetBuilder bz= new DataSetBuilder(1,yds.length()); - bz.putProperty( QDataSet.UNITS, tds1.property(QDataSet.UNITS ) ); - DataSetBuilder bx= new DataSetBuilder(1,yds.length()); - - int lastIndex= iy; - - //int st1=0, st2=0, st3=0; - - for ( int i=0; i0 ) title= title+"!c"; - myPlot.setTitle( title + "x: " + formatter.format(xValue) + " y: " + yformatter.format(yy) ); - ySlice= yy; - if ( !myPlot.getXAxis().getLabel().equals( sourceXAxis.getLabel() ) ) { - myPlot.getXAxis().setLabel( sourceXAxis.getLabel() ); + setTitle("x: "+ formatter.format(xValue) + " y: "+yValue); + + if (!(popupWindow == null || popupWindow.isVisible()) || getCanvas() == null) { + showPopup(); } - if ( !myPlot.getYAxis().getLabel().equals( sourceZAxis.getLabel() ) ) { - myPlot.getYAxis().setLabel( sourceZAxis.getLabel() ); - } - //eventBirthMilli= e.birthMilli; - return true; } - - /** - * the color for the mark (vertical bar) indicating slice position - * @return the mark color - */ - public Color getMarkColor() { - return markColor; + + public void drawContent(Graphics2D g) { + super.drawContent(g); + int ix= (int)this.getXAxis().transform(xValue); + DasRow row= this.getRow(); + int iy0= row.getDMinimum(); + int iy1= row.getDMaximum(); + g.drawLine(ix+3,iy0,ix,iy0+3); + g.drawLine(ix-3,iy0,ix,iy0+3); + g.drawLine(ix+3,iy1,ix,iy1-3); + g.drawLine(ix-3,iy1,ix,iy1-3); + + g.setColor( new Color(230,230,230) ); + g.drawLine( ix, iy0+4, ix, iy1-4 ); } - - /** - * set the color for the mark (vertical bar) indicating slice position - * Color(230,230,230) is the default. - * @param markColor the color - */ - public void setMarkColor(Color markColor) { - this.markColor = markColor; + + protected void uninstallComponent() { + super.uninstallComponent(); + } + + protected void installComponent() { + super.installComponent(); + } + + protected void processDasUpdateEvent(org.das2.event.DasUpdateEvent e) { + if (isDisplayable()) { + updateImmediately(); + resize(); + } } } diff --git a/dasCore/src/org/das2/components/SliceSettings.java b/dasCore/src/org/das2/components/SliceSettings.java deleted file mode 100644 index 1fed651ed..000000000 --- a/dasCore/src/org/das2/components/SliceSettings.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.components; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; - -/** - * - * @author jbf - */ -public class SliceSettings { - - private boolean sliceRebinnedData = true; - public static final String PROP_SLICEREBINNEDDATA = "sliceRebinnedData"; - - public boolean isSliceRebinnedData() { - return sliceRebinnedData; - } - - public void setSliceRebinnedData(boolean sliceRebinnedData) { - boolean oldSliceRebinnedData = this.sliceRebinnedData; - this.sliceRebinnedData = sliceRebinnedData; - propertyChangeSupport.firePropertyChange(PROP_SLICEREBINNEDDATA, oldSliceRebinnedData, sliceRebinnedData); - } - - - - private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); - - public void addPropertyChangeListener(PropertyChangeListener listener) { - propertyChangeSupport.addPropertyChangeListener(listener); - } - - public void removePropertyChangeListener(PropertyChangeListener listener) { - propertyChangeSupport.removePropertyChangeListener(listener); - } - -} diff --git a/dasCore/src/org/das2/components/TearoffTabbedPane.java b/dasCore/src/org/das2/components/TearoffTabbedPane.java index ef78faf4b..86226e6cd 100644 --- a/dasCore/src/org/das2/components/TearoffTabbedPane.java +++ b/dasCore/src/org/das2/components/TearoffTabbedPane.java @@ -9,30 +9,24 @@ package org.das2.components; import java.awt.BorderLayout; -import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; -import java.awt.Frame; -import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.Point; -import java.awt.RenderingHints; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowStateListener; import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.logging.Level; +import java.util.HashMap; +import java.util.Iterator; import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.ActionMap; @@ -46,14 +40,9 @@ import javax.swing.JPopupMenu; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import org.das2.util.LoggerManager; -import test.components.TearoffTabbedPaneDemo; /** - * Like the Swing TabbedPane, but this allows the tabs to be - * removed to other windows or other TearoffTabbedPanes. + * * @author Jeremy */ public class TearoffTabbedPane extends JTabbedPane { @@ -65,24 +54,16 @@ public class TearoffTabbedPane extends JTabbedPane { JPopupMenu tearOffMenu = new JPopupMenu(); JPopupMenu dockMenu = new JPopupMenu(); - private TearoffTabbedPane parentPane; // non-null for babysitter panes. + private TearoffTabbedPane parentPane; private TearoffTabbedPane rightPane = null; - private TearoffTabbedPane dropDirty = null; - private JFrame rightFrame = null; private ComponentListener rightFrameListener; private int rightOffset= 0; - - - private final static Logger logger= Logger.getLogger( "das2.gui" ); - /** - * size of top region that accepts drop - */ - private static int TOP_DROP_MARGIN=200; + private final static Logger logger= Logger.getLogger( TearoffTabbedPane.class.getCanonicalName() ); - LinkedHashMap tabs = new LinkedHashMap(); + HashMap tabs = new HashMap(); int lastSelected; /* keep track of selected index before context menu */ private static void copyInputMap(JFrame parent, JFrame babySitter) { @@ -112,58 +93,28 @@ private static void copyInputMap(JFrame parent, JFrame babySitter) { } babySitterC.setActionMap(am); } - - private boolean dropDecorate; - /** - * paint decorations to indicate this will accept a drop. - * @param b - */ - private void setDropDecorate(boolean b) { - this.dropDecorate= b; + public void slideRight(Component tab) { + throw new UnsupportedOperationException("Not yet implemented"); } - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); //To change body of generated methods, choose Tools | Templates. - if ( dropDecorate ) { - Graphics2D g2= (Graphics2D)g; - int h= g.getFontMetrics().getHeight(); - g2.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON ); - Color c= this.getBackground(); - c= new Color( c.getRed(), c.getGreen(), c.getBlue(), 220 ); - g2.setColor( c ); - g2.fill( g.getClip() ); - g2.setColor( this.getForeground() ); - g2.drawString( "(dock)", h*3, h ); - } - } - - private static class TabDesc { + class TabDesc { Icon icon; String title; String tip; int index; - /** This is the JFrame that contains the child, another TearOffTabbedPane, or null. **/ Container babysitter; + Component component; - TabDesc(String title, Icon icon, String tip, int index) { + TabDesc(String title, Icon icon, Component component, String tip, int index) { this.title = title; this.icon = icon; + this.component = component; this.tip = tip; this.index = index; this.babysitter = null; } - - @Override - public String toString() { - if ( this.babysitter==null ) { - return this.title + "@"+ this.index + ": (docked)"; - } else { - return this.title + "@"+ this.index + ": "+ this.babysitter.getName(); - } - } } public TearoffTabbedPane() { @@ -173,42 +124,13 @@ public TearoffTabbedPane() { private TearoffTabbedPane(TearoffTabbedPane parent) { super(); if (parent == null) { - //TODO: need a way to remove mouse adapter when parent isn't JFrame - MouseAdapter ma = new ParentMouseAdapter(); + MouseAdapter ma = getParentMouseAdapter(); addMouseListener(ma); addMouseMotionListener(getMouseMotionListener()); } else { parentPane = parent; addMouseListener(getChildMouseAdapter()); - addMouseMotionListener(getChildMouseMotionListener()); } - super.addChangeListener( new ChangeListener() { - public void stateChanged(ChangeEvent e) { - try { - LoggerManager.logGuiEvent(e); - } catch ( Exception ex ) { - - } - } - }); - } - - /** - * I needed a way to hide the mouseAdapter, since we can't do this automatically. - */ - public void hideMouseAdapter() { - // https://sourceforge.net/tracker/?func=detail&aid=3377337&group_id=199733&atid=970682 - MouseListener[] mls= getMouseListeners(); - if ( mls.length>0 ) { - MouseListener ml= mls[mls.length-1]; - removeMouseListener( ml ); - } - MouseMotionListener[] mmls= getMouseMotionListeners(); - if ( getMouseMotionListeners().length>0 ) { - MouseMotionListener ml= mmls[mmls.length-1]; - removeMouseMotionListener( ml ); - } - } private MouseMotionListener getMouseMotionListener() { @@ -222,9 +144,7 @@ public void mouseDragged(MouseEvent e) { dragStart = e.getPoint(); } else { if (dragStart.distance(e.getPoint()) > 10) { - if (draggingFrame == null ) { - setSelectedIndex(selectedTab); - getComponentAt(selectedTab).setVisible(true); + if (draggingFrame == null) { dragOffset= getComponentAt(selectedTab).getLocationOnScreen(); Point ds= new Point(dragStart); SwingUtilities.convertPointToScreen(ds, e.getComponent() ); @@ -244,30 +164,12 @@ public void mouseDragged(MouseEvent e) { SwingUtilities.convertPointToScreen(p, (Component) e.getSource()); p.translate(dragOffset.x, dragOffset.y); draggingFrame.setLocation(p); - - TearoffTabbedPane drop= getHoverTP( e.getComponent(), e.getPoint() ); - - if ( dropDirty!=null ) { // give some hint that this is a drop target. - dropDirty.setDropDecorate(false); - dropDirty.repaint(); - } - - if ( drop!=null ) { - drop.setDropDecorate(true); - drop.repaint(); - dropDirty= drop; - } else { - dropDirty= null; - } - } } } public void mouseMoved(MouseEvent e) { } - - }; } @@ -289,10 +191,17 @@ private MouseAdapter getChildMouseAdapter() { Component selectedComponent; + { - dockMenu.add(new JMenuItem(new AbstractAction("return undocked tab") { + dockMenu.add(new JMenuItem(new AbstractAction("dock") { public void actionPerformed(ActionEvent event) { + TabDesc desc = null; + + for (Iterator i = tabs.keySet().iterator(); i.hasNext();) { + Component key = (Component) i.next(); + TabDesc d = (TabDesc) tabs.get(key); + } if (parentPane != null) { selectedComponent = getComponent(selectedTab); @@ -300,18 +209,18 @@ public void actionPerformed(ActionEvent event) { parentPane.dock(selectedComponent); if ( getTabCount()==0 ) { SwingUtilities.getWindowAncestor(TearoffTabbedPane.this).dispose(); - } else { - TearoffTabbedPane.this.resetTearOffBabysitterName(); - } + } } else { - throw new IllegalArgumentException("parentPane must not be null"); //findbugs pointed this out + if (desc.babysitter instanceof Window) { + ((Window) desc.babysitter).dispose(); + } + parentPane.dock(selectedComponent); } } })); } - @Override public void mousePressed(MouseEvent event) { selectedTab = TearoffTabbedPane.this.indexAtLocation(event.getX(), event.getY()); if (event.isPopupTrigger()) { @@ -321,24 +230,6 @@ public void mousePressed(MouseEvent event) { @Override public void mouseReleased(MouseEvent e) { - TearoffTabbedPane draggingTearOff; - if ( draggingFrame!=null ) { - draggingTearOff= getTabbedPane(draggingFrame); - if ( draggingTearOff!=null && TearoffTabbedPane.this.parentPane.contains( SwingUtilities.convertPoint( e.getComponent(), e.getPoint(), TearoffTabbedPane.this.parentPane ) ) ) { - TearoffTabbedPane.this.parentPane.dock(draggingTearOff.getComponentAt(0)); - TearoffTabbedPane.this.parentPane.setDropDecorate(false); - draggingFrame.dispose(); - } else { - if ( draggingTearOff!=null ) { - draggingTearOff.resetTearOffBabysitterName(); - } - } - TearoffTabbedPane oldChildParent= getTabbedPane(e.getComponent()); - if ( oldChildParent.getTabCount()==0 ) { - SwingUtilities.getWindowAncestor(e.getComponent()).dispose(); - } - - } if (dragStart != null && selectedTab != -1) { //JFrame f= TearoffTabbedPane.this.tearOffIntoFrame( selectedTab ); //Point p= e.getPoint(); @@ -356,208 +247,51 @@ public void mouseReleased(MouseEvent e) { } - private MouseMotionListener getChildMouseMotionListener() { - return new MouseMotionListener() { - - public void mouseDragged(MouseEvent e) { - if (selectedTab == -1) { - return; - } - if (dragStart == null) { - dragStart = e.getPoint(); - } else { - if (dragStart.distance(e.getPoint()) > 10) { - if (draggingFrame == null) { - setSelectedIndex(selectedTab); - getComponentAt(selectedTab).setVisible(true); - dragOffset= getComponentAt(selectedTab).getLocationOnScreen(); - Point ds= new Point(dragStart); - SwingUtilities.convertPointToScreen(ds, e.getComponent() ); - int tabAndWindowHeight=40; // ubuntu, TODO: calculate - dragOffset.translate( -ds.x, -ds.y - tabAndWindowHeight ); - final Component c = getComponentAt(selectedTab); - draggingFrame = TearoffTabbedPane.this.tearOffIntoFrame(selectedTab); - TearoffTabbedPane carry= getTabbedPane(draggingFrame); - carry.resetTearOffBabysitterName(); - carry.parentPane= TearoffTabbedPane.this.parentPane; - - TabDesc tabDesc= carry.parentPane.getTabDescByComponent(c); - tabDesc.babysitter= carry; - //TearoffTabbedPane.super.removeTabAt(selectedTab); - removeTabAt(selectedTab,false); - if (draggingFrame == null) { - return; - } - setCursor(new Cursor(Cursor.MOVE_CURSOR)); - if ( draggingFrame.getWidth()< -1*dragOffset.x ) { - int borderWidth=5; - dragOffset.x= -1*(draggingFrame.getWidth()-borderWidth); - } - } - Point p = e.getPoint(); - SwingUtilities.convertPointToScreen(p, (Component) e.getSource()); - p.translate(dragOffset.x, dragOffset.y); - draggingFrame.setLocation(p); - - TearoffTabbedPane drop=null; - Point o1= SwingUtilities.convertPoint( e.getComponent(), e.getPoint(), TearoffTabbedPane.this.parentPane ); - if ( TearoffTabbedPane.this.parentPane.contains( o1 ) - && Math.abs( o1.getY() - TearoffTabbedPane.this.parentPane.getY() )< TOP_DROP_MARGIN ) { - drop= TearoffTabbedPane.this.parentPane; - } - - if ( dropDirty!=null ) { // give some hint that this is a drop target. - dropDirty.setDropDecorate(false); - dropDirty.repaint(); - } - - if ( drop!=null ) { - drop.setDropDecorate(true); - drop.repaint(); - dropDirty= drop; - } else { - dropDirty= null; - } - - } - } - resetTearOffBabysitterName(); - } - - public void mouseMoved(MouseEvent e) { - } - - - }; - } - - /** - * show all the tabs descriptions - */ - public void peek() { - System.err.println("--"); - for ( Component key: tabs.keySet() ) { - TabDesc d = (TabDesc) tabs.get(key); - System.err.println(d); - } - } - - private void showIt() { - - TabDesc desc = null; - Component babyComponent = null; - for ( Component key: tabs.keySet() ) { - TabDesc d = (TabDesc) tabs.get(key); - if (d.index == selectedTab) { - desc = d; - babyComponent = key; - break; - } - } - if (desc==null) return; - if (desc.babysitter instanceof Window) { - Window babySitter = (Window) desc.babysitter; - raiseApplicationWindow(babySitter); - getTabbedPane(babyComponent).setSelectedComponent(babyComponent); - } else if ( desc.babysitter instanceof TearoffTabbedPane ) { - Window parent= SwingUtilities.getWindowAncestor(babyComponent); - ((TearoffTabbedPane)desc.babysitter).setSelectedComponent(babyComponent); - raiseApplicationWindow(parent); - } - } - - /** - * raise the application window - * http://stackoverflow.com/questions/309023/howto-bring-a-java-window-to-the-front - */ - private static void raiseApplicationWindow( java.awt.Window window ) { - window.setVisible(true); - if ( window instanceof Frame ) { - Frame frame= (Frame)window; - int state = frame.getExtendedState(); - state &= ~JFrame.ICONIFIED; - frame.setExtendedState(state); - } - window.setAlwaysOnTop(true); // security exception - window.toFront(); - window.requestFocus(); - window.setAlwaysOnTop(false); // security exception //ubuntu 10.04 this returns to the bottom - //window.setVisible(false); - //window.setVisible(true); - } - - /** - * tearoff into another tabbed pane, or create one if target is null. - * @param target - * @param selectedTab - */ - private void tearoffIntoTearoffTabbedPane( TearoffTabbedPane target, int selectedTab ) { - if (target != null) { - TabDesc desc = getTabDesc(selectedTab); - Component selectedComponent = getComponentAt(desc.index); - //System.err.println("name="+selectedComponent.getName()+" hash="+selectedComponent.hashCode()); // see TearoffTabbedPaneDemo which sets name and indicates hashcode. - TearoffTabbedPane.this.tearOff(selectedTab, target); - target.add(desc.title, selectedComponent); - target.setSelectedIndex(target.getTabCount() - 1); - target.resetTearOffBabysitterName(); - Window w = SwingUtilities.getWindowAncestor(target); - if (!target.isShowing()) { - //w.setVisible(false); - w.setVisible(true); - } - raiseApplicationWindow(w); - } else { - TearoffTabbedPane.this.tearOffIntoFrame(selectedTab); - } - } - - private TabDesc getTabDesc( int tabNumber ) { - for ( TabDesc td: tabs.values() ) { - if ( td.index==tabNumber ) { - return td; - } - } - throw new IllegalArgumentException("no tab at index: "+tabNumber); - } - - private TearoffTabbedPane getHoverTP( Component myFrame, Point myPosition ) { - TearoffTabbedPane last=null; - TearoffTabbedPane me= getTabbedPane( draggingFrame ); - - for ( Component key: tabs.keySet() ) { - TabDesc d = (TabDesc) tabs.get(key); - if ( d.babysitter!=null ) { - Component maybe= getTabbedPane(d.babysitter); - if ( maybe!=null && maybe!=me ) { - Point p= SwingUtilities.convertPoint( myFrame, myPosition, maybe ); - if ( maybe.getBounds().contains(p) && ( p.getY() - maybe.getY() ) < TOP_DROP_MARGIN ) { - last= (TearoffTabbedPane)maybe; - } - } - } - } - return last; - } - - private class ParentMouseAdapter extends MouseAdapter { + private MouseAdapter getParentMouseAdapter() { + return new MouseAdapter() { - private ParentMouseAdapter() { + { tearOffMenu.add(new JMenuItem(new AbstractAction("undock") { + public void actionPerformed(ActionEvent event) { TearoffTabbedPane.this.tearOffIntoFrame(selectedTab); } })); - tearOffMenu.add(new JMenuItem(new AbstractAction("slide right") { + public void actionPerformed(ActionEvent event) { TearoffTabbedPane.this.slideRight(selectedTab); } })); + } + Component selectedComponent; + + { dockMenu.add(new JMenuItem(new AbstractAction("show") { - public void actionPerformed(ActionEvent event) { - showIt(); + public void actionPerformed(ActionEvent event) { + TabDesc desc = null; + Component babyComponent = null; + for (Iterator i = tabs.keySet().iterator(); i.hasNext();) { + Component key = (Component) i.next(); + TabDesc d = (TabDesc) tabs.get(key); + if (d.index == selectedTab) { + desc = d; + babyComponent = key; + break; + } + } + if (desc.babysitter instanceof Window) { + Window babySitter = (Window) desc.babysitter; + babySitter.setVisible(false); + babySitter.setVisible(true); + } else if ( desc.babysitter instanceof TearoffTabbedPane ) { + Window parent= SwingUtilities.getWindowAncestor(babyComponent); + parent.setVisible(false); + parent.setVisible(true); + } + //babySitter.toFront(); // no effect on Linux/Gnome } })); @@ -566,7 +300,8 @@ public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) { TabDesc desc = null; Component babyComponent = null; - for ( Component key: tabs.keySet() ) { + for (Iterator i = tabs.keySet().iterator(); i.hasNext();) { + Component key = (Component) i.next(); TabDesc d = (TabDesc) tabs.get(key); if (d.index == selectedTab) { desc = d; @@ -575,33 +310,22 @@ public void actionPerformed(ActionEvent event) { } } - - TearoffTabbedPane babySitterToUpdate= null; - if (desc==null) return; + if (desc.babysitter instanceof Window) { ((Window) desc.babysitter).dispose(); } else if ( desc.babysitter instanceof TearoffTabbedPane ) { TearoffTabbedPane bb= (TearoffTabbedPane) desc.babysitter; if ( bb.getTabCount()==1 ) { SwingUtilities.getWindowAncestor(bb).dispose(); - } else { - babySitterToUpdate= bb; } // do nothing } - TearoffTabbedPane.this.dock(babyComponent); - - if ( babySitterToUpdate!=null ) { - babySitterToUpdate.resetTearOffBabysitterName(); - } - } })); } - @Override public void mousePressed(MouseEvent event) { selectedTab = TearoffTabbedPane.this.indexAtLocation(event.getX(), event.getY()); if (event.isPopupTrigger()) { @@ -609,15 +333,6 @@ public void mousePressed(MouseEvent event) { } } - @Override - public void mouseClicked(MouseEvent e) { - if ( e.getClickCount()==2 ) { - showIt(); - e.consume(); - } - } - - @Override public void mouseReleased(MouseEvent e) { if (dragStart != null && selectedTab != -1) { @@ -626,61 +341,14 @@ public void mouseReleased(MouseEvent e) { //SwingUtilities.convertPointToScreen( p ,(Component) e.getSource() ); //f.setLocation( p ); setCursor(null); - - // See if there is another TearoffTabbedPane we can dock into. - TearoffTabbedPane last=null; - if ( draggingFrame!=null ) last= getHoverTP( e.getComponent(), e.getPoint() ); - - if ( last!=null ) { - TearoffTabbedPane babyComponent= getTabbedPane( draggingFrame ); - if ( last!=babyComponent && babyComponent.getTabCount()==1 ) { // assert tabCount=1. - int i= TearoffTabbedPane.this.getSelectedIndex(); - if (i>-1) TearoffTabbedPane.this.lastSelected= i; - TearoffTabbedPane.this.dock(babyComponent.getComponentAt(0)); // we need to dock it first, then tear it off into the other tab. - if (i>-1) TearoffTabbedPane.this.setSelectedIndex(i); - tearoffIntoTearoffTabbedPane( last, selectedTab ); - draggingFrame.dispose(); - } - } - - - if ( dropDirty!=null ) { - dropDirty.setDropDecorate(false); - dropDirty.repaint(); - } - draggingFrame = null; -// -// if ( e.isShiftDown() ) { -// System.err.println("shift is down"); -// //check for another Babysitter, dock into it... Provide feedback... -// } } dragStart = null; if (e.isPopupTrigger()) { showPopupMenu(e); } } - - } - - /** - * isolate the logic of finding the TearoffTabbedPane. This looks for compoents: - * * that have child TearoffTabbedPane - * * are child of a TearoffTabbedPane - * @param comp - * @return - */ - private TearoffTabbedPane getTabbedPane( Component comp ) { - if ( comp instanceof JFrame && ((JFrame)comp).getContentPane().getComponent(0) instanceof TearoffTabbedPane ) { - return (TearoffTabbedPane)(((JFrame)comp).getContentPane().getComponent(0)); - } else if ( comp instanceof TearoffTabbedPane ) { - return (TearoffTabbedPane) comp; - } else if ( comp.getParent()!=null && ( comp.getParent() instanceof TearoffTabbedPane ) ) { - return (TearoffTabbedPane)(comp.getParent()); - } else { - return null; - } + }; } /** @@ -695,33 +363,18 @@ static Component getTornOffComponent() { } public void tearOff(int tabIndex, Container newContainer) { - logger.log( Level.FINE, "tearOff({0},{1})", new Object[]{tabIndex, newContainer}); - int lastSelected1 = this.lastSelected; + int lastSelected = this.lastSelected; Component c = getComponentAt(tabIndex); String title = super.getTitleAt(tabIndex); super.removeTabAt(tabIndex); - super.insertTab("(" + title + ")", null, getTornOffComponent(), null, tabIndex); // we don't really need to do this for child tabs. + super.insertTab("(" + title + ")", null, getTornOffComponent(), null, tabIndex); super.setEnabledAt(tabIndex, false); TabDesc td = ((TabDesc) tabs.get(c)); - if ( td!=null ) td.babysitter = newContainer; // drop into another frame - if ( newContainer instanceof TearoffTabbedPane ) { // slide right - TearoffTabbedPane tt= (TearoffTabbedPane)newContainer; - Window ttp= SwingUtilities.getWindowAncestor(tt); - int dx= ttp.getWidth() - ( tt.getWidth() - 20 ); - int dy= ttp.getHeight() - ( tt.getHeight() - 40 ); // kludge for size of labels - if ( tt.getTabCount()==0 ) { - ttp.setSize( c.getPreferredSize().width + dx, c.getPreferredSize().height + dy); - } - - } - if ( this.parentPane==null ) { - setSelectedIndex(lastSelected1); - } + td.babysitter = newContainer; + setSelectedIndex(lastSelected); } - /** - * move the frame to follow the master frame. - */ + private final static Object STICK_LEFT= "left"; private final static Object STICK_RIGHT= "right"; /** @@ -730,8 +383,8 @@ public void tearOff(int tabIndex, Container newContainer) { * @param frame1 master frame that controls. * @param panel2 component within the compliant frame * @param frame2 compliant frame that follows. - * @param direction the direction, which is STICK_RIGHT (private) or null - * @return a listener + * @param direction + * @return */ public ComponentListener getFrameComponentListener( final Component panel1, final Component frame1, @@ -780,25 +433,18 @@ private void updateAttached( Dimension s1= panel1.getSize(); Dimension frameSize1= frame1.getSize(); Dimension s2= panel2.getSize(); + Dimension frameSize2= frame2.getSize(); if ( direction==STICK_RIGHT ) { if ( active==frame1 ) { - int delta= frame2.getWidth() - (int)s2.getWidth(); - // wdelta shrinks right frame - //GraphicsDevice gd= java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0]; - //TODO: bad assumption what if left is not index 0? - if ( updateSize ) { - //int leftWidth= gd.getDisplayMode().getWidth(); - //int wdelta= leftWidth - ( frame1.getX() + frame1.getWidth() + rightOffset + frame2.getWidth() ); - //if ( frame1.getX() + frame1.getWidth() > leftWidth ) { - // wdelta= 0; - //} // if we're not on the left side - //frame2.setSize( new Dimension( s2.width + delta + wdelta, s1.height + p2.y ) ); - frame2.setSize( new Dimension( s2.width + delta, s1.height + p2.y ) ); - //frame2.setSize( new Dimension( s1.width, s1.height + p2.y ) ); // old code - } + if ( updateSize ) frame2.setSize( new Dimension( s1.width, s1.height + p2.y ) ); frame2.setLocation( frame1.getX() + frame1.getWidth() - p2.x + rightOffset, frame1.getY() + p.y - p2.y ); } else { + if ( false && updateSize ) { + int frame1NotTabs= frameSize1.height-s1.height; + System.err.println(frame1NotTabs); + frame1.setSize( new Dimension( frameSize1.width, ( frameSize1.height-s1.height ) + s2.height ) ); + } int x= Math.max( frame1.getX(), frame2.getX()-frameSize1.width + p2.x ); rightOffset= frame2.getX()-s1.width - frame1.getX(); if ( rightOffset>0 ) rightOffset=0; @@ -811,21 +457,15 @@ private void updateAttached( } } - /** - * provide a reference to the right tabbed pane, possibly creating it. - * @return - */ - private synchronized TearoffTabbedPane getRightTabbedPane( ) { + private synchronized TearoffTabbedPane getRightTabbedPane() { if (rightPane == null) { final JFrame parent = (JFrame) SwingUtilities.getWindowAncestor(this); rightPane = new TearoffTabbedPane(this); - rightPane.setName("rightTearoffTabbedPane"); rightFrame = new JFrame(); rightFrame.add(rightPane); rightFrame.setIconImage( parent.getIconImage() ); - rightFrame.setTitle( parent.getTitle().toLowerCase() ); final WindowStateListener listener = new WindowStateListener() { @@ -837,16 +477,14 @@ public void windowStateChanged(WindowEvent e) { rightFrame.addWindowListener(new WindowAdapter() { - @Override public void windowClosing(WindowEvent e) { parent.removeWindowStateListener(listener); parent.removeComponentListener(rightFrameListener); - if ( rightPane!=null ) { - for (Component c : new ArrayList(rightPane.tabs.keySet())) { - TearoffTabbedPane.this.dock(c); - } + for (Component c : new ArrayList(rightPane.tabs.keySet())) { + TearoffTabbedPane.this.dock(c); } + rightFrame = null; rightPane = null; } @@ -870,15 +508,10 @@ public void windowClosing(WindowEvent e) { return rightPane; } - /** - * instead of undocking, "slide" the component into a second JFrame that follows the first. - * This may create the JFrame that accepts tabs. - * @param tabIndex the tab to slide (0 is the left or first tab) - */ - protected void slideRight(int tabIndex) { + public void slideRight(int tabIndex) { final Component c = getComponentAt(tabIndex); - logger.log(Level.FINEST, "slideRight {0}", c); + logger.finest("slideRight "+c); setSelectedIndex(tabIndex); c.setVisible(true); // darwin bug297 @@ -887,6 +520,7 @@ protected void slideRight(int tabIndex) { if (td == null) { return; } + final JFrame parent = (JFrame) SwingUtilities.getWindowAncestor(this); TearoffTabbedPane right = getRightTabbedPane(); @@ -901,15 +535,9 @@ protected void slideRight(int tabIndex) { } } - /** - * create a new Frame to contain the tab that was torn off. This may happen - * with the menu item "undock" or when a drag is begun within the tab. - * @param tabIndex the tab to slide (0 is the left or first tab) - * @return the new frame - */ protected JFrame tearOffIntoFrame(int tabIndex) { final Component c = getComponentAt(tabIndex); - logger.log(Level.FINEST, "tearOffInfoFrame {0}", c); + logger.finest("tearOffInfoFrame "+c); setSelectedIndex(tabIndex); c.setVisible(true); // darwin bug297 Point p = c.getLocationOnScreen(); @@ -918,172 +546,75 @@ protected JFrame tearOffIntoFrame(int tabIndex) { return null; } final JFrame parent = (JFrame) SwingUtilities.getWindowAncestor(this); - final JFrame newParent = new JFrame(td.title); - newParent.setIconImage( parent.getIconImage() ); + final JFrame babySitter = new JFrame(td.title); + babySitter.setIconImage( parent.getIconImage() ); final WindowStateListener listener = new WindowStateListener() { public void windowStateChanged(WindowEvent e) { - newParent.setExtendedState(parent.getExtendedState()); + babySitter.setExtendedState(parent.getExtendedState()); } }; parent.addWindowStateListener(listener); - final TearoffTabbedPane pane = new TearoffTabbedPane(this); - - final TearoffTabbedPane dockParent= this.parentPane!=null ? this.parentPane : this ; - p.translate(20, 20); - newParent.setLocation(p); - newParent.addWindowListener(new WindowAdapter() { + babySitter.setLocation(p); + babySitter.addWindowListener(new WindowAdapter() { - @Override public void windowClosing(WindowEvent e) { - Component[] cc= pane.getComponents(); parent.removeWindowStateListener(listener); - for ( Component c: cc ) { - dockParent.dock(c); - } + dock(c); } }); - copyInputMap(parent, newParent); + copyInputMap(parent, babySitter); - newParent.getContentPane().add(pane); + JTabbedPane pane = new TearoffTabbedPane(this); + babySitter.getContentPane().add(pane); - tearOff(tabIndex, pane); - td.babysitter= pane; + tearOff(tabIndex, babySitter); pane.add(td.title, c); - pane.setName(td.title); - - newParent.pack(); - newParent.setVisible(true); - return newParent; - } + babySitter.pack(); + babySitter.setVisible(true); - private void resetTearOffBabysitterName( ) { - Window wparent= SwingUtilities.getWindowAncestor(this); - if ( wparent==null ) { - return; - } - if ( !( wparent instanceof JFrame ) ) { - throw new RuntimeException( "internal error, parent was not instance of JFrame" ); - } - JFrame parent= (JFrame)wparent; - - if ( this.parentPane==null ) { - throw new IllegalStateException("name should not be set for parent, only babysitters"); - } - Container p= parent.getContentPane(); - Component tp= p.getComponent(0); - if ( tp instanceof TearoffTabbedPane ) { - TearoffTabbedPane tt= (TearoffTabbedPane)tp; - - StringBuilder b= new StringBuilder(); - for ( int i=0; i0 ) { - parent.setTitle( b.toString().substring(1) ); - parent.setName( b.toString().substring(1).replaceAll(",","_") ); - tp.setName( b.toString().substring(1).replaceAll(",","_") ); - } - } + return babySitter; } - + public void dock(Component c) { - logger.log(Level.FINEST, "dock {0}", c); + logger.finest("dock "+c); int selectedIndex = getSelectedIndex(); - TabDesc td = (TabDesc) this.tabs.get(c); - if ( td==null ) { - logger.log( Level.WARNING, "I thought this might happen. td==null in dock..."); - return; - } + TabDesc td = (TabDesc) tabs.get(c); int index = td.index; - if ( index>=super.getTabCount() ) { - System.err.println("something has gone wrong. We haven't accounted for a tab which was removed."); - } else { - super.removeTabAt(index); - } + super.removeTabAt(index); super.insertTab(td.title, td.icon, c, td.tip, index); super.setEnabledAt(index, true); - Container babysitter= td.babysitter; - td.babysitter= null; // get rid of reference so it will be garbage collected. - if ( babysitter!=null ) { - if ( babysitter instanceof TearoffTabbedPane ) { - TearoffTabbedPane tbabysitter= (TearoffTabbedPane)babysitter; - if ( tbabysitter.getTabCount()==0 ) { - Window w= SwingUtilities.getWindowAncestor(tbabysitter); - if ( w.getComponentCount()==1 ) { // the tearoff tabbed pane - w.dispose(); - } else { - tbabysitter.resetTearOffBabysitterName(); - } - } - } else { - babysitter.setVisible(false); - } - } - - if ( parentPane!=null ) { - resetTearOffBabysitterName(); - } - raiseApplicationWindow( SwingUtilities.getWindowAncestor(this) ); setSelectedIndex(selectedIndex); } - @Override public void addTab(String title, Icon icon, Component component) { super.addTab(title, icon, component); - TabDesc td = new TabDesc(title, icon, null, indexOfComponent(component)); + TabDesc td = new TabDesc(title, icon, component, null, indexOfComponent(component)); tabs.put(component, td); } - @Override public void addTab(String title, Component component) { super.addTab(title, component); - TabDesc td = new TabDesc(title, null, null, indexOfComponent(component)); + TabDesc td = new TabDesc(title, null, component, null, indexOfComponent(component)); tabs.put(component, td); } - @Override public void insertTab(String title, Icon icon, Component component, String tip, int index) { super.insertTab(title, icon, component, tip, index); - TabDesc td = new TabDesc(title, icon, tip, index); + TabDesc td = new TabDesc(title, icon, component, tip, index); tabs.put(component, td); } - @Override public void addTab(String title, Icon icon, Component component, String tip) { super.addTab(title, icon, component, tip); - TabDesc td = new TabDesc(title, icon, tip, indexOfComponent(component)); + TabDesc td = new TabDesc(title, icon, component, tip, indexOfComponent(component)); tabs.put(component, td); } - @Override - public void remove( Component c ) { - logger.log(Level.FINE, "remove({0})", c); - TabDesc desc= tabs.get(c); - if ( desc==null ) { - //System.err.println("here c has no desc"); - throw new IllegalArgumentException("Component does not appear to be associated with this TearoffTabbedPane"); - } - if ( desc.babysitter!=null ) { - this.dock(c); - } - super.remove(c); - } - - /** - * return the component with the tab description containing this index. - * @param index - * @return - */ private Component getTabComponentByIndex(int index) { for (Component key : tabs.keySet()) { TabDesc td = tabs.get(key); @@ -1093,51 +624,40 @@ private Component getTabComponentByIndex(int index) { } return null; } - - private TabDesc getTabDescByComponent( Component c ) { - return tabs.get(c); + + private Component getTabComponentByTitle(String title) { + for (Component key : tabs.keySet()) { + TabDesc td = tabs.get(key); + if (td.title == title) { + return key; + } + } + return null; } - @Override public void removeTabAt(int index) { - removeTabAt( index, true ); - } - - private void removeTabAt(int index,boolean dock) { - logger.log(Level.FINE, "removeTabAt({0})", index); Component c = getTabComponentByIndex(index); - if ( c==null ) { - System.err.println("no tab at index: "+index); - } + super.removeTabAt(index); TabDesc tab = tabs.get(c); if ( tab!=null ) { - if ( dock && tab.babysitter != null ) { //perhaps better to dock it first - dock(c); + if ( tab.babysitter != null ) { //perhaps better to dock it first + if (tab.babysitter instanceof Window) { + ((Window) tab.babysitter).dispose(); + } } tabs.remove(c); } else { logger.fine("tabs didn't contain c, someone else removed it."); //TODO: clean this up. } - for ( TabDesc t: tabs.values() ) { - if ( t.index>=index ) { - t.index--; - } - } - super.removeTabAt(index); + } - @Override public void setSelectedIndex(int index) { - logger.log( Level.FINER, "setSelectedIndex({0})", index ); - if (index != getSelectedIndex()) { lastSelected = getSelectedIndex(); } super.setSelectedIndex(index); - } - - public static void main( String[] args ) { - TearoffTabbedPaneDemo.main(args); + logger.finest("setSelectedIndex "+getSelectedComponent()); } } diff --git a/dasCore/src/org/das2/components/TimeRangeEditor.java b/dasCore/src/org/das2/components/TimeRangeEditor.java deleted file mode 100644 index 7a6fea3bc..000000000 --- a/dasCore/src/org/das2/components/TimeRangeEditor.java +++ /dev/null @@ -1,504 +0,0 @@ -/* File: DasTimeRangeSelector.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.components; - -import org.das2.datum.DatumRange; -import org.das2.datum.Units; -import org.das2.datum.Datum; -import org.das2.datum.DatumRangeUtil; -import org.das2.util.DasExceptionHandler; -import org.das2.datum.TimeUtil; -import org.das2.event.TimeRangeSelectionEvent; -import org.das2.event.TimeRangeSelectionListener; -import org.das2.system.DasLogger; -import java.awt.CardLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import javax.swing.event.EventListenerList; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.text.*; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.prefs.*; -import javax.swing.*; -import org.das2.datum.UnitsUtil; - -/** - * code copied from DasTimeRangeSelector for use with Autoplot. - * DasTimeRangeSelector wasn't very beany... - * @author jbf - */ -public class TimeRangeEditor implements TimeRangeSelectionListener { - - private DatumRange range= null; - - JPanel panel; - JTextField idStart= null; - JTextField idStop= null; - JButton viewButton= null; - JPanel startStopModePane=null; - CardLayout cardLayout= null; - - boolean updateRangeString= false; // true indicates use formatted range string in start time cell. - - /** Utility field used by event firing mechanism. */ - private EventListenerList listenerList = null; - - /** Action that is associated with the previous button. - * Access is given to subclasses so that other widgets can be associated - * with this action (Popup menu, etc). - */ - protected final Action previousAction = new AbstractAction("<<") { - @Override - public void actionPerformed(ActionEvent e) { - fireTimeRangeSelectedPrevious(); - } - }; - - /** Action that is associated with the next button. - * Access is given to subclasses so that other widgets can be associated - * with this action (Popup menu, etc). - */ - protected final Action nextAction = new AbstractAction(">>") { - @Override - public void actionPerformed(ActionEvent e) { - fireTimeRangeSelectedNext(); - } - }; - - protected final Action rangeAction = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - fireTimeRangeSelected(); - } - }; - - private boolean favoritesEnabled= false; - - private List favoritesList= null; - private JPopupMenu favoritesMenu= null; - - private final int FAVORITES_LIST_SIZE= 5; - - private String favoritesGroup; - - private JButton favoritesButton; - - private JPanel timesPane; - - private JComboBox rangeComboBox; - - /** - * creates an instance of the editor, with an arbitrary range (today) loaded. - */ - public TimeRangeEditor() { - updateRangeString= Preferences.userNodeForPackage(this.getClass()).getBoolean("updateRangeString", false); - buildComponents(); - Datum tnow= TimeUtil.prevMidnight( TimeUtil.now().convertTo(Units.us2000) ); - this.range= new DatumRange( tnow, TimeUtil.next( TimeUtil.DAY, tnow ) ); - update(); - } - - private Action getModeAction() { - return new AbstractAction("mode") { - @Override - public void actionPerformed( ActionEvent e ) { - updateRangeString= !updateRangeString; - Preferences.userNodeForPackage(this.getClass()).putBoolean("updateRangeString", updateRangeString ); - revalidateUpdateMode(); - update(); - } - }; - } - - private void revalidateUpdateMode() { - if ( updateRangeString ) { - //idStop.setColumns(8); - idStart.setColumns(28); - idStop.setVisible(false); - viewButton.setVisible(true); - //cardLayout.show( timesPane, "range" ); - } else { - idStart.setColumns(18); - // idStop.setColumns(18); - idStop.setVisible(true); - //cardLayout.show( timesPane, "startStop" ); - } - startStopModePane.revalidate(); - } - - private void buildComponents() { - panel= new JPanel(); - panel.setLayout(new FlowLayout()); - - JButton b= new JButton(); - b.setAction(previousAction); - b.setActionCommand("previous"); - b.setToolTipText("Scan back in time"); - panel.add(b); - - startStopModePane= new JPanel(new FlowLayout()); - - cardLayout= new CardLayout(); - timesPane= new JPanel( cardLayout ); - - JPanel startStopPane2= new JPanel(new FlowLayout()); - - idStart= new JTextField(18); - idStart.setAction(rangeAction); - idStart.setActionCommand("startTime"); - startStopPane2.add(idStart); - - idStop= new JTextField(18); - idStop.addActionListener(rangeAction); - idStop.setActionCommand("endTime"); - startStopPane2.add(idStop); - - timesPane.add( startStopPane2, "startStop" ); - - startStopModePane.add( timesPane ); - favoritesButton= new JButton("v"); - favoritesButton.setToolTipText("recent entry times"); - favoritesButton.setPreferredSize(new Dimension( 20,20 ) ); - favoritesButton.setVisible(false); - startStopModePane.add(favoritesButton); - - viewButton= new JButton(getModeAction()); - viewButton.setToolTipText("input mode: start/end vs time range string"); - viewButton.setPreferredSize(new Dimension( 20,20 ) ); - startStopModePane.add(viewButton); - - panel.add(startStopModePane); - - b= new JButton(); - b.setAction(nextAction); - b.setActionCommand("next"); - b.setToolTipText("Scan forward in time"); - panel.add(b); - - revalidateUpdateMode(); - } - - /** - * create an editor with the initial range. - * @param startTime the start time - * @param endTime the end time, which must be greater than startTime. - */ - public TimeRangeEditor(Datum startTime, Datum endTime) { - this(new DatumRange( startTime, endTime )); - } - - /** - * create an editor with the initial range. - * @param range the range - */ - public TimeRangeEditor( DatumRange range ) { - this(); - this.range= range; - update(); - } - - private void parseRange() { - boolean updateRangeString0= updateRangeString; - if ( idStop.getText().equals("") ) { - DatumRange dr; - try { - String rangeString= idStart.getText(); - if ( rangeString.equals("") ) { - rangeString= (String)rangeComboBox.getEditor().getItem(); - } - dr= DatumRangeUtil.parseTimeRange(rangeString); - DatumRange oldRange= range; - range= dr; - updateRangeString= true; - pcs.firePropertyChange( "range", oldRange, range ); - } catch ( ParseException e ) { - DasExceptionHandler.handle(e); - } - } else { - updateRangeString= false; - try { - Datum s1= TimeUtil.create(idStart.getText()); - Datum s2= TimeUtil.create(idStop.getText()); - DatumRange oldRange= range; - range= new DatumRange(s1,s2); - pcs.firePropertyChange( "range", oldRange, range ); - } catch ( ParseException e ) { - DasExceptionHandler.handle(e); - } - } - if ( updateRangeString!=updateRangeString0 ) - Preferences.userNodeForPackage(getClass()).putBoolean("updateRangeString", updateRangeString ); - - } - - private void refreshFavorites() { - favoritesMenu.removeAll(); - - for ( Iterator i= favoritesList.iterator(); i.hasNext(); ) { - final String fav= (String) i.next(); - Action favAction= new AbstractAction( fav ) { - @Override - public void actionPerformed( ActionEvent e ) { - TimeRangeEditor.this.setRange( DatumRangeUtil.parseTimeRangeValid(fav) ); - fireTimeRangeSelected(new TimeRangeSelectionEvent(this,range)); - } - }; - favoritesMenu.add( favAction ); - } - } - - private void buildFavorites( ) { - String favorites= Preferences.userNodeForPackage(getClass()).get( "timeRangeSelector.favorites."+favoritesGroup, "" ); - String[] ss= favorites.split("\\|\\|"); - favoritesList= new ArrayList(); - for (String s : ss) { - if (!"".equals(s)) { - favoritesList.add(s); - } - } - favoritesMenu= new JPopupMenu(); - refreshFavorites(); - favoritesButton.add( favoritesMenu ); - favoritesButton.addActionListener( getFavoritesListener()); - } - - private ActionListener getFavoritesListener() { - return new ActionListener() { - @Override - public void actionPerformed( ActionEvent e ) { - favoritesMenu.show(panel, favoritesButton.getX(), favoritesButton.getY() ); - } - }; - } - /** - * adds a droplist of recently entered times. This should be a spacecraft string, or null. - * @param group an arbitrary identifier for the group. - */ - public void enableFavorites( String group ) { - if ( group==null ) group="default"; - this.favoritesGroup= group; - favoritesEnabled= true; - favoritesButton.setVisible(true); - buildFavorites( ); - - } - - public Datum getStartTime() { - parseRange(); - return range.min(); - } - - public Datum getEndTime() { - parseRange(); - return range.max(); - } - - public DatumRange getRange() { - return range; - } - - public void setRange( DatumRange value ) { - DatumRange oldValue= this.range; - this.range= value; - update(); - if (oldValue != value && oldValue != null && !oldValue.equals(value)) { - pcs.firePropertyChange("range", oldValue, value); - } - } - - private void update() { - if ( range!=null ) { - if ( UnitsUtil.isTimeLocation( range.getUnits() ) ) { - if ( updateRangeString ) { - String rangeString= DatumRangeUtil.formatTimeRange(range); - idStart.setText( rangeString ); - idStop.setText(""); - } else { - idStart.setText(range.min().toString()); - idStop.setText(range.max().toString()); - } - } else { - if ( updateRangeString ) { - String rangeString= range.toString(); - idStart.setText( rangeString ); - idStop.setText(""); - } else { - idStart.setText(range.min().toString()); - idStop.setText(range.max().toString()); - } - } - } - } - - public void setStartTime(Datum s1) { - if ( range==null ) { - return; - } else { - Datum endTime= range.max(); - if ( endTime.le(s1) ) { - endTime= s1.add(1,Units.seconds); - } - range= new DatumRange( s1, endTime ); - } - update(); - } - - public void setEndTime(Datum s2) { - if ( range==null ) { - return; - } else { - Datum startTime= range.min(); - if ( startTime.ge(s2) ) { - startTime= s2.subtract(1, Units.seconds); - } - range= new DatumRange( startTime, s2 ); - } - update(); - } - - public boolean isWithin(Datum s1, Datum s2) { - Datum startTime= getStartTime(); - Datum endTime= getEndTime(); - return s1.compareTo(startTime) <= 0 && endTime.compareTo(s2) <= 0; - } - - - @Override - public void timeRangeSelected(TimeRangeSelectionEvent e) { - DatumRange lrange= e.getRange(); - if ( !lrange.equals(this.range) ) { - setRange( e.getRange() ); - fireTimeRangeSelected(e); - } - } - - /** Registers TimeRangeSelectionListener to receive events. - * @param listener The listener to register. - */ - public synchronized void addTimeRangeSelectionListener(TimeRangeSelectionListener listener) { - if (listenerList == null ) { - listenerList = new EventListenerList(); - } - listenerList.add(TimeRangeSelectionListener.class, listener); - } - - /** Removes TimeRangeSelectionListener from the list of listeners. - * @param listener The listener to remove. - */ - public synchronized void removeTimeRangeSelectionListener(TimeRangeSelectionListener listener) { - listenerList.remove(TimeRangeSelectionListener.class, listener); - } - - protected void fireTimeRangeSelectedPrevious() { - setRange( range.previous() ); - fireTimeRangeSelected(new TimeRangeSelectionEvent( this, range )); - } - - protected void fireTimeRangeSelectedNext() { - setRange( range.next() ); - fireTimeRangeSelected(new TimeRangeSelectionEvent(this,range)); - } - - protected void fireTimeRangeSelected() { - parseRange(); - update(); - if ( favoritesEnabled ) { - String store= range.toString(); - if ( favoritesList.contains( store ) ) favoritesList.remove(store); // bring to front - favoritesList.add( 0, store ); - for ( int i=FAVORITES_LIST_SIZE; i=0; i-=2) { - if (listeners[i]==TimeRangeSelectionListener.class) { - String logmsg= "fire event: "+this.getClass().getName()+"-->"+listeners[i+1].getClass().getName()+" "+event; - DasLogger.getLogger( DasLogger.GUI_LOG ).fine(logmsg); - ((org.das2.event.TimeRangeSelectionListener)listeners[i+1]).timeRangeSelected(event); - ((TimeRangeSelectionListener)listeners[i+1]).timeRangeSelected(event); - } - } - } - - public Dimension getMaximumSize() { - return panel.getPreferredSize(); - } - - public Dimension getMinimumSize() { - return panel.getPreferredSize(); - } - - /** - * get the GUI panel. All this was because it was firing off events on - * construction, and I was copying DatumEditor. - * @return the GUI panel. - */ - public JPanel getPanel() { - return panel; - } - - private void saveFavorites() { - if ( favoritesList.isEmpty() ) return; - StringBuilder favorites= new StringBuilder( (String)favoritesList.get(0) ); - for ( int i=1; itotalwidth-100 ) { - xx= totalwidth-100; - } - if ( yy>totalheight-100 ) { - yy= totalheight-100; - } - popupWindow.setLocation(xx,yy); + popupWindow.setLocation(parentLocation.x + parentPlot.getCanvas().getWidth(),parentLocation.y); } - protected boolean isPopupVisible() { - return ( popupWindow != null && popupWindow.isVisible()) && myPlot.getCanvas() != null; + @Override + protected void drawContent(Graphics2D g) { + super.drawContent(g); + /*int ix= (int)this.getXAxis().transform(yValue); + DasRow row= this.getRow(); + int iy0= (int)row.getDMinimum(); + int iy1= (int)row.getDMaximum(); + g.drawLine(ix+3,iy0,ix,iy0+3); + g.drawLine(ix-3,iy0,ix,iy0+3); + g.drawLine(ix+3,iy1,ix,iy1-3); + g.drawLine(ix-3,iy1,ix,iy1-3);*/ } + @Override public void dataRangeSelected(DataRangeSelectionEvent e) { - QDataSet ds = e.getDataSet(); - - if (ds==null || ! SemanticOps.isTableDataSet(ds) ) { + DataSet ds = e.getDataSet(); + if (ds==null || !(ds instanceof TableDataSet)) return; - } - - QDataSet xtys = (QDataSet)ds; + TableDataSet xtys = (TableDataSet)ds; Datum xValue1 = e.getMinimum(); Datum xValue2 = e.getMaximum(); if ( xValue2.equals(xValue1) ) { return; } - - if (!isPopupVisible()) { - showPopup(); - } + + this.setTitle( new DatumRange( xValue1, xValue2 ).toString() ); RebinDescriptor ddX = new RebinDescriptor(xValue1, xValue2, 1, false); ddX.setOutOfBoundsAction(RebinDescriptor.MINUSONE); AverageTableRebinner rebinner = new AverageTableRebinner(); try { - if ( xtys.rank()==3 ) { - QDataSet jds= null; - for ( int i=0; i0 ) { - title= "Averaged " + title+"!c"; - } - myPlot.setTitle( title + new DatumRange( xValue1, xValue2 ).toString() ); - if ( !myPlot.getXAxis().getLabel().equals( sourceXAxis.getLabel() ) ) { - myPlot.getXAxis().setLabel( sourceXAxis.getLabel() ); - } - if ( !myPlot.getYAxis().getLabel().equals( sourceZAxis.getLabel() ) ) { - myPlot.getYAxis().setLabel( sourceZAxis.getLabel() ); + if (!(popupWindow == null || popupWindow.isVisible()) || getCanvas() == null) { + showPopup(); + } else { + repaint(); } - //eventBirthMilli= e.birthMilli; } - + @Override + protected void uninstallComponent() { + super.uninstallComponent(); + } + + @Override + protected void installComponent() { + super.installComponent(); + getCanvas().getGlassPane().setVisible(false); + } + } diff --git a/dasCore/src/org/das2/components/VerticalSpectrogramSlicer.java b/dasCore/src/org/das2/components/VerticalSpectrogramSlicer.java index 61aa121f1..cbabcaf6a 100755 --- a/dasCore/src/org/das2/components/VerticalSpectrogramSlicer.java +++ b/dasCore/src/org/das2/components/VerticalSpectrogramSlicer.java @@ -23,116 +23,64 @@ package org.das2.components; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dialog; -import java.awt.Frame; -import java.awt.Graphics; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.Point; -import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.List; -import javax.swing.AbstractAction; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; -import org.das2.components.propertyeditor.PropertyEditor; +import org.das2.graph.SymbolLineRenderer; +import org.das2.graph.DasColumn; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasRow; +import org.das2.graph.DasPlot; +import org.das2.graph.DasAxis; import org.das2.dataset.TableDataSetConsumer; -import org.das2.datum.Datum; -import org.das2.datum.DatumRange; -import org.das2.datum.DatumRangeUtil; -import org.das2.datum.InconvertibleUnitsException; -import org.das2.datum.TimeLocationUnits; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.DataSetUtil; import org.das2.datum.format.DatumFormatter; import org.das2.datum.format.TimeDatumFormatter; +import org.das2.datum.TimeLocationUnits; +import org.das2.system.DasLogger; +import org.das2.datum.Datum; import org.das2.event.DataPointSelectionEvent; import org.das2.event.DataPointSelectionListener; -import org.das2.event.MouseModule; -import org.das2.event.PointSlopeDragRenderer; -import org.das2.graph.DasAxis; -import org.das2.graph.DasCanvas; -import org.das2.graph.DasColumn; -import org.das2.graph.DasPlot; -import org.das2.graph.DasRow; -import org.das2.graph.Renderer; -import org.das2.graph.SpectrogramRenderer; -import org.das2.graph.SymbolLineRenderer; -import org.das2.system.DasLogger; -import org.das2.util.monitor.ProgressMonitor; -import org.virbo.dataset.DataSetOps; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.*; -public class VerticalSpectrogramSlicer implements DataPointSelectionListener { +public class VerticalSpectrogramSlicer +extends DasPlot implements DataPointSelectionListener { private JDialog popupWindow; - private final DasPlot parentPlot; - private DasPlot myPlot; - private final DasAxis sourceZAxis; - private final DasAxis sourceXAxis; - - protected Datum xValue; - protected Datum yValue; - - //private long eventBirthMilli; + private DasPlot parentPlot; + protected Datum yValue; + private long eventBirthMilli; private SymbolLineRenderer renderer; - private Color markColor = new Color(230,230,230); + private Color yMarkColor = new Color(230,230,230); - protected VerticalSpectrogramSlicer( DasPlot parent, DasAxis sourceXAxis, DasAxis sourceZAxis ) { - this.sourceZAxis= sourceZAxis; - this.sourceXAxis= sourceXAxis; - this.parentPlot= parent; - } - - private void initPlot() { - DasAxis xAxis= sourceXAxis.createAttachedAxis( DasAxis.HORIZONTAL ); - DasAxis yAxis = sourceZAxis.createAttachedAxis(DasAxis.VERTICAL); - myPlot= new DasPlot( xAxis, yAxis); + protected VerticalSpectrogramSlicer(DasPlot parent, DasAxis xAxis, DasAxis yAxis) { + super( xAxis, yAxis); + this.parentPlot = parent; renderer= new SymbolLineRenderer(); - renderer.setAntiAliased(true); - myPlot.addRenderer(renderer); - myPlot.addRenderer( new Renderer() { - @Override - public void render(Graphics g, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { - int ix= (int)myPlot.getXAxis().transform(yValue); - DasRow row= myPlot.getRow(); - int iy0= (int)row.getDMinimum(); - int iy1= (int)row.getDMaximum(); - g.drawLine(ix+3,iy0,ix,iy0+3); - g.drawLine(ix-3,iy0,ix,iy0+3); - g.drawLine(ix+3,iy1,ix,iy1-3); - g.drawLine(ix-3,iy1,ix,iy1-3); - - g.setColor(markColor); - g.drawLine( ix, iy0+4, ix, iy1-4 ); - } - } ); - myPlot.getDasMouseInputAdapter().addMouseModule(new MouseModule(myPlot, new PointSlopeDragRenderer(myPlot, myPlot.getXAxis(), myPlot.getYAxis()), "Slope")); + addRenderer(renderer); } - protected void setDataSet( QDataSet ds ) { + protected void setDataSet( VectorDataSet ds ) { renderer.setDataSet(ds); } public static VerticalSpectrogramSlicer createSlicer( DasPlot plot, TableDataSetConsumer dataSetConsumer) { DasAxis sourceYAxis = plot.getYAxis(); DasAxis sourceZAxis = dataSetConsumer.getZAxis(); - return new VerticalSpectrogramSlicer(plot, sourceYAxis, sourceZAxis); + DasAxis xAxis = sourceYAxis.createAttachedAxis(DasAxis.HORIZONTAL); + DasAxis yAxis = sourceZAxis.createAttachedAxis(DasAxis.VERTICAL); + return new VerticalSpectrogramSlicer(plot, xAxis, yAxis); } public static VerticalSpectrogramSlicer createSlicer( DasPlot plot, DasAxis xAxis, TableDataSetConsumer dataSetConsumer) { + DasAxis sourceYAxis = plot.getYAxis(); DasAxis sourceZAxis = dataSetConsumer.getZAxis(); - return new VerticalSpectrogramSlicer(plot, xAxis, sourceZAxis ); + DasAxis yAxis = sourceZAxis.createAttachedAxis(DasAxis.VERTICAL); + return new VerticalSpectrogramSlicer(plot, xAxis, yAxis); } public void showPopup() { @@ -145,7 +93,6 @@ public void run() { showPopupImpl(); } }; - SwingUtilities.invokeLater(r); } } @@ -157,255 +104,131 @@ private void showPopupImpl() { popupWindow.setVisible(true); } - /** - * dispose of the popup slicer. - */ - public void dispose() { - if ( popupWindow!=null ) { - popupWindow.setVisible(false); - popupWindow.dispose(); - } - } - - /** - * clear the current dataset to avoid units errors. If the - * new dataset can be used, use it. - * @param tds the new dataset - */ - public void clear( QDataSet tds ) { - if ( renderer!=null ) { - if ( tds==null ) { - this.renderer.setDataSet(null); - } else { - try { - if ( isPopupVisible() ) { - showSlice( tds, xValue, yValue ); - } - } catch ( InconvertibleUnitsException ex ) { - this.renderer.setDataSet(null); - } - } - } - } - - /** This method should ONLY be called by the AWT event thread */ private void createPopup() { - if ( myPlot==null ) { - initPlot(); - } int width = parentPlot.getCanvas().getWidth() / 2; int height = parentPlot.getCanvas().getHeight() / 2; - final DasCanvas canvas = new DasCanvas(width, height); - DasRow row = new DasRow(canvas, null, 0, 1.0, 3, -5, 0, 0 ); - DasColumn column = new DasColumn(canvas, null, 0, 1.0, 7, -3, 0, 0 ); - canvas.add( myPlot, row, column); + DasCanvas canvas = new DasCanvas(width, height); + DasRow row = new DasRow(canvas, 0.1, 0.9); + DasColumn column = new DasColumn(canvas, 0.1, 0.9); + canvas.add(this, row, column); JPanel content = new JPanel(new BorderLayout()); JPanel buttonPanel = new JPanel(); BoxLayout buttonLayout = new BoxLayout(buttonPanel, BoxLayout.X_AXIS); - buttonPanel.setLayout(buttonLayout); - - buttonPanel.add(Box.createHorizontalGlue()); - - JButton printButton= new JButton( new AbstractAction("Print...") { - @Override - public void actionPerformed( ActionEvent e ) { - canvas.makeCurrent(); - DasCanvas.PRINT_ACTION.actionPerformed(e); - } - }); - buttonPanel.add( printButton ); - - JButton settingsButton= new JButton( new AbstractAction("Settings...") { - @Override - public void actionPerformed( ActionEvent e ) { - SpectrogramRenderer rend=null; - for ( Renderer r : parentPlot.getRenderers() ) { - if ( r instanceof SpectrogramRenderer ) { - rend= (SpectrogramRenderer) r; - break; - } - } - if ( rend==null ) { - JOptionPane.showMessageDialog( null, "Unable to find associated Spectrogram" ); - return; - } - SliceSettings settings= new SliceSettings(); - settings.setSliceRebinnedData( rend.isSliceRebinnedData() ); - new PropertyEditor(settings).showModalDialog(canvas); - rend.setSliceRebinnedData(settings.isSliceRebinnedData()); - - QDataSet ds= rend.getConsumedDataSet(); - showSlice( ds, xValue, yValue ); - - } - }); - buttonPanel.add( settingsButton ); - JButton close = new JButton("Hide Window"); close.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { popupWindow.setVisible(false); } }); - + buttonPanel.setLayout(buttonLayout); + buttonPanel.add(Box.createHorizontalGlue()); buttonPanel.add(close); content.add(canvas, BorderLayout.CENTER); content.add(buttonPanel, BorderLayout.SOUTH); Window parentWindow = SwingUtilities.getWindowAncestor(parentPlot); - popupWindow = new JDialog(parentWindow); + if (parentWindow instanceof Frame) { + popupWindow = new JDialog((Frame)parentWindow); + } + else if (parentWindow instanceof Dialog) { + popupWindow = new JDialog((Dialog)parentWindow); + } + else { + popupWindow = new JDialog(); + } popupWindow.setTitle("Vertical Slicer"); popupWindow.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); popupWindow.setContentPane(content); popupWindow.pack(); - Point parentLocation = new Point( 0, parentPlot.getY() ); - parentLocation.translate( parentPlot.getX()/20, -1 * myPlot.getRow().getDMinimum() ); + Point parentLocation = new Point(); SwingUtilities.convertPointToScreen(parentLocation, parentPlot.getCanvas()); - - //make sure some portion of the slice window is visible on the screen. - int xx= parentLocation.x + parentPlot.getCanvas().getWidth(); - int yy= parentLocation.y; - - GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); - int totalwidth = gd.getDisplayMode().getWidth(); - int totalheight = gd.getDisplayMode().getHeight(); - - if ( xx>totalwidth-100 ) { - xx= totalwidth-100; - } - if ( yy>totalheight-100 ) { - yy= totalheight-100; - } - popupWindow.setLocation(xx,yy); + popupWindow.setLocation(parentLocation.x + parentPlot.getCanvas().getWidth(),parentLocation.y); + } + + protected void drawContent(Graphics2D g) { + long x; + x= System.currentTimeMillis()-eventBirthMilli; + + int ix= (int)this.getXAxis().transform(yValue); + DasRow row= this.getRow(); + int iy0= (int)row.getDMinimum(); + int iy1= (int)row.getDMaximum(); + g.drawLine(ix+3,iy0,ix,iy0+3); + g.drawLine(ix-3,iy0,ix,iy0+3); + g.drawLine(ix+3,iy1,ix,iy1-3); + g.drawLine(ix-3,iy1,ix,iy1-3); + + g.setColor(yMarkColor); + g.drawLine( ix, iy0+4, ix, iy1-4 ); + + super.drawContent(g); + x= System.currentTimeMillis()-eventBirthMilli; + //org.das2.util.DasDie.println("event handled in "+x+" milliseconds"); } protected boolean isPopupVisible() { - return ( popupWindow != null && popupWindow.isVisible()) && myPlot.getCanvas() != null; + return ( popupWindow != null && popupWindow.isVisible()) && getCanvas() != null; } - /** - * show the slice at the data point selected. - * @param e the selection event containing the data point. - */ - @Override public void dataPointSelected(DataPointSelectionEvent e) { - - yValue = e.getY(); - xValue = e.getX(); - - QDataSet ds = e.getDataSet(); - if (ds==null || ! SemanticOps.isTableDataSet(ds) ) - return; - - QDataSet tds = (QDataSet)ds; - - showSlice( tds, xValue, yValue ); - - } - - private boolean showSlice( QDataSet tds, Datum xValue, Datum yValue ) { - QDataSet tds1 = null; - List tdss= new ArrayList(); - if (tds.rank() == 3) { - // slice to get the correct table; - for (int i = 0; i < tds.length(); i++) { - QDataSet bounds = DataSetOps.dependBounds(tds.slice(i)); - DatumRange xrange= DataSetUtil.asDatumRange( bounds.slice(0), true ); - if ( xrange.contains( xValue )) { - tdss.add(tds.slice(i)); - } - } - } else { - QDataSet bounds = DataSetOps.dependBounds(tds); - if (DataSetOps.boundsContains(bounds, xValue, yValue)) { - tds1 = tds; - } - } - - int ix; - Datum xx=null; - DatumRange xdr= null; // if the xx's aren't identical, show the range instead. - - QDataSet sliceDataSet; - if (tds1 == null) { - QDataSet jds= null; - assert tdss.size()>0; - for (QDataSet tds2 : tdss) { - tds1 = tds2; - QDataSet xds = SemanticOps.xtagsDataSet(tds1); - ix = org.virbo.dataset.DataSetUtil.closestIndex(xds, xValue); - QDataSet s1 = tds1.slice(ix); - if ( xx!=null ) { - Datum xx1= DataSetUtil.asDatum(xds.slice(ix)); - if ( !xx1.equals(xx) ) { - xdr= DatumRangeUtil.union( xx, xx1 ); - } - } - xx= DataSetUtil.asDatum(xds.slice(ix)); - jds= org.virbo.dsops.Ops.concatenate( jds, s1 ); - } - sliceDataSet= jds; - - } else { + long xxx[]= { 0,0,0,0 }; + xxx[0] = System.currentTimeMillis()-e.birthMilli; - QDataSet xds = SemanticOps.xtagsDataSet(tds1); - ix = org.virbo.dataset.DataSetUtil.closestIndex(xds, xValue); - xx= DataSetUtil.asDatum(xds.slice(ix)); - sliceDataSet = tds1.slice(ix); - } + DataSet ds = e.getDataSet(); + if (ds==null || !(ds instanceof TableDataSet)) + return; - if ( sliceDataSet==null ) { - return false; - } + TableDataSet tds = (TableDataSet)ds; - DasLogger.getLogger(DasLogger.GUI_LOG).finest("setDataSet sliceDataSet"); + VectorDataSet sliceDataSet= tds.getXSlice( DataSetUtil.closestColumn( tds, e.getX() ) ); + + renderer.setDataSet(sliceDataSet); + DasLogger.getLogger(DasLogger.GUI_LOG).finest("setDataSet sliceDataSet"); if (!isPopupVisible()) { showPopup(); } - renderer.setDataSet(sliceDataSet); + + yValue= e.getY(); + Datum xValue = e.getX(); + DatumFormatter formatter; - if (xValue.getUnits() instanceof TimeLocationUnits) { - formatter = TimeDatumFormatter.DEFAULT; + if ( xValue.getUnits() instanceof TimeLocationUnits ) { + formatter= TimeDatumFormatter.DEFAULT; } else { - formatter = xValue.getFormatter(); + formatter= xValue.getFormatter(); } - String title= parentPlot.getTitle().trim(); - if ( title.length()>0 ) title= title+"!c"; - if ( xdr!=null ) { - myPlot.setTitle( title + "x: " + xdr + " y: " + yValue ); - } else { - myPlot.setTitle( title + "x: " + formatter.format(xx) + " y: " + yValue ); - } - if ( !myPlot.getXAxis().getLabel().equals( sourceXAxis.getLabel() ) ) { - myPlot.getXAxis().setLabel( sourceXAxis.getLabel() ); - } - if ( !myPlot.getYAxis().getLabel().equals( sourceZAxis.getLabel() ) ) { - myPlot.getYAxis().setLabel( sourceZAxis.getLabel() ); - } - //eventBirthMilli= e.birthMilli; - return true; + + setTitle("x: "+ formatter.format(xValue) + " y: "+yValue); + + eventBirthMilli= e.birthMilli; } - /** - * the color for the mark (vertical bar) indicating slice position - * @return the mark color - */ - public Color getMarkColor() { - return markColor; + protected void uninstallComponent() { + super.uninstallComponent(); + } + + protected void installComponent() { + super.installComponent(); + getCanvas().getGlassPane().setVisible(false); + } + + protected void processDasUpdateEvent(org.das2.event.DasUpdateEvent e) { + if (isDisplayable()) { + updateImmediately(); + resize(); + } + } + + public Color getYMarkColor() { + return yMarkColor; } - /** - * set the color for the mark (vertical bar) indicating slice position - * Color(230,230,230) is the default. - * @param markColor the color - */ - public void setMarkColor(Color markColor) { - this.markColor = markColor; + public void setYMarkColor(Color yMarkColor) { + this.yMarkColor = yMarkColor; } } diff --git a/dasCore/src/org/das2/components/propertyeditor/BooleanEditor.java b/dasCore/src/org/das2/components/propertyeditor/BooleanEditor.java index c11abd510..4eda4af37 100644 --- a/dasCore/src/org/das2/components/propertyeditor/BooleanEditor.java +++ b/dasCore/src/org/das2/components/propertyeditor/BooleanEditor.java @@ -11,8 +11,19 @@ import java.awt.Rectangle; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.EventObject; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import javax.swing.DefaultButtonModel; +import javax.swing.DefaultListCellRenderer; import javax.swing.JCheckBox; +import javax.swing.JList; import javax.swing.JTable; import javax.swing.JToggleButton; import javax.swing.event.CellEditorListener; diff --git a/dasCore/src/org/das2/components/propertyeditor/ColorCellRenderer.java b/dasCore/src/org/das2/components/propertyeditor/ColorCellRenderer.java index c5fd569b2..d9c7f049d 100644 --- a/dasCore/src/org/das2/components/propertyeditor/ColorCellRenderer.java +++ b/dasCore/src/org/das2/components/propertyeditor/ColorCellRenderer.java @@ -34,8 +34,6 @@ class ColorCellRenderer implements ListCellRenderer, TableCellRenderer, Icon { names.put(Color.BLUE, "blue"); names.put(Color.CYAN, "cyan"); names.put(Color.DARK_GRAY, "dark gray"); - names.put(Color.GREEN.darker(),"dark green"); - names.put(new Color(128, 128, 255), "purple" ); names.put(Color.GRAY, "gray"); names.put(Color.GREEN, "green"); names.put(Color.LIGHT_GRAY, "light gray"); @@ -46,10 +44,6 @@ class ColorCellRenderer implements ListCellRenderer, TableCellRenderer, Icon { names.put(Color.YELLOW, "yellow"); } - protected static void addName( Color c, String name ) { - names.put( c, name ); - } - private JLabel label; private Border noFocusBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1); private Color iconColor; diff --git a/dasCore/src/org/das2/components/propertyeditor/ColorEditor.java b/dasCore/src/org/das2/components/propertyeditor/ColorEditor.java index 50e30d0f6..dd94fc368 100644 --- a/dasCore/src/org/das2/components/propertyeditor/ColorEditor.java +++ b/dasCore/src/org/das2/components/propertyeditor/ColorEditor.java @@ -23,10 +23,8 @@ import javax.swing.ComboBoxModel; import javax.swing.JColorChooser; import javax.swing.JComboBox; -import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.table.TableCellEditor; -import org.das2.util.DesktopColorChooserPanel; /** * @@ -34,50 +32,33 @@ */ public class ColorEditor extends AbstractCellEditor implements java.beans.PropertyEditor, TableCellEditor { - private static final List colors = new ArrayList(); + private static List colors = new ArrayList(); static { colors.add(Color.BLACK); - colors.add(Color.BLUE); // dark BRG - colors.add(Color.RED); - colors.add(Color.GREEN.darker()); - colors.add(Color.DARK_GRAY); // grey scale + colors.add(Color.WHITE); + colors.add(Color.BLUE); + colors.add(Color.CYAN); + colors.add(Color.DARK_GRAY); colors.add(Color.GRAY); + colors.add(Color.GREEN); colors.add(Color.LIGHT_GRAY); - colors.add(Color.WHITE); - colors.add( new Color(128, 128, 255) ); // light BRG + colors.add(Color.MAGENTA); + colors.add(Color.ORANGE); colors.add(Color.PINK); - colors.add(Color.GREEN); - colors.add(Color.CYAN); + colors.add(Color.RED); colors.add(Color.YELLOW); - colors.add(Color.MAGENTA); // others - colors.add(Color.ORANGE); colors.add( new Color(0,true) ); } - + private JColorChooser custom; - private transient final PropertyEditorSupport editorSupport; + private PropertyEditorSupport editorSupport; private JComboBox choice; - - /** - * allow clients to add additional colors. - * @param c - * @param name - */ - public static void addColor( Color c, String name ) { - if ( colors.contains(c) ) return; - colors.add(colors.size()-1,c); // before "none" - ColorCellRenderer.addName( c, name ); - } - /** Creates a new instance of ColorEditor */ public ColorEditor() { - //long t0= System.currentTimeMillis(); - editorSupport = new PropertyEditorSupport(this){}; - custom = null; + custom = new JColorChooser(); choice = new JComboBox(new ColorChoiceModel()) { - @Override public void setBounds(int x, int y, int width, int height) { Dimension preferred = getPreferredSize(); super.setBounds(x, y, width, preferred.height); @@ -93,8 +74,11 @@ public void itemStateChanged(ItemEvent e) { } } }); - //System.err.println( String.format( "total time in init ColorEditor: %d", ( System.currentTimeMillis()-t0 ) )); - + custom.addPropertyChangeListener("color", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + setValue(e.getNewValue()); + } + }); } public boolean supportsCustomEditor() { return true; } @@ -109,23 +93,9 @@ public String getAsText() { } return hex; } - - private void initCustom() { - custom= new JColorChooser(); - custom.addPropertyChangeListener("color", new PropertyChangeListener() { - public void propertyChange(PropertyChangeEvent e) { - setValue(e.getNewValue()); - } - } ); - custom.setColor( (Color)getValue() ); - custom.addChooserPanel(new DesktopColorChooserPanel()); // TODO: this doesn't work on Linux! - } - + public Component getCustomEditor() { Color c = (Color)getValue(); - if ( custom==null ) { - initCustom(); - } custom.setColor(c); return custom; } @@ -190,7 +160,7 @@ public Component getSmallEditor() { private class ColorChoiceModel extends AbstractListModel implements ComboBoxModel { - private final static String CUSTOM_LABEL = "custom..."; + private final String CUSTOM_LABEL = "custom..."; public Object getElementAt(int index) { if (index < colors.size()) { @@ -217,11 +187,9 @@ public void setSelectedItem(Object obj) { setValue(obj); } else if (CUSTOM_LABEL.equals(obj)) { - if ( custom==null ) { - initCustom(); - } - if ( JOptionPane.OK_OPTION==JOptionPane.showConfirmDialog( choice, custom, "Color Editor", JOptionPane.OK_CANCEL_OPTION ) ) { - setValue(custom.getColor()); + Color c = custom.showDialog(choice, "Color Editor", (Color)getValue()); + if (c != null) { + setValue(c); } } else { diff --git a/dasCore/src/org/das2/components/propertyeditor/Displayable.java b/dasCore/src/org/das2/components/propertyeditor/Displayable.java index 664129f66..dc443d74f 100644 --- a/dasCore/src/org/das2/components/propertyeditor/Displayable.java +++ b/dasCore/src/org/das2/components/propertyeditor/Displayable.java @@ -1,38 +1,26 @@ package org.das2.components.propertyeditor; -import java.awt.Graphics2D; import javax.swing.Icon; -/** - * Type-safe enumerations that are used as property types +/** Type-safe enumerations that are used as property types * that are editable with a PropertyEditor should * implement this interface. + * */ public interface Displayable { - /** - * return a String that will help the user + /** return a String that will help the user * identify this item when choosing from a list. - * @return the list label. */ String getListLabel(); - /** - * An icon can be provided that will be shown in a list + /** An icon can be provided that will be shown in a list * along with the textual description of the element. * This method should return null if there - * is no icon available, or a roughly 16x16 pixel icon. + * is no icon available. * - * @return the icon. */ Icon getListIcon(); - /** - * implement this to provide nice drawing of icon on printing graphics context. - * @param g the graphics context. - * @param x the x position, typically 0. - * @param y the y position, typically 0. - */ - void drawListIcon( Graphics2D g, int x, int y ); } diff --git a/dasCore/src/org/das2/components/propertyeditor/EnumerationEditor.java b/dasCore/src/org/das2/components/propertyeditor/EnumerationEditor.java index e65bc6fcc..be9f7cd50 100644 --- a/dasCore/src/org/das2/components/propertyeditor/EnumerationEditor.java +++ b/dasCore/src/org/das2/components/propertyeditor/EnumerationEditor.java @@ -44,6 +44,7 @@ public class EnumerationEditor implements java.beans.PropertyEditor, TableCellEd private Model model; private Object selected; private Class type; + private boolean guessType; private PropertyChangeSupport pcSupport; private EventListenerList listeners = new EventListenerList(); private Map valueMap; @@ -52,11 +53,13 @@ public class EnumerationEditor implements java.beans.PropertyEditor, TableCellEd /** Creates a new instance of EnumerationEditor */ public EnumerationEditor() { + guessType = true; pcSupport = new PropertyChangeSupport(this); } protected EnumerationEditor(Class c) { setClass(c); + guessType = false; } private void initEditor() { @@ -120,27 +123,6 @@ private void setClass(Class c) { toStringMap = toStringM; } - /** remove the item from the list of selections - * - * @param j - */ - public void removeItem( Object j ) { - Field[] fields = type.getDeclaredFields(); - for ( Field f: fields ) { - try { - if ( f.get(selected).equals(j) ) { - nameMap.remove( f.getName() ); - } - } catch ( IllegalAccessException ex ) { - - } - } - if ( editor!=null ) { - model = new Model(); - editor.setModel(model); - } - } - public String getAsText() { return selected.toString(); } @@ -301,7 +283,6 @@ private void fireEditingCanceled() { private class Renderer extends DefaultListCellRenderer { - @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) { String s = (String) valueMap.get(value); super.getListCellRendererComponent(list, s, index, isSelected, hasFocus); @@ -340,19 +321,16 @@ public int getSize() { } //TODO: remove? - @Override protected void fireIntervalRemoved(Object source, int index0, int index1) { super.fireIntervalRemoved(source, index0, index1); } //TODO: remove? - @Override protected void fireIntervalAdded(Object source, int index0, int index1) { super.fireIntervalAdded(source, index0, index1); } //TODO: remove? - @Override protected void fireContentsChanged(Object source, int index0, int index1) { super.fireContentsChanged(source, index0, index1); } diff --git a/dasCore/src/org/das2/components/propertyeditor/IndexedPropertyItemTreeNode.java b/dasCore/src/org/das2/components/propertyeditor/IndexedPropertyItemTreeNode.java index c1e16fdb8..c7ec149ea 100644 --- a/dasCore/src/org/das2/components/propertyeditor/IndexedPropertyItemTreeNode.java +++ b/dasCore/src/org/das2/components/propertyeditor/IndexedPropertyItemTreeNode.java @@ -4,7 +4,7 @@ import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Iterator; +import java.util.*; class IndexedPropertyItemTreeNode extends PropertyTreeNode { @@ -21,22 +21,19 @@ class IndexedPropertyItemTreeNode extends PropertyTreeNode { this.indexedPropertyDescriptor = indexedPropertyDescriptor; } - @Override public boolean getAllowsChildren() { return indexedPropertyDescriptor.getPropertyEditorClass() == null; } - @Override public String getDisplayName() { return propertyDescriptor.getName() + "[" + index + "]"; } - @Override public void flush() { try { if (dirty) { Method writeMethod = indexedPropertyDescriptor.getIndexedWriteMethod(); - writeMethod.invoke(parent.parent.value, new Object[]{ index, value} ); + writeMethod.invoke(parent.parent.value, new Object[]{new Integer(index), value} ); dirty = false; } if (childDirty) { @@ -53,13 +50,11 @@ public void flush() { } } - @Override protected Object read() { Object[] parentValue= (Object[])parent.read(); return parentValue[this.index]; } - @Override public void refresh( ) { Object newValue = read(); boolean foldMe= false; diff --git a/dasCore/src/org/das2/components/propertyeditor/IndexedPropertyTreeNode.java b/dasCore/src/org/das2/components/propertyeditor/IndexedPropertyTreeNode.java index d1f3132eb..c32f9330a 100644 --- a/dasCore/src/org/das2/components/propertyeditor/IndexedPropertyTreeNode.java +++ b/dasCore/src/org/das2/components/propertyeditor/IndexedPropertyTreeNode.java @@ -4,6 +4,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.MutableTreeNode; class IndexedPropertyTreeNode extends PropertyTreeNode { diff --git a/dasCore/src/org/das2/components/propertyeditor/PeerPropertyTreeNode.java b/dasCore/src/org/das2/components/propertyeditor/PeerPropertyTreeNode.java index ca5307bd9..998a1d3ed 100644 --- a/dasCore/src/org/das2/components/propertyeditor/PeerPropertyTreeNode.java +++ b/dasCore/src/org/das2/components/propertyeditor/PeerPropertyTreeNode.java @@ -5,10 +5,10 @@ * * */ + package org.das2.components.propertyeditor; import java.beans.PropertyDescriptor; -import java.util.Arrays; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; @@ -17,199 +17,151 @@ * @author Jeremy */ public class PeerPropertyTreeNode implements PropertyTreeNodeInterface { - PeerPropertyTreeNode parent; - PeerPropertyTreeNode[] children = null; PropertyTreeNode leader; PropertyTreeNode[] peers; - private DefaultTreeModel treeModel; - - public PeerPropertyTreeNode(PeerPropertyTreeNode parent, PropertyTreeNode leader, PropertyTreeNode[] peers) { - this.parent = parent; - this.leader = leader; - this.peers = Arrays.copyOf( peers, peers.length ); + + public PeerPropertyTreeNode( PeerPropertyTreeNode parent, PropertyTreeNode leader, PropertyTreeNode[] peers ) { + this.parent= parent; + this.leader= leader; + this.peers= peers; } - + public java.util.Enumeration children() { return new java.util.Enumeration() { - - int index = 0; - + int index=0; public boolean hasMoreElements() { - return index < getChildCount(); + return index 1) { - return String.valueOf(peers[columnIndex - 2].getDisplayValue()); - } else { - return leader.getColumnName(columnIndex); - } + return leader.getColumnName(columnIndex); } - + public int getIndex(TreeNode node) { - PeerPropertyTreeNode[] kids= maybeCreateChildren(); - for (int i = 0; i < kids.length; i++) { - if (node == kids[i]) { - return i; - } - } - return -1; + return leader.getIndex(node); } - + public TreeNode getParent() { return parent; } - + public Object getValueAt(int column) { - if (column > 1) { - return peers[column - 2].getValueAt(1); - } else { - switch (column) { - case 0: - return leader.getDisplayName(); - case 1: - return getDisplayValue(); - default: - throw new IllegalArgumentException("No such column: " + column); - } + switch (column) { + case 0: + return leader.getDisplayName(); + case 1: + return getDisplayValue(); + default: throw new IllegalArgumentException("No such column: " + column); } } - + public boolean isCellEditable(int column) { - return column > 0; + return leader.isCellEditable(column); } - + public boolean isDirty() { return leader.isDirty(); } - + public boolean isLeaf() { return leader.isLeaf(); } - + public void refresh() { leader.refresh(); - for (int i = 0; i < peers.length; i++) { - peers[i].refresh(); - } } - + public void setValueAt(Object value, int column) { - if (column > 1) { - peers[column - 2].setValueAt(value, 1); - } else { - switch (column) { - case 0: - throw new IllegalArgumentException("Cell is not editable"); - case 1: - if (value != PropertyEditor.MULTIPLE) { - setValue(value); - } - break; - default: - throw new IllegalArgumentException("No such column: " + column); - } + switch (column) { + case 0: + throw new IllegalArgumentException("Cell is not editable"); + case 1: + setValue(value); + break; + default: throw new IllegalArgumentException("No such column: " + column); } - //treeModel.nodeStructureChanged( this ); - treeModel.nodeChanged(this); //TODO why not this? } - + public void setTreeModel(DefaultTreeModel treeModel) { - this.treeModel = treeModel; - for (int i = 0; i < peers.length; i++) { + for ( int i=0; i5 ) { - this.setPreferredSize( new Dimension( (int)Math.min( Toolkit.getDefaultToolkit().getScreenSize().getWidth(), table.getColumnCount()*100 ), 500 ) ); - } } - /** - * create a PropertyEditor for the bean. - * @param bean a java bean - */ public PropertyEditor(Object bean) { this(new PropertyTreeNode(bean), bean); - if (bean instanceof PropertyTreeNodeInterface) { // this was some bug + if (bean instanceof PropertyTreeNodeInterface) { throw new IllegalArgumentException("whoops!"); } } - /** - * create a PropertyEditor when we are editing several beans at once. - * @param leader the leader bean, which is used to get settings for all components. - * @param peers the peers, which must all be instances of the leader's class. - * @return a PropertyEditor for editing several beans at once. - */ public static PropertyEditor createPeersEditor(Object leader, Object[] peers) { - Class leaderClass= leader.getClass(); - for (Object peer : peers) { - if (!leaderClass.isInstance(peer)) { - throw new IllegalArgumentException("child is not instance of leader class: " + peer.getClass().getName() + ", should be " + leaderClass.getName()); - } - } PropertyTreeNode[] peerNodes = new PropertyTreeNode[peers.length]; for (int i = 0; i < peers.length; i++) { peerNodes[i] = new PropertyTreeNode(peers[i]); @@ -180,7 +171,7 @@ public static PropertyEditor createPeersEditor(Object leader, Object[] peers) { private void addActions(final JTable table) { table.getActionMap().put("MY_EDIT", new AbstractAction() { - @Override + public void actionPerformed(ActionEvent e) { table.editCellAt(focusRow, 1); } @@ -192,17 +183,19 @@ public void actionPerformed(ActionEvent e) { private ListSelectionListener getListSelectionListener() { return new ListSelectionListener() { - @Override + public void valueChanged(ListSelectionEvent e) { focusRow = table.getSelectedRow(); // we could do a better job here - logger.log(Level.FINE, "focusRow={0}", focusRow); + + logger.fine("focusRow=" + focusRow); } }; } private KeyListener getKeyListener() { + KeyAdapter ka; return new KeyAdapter() { - @Override + public void keyReleased(KeyEvent event) { logger.fine(String.valueOf(event)); if (event.getKeyCode() == KeyEvent.VK_RIGHT) { @@ -216,38 +209,118 @@ public void keyReleased(KeyEvent event) { }; } + private Action createSaveAction(final Object bean) { + return new AbstractAction("Save") { + + public void actionPerformed(ActionEvent ev) { + try { + JFileChooser chooser = new JFileChooser(); + chooser.setFileFilter(new javax.swing.filechooser.FileFilter() { + + public boolean accept(File f) { + return f.toString().matches(".*\\.das2PropertySheet"); + } + + public String getDescription() { + return "*.das2PropertySheet"; + } + }); + chooser.setSelectedFile(new File("default.das2PropertySheet")); + int result = chooser.showSaveDialog(PropertyEditor.this); + if (result == JFileChooser.APPROVE_OPTION) { + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + Element element = SerializeUtil.getDOMElement(document, bean); + document.appendChild(element); + OutputStream out = new FileOutputStream(chooser.getSelectedFile()); + + StringWriter writer = new StringWriter(); + DOMImplementation impl = document.getImplementation(); + DOMImplementationLS ls = (DOMImplementationLS)impl.getFeature("LS", "3.0"); + LSSerializer serializer = ls.createLSSerializer(); + LSOutput output = ls.createLSOutput(); + output.setEncoding("UTF-8"); + output.setByteStream(out); + serializer.write(document, output); + + //OutputFormat format = new OutputFormat(org.apache.xml.serialize.Method.XML, "UTF-8", true); + //XMLSerializer serializer = new XMLSerializer( new OutputStreamWriter(out), format); + //serializer.serialize(document); + out.close(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + } + + private static Document readDocument(File file) throws IOException, ParserConfigurationException, SAXException { + InputStream in = new FileInputStream(file); + InputSource source = new InputSource(); + source.setCharacterStream(new InputStreamReader(in)); + DocumentBuilder builder; + ErrorHandler eh = null; + DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); + builder = domFactory.newDocumentBuilder(); + builder.setErrorHandler(eh); + Document document = builder.parse(source); + return document; + } + + private Action createLoadAction(final Object bean) { + return new AbstractAction("Load") { + + public void actionPerformed(ActionEvent ev) { + try { + JFileChooser chooser = new JFileChooser(); + chooser.setFileFilter(new javax.swing.filechooser.FileFilter() { + + public boolean accept(File f) { + return f.toString().matches(".*\\.das2PropertySheet"); + } + + public String getDescription() { + return "*.das2PropertySheet"; + } + }); + int result = chooser.showOpenDialog(PropertyEditor.this); + if (result == JFileChooser.APPROVE_OPTION) { + try { + Document document = readDocument(chooser.getSelectedFile()); + Element element = document.getDocumentElement(); + SerializeUtil.processElement(element, bean); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } catch (SAXException e) { + throw new RuntimeException(e); + } + + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + } + private Action getEditSelectedAction() { return new AbstractAction("Edit Selected") { - @Override + public void actionPerformed(ActionEvent e) { PropertyEditor p; TreeTableModel model = (TreeTableModel) table.getModel(); PropertyTreeNodeInterface node = (PropertyTreeNodeInterface) model.getNodeForRow(focusRow); - int[] selected = table.getSelectedRows(); - if ( selected.length==0 ) { - JOptionPane.showMessageDialog( table, "No items selected" ); - return; - } else if (selected.length == 1) { + int[] selected = table.getSelectedRows(); + if (selected.length == 1) { p = new PropertyEditor(node.getValue()); } else { - int ileader= table.getSelectedRow(); - java.util.List peers = new ArrayList(selected.length); - Object leader= ((PropertyTreeNode) model.getNodeForRow(ileader)).getValue(); + Object[] peers = new Object[selected.length]; for (int i = 0; i < selected.length; i++) { - Object peer= ((PropertyTreeNode) model.getNodeForRow(selected[i])).getValue(); - if ( leader.getClass().isInstance(peer) ) peers.add(peer); - } - if ( peers.size()==1 && peers.get(0)==leader ) { //bug where hidden selected parent is leader - peers.remove(leader); - Object newLeader= ((PropertyTreeNode) model.getNodeForRow(selected[0])).getValue(); - if ( newLeader==leader ) newLeader= ((PropertyTreeNode) model.getNodeForRow(selected[1])).getValue(); - leader= newLeader; - for (int i = 0; i < selected.length; i++) { - Object peer= ((PropertyTreeNode) model.getNodeForRow(selected[i])).getValue(); - if ( leader.getClass().isInstance(peer) ) peers.add(peer); - } + peers[i] = ((PropertyTreeNode) model.getNodeForRow(selected[i])).getValue(); } - p = createPeersEditor( leader, peers.toArray() ); + p = createPeersEditor(node.getValue(), peers); } p.showDialog(PropertyEditor.this); @@ -261,46 +334,33 @@ private void initPopupMenu() { } private void initButtonPanel(boolean saveLoadButton) { - buttonPanel = new JPanel(); - buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS)); + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); if (saveLoadButton) { - //JButton saveButton = new JButton(createSaveAction(this.bean)); - //buttonPanel.add(saveButton); - //JButton loadButton = new JButton(createLoadAction(this.bean)); - //buttonPanel.add(loadButton); + JButton saveButton = new JButton(createSaveAction(this.bean)); + buttonPanel.add(saveButton); + JButton loadButton = new JButton(createLoadAction(this.bean)); + buttonPanel.add(loadButton); } - final JButton cancel = new JButton("Cancel"); - - final JButton apply = new JButton("Apply"); - - closeButton = new JButton("OK"); + final JButton apply = new JButton("Apply Changes"); + closeButton = new JButton("Dismiss"); ActionListener al = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent ef) { - SwingUtilities.invokeLater( new Runnable() { // allow focus event to occur first. - @Override - public void run() { - if (ef.getSource() == apply) { - globalApplyChanges(); - refresh(); - } else if (ef.getSource() == closeButton) { - globalApplyChanges(); - dismissDialog(false); - } else if ( ef.getSource()==cancel ) { - dismissDialog(false); - } - } - } ); + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == apply) { + globalApplyChanges(); + refresh(); + } else if (e.getSource() == closeButton) { + dismissDialog(); + } } }; apply.addActionListener(al); closeButton.addActionListener(al); - cancel.addActionListener(al); JButton refresh = new JButton("Refresh"); refresh.addActionListener(new ActionListener() { - @Override + public void actionPerformed(ActionEvent e) { refresh(); } @@ -308,7 +368,6 @@ public void actionPerformed(ActionEvent e) { buttonPanel.add(refresh); buttonPanel.add(Box.createHorizontalGlue()); - buttonPanel.add(cancel); buttonPanel.add(apply); buttonPanel.add(closeButton); add(buttonPanel, BorderLayout.SOUTH); @@ -327,9 +386,9 @@ private void globalApplyChanges() { root.flush(); } - private void dismissDialog(boolean checkDirty) { + private void dismissDialog() { PropertyTreeNodeInterface root = (PropertyTreeNodeInterface) ((TreeTableModel) table.getModel()).getRoot(); - if (checkDirty && root.isDirty()) { + if (root.isDirty()) { String[] message = new String[]{ "You have unsaved changes", "Would you like to apply them?" @@ -358,13 +417,8 @@ public void showModalDialog(Component c) { dialog.setModal(true); dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); dialog.addWindowListener(new WindowAdapter() { - @Override public void windowClosing(WindowEvent e) { - SwingUtilities.invokeLater( new Runnable() { - public void run() { - dismissDialog(true); - } - } ); + dismissDialog(); } }); dialog.setContentPane(this); @@ -376,74 +430,32 @@ public void run() { } public void showDialog(Component c) { - getDialog(c); - if (c != null) { - dialog.setLocationRelativeTo(c); - } - dialog.setVisible(true); - } - - /** - * allow clients to get at the dialog so it can be positioned. - * @param c parent, if initializing. - * @return the dialog. - */ - public JDialog getDialog( Component c ) { if (dialog == null) { - Container top=null; - if ( c!=null ) { - top = SwingUtilities.getAncestorOfClass(Window.class, c); - if ( top==null && c instanceof JFrame ) top= (JFrame)c; - } + Container top = (c == null ? null : SwingUtilities.getAncestorOfClass(Window.class, c)); if (top instanceof JFrame) { dialog = new JDialog((JFrame) top); } else if (top instanceof JDialog) { dialog = new JDialog((JDialog) top); } else { dialog = new JDialog(); - } - if ( this.bean==null ) { - dialog.setTitle("Property Editor"); - } else { - dialog.setTitle("Property Editor for "+this.bean ); } dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); dialog.addWindowListener(new WindowAdapter() { - @Override public void windowClosing(WindowEvent e) { - SwingUtilities.invokeLater( new Runnable() { - public void run() { - dismissDialog(true); - } - } ); + dismissDialog(); } }); dialog.setContentPane(this); dialog.pack(); - - } else { - if ( c!=dialog.getParent()) { - logger.warning("properties dialog parent cannot change."); - } } - return dialog; - } - - /** - * display the dialog, and use the given image for the icon. - * @param c the parent focus - * @param title the dialog title. - * @param icon the icon branding the application. - */ - public void showDialog( Component c, String title, Image icon ) { - showDialog(c); - dialog.setTitle(title); - //dialog.setIconImage(icon); // java6 + if (c != null) { + dialog.setLocationRelativeTo(c); + } + dialog.setVisible(true); } - @Override public void doLayout() { if (SwingUtilities.isDescendingFrom(this, dialog)) { closeButton.setVisible(true); @@ -453,27 +465,12 @@ public void doLayout() { super.doLayout(); } - /** - * add a save button, and perform the given action. - * @param abstractAction the action to perform. - */ - public void addSaveAction(final AbstractAction abstractAction) { - buttonPanel.add( new JButton( new AbstractAction( "Save") { - @Override - public void actionPerformed(ActionEvent e) { - globalApplyChanges(); - refresh(); - abstractAction.actionPerformed(e); - } - })); - } - class PropertyTableMouseListener extends MouseAdapter { - @Override public void mouseClicked(MouseEvent event) { Point p = event.getPoint(); int row = table.rowAtPoint(p); + int column = table.columnAtPoint(p); TreeTableModel model = (TreeTableModel) table.getModel(); PropertyTreeNodeInterface node = (PropertyTreeNodeInterface) model.getNodeForRow(row); @@ -485,20 +482,10 @@ public void mouseClicked(MouseEvent event) { } } - @Override public void mousePressed(MouseEvent event) { Point p = event.getPoint(); focusRow = table.rowAtPoint(p); - if (event.isPopupTrigger()) { - popupMenu.show(PropertyEditor.this.table, event.getX(), event.getY()); - } - } - - @Override - public void mouseReleased(MouseEvent event) { - Point p = event.getPoint(); - focusRow = table.rowAtPoint(p); - if (event.isPopupTrigger()) { + if (event.getButton() == MouseEvent.BUTTON3) { popupMenu.show(PropertyEditor.this.table, event.getX(), event.getY()); } } diff --git a/dasCore/src/org/das2/components/propertyeditor/PropertyEditorAdapter.java b/dasCore/src/org/das2/components/propertyeditor/PropertyEditorAdapter.java index 2a69fb272..f1c20215b 100644 --- a/dasCore/src/org/das2/components/propertyeditor/PropertyEditorAdapter.java +++ b/dasCore/src/org/das2/components/propertyeditor/PropertyEditorAdapter.java @@ -16,7 +16,6 @@ import java.beans.PropertyEditor; import java.util.EventObject; import javax.swing.JButton; -import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.JTextField; @@ -65,14 +64,9 @@ public Component getTableCellEditorComponent(JTable table, Object value, boolean editor = getEditor(pd); if (editor == null) { cancelCellEditing(); - return new JLabel(); } else { - if ( value==org.das2.components.propertyeditor.PropertyEditor.MULTIPLE ) { - editor.setValue(((PeerPropertyTreeNode)node).leader.getValueAt(1)); - } else { - editor.setValue(value); - } + editor.setValue(value); } if (editor instanceof TableCellEditor) { @@ -127,7 +121,6 @@ public boolean shouldSelectCell(EventObject eventObject) { } public boolean stopCellEditing() { - if (state == null) return false; boolean stopped = state.stop(); if (stopped) { fireEditingStopped(); @@ -164,7 +157,7 @@ private void fireEditingStopped() { private static interface EditorState { void cancel(); - boolean stop(); //TODO: document me--return value is rarely used + boolean stop(); Component getEditorComponent(JTable table, boolean selected, int rowIndex, int columnIndex); } diff --git a/dasCore/src/org/das2/components/propertyeditor/PropertyTreeNode.java b/dasCore/src/org/das2/components/propertyeditor/PropertyTreeNode.java index 1ca9d29c1..589ccd869 100644 --- a/dasCore/src/org/das2/components/propertyeditor/PropertyTreeNode.java +++ b/dasCore/src/org/das2/components/propertyeditor/PropertyTreeNode.java @@ -12,7 +12,7 @@ import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; -public class PropertyTreeNode implements PropertyTreeNodeInterface { +class PropertyTreeNode implements PropertyTreeNodeInterface { protected static final Object[] NULL_ARGS = new Object[0]; @@ -173,8 +173,8 @@ public Object getDisplayValue() { boolean allowsChildren= getAllowsChildren(); if ( allowsChildren ) { - /*String ss= String.valueOf(value); - if ( ss.length()<100 ) { + String ss= String.valueOf(value); + /*if ( ss.length()<100 ) { return ss; } else { return ss.substring(0,100) + "..."; @@ -228,7 +228,7 @@ public void flush() { if (dirty) { DasLogger.getLogger( DasLogger.DASML_LOG).fine("flushing property "+absPropertyName()+"="+value ); Method writeMethod = propertyDescriptor.getWriteMethod(); - writeMethod.invoke(parent.value, new Object[]{value}); + writeMethod.invoke(parent.value, new Object[]{value} ); dirty = false; } if (childDirty) { diff --git a/dasCore/src/org/das2/components/propertyeditor/PropertyTreeNodeInterface.java b/dasCore/src/org/das2/components/propertyeditor/PropertyTreeNodeInterface.java index c5993307e..75947556a 100644 --- a/dasCore/src/org/das2/components/propertyeditor/PropertyTreeNodeInterface.java +++ b/dasCore/src/org/das2/components/propertyeditor/PropertyTreeNodeInterface.java @@ -22,8 +22,6 @@ public interface PropertyTreeNodeInterface extends TreeNode, TreeTableNode { boolean isDirty(); - // this should really be called revert, since it reads the value of the object into - // the node. This is the action that is taken when the "refresh" button is pressed. void refresh(); void setTreeModel(DefaultTreeModel treeModel); diff --git a/dasCore/src/org/das2/components/treetable/TreeTableModel.java b/dasCore/src/org/das2/components/treetable/TreeTableModel.java index 7de2bf652..c7f941935 100644 --- a/dasCore/src/org/das2/components/treetable/TreeTableModel.java +++ b/dasCore/src/org/das2/components/treetable/TreeTableModel.java @@ -8,6 +8,7 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; public class TreeTableModel extends AbstractTableModel implements TableModel { @@ -20,10 +21,10 @@ public TreeTableModel(TreeTableNode root, JTree tree) { this.root = root; this.tree = tree; tree.addTreeExpansionListener(new TreeTableTreeListener()); + TreeModelListener treeModelListener= new TreeTableTreeModelListener(); tree.getModel().addTreeModelListener(new TreeTableTreeModelListener()); } - @Override public Class getColumnClass(int columnIndex) { return root.getColumnClass(columnIndex); } @@ -32,7 +33,6 @@ public int getColumnCount() { return root.getColumnCount(); } - @Override public String getColumnName(int columnIndex) { return root.getColumnName(columnIndex); } @@ -89,17 +89,12 @@ public void setRoot(TreeTableNode node) { private class TreeTableTreeModelListener implements TreeModelListener { public void treeNodesChanged(TreeModelEvent e) { - TreePath path = e.getTreePath(); - int minRow = Integer.MAX_VALUE; - int maxRow = Integer.MIN_VALUE; - for (Object child : e.getChildren()) { - TreePath childPath = path.pathByAddingChild(child); - int row = tree.getRowForPath(childPath); - if ( row>-1 && row>maxRow ) maxRow= row; - if ( row>-1 && row7) { - recentFiles.remove(7); - } - Preferences prefs= Preferences.userNodeForPackage(PersistentStateSupport.class); - prefs.put( "PersistentStateSupport"+ext+"_recent", getRencentFilesString() ); - refreshRecentFilesMenu(); - } - - public Action createOpenAction() { - return new AbstractAction("Open...") { - public void actionPerformed( ActionEvent ev ) { - try { - JFileChooser chooser = new JFileChooser(); - if ( getCurrentFile()!=null ) chooser.setCurrentDirectory(getCurrentFile().getParentFile()); - chooser.setFileFilter( simpleFilter( "*"+ext ) ); - int result = chooser.showOpenDialog(component); - if (result == JFileChooser.APPROVE_OPTION) { - open( chooser.getSelectedFile() ); - addToRecent(getCurrentFile()); - if ( saveMenuItem!=null ) saveMenuItem.setText("Save"); - } - } catch ( Exception e ) { - throw new RuntimeException(e); - } - } - }; - } - - /** - * override me. If open fails, throw an exception. - */ - protected void openImpl( File file ) throws Exception { - Document document= readDocument( file ); - strategy.deserialize( document, DasProgressPanel.createFramed("deserializing") ); - } - - private void open( final File file ) { - setOpening( true ); - Runnable run = new Runnable() { - public void run() { - try { - if ( !file.exists() ) { - JOptionPane.showMessageDialog(component,"File not found: "+file, "File not found", JOptionPane.WARNING_MESSAGE ); - return; - } - openImpl(file); - setOpening( false ); - setDirty(false); - setCurrentFile(file); - setCurrentFileOpened(true); - update(); - } catch ( IOException e ) { - throw new RuntimeException(e); - } catch ( ParserConfigurationException e ) { - throw new RuntimeException(e); - } catch ( SAXException e ) { - throw new RuntimeException(e); - } catch ( Exception e ) { - throw new RuntimeException(e); - } - } - }; - new Thread( run, "PersistentStateSupport.open" ).start(); - } - - /** - * @deprecated What is the purpose of this method? - */ - public void close() { - setCurrentFile(null); - } - - public void markDirty() { - this.setDirty( true ); - update(); - } - - private void update() { - if ( currentFileLabel!=null ) this.currentFileLabel.setText( getCurrentFile() + ( dirty ? " *" : "" ) ); - } - /** Creates a new instance of PersistentStateSupport */ - public PersistentStateSupport() { - } - - /** - * Utility field used by bound properties. - */ - private final java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); - - /** - * Adds a PropertyChangeListener to the listener list. - * @param l The listener to add. - */ - public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { - propertyChangeSupport.addPropertyChangeListener(l); - } - - /** - * Removes a PropertyChangeListener from the listener list. - * @param l The listener to remove. - */ - public void removePropertyChangeListener(java.beans.PropertyChangeListener l) { - propertyChangeSupport.removePropertyChangeListener(l); - } - - /** - * Getter for property dirty. - * @return Value of property dirty. - */ - public boolean isDirty() { - return this.dirty; - } - - /** - * Setter for property dirty. - * @param dirty New value of property dirty. - */ - public void setDirty(boolean dirty) { - boolean oldDirty = this.dirty; - this.dirty = dirty; - propertyChangeSupport.firePropertyChange ( PROPERTY_DIRTY, Boolean.valueOf(oldDirty), Boolean.valueOf(dirty) ); - } - - public File getCurrentFile() { - return currentFile; - } - - public void setCurrentFile(File currentFile) { - File oldFile = this.currentFile; - this.currentFile = currentFile; - propertyChangeSupport.firePropertyChange ( PROPERTY_CURRENT_FILE, oldFile, currentFile ); - } - - /** - * Holds value of property loading. - */ - private boolean opening; - - /** - * Property loading is true when a load operation is being performed. - * @return Value of property loading. - */ - public boolean isOpening() { - return this.opening; - } - - /** - * Holds value of property saving. - */ - private boolean saving; - - /** - * Property saving is true when a save operation is being performed. - * @return Value of property saving. - */ - public boolean isSaving() { - return this.saving; - } - - private void setOpening(boolean b) { - boolean old= this.opening; - this.opening= b; - propertyChangeSupport.firePropertyChange( PROPERTY_OPENING, Boolean.valueOf(old), Boolean.valueOf(b) ); - - } - - - private void setSaving(boolean b) { - boolean old= this.saving; - this.saving= b; - propertyChangeSupport.firePropertyChange( PROPERTY_SAVING, Boolean.valueOf(old), Boolean.valueOf(b) ); - - } - - /** - * Holds value of property currentFileOpened. - */ - private boolean currentFileOpened; - - /** - * Property currentFileOpened indicates if the current file has ever been opened. This - * is to handle the initial state where the current file is set, but should not be - * displayed because it has not been opened. - * @return Value of property currentFileOpened. - */ - public boolean isCurrentFileOpened() { - return this.currentFileOpened; - } - - /** - * Setter for property currentFileOpened. - * @param currentFileOpened New value of property currentFileOpened. - */ - public void setCurrentFileOpened(boolean currentFileOpened) { - boolean oldCurrentFileOpened = this.currentFileOpened; - this.currentFileOpened = currentFileOpened; - propertyChangeSupport.firePropertyChange ("currentFileOpened", Boolean.valueOf(oldCurrentFileOpened), Boolean.valueOf(currentFileOpened)); - } - -} diff --git a/dasCore/src/org/das2/dasml/Processor.java b/dasCore/src/org/das2/dasml/Processor.java deleted file mode 100644 index d6db7c209..000000000 --- a/dasCore/src/org/das2/dasml/Processor.java +++ /dev/null @@ -1,1075 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.dasml; - -import java.awt.Component; -import java.awt.Dimension; -import java.lang.reflect.InvocationTargetException; -import java.text.ParseException; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.das2.DasApplication; -import org.das2.DasException; -import org.das2.DasNameException; -import org.das2.DasPropertyException; -import org.das2.NameContext; -import org.das2.dataset.DataSetDescriptor; -import org.das2.datum.Datum; -import org.das2.datum.TimeUtil; -import org.das2.datum.Units; -import org.das2.graph.DasAxis; -import org.das2.graph.DasCanvas; -import org.das2.graph.DasCanvasComponent; -import org.das2.graph.DasColorBar; -import org.das2.graph.DasColumn; -import org.das2.graph.DasDevicePosition; -import org.das2.graph.DasLabelAxis; -import org.das2.graph.DasPlot; -import org.das2.graph.DasRow; -import org.das2.graph.DefaultPlotSymbol; -import org.das2.graph.PlotSymbol; -import org.das2.graph.Psym; -import org.das2.graph.Renderer; -import org.das2.graph.SeriesRenderer; -import org.das2.graph.SpectrogramRenderer; -import org.das2.graph.StackedHistogramRenderer; -import org.das2.graph.SymColor; -import org.das2.graph.SymbolLineRenderer; -import org.das2.system.DasLogger; -import org.das2.util.DasExceptionHandler; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -/** - * Introduced to factor out all the dasml stuff from the graph package. - * @author jbf - */ -public class Processor { - /** Process an <axis> element. - * - * @param element The DOM tree node that represents the element - */ - static DasAxis processAxisElement(Element element, FormBase form) throws DasPropertyException, DasNameException, ParseException { - String name = element.getAttribute("name"); - boolean log = element.getAttribute(DasAxis.PROP_LOG).equals("true"); - Datum dataMinimum; - Datum dataMaximum; - if ("TIME".equals(element.getAttribute("units"))) { - String min = element.getAttribute("dataMinimum"); - String max = element.getAttribute("dataMaximum"); - dataMinimum = (min == null || min.equals("") ? TimeUtil.create("1979-02-26") : TimeUtil.create(min)); - dataMaximum = (max == null || max.equals("") ? TimeUtil.create("1979-02-27") : TimeUtil.create(max)); - } else { - String min = element.getAttribute("dataMinimum"); - String max = element.getAttribute("dataMaximum"); - dataMinimum = (min == null || min.equals("") ? Datum.create(1.0) : Datum.create(Double.parseDouble(min))); - dataMaximum = (max == null || max.equals("") ? Datum.create(10.0) : Datum.create(Double.parseDouble(max))); - } - int orientation = parseOrientationString(element.getAttribute("orientation")); - DasAxis axis = new DasAxis(dataMinimum, dataMaximum, orientation, log); - String rowString = element.getAttribute("row"); - if (!rowString.equals("")) { - DasRow row = (DasRow) form.checkValue(rowString, DasRow.class, ""); - axis.setRow(row); - } - String columnString = element.getAttribute("column"); - if (!columnString.equals("")) { - DasColumn column = (DasColumn) form.checkValue(columnString, DasColumn.class, ""); - axis.setColumn(column); - } - - axis.setLabel(element.getAttribute(DasAxis.PROP_LABEL)); - axis.setOppositeAxisVisible(!element.getAttribute(DasAxis.PROP_OPPOSITE_AXIS_VISIBLE).equals("false")); - axis.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); - - axis.setDasName(name); - DasApplication app = form.getDasApplication(); - NameContext nc = app.getNameContext(); - nc.put(name, axis); - - return axis; - } - - /** TODO - * @param i - * @return - */ - protected static String orientationToString(int i) { - switch (i) { - case DasAxis.TOP: - return "top"; - case DasAxis.BOTTOM: - return "bottom"; - case DasAxis.LEFT: - return "left"; - case DasAxis.RIGHT: - return "right"; - default: - throw new IllegalStateException("invalid orienation: " + i); - } - } - - /** TODO - * @param orientationString - * @return - */ - protected static int parseOrientationString(String orientationString) { - if (orientationString.equals("horizontal")) { - return DasAxis.HORIZONTAL; - } else if (orientationString.equals("vertical")) { - return DasAxis.VERTICAL; - } else if (orientationString.equals("left")) { - return DasAxis.LEFT; - } else if (orientationString.equals("right")) { - return DasAxis.RIGHT; - } else if (orientationString.equals("top")) { - return DasAxis.TOP; - } else if (orientationString.equals("bottom")) { - return DasAxis.BOTTOM; - } else { - throw new IllegalArgumentException("Invalid orientation: " + orientationString); - } - } - - /** TODO - * @param document - * @return - */ - public static Element getDOMElement( DasAxis axis, Document document) { - Element element; - if (axis.isAttached()) { - element = document.createElement("attachedaxis"); - } else { - element = document.createElement("axis"); - } - if (axis.isAttached()) { - element.setAttribute("ref", axis.getMasterAxis().getDasName()); - } else { - String minimumStr = axis.getDataMinimum().toString(); - element.setAttribute("dataMinimum", minimumStr); - String maximumStr = axis.getDataMaximum().toString(); - element.setAttribute("dataMaximum", maximumStr); - } - - element.setAttribute("name", axis.getDasName()); - element.setAttribute("row", axis.getRow().getDasName()); - element.setAttribute("column", axis.getColumn().getDasName()); - - element.setAttribute(DasAxis.PROP_LABEL, axis.getLabel()); - element.setAttribute(DasAxis.PROP_LOG, Boolean.toString(axis.isLog())); - element.setAttribute("tickLabelsVisible", Boolean.toString(axis.isTickLabelsVisible())); - element.setAttribute(DasAxis.PROP_OPPOSITE_AXIS_VISIBLE, Boolean.toString(axis.isOppositeAxisVisible())); - element.setAttribute("animated", Boolean.toString(axis.isAnimated())); - element.setAttribute("orientation", orientationToString(axis.getOrientation())); - - return element; - } - - static DasAxis processTimeaxisElement(Element element, FormBase form) throws org.das2.DasPropertyException, org.das2.DasNameException, DasException, java.text.ParseException { - String name = element.getAttribute("name"); - Datum timeMinimum = TimeUtil.create(element.getAttribute("timeMinimum")); - Datum timeMaximum = TimeUtil.create(element.getAttribute("timeMaximum")); - int orientation = parseOrientationString(element.getAttribute("orientation")); - - DasAxis timeaxis = new DasAxis(timeMinimum, timeMaximum, orientation); - - String rowString = element.getAttribute("row"); - if (!rowString.equals("")) { - DasRow row = (DasRow) form.checkValue(rowString, DasRow.class, ""); - timeaxis.setRow(row); - } - String columnString = element.getAttribute("column"); - if (!columnString.equals("")) { - DasColumn column = (DasColumn) form.checkValue(columnString, DasColumn.class, ""); - timeaxis.setColumn(column); - } - - timeaxis.setDataPath(element.getAttribute("dataPath")); - timeaxis.setDrawTca(element.getAttribute("showTca").equals("true")); - timeaxis.setLabel(element.getAttribute(DasAxis.PROP_LABEL)); - timeaxis.setOppositeAxisVisible(!element.getAttribute(DasAxis.PROP_OPPOSITE_AXIS_VISIBLE).equals("false")); - timeaxis.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); - - timeaxis.setDasName(name); - DasApplication app = form.getDasApplication(); - NameContext nc = app.getNameContext(); - nc.put(name, timeaxis); - - return timeaxis; - } - - /** Process a <attachedaxis> element. - * - * @param element The DOM tree node that represents the element - */ - static DasAxis processAttachedaxisElement(Element element, FormBase form) throws DasPropertyException, DasNameException { - String name = element.getAttribute("name"); - DasAxis ref = (DasAxis) form.checkValue(element.getAttribute("ref"), DasAxis.class, ""); - int orientation = (element.getAttribute("orientation").equals("horizontal") ? DasAxis.HORIZONTAL : DasAxis.VERTICAL); - - DasAxis axis = ref.createAttachedAxis(orientation); - - String rowString = element.getAttribute("row"); - if (!rowString.equals("")) { - DasRow row = (DasRow) form.checkValue(rowString, DasRow.class, ""); - axis.setRow(row); - } - String columnString = element.getAttribute("column"); - if (!columnString.equals("")) { - DasColumn column = (DasColumn) form.checkValue(columnString, DasColumn.class, ""); - axis.setColumn(column); - } - - axis.setDataPath(element.getAttribute("dataPath")); - axis.setDrawTca(element.getAttribute("showTca").equals("true")); - axis.setLabel(element.getAttribute(DasAxis.PROP_LABEL)); - axis.setOppositeAxisVisible(!element.getAttribute(DasAxis.PROP_OPPOSITE_AXIS_VISIBLE).equals("false")); - axis.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); - - axis.setDasName(name); - DasApplication app = form.getDasApplication(); - NameContext nc = app.getNameContext(); - nc.put(name, axis); - - return axis; - } - - /** TODO - * @param name - * @return - */ - public static DasAxis createNamedAxis(String name) { - DasAxis axis = new DasAxis(Datum.create(1.0, Units.dimensionless), Datum.create(10.0, Units.dimensionless), DasAxis.HORIZONTAL); - if (name == null) { - name = "axis_" + Integer.toHexString(System.identityHashCode(axis)); - } - try { - axis.setDasName(name); - } catch (DasNameException dne) { - DasExceptionHandler.handle(dne); - } - return axis; - } - - /** TODO - * @return - * @param document - */ - public static Element getDOMElement( DasCanvas canvas, Document document) { - Element element = document.createElement("canvas"); - Dimension size = canvas.getPreferredSize(); - element.setAttribute("name", canvas.getDasName()); - element.setAttribute("width", Integer.toString(size.width)); - element.setAttribute("height", Integer.toString(size.height)); - - for (int index = 0; index < canvas.devicePositionList().size(); index++) { - Object obj = canvas.devicePositionList().get(index); - if (obj instanceof DasRow) { - DasRow row = (DasRow) obj; - element.appendChild(getDOMElement(row,document)); - } else if (obj instanceof DasColumn) { - DasColumn column = (DasColumn) obj; - element.appendChild(getDOMElement(column,document)); - } - } - - Component[] components = canvas.getCanvasComponents(); - Map elementMap = new LinkedHashMap(); - - //THREE PASS ALGORITHM. - //1. Process all DasAxis components. - // Add all , , elements to elementList. - //2. Process all DasColorBar components. - // Remove all elements that correspond to axis property of colorbars. - // Add all elements to elementList. - //3. Process all DasSpectrogramPlot and DasPlot components. - // Remove all , , , and elements - // that correspond to xAxis, yAxis, and colorbar properties of - // plots spectrograms and spectrogram renderers. - // Add all elements to elementList. - - for (int index = 0; index < components.length; index++) { - if (components[index] instanceof DasAxis) { - DasAxis axis = (DasAxis) components[index]; - elementMap.put(axis.getDasName(), getDOMElement( axis,document)); - } - } - for (int index = 0; index < components.length; index++) { - if (components[index] instanceof DasColorBar) { - DasColorBar colorbar = (DasColorBar) components[index]; - elementMap.put(colorbar.getDasName(), getDOMElement(colorbar,document)); - } - } - for (int index = 0; index < components.length; index++) { - if (components[index] instanceof DasPlot) { - DasPlot plot = (DasPlot) components[index]; - elementMap.remove(plot.getXAxis().getDasName()); - elementMap.remove(plot.getYAxis().getDasName()); - Renderer[] renderers = plot.getRenderers(); - for (int i = 0; i < renderers.length; i++) { - if (renderers[i] instanceof SpectrogramRenderer) { - SpectrogramRenderer spectrogram = (SpectrogramRenderer) renderers[i]; - elementMap.remove(spectrogram.getColorBar().getDasName()); - } - } - elementMap.put(plot.getDasName(), getDOMElement(plot,document)); - } - } - - for (Iterator iterator = elementMap.values().iterator(); iterator.hasNext();) { - Element e = (Element) iterator.next(); - if (e != null) { - element.appendChild(e); - } - } - return element; - } - - /** Process a <canvas> element. - * - * @param form - * @param element The DOM tree node that represents the element - * @throws DasPropertyException - * @throws DasNameException - * @throws ParsedExpressionException - * @return - */ - public static DasCanvas processCanvasElement(Element element, FormBase form) - throws DasPropertyException, DasNameException, DasException, ParsedExpressionException, java.text.ParseException { - try { - Logger log = DasLogger.getLogger(DasLogger.DASML_LOG); - - String name = element.getAttribute("name"); - int width = Integer.parseInt(element.getAttribute("width")); - int height = Integer.parseInt(element.getAttribute("height")); - - DasApplication app = form.getDasApplication(); - NameContext nc = app.getNameContext(); - - DasCanvas canvas = new DasCanvas(width, height); - - NodeList children = element.getChildNodes(); - int childCount = children.getLength(); - for (int index = 0; index < childCount; index++) { - Node node = children.item(index); - log.fine("node=" + node.getNodeName()); - if (node instanceof Element) { - String tagName = node.getNodeName(); - if (tagName.equals("row")) { - DasRow row = processRowElement((Element) node, canvas, form); - } else if (tagName.equals("column")) { - DasColumn column = processColumnElement((Element) node, canvas, form); - } else if (tagName.equals("axis")) { - DasAxis axis = processAxisElement((Element) node, form); - canvas.add(axis); - } else if (tagName.equals("timeaxis")) { - DasAxis timeaxis = processTimeaxisElement((Element) node, form); - canvas.add(timeaxis); - } else if (tagName.equals("attachedaxis")) { - DasAxis attachedaxis = processAttachedaxisElement((Element) node, form); - canvas.add(attachedaxis); - } else if (tagName.equals("colorbar")) { - DasColorBar colorbar = processColorbarElement((Element) node, form); - canvas.add(colorbar); - } else if (tagName.equals("plot")) { - DasPlot plot = processPlotElement((Element) node, form); - canvas.add(plot); - } else if (tagName.equals("spectrogram")) { - DasPlot plot = processPlotElement((Element) node, form); - canvas.add(plot); - } - - } - } - canvas.setDasName(name); - nc.put(name, canvas); - - return canvas; - } catch (org.das2.DasPropertyException dpe) { - if (!element.getAttribute("name").equals("")) { - dpe.setObjectName(element.getAttribute("name")); - } - throw dpe; - } - } - - /** Process a <row> element. - * - * @param element The DOM tree node that represents the element - */ - static DasRow processRowElement(Element element, DasCanvas canvas, FormBase form) throws DasException { - String name = element.getAttribute("name"); - double minimum = Double.parseDouble(element.getAttribute("minimum")); - double maximum = Double.parseDouble(element.getAttribute("maximum")); - DasRow row = new DasRow(canvas, minimum, maximum); - row.setDasName(name); - DasApplication app = form.getDasApplication(); - NameContext nc = app.getNameContext(); - nc.put(name, row); - return row; - } - - public static Element getDOMElement( DasRow row, Document document) { - Element element = document.createElement("row"); - element.setAttribute("name", row.getDasName()); - element.setAttribute("minimum", Double.toString(row.getMinimum())); - element.setAttribute("maximum", Double.toString(row.getMaximum())); - return element; - } - - /** Process a <column7gt; element. - * - * @param element The DOM tree node that represents the element - */ - static DasColumn processColumnElement(Element element, DasCanvas canvas, FormBase form) throws DasException { - String name = element.getAttribute("name"); - double minimum - = Double.parseDouble(element.getAttribute("minimum")); - double maximum - = Double.parseDouble(element.getAttribute("maximum")); - DasColumn column = new DasColumn(canvas, minimum, maximum); - column.setDasName(name); - DasApplication app = form.getDasApplication(); - NameContext nc = app.getNameContext(); - nc.put(name, column); - return column; - } - - public static Element getDOMElement(DasColumn column, Document document) { - Element element = document.createElement("column"); - element.setAttribute("name", column.getDasName()); - element.setAttribute("minimum", Double.toString(column.getMinimum())); - element.setAttribute("maximum", Double.toString(column.getMaximum())); - return element; - } - - public static SpectrogramRenderer processSpectrogramElement( - Element element, DasPlot parent, FormBase form) throws DasPropertyException, DasNameException, ParseException { - String dataSetID = element.getAttribute("dataSetID"); - DasColorBar colorbar = null; - - NodeList children = element.getChildNodes(); - for (int index = 0; index < children.getLength(); index++) { - Node node = children.item(index); - if (node instanceof Element && node.getNodeName().equals("zAxis")) { - colorbar = processZAxisElement((Element) node, form); - } - } - - if (colorbar == null) { - try { - colorbar = (DasColorBar) form.checkValue(element.getAttribute("colorbar"), DasColorBar.class, ""); - } catch (DasPropertyException dpe) { - dpe.setPropertyName("colorbar"); - throw dpe; - } - } - - SpectrogramRenderer renderer = new SpectrogramRenderer( null, colorbar); - parent.addRenderer(renderer); - try { - renderer.setDataSetID(dataSetID); - } catch (DasException de) { - DasExceptionHandler.handle(de); - } - return renderer; - } - - private static DasColorBar processZAxisElement(Element element, FormBase form) throws DasPropertyException, DasNameException, ParseException { - NodeList children = element.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node node = children.item(i); - if (node instanceof Element) { - if (node.getNodeName().equals("colorbar")) { - return processColorbarElement((Element) node, form); - } - } - } - return null; - } - - - /** Process a <colorbar> element. - * - * @param element The DOM tree node that represents the element - */ - static DasColorBar processColorbarElement(Element element, FormBase form) throws org.das2.DasPropertyException,org.das2.DasNameException, java.text.ParseException { - String name = element.getAttribute("name"); - boolean log = element.getAttribute("log").equals("true"); - String unitStr = element.getAttribute("units"); - if (unitStr == null) { - unitStr = ""; - } - Datum dataMinimum; - Datum dataMaximum; - if (unitStr.equals("TIME")) { - String min = element.getAttribute("dataMinimum"); - String max = element.getAttribute("dataMaximum"); - dataMinimum = (min == null || min.equals("") ? TimeUtil.create("1979-02-26") : TimeUtil.create(min)); - dataMaximum = (max == null || max.equals("") ? TimeUtil.create("1979-02-27") : TimeUtil.create(max)); - } else { - Units units = Units.getByName(unitStr); - String min = element.getAttribute("dataMinimum"); - String max = element.getAttribute("dataMaximum"); - dataMinimum = (min == null || min.equals("") ? Datum.create(1.0, units) : Datum.create(Double.parseDouble(min), units)); - dataMaximum = (max == null || max.equals("") ? Datum.create(10.0, units) : Datum.create(Double.parseDouble(max), units)); - } - int orientation = parseOrientationString(element.getAttribute("orientation")); - - DasColorBar cb = new DasColorBar(dataMinimum, dataMaximum, orientation, log); - - String rowString = element.getAttribute("row"); - if (!rowString.equals("")) { - DasRow row = (DasRow)form.checkValue(rowString, DasRow.class, ""); - cb.setRow(row); - } - String columnString = element.getAttribute("column"); - if (!columnString.equals("")) { - DasColumn column = (DasColumn)form.checkValue(columnString, DasColumn.class, ""); - cb.setColumn(column); - } - - cb.setLabel(element.getAttribute("label")); - cb.setOppositeAxisVisible(!element.getAttribute("oppositeAxisVisible").equals("false")); - cb.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); - cb.setType(DasColorBar.Type.parse(element.getAttribute(DasColorBar.PROPERTY_TYPE))); - - cb.setDasName(name); - DasApplication app = form.getDasApplication(); - NameContext nc = app.getNameContext(); - nc.put(name, cb); - - return cb; - } - - public static Element getDOMElement( DasColorBar colorbar, Document document) { - Element element = document.createElement("colorbar"); - String minimumStr = colorbar.getDataMinimum().toString(); - element.setAttribute("dataMinimum", minimumStr); - String maximumStr = colorbar.getDataMaximum().toString(); - element.setAttribute("dataMaximum", maximumStr); - - element.setAttribute("name", colorbar.getDasName()); - element.setAttribute("row", colorbar.getRow().getDasName()); - element.setAttribute("column", colorbar.getColumn().getDasName()); - - element.setAttribute("label", colorbar.getLabel()); - element.setAttribute("log", Boolean.toString(colorbar.isLog())); - element.setAttribute("tickLabelsVisible", Boolean.toString(colorbar.isTickLabelsVisible())); - element.setAttribute("oppositeAxisVisible", Boolean.toString(colorbar.isOppositeAxisVisible())); - element.setAttribute("animated", Boolean.toString(colorbar.isAnimated())); - element.setAttribute("orientation", orientationToString(colorbar.getOrientation())); - element.setAttribute(DasColorBar.PROPERTY_TYPE, colorbar.getType().toString()); - - return element; - } - - public static DasColorBar createNamedColorBar(String name) { - DasColorBar cb = new DasColorBar(Datum.create(1.0, Units.dimensionless), Datum.create(10.0, Units.dimensionless), false); - if (name == null) { - name = "colorbar_" + Integer.toHexString(System.identityHashCode(cb)); - } - try { - cb.setDasName(name); - } catch (org.das2.DasNameException dne) { - org.das2.util.DasExceptionHandler.handle(dne); - } - return cb; - } - - public static DasPlot processPlotElement(Element element, FormBase form) throws org.das2.DasPropertyException, org.das2.DasNameException, DasException, java.text.ParseException { - String name = element.getAttribute("name"); - - DasRow row = (DasRow) form.checkValue(element.getAttribute("row"), DasRow.class, ""); - DasColumn column = (DasColumn) form.checkValue(element.getAttribute("column"), DasColumn.class, ""); - - DasAxis xAxis = null; - DasAxis yAxis = null; - DasColorBar colorbar = null; - - //Get the axes - NodeList children = element.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node node = children.item(i); - if (node instanceof Element) { - if (node.getNodeName().equals("xAxis")) { - xAxis = processXAxisElement((Element) node, row, column, form); - } else if (node.getNodeName().equals("yAxis")) { - yAxis = processYAxisElement((Element) node, row, column, form); - } else if (node.getNodeName().equals("zAxis")) { - colorbar = processZAxisElement((Element) node, row, column, form); - } - - } - } - - if (xAxis == null) { - xAxis = (DasAxis) form.checkValue(element.getAttribute("xAxis"), DasAxis.class, " or "); - } - if (yAxis == null) { - yAxis = (DasAxis) form.checkValue(element.getAttribute("yAxis"), DasAxis.class, " or "); - } - - DasPlot plot = new DasPlot(xAxis, yAxis); - - if (element.getNodeName().equals("spectrogram")) { - SpectrogramRenderer rend = new SpectrogramRenderer(null, colorbar); - plot.addRenderer(rend); - } - - plot.setTitle(element.getAttribute("title")); - plot.setDasName(name); - plot.setRow(row); - plot.setColumn(column); - DasApplication app = form.getDasApplication(); - NameContext nc = app.getNameContext(); - nc.put(name, plot); - - for (int i = 0; i < children.getLength(); i++) { - Node node = children.item(i); - if (node instanceof Element) { - if (node.getNodeName().equals("renderers")) { - processRenderersElement((Element) node, plot, form); - } - } - } - - return plot; - } - - private static DasAxis processXAxisElement(Element element, DasRow row, DasColumn column, FormBase form) throws org.das2.DasPropertyException, org.das2.DasNameException, DasException, java.text.ParseException { - NodeList children = element.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node node = children.item(i); - if (node instanceof Element) { - Element e = (Element) node; - if (node.getNodeName().equals("axis")) { - DasAxis axis = processAxisElement(e, form); - if (!axis.isHorizontal()) { - axis.setOrientation(DasAxis.HORIZONTAL); - } - return axis; - } else if (node.getNodeName().equals("timeaxis")) { - DasAxis axis = processTimeaxisElement(e, form); - if (!axis.isHorizontal()) { - axis.setOrientation(DasAxis.HORIZONTAL); - } - return axis; - } else if (node.getNodeName().equals("attachedaxis")) { - DasAxis axis = processAttachedaxisElement(e, form); - if (!axis.isHorizontal()) { - axis.setOrientation(DasAxis.HORIZONTAL); - } - return axis; - } - } - } - return null; - } - - private static DasAxis processYAxisElement(Element element, DasRow row, DasColumn column, FormBase form) throws org.das2.DasPropertyException, org.das2.DasNameException, org.das2.DasException, java.text.ParseException { - NodeList children = element.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node node = children.item(i); - if (node instanceof Element) { - Element e = (Element) node; - if (node.getNodeName().equals("axis")) { - DasAxis axis = processAxisElement(e, form); - if (axis.isHorizontal()) { - axis.setOrientation(DasAxis.VERTICAL); - } - return axis; - } else if (node.getNodeName().equals("timeaxis")) { - DasAxis axis = processTimeaxisElement(e, form); - if (axis.isHorizontal()) { - axis.setOrientation(DasAxis.VERTICAL); - } - return axis; - } else if (node.getNodeName().equals("attachedaxis")) { - DasAxis axis = processAttachedaxisElement(e, form); - if (axis.isHorizontal()) { - axis.setOrientation(DasAxis.VERTICAL); - } - return axis; - } - } - } - return null; - } - - private static DasColorBar processZAxisElement(Element element, DasRow row, DasColumn column, FormBase form) throws DasPropertyException, DasNameException, DasException, java.text.ParseException { - NodeList children = element.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node node = children.item(i); - if (node instanceof Element) { - if (node.getNodeName().equals("colorbar")) { - return processColorbarElement((Element) node, form); - } - } - } - return null; - } - - private static void processRenderersElement(Element element, DasPlot parent, FormBase form) throws org.das2.DasPropertyException, org.das2.DasNameException, org.das2.DasException, java.text.ParseException { - NodeList children = element.getChildNodes(); - for (int index = 0; index < children.getLength(); index++) { - Node node = children.item(index); - if (node instanceof Element) { - if (node.getNodeName().equals("spectrogram")) { - parent.addRenderer( processSpectrogramElement((Element) node, parent, form)); - } else if (node.getNodeName().equals("lineplot")) { - parent.addRenderer( processLinePlotElement((Element) node, parent, form)); - } - } - } - } - - public static SeriesRenderer processLinePlotElement(Element element, DasPlot parent, FormBase form) { - String dataSetID = element.getAttribute("dataSetID"); - PlotSymbol psym = DefaultPlotSymbol.BOX; //TODO: parsePsym(element.getAttribute("psym")); - SymColor color = SymColor.parseSymColor(element.getAttribute("color")); - SeriesRenderer renderer = new SeriesRenderer(); - parent.addRenderer(renderer); - float lineWidth = Float.parseFloat(element.getAttribute("lineWidth")); - try { - renderer.setDataSetID(dataSetID); - } catch (org.das2.DasException de) { - org.das2.util.DasExceptionHandler.handle(de); - } - renderer.setPsym(psym); - renderer.setColor(color); - renderer.setLineWidth(lineWidth); - return renderer; - } - - - public static Element getDOMElement(DasPlot plot,Document document) { - - Element element = document.createElement("plot"); - element.setAttribute("name", plot.getDasName()); - element.setAttribute("row", plot.getRow().getDasName()); - element.setAttribute("column", plot.getColumn().getDasName()); - element.setAttribute("title", plot.getTitle()); - - Element xAxisChild = document.createElement("xAxis"); - Element xAxisElement = getDOMElement(plot.getXAxis(),document); - xAxisElement.removeAttribute("orientation"); - if (xAxisElement.getAttribute("row").equals(plot.getRow().getDasName())) { - xAxisElement.removeAttribute("row"); - } - if (xAxisElement.getAttribute("column").equals(plot.getColumn().getDasName())) { - xAxisElement.removeAttribute("column"); - } - xAxisChild.appendChild(xAxisElement); - element.appendChild(xAxisChild); - - Element yAxisChild = document.createElement("yAxis"); - Element yAxisElement = getDOMElement(plot.getYAxis(),document); - yAxisElement.removeAttribute("orientation"); - if (yAxisElement.getAttribute("row").equals(plot.getRow().getDasName())) { - yAxisElement.removeAttribute("row"); - } - if (yAxisElement.getAttribute("column").equals(plot.getColumn().getDasName())) { - yAxisElement.removeAttribute("column"); - } - yAxisChild.appendChild(yAxisElement); - element.appendChild(yAxisChild); - - Renderer[] renderers = plot.getRenderers(); - if (renderers.length > 0) { - Element renderersChild = document.createElement("renderers"); - for (int index = 0; index < renderers.length; index++) { - renderersChild.appendChild( getDOMElement(renderers[index],document) ); - } - element.appendChild(renderersChild); - } - return element; - } - - public static DasPlot createNamedPlot(String name) { - DasAxis xAxis = createNamedAxis(null); - xAxis.setOrientation(DasAxis.BOTTOM); - DasAxis yAxis = createNamedAxis(null); - yAxis.setOrientation(DasAxis.LEFT); - DasPlot plot = new DasPlot(xAxis, yAxis); - if (name == null) { - name = "plot_" + Integer.toHexString(System.identityHashCode(plot)); - } - try { - plot.setDasName(name); - } catch (org.das2.DasNameException dne) { - org.das2.util.DasExceptionHandler.handle(dne); - } - return plot; - } - - public static Element getDOMElement( Renderer r, Document doc ) { - Element element=null; - if ( r instanceof SymbolLineRenderer ) { - SymbolLineRenderer slr= (SymbolLineRenderer)r; - element = doc.createElement("lineplot"); - element.setAttribute("dataSetID", slr.getDataSetID()); - element.setAttribute("psym", slr.getPsym().toString()); - element.setAttribute("color", slr.getColor().toString()); - } else if ( r instanceof SpectrogramRenderer ) { - SpectrogramRenderer spec= (SpectrogramRenderer)r; - element = doc.createElement("spectrogram"); - element.setAttribute("dataSetID", spec.getDataSetID()); - - Element zAxisChild = doc.createElement("zAxis"); - Element zAxisElement = getDOMElement( spec.getColorBar(),doc ); - if (zAxisElement.getAttribute("row").equals(spec.getParent().getRow().getDasName())) { - zAxisElement.removeAttribute("row"); - } - if (zAxisElement.getAttribute("column").equals(spec.getParent().getColumn().getDasName())) { - zAxisElement.removeAttribute("column"); - } - zAxisChild.appendChild(zAxisElement); - element.appendChild(zAxisChild); - - } else if ( r instanceof StackedHistogramRenderer ) { - StackedHistogramRenderer shr= (StackedHistogramRenderer)r; - element = doc.createElement("stackedHistogram"); - element.setAttribute("zAxis", shr.getZAxis().getDasName() ); - element.setAttribute("dataSetID", shr.getDataSetID() ); - - } - - return element; - } - - - public static Renderer processStackedHistogramElement(Element element, DasPlot parent, FormBase form) throws DasPropertyException, DasNameException, ParseException { - String dataSetID = element.getAttribute("dataSetID"); - - Renderer renderer = new StackedHistogramRenderer( parent, (DataSetDescriptor)null, (DasAxis)null, (DasLabelAxis)parent.getYAxis() ); - try { - renderer.setDataSetID(dataSetID); - } catch (DasException de) { - DasExceptionHandler.handle(de); - } - return renderer; - } - - /** - * @param name - * @param width - * @param height - * @return DasCanvas with a name. - */ - public static DasCanvas createFormCanvas(String name, int width, int height) { - DasCanvas canvas = new DasCanvas(width, height); - if (name == null) { - name = "canvas_" + Integer.toHexString(System.identityHashCode(canvas)); - } - try { - canvas.setDasName(name); - } catch (org.das2.DasNameException dne) { - org.das2.util.DasExceptionHandler.handle(dne); - } - return canvas; - } - - /** TODO - * @return - */ - public FormBase getForm( DasCanvas canvas ) { - Component parent = canvas.getParent(); - if (parent instanceof FormComponent) { - return ((FormComponent) parent).getForm(); - } - return null; - } - - public void deregisterComponent(DasCanvas canvas) { - DasApplication app = canvas.getDasApplication(); - if (app != null) { - NameContext nc = app.getNameContext(); - for (Iterator i = canvas.devicePositionList().iterator(); i.hasNext();) { - DasDevicePosition dp = (DasDevicePosition) i.next(); - try { - if (nc.get(dp.getDasName()) == dp) { - nc.remove(dp.getDasName()); - } - } catch (DasPropertyException dpe) { - //This exception would only occur due to some invalid state. - //So, wrap it and toss it. - IllegalStateException se = new IllegalStateException(dpe.toString()); - se.initCause(dpe); - throw se; - } catch (java.lang.reflect.InvocationTargetException ite) { - //This exception would only occur due to some invalid state. - //So, wrap it and toss it. - IllegalStateException se = new IllegalStateException(ite.toString()); - se.initCause(ite); - throw se; - } - } - for (int index = 0; index < canvas.getComponentCount(); index++) { - Component c = canvas.getComponent(index); - if (c instanceof DasCanvasComponent) { - DasCanvasComponent cc = (DasCanvasComponent) c; - try { - if (nc.get(cc.getDasName()) == cc) { - nc.remove(cc.getDasName()); - } - } catch (DasPropertyException dpe) { - //This exception would only occur due to some invalid state. - //So, wrap it and toss it. - IllegalStateException se = new IllegalStateException(dpe.toString()); - se.initCause(dpe); - throw se; - } catch (java.lang.reflect.InvocationTargetException ite) { - //This exception would only occur due to some invalid state. - //So, wrap it and toss it. - IllegalStateException se = new IllegalStateException(ite.toString()); - se.initCause(ite); - throw se; - } - } - } - try { - if (nc.get(canvas.getDasName()) == this) { - nc.remove(canvas.getDasName()); - } - } catch (DasPropertyException dpe) { - //This exception would only occur due to some invalid state. - //So, wrap it and toss it. - IllegalStateException se = new IllegalStateException(dpe.toString()); - se.initCause(dpe); - throw se; - } catch (java.lang.reflect.InvocationTargetException ite) { - //This exception would only occur due to some invalid state. - //So, wrap it and toss it. - IllegalStateException se = new IllegalStateException(ite.toString()); - se.initCause(ite); - throw se; - } - } - } - - public void registerComponent( DasCanvas canvas ) throws org.das2.DasException { - try { - DasApplication app = canvas.getDasApplication(); - if (app != null) { - NameContext nc = app.getNameContext(); - for (Iterator i = canvas.devicePositionList().iterator(); i.hasNext();) { - DasDevicePosition dp = (DasDevicePosition) i.next(); - nc.put(dp.getDasName(), dp); - } - for (int index = 0; index < canvas.getComponentCount(); index++) { - Component c = canvas.getComponent(index); - if (c instanceof DasCanvasComponent) { - DasCanvasComponent cc = (DasCanvasComponent) c; - nc.put(cc.getDasName(), cc); - } - } - nc.put(canvas.getDasName(), this); - } - } catch (DasNameException dne) { - deregisterComponent(canvas); - throw dne; - } - } - - public static final Pattern refPattern = Pattern.compile("\\$\\{([^\\}]+)\\}"); - public static final Pattern intPattern = Pattern.compile("-?(0|[1-9][0-9]*)"); - public static final Pattern floatPattern = Pattern.compile("-?[0-9]*(\\.[0-9]*)?([eE]-?[0-9]+)?"); - - protected static String replaceReferences( NameContext n, String str) throws DasPropertyException, InvocationTargetException { - Matcher matcher = refPattern.matcher(str); - while (matcher.find()) { - String name = matcher.group(1).trim(); - Object value = n.get(name); - str = matcher.replaceFirst(value.toString()); - matcher.reset(str); - } - return str; - } - - /** - * Parses the given String object in an attempt to - * produce the an object of the given type. - * - * @param valueString the given String - * @param type the given type - */ - public static Object parseValue( NameContext nameContext, String valueString, Class type) throws org.das2.dasml.ParsedExpressionException, InvocationTargetException, DasPropertyException { - Object parsedValue; - valueString = replaceReferences(nameContext,valueString); - if (type == String.class) { - return valueString; - } - else if (type == boolean.class || type == Boolean.class) { - if (valueString.equals("true")) return Boolean.TRUE; - if (valueString.equals("false")) return Boolean.FALSE; - ParsedExpression exp = new ParsedExpression(valueString); - Object o = exp.evaluate(nameContext); - if (!(o instanceof Boolean)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a boolean value"); - return o; - } - else if (type == int.class || type == Integer.class) { - if (intPattern.matcher(valueString).matches()) { - return new Integer(valueString); - } - ParsedExpression exp = new ParsedExpression(valueString); - Object o = exp.evaluate(nameContext); - if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); - return (o instanceof Integer ? (Integer)o : new Integer(((Number)o).intValue())); - } - else if (type == long.class || type == Long.class) { - if (intPattern.matcher(valueString).matches()) { - parsedValue = new Long(valueString); - } - ParsedExpression exp = new ParsedExpression(valueString); - Object o = exp.evaluate(nameContext); - if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); - return new Long(((Number)o).longValue()); - } - else if (type == float.class || type == Float.class) { - if (floatPattern.matcher(valueString).matches()) { - parsedValue = new Float(valueString); - } - ParsedExpression exp = new ParsedExpression(valueString); - Object o = exp.evaluate(nameContext); - if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); - return new Float(((Number)o).floatValue()); - } - else if (type == double.class || type == Double.class) { - if (floatPattern.matcher(valueString).matches()) { - parsedValue = new Double(valueString); - } - ParsedExpression exp = new ParsedExpression(valueString); - Object o = exp.evaluate(nameContext); - if (!(o instanceof Number)) throw new ParsedExpressionException("'" + valueString + "' does not evaluate to a numeric value"); - return (o instanceof Double ? (Double)o : new Double(((Number)o).doubleValue())); - } - else if (type == Datum.class) { - try { - return TimeUtil.create(valueString); - } - catch ( java.text.ParseException ex ) { - try { - return Datum.create(Double.parseDouble(valueString)); - } - catch (NumberFormatException iae) { - throw new ParsedExpressionException(valueString + " cannot be parsed as a Datum"); - } - } - - } - else { - throw new IllegalStateException(type.getName() + " is not a recognized type"); - } - } - -} diff --git a/dasCore/src/org/das2/dasml/SerializeUtil.java b/dasCore/src/org/das2/dasml/SerializeUtil.java index 821c17a81..7d019b707 100644 --- a/dasCore/src/org/das2/dasml/SerializeUtil.java +++ b/dasCore/src/org/das2/dasml/SerializeUtil.java @@ -86,16 +86,16 @@ public static org.w3c.dom.Element getDOMElement( Document document, Object objec String propertyName= propertyNameList[i]; - log.log( Level.FINE, "serializing property {0} of {1}", new Object[]{propertyName, elementName}); + log.fine( "serializing property "+propertyName + " of "+elementName ); if ( propertyName.equals("parent") ) { - log.info( "kludge to avoid cycles in bean graph due to parent property, ignoring"); + log.fine( "kludge to avoid cycles in bean graph due to parent property, ignoring"); } PropertyDescriptor pd= (PropertyDescriptor)nameMap.get(propertyName); if ( pd==null ) { - log.log(Level.WARNING, "unable to locate property: {0}, ignoring", propertyName); + log.warning("unable to locate property: "+propertyName+", ignoring"); continue; } @@ -103,7 +103,7 @@ public static org.w3c.dom.Element getDOMElement( Document document, Object objec if ( readMethod==null ) { // note this happens with the indexed property getRBG of ColorBar.Type - log.log( Level.INFO, "skipping property {0} of {1}, failed to find read method.", new Object[]{propertyName, elementName}); + log.fine( "skipping property "+propertyName+" of "+elementName+", failed to find read method." ); continue; } @@ -114,7 +114,7 @@ public static org.w3c.dom.Element getDOMElement( Document document, Object objec value= readMethod.invoke( object, new Object[0] ); if ( value==null ) { - log.log( Level.INFO, "skipping property {0} of {1}, value is null.", new Object[]{propertyName, elementName}); + log.fine( "skipping property "+propertyName+" of "+elementName+", value is null." ); continue; } @@ -180,7 +180,7 @@ private static void processNode( Node node, Object object, String className, Map PropertyDescriptor pd= (PropertyDescriptor)nameMap.get(propertyName); if ( pd==null ) { - log.log(Level.WARNING, "unable to locate property: {0} of {1}, ignoring", new Object[]{propertyName, className}); + log.warning("unable to locate property: "+propertyName+" of "+className+", ignoring"); return; } @@ -205,18 +205,18 @@ private static void processNode( Node node, Object object, String className, Map Method writeMethod= pd.getWriteMethod(); if ( writeMethod==null ) { - log.log(Level.WARNING, "read-only property \"{0}\" of {1} ignored", new Object[]{propertyName, className}); + log.warning("read-only property \""+propertyName+"\" of "+className+" ignored" ); return; } Object newValue= editor.getValue(); if ( propertyName.equals("dataSetID" ) ) { - log.info( "kludge to avoid setting dataSetID to null, ignoring" ); + log.fine( "kludge to avoid setting dataSetID to null, ignoring" ); } if ( propertyName.equals("dataSetID" ) && ( value==null || value.equals("") ) ) { - log.info( "kludge to avoid setting dataSetID to null, ignoring" ); + log.fine( "kludge to avoid setting dataSetID to null, ignoring" ); return; } @@ -271,7 +271,7 @@ public static void processElement( Element element, Object object ) { String elementName= element.getTagName(); elementName= elementName.replaceAll( "\\_dollar_", "\\$" ); - log.log(Level.FINE, "handling {0}", elementName); + log.fine("handling "+elementName); if ( !object.getClass().getName().equals( elementName ) ) { throw new IllegalArgumentException("class name doesn't match: expected "+ @@ -281,6 +281,7 @@ public static void processElement( Element element, Object object ) { AccessLevelBeanInfo info = BeansUtil.asAccessLevelBeanInfo( BeansUtil.getBeanInfo(object.getClass()), object.getClass() ); PropertyDescriptor[] properties = info.getPropertyDescriptors( AccessLevelBeanInfo.PersistenceLevel.PERSISTENT ) ; + String[] propertyNameList= BeansUtil.getPropertyNames(object.getClass()); HashMap nameMap= new HashMap(); @@ -288,10 +289,12 @@ public static void processElement( Element element, Object object ) { nameMap.put( properties[i].getName(), properties[i] ); } + HashMap serializedObjects= new HashMap(); + NamedNodeMap attrs= element.getAttributes(); for ( int i=0; i properties; + private Map properties; private double[] xTags; @@ -73,6 +73,10 @@ private AbstractDataSet() { public Object getProperty(String name) { return properties.get(name); } + + protected boolean hasProperty(String name) { + return properties.containsKey(name); + } public Map getProperties() { return new HashMap( properties ); diff --git a/dasCore/src/org/das2/dataset/AbstractDataSetCache.java b/dasCore/src/org/das2/dataset/AbstractDataSetCache.java index dbb8dda29..72f1e6535 100644 --- a/dasCore/src/org/das2/dataset/AbstractDataSetCache.java +++ b/dasCore/src/org/das2/dataset/AbstractDataSetCache.java @@ -23,11 +23,9 @@ package org.das2.dataset; -import org.das2.datum.CacheTag; -import java.awt.Graphics2D; -import java.util.logging.Level; import org.das2.components.propertyeditor.Displayable; import org.das2.datum.Datum; +import org.das2.DasApplication; import org.das2.system.DasLogger; import java.text.*; import java.util.*; @@ -40,7 +38,7 @@ */ public abstract class AbstractDataSetCache implements DataSetCache { - protected static class Entry implements Displayable { + protected class Entry implements Displayable { protected DataSetDescriptor dsd; protected CacheTag cacheTag; @@ -77,7 +75,6 @@ protected boolean satifies( Entry entry ) { return result; } - @Override public String toString() { long sizeBytes= DataSetUtil.guessSizeBytes(this.data); String sizeBytesString= " ("+NumberFormat.getIntegerInstance().format(sizeBytes)+" bytes)"; @@ -92,10 +89,6 @@ public String getListLabel() { return toString(); } - public void drawListIcon(Graphics2D g, int x, int y) { - // do nothing - } - public CacheTag getCacheTag() { return this.cacheTag; } @@ -114,10 +107,10 @@ public CacheTag getCacheTag() { public boolean haveStored( DataSetDescriptor dsd, CacheTag cacheTag ) { boolean result= haveStoredImpl( dsd, cacheTag ); if ( result ) { - logger.log(Level.FINE, "cache hit {0} {1}", new Object[]{dsd, cacheTag}); + logger.fine("cache hit "+dsd+" "+cacheTag); hits++; } else { - logger.log(Level.FINE, "cache miss {0} {1}", new Object[]{dsd, cacheTag}); + logger.fine("cache miss "+dsd+" "+cacheTag); misses++; } return result; @@ -144,20 +137,21 @@ public DataSet retrieve( DataSetDescriptor dsd, CacheTag cacheTag ) { /** * return the DataSet described by the set of DataSets if possible. - * @throws IllegalArgumentException if a subset is not continuous, + * @throws IllegalArgumentException if a subset is not continous, * non-overlapping, and of the same resolution. Removes * elements from the list that are not needed for the set. */ public DataSet coalese( List result ) { Collections.sort( result, new Comparator() { public int compare( Object o1, Object o2 ) { - return ((Entry)o1).cacheTag.getRange().compareTo( ((Entry)o2).cacheTag.getRange() ); + return ((Entry)o1).cacheTag.range.compareTo( ((Entry)o2).cacheTag.range ); } } ); Entry e0= (Entry)result.get(0); CacheTag ct= e0.cacheTag; - Datum t1= ct.getRange().max(); + Datum t1= ct.range.max(); + Datum resolution= ct.resolution; DataSet ds= e0.data; @@ -165,11 +159,11 @@ public int compare( Object o1, Object o2 ) { for ( int i=1; i{@link VectorDataSet#getDouble(int, org.das2.datum.Units)} *
  • {@link VectorDataSet#getInt(int, org.das2.datum.Units)}
  • *
  • {@link DataSet#getPlanarView(java.lang.String)}
  • - * + * * @author Edward West */ public abstract class AbstractVectorDataSet extends AbstractDataSet implements DataSet, VectorDataSet { diff --git a/dasCore/src/org/das2/dataset/AppendTableDataSet.java b/dasCore/src/org/das2/dataset/AppendTableDataSet.java index a5e212d59..3255c99b2 100644 --- a/dasCore/src/org/das2/dataset/AppendTableDataSet.java +++ b/dasCore/src/org/das2/dataset/AppendTableDataSet.java @@ -33,8 +33,8 @@ public AppendTableDataSet( TableDataSet tds1, TableDataSet tds2 ) { firstIndexsList.addAll( box( atds1.firstIndexs ) ); firstTablesList.addAll( box( atds1.firstTables ) ); tableDataSetsList.add( tds2 ); - firstIndexsList.add( Integer.valueOf( atds1.firstIndexs[atds1.tableDataSets.length]+tds2.getXLength() ) ); - firstTablesList.add( Integer.valueOf( atds1.firstTables[atds1.tableDataSets.length]+tds2.getXLength() ) ); + firstIndexsList.add( new Integer( atds1.firstIndexs[atds1.tableDataSets.length]+tds2.getXLength() ) ); + firstTablesList.add( new Integer( atds1.firstTables[atds1.tableDataSets.length]+tds2.getXLength() ) ); tableDataSets= (TableDataSet[])tableDataSetsList.toArray( new TableDataSet[ tableDataSetsList.size() ] ); firstIndexs= unbox( firstIndexsList ); diff --git a/dasCore/src/org/das2/dataset/AverageNoInterpolateTableRebinner.java b/dasCore/src/org/das2/dataset/AverageNoInterpolateTableRebinner.java index 8a9ccacd3..b1b2bfdbd 100644 --- a/dasCore/src/org/das2/dataset/AverageNoInterpolateTableRebinner.java +++ b/dasCore/src/org/das2/dataset/AverageNoInterpolateTableRebinner.java @@ -15,6 +15,7 @@ import org.das2.datum.Units; import org.das2.datum.UnitsUtil; import org.das2.system.DasLogger; +import org.das2.util.DasMath; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -28,7 +29,7 @@ * * @author Jeremy */ -public class AverageNoInterpolateTableRebinner { // { implements DataSetRebinner { +public class AverageNoInterpolateTableRebinner implements DataSetRebinner { Logger logger; boolean nearestNeighbor= false; @@ -64,7 +65,7 @@ private static DatumRange[] getXTagRanges( DataSet ds, int i0, int i1 ) { private static DatumRange[] getLogYTagRanges( TableDataSet ds, int itable ) { Datum tagWidth= TableUtil.guessYTagWidth(ds,itable); - double ratio= Math.pow( 10, tagWidth.doubleValue( Units.log10Ratio ) / 2. ); + double ratio= DasMath.exp10( tagWidth.doubleValue( Units.log10Ratio ) / 2. ); Units units= ds.getYUnits(); DatumRange[] result= new DatumRange[ ds.getYLength(itable) ]; for ( int i=0; i-1 && ibin 0 ) { - UnitsConverter xc= xunits.getConverter(ddX.getUnits()); - QDataSet bounds= SemanticOps.bounds(tds); - double start = xc.convert( bounds.value(0,0) ); - double end = xc.convert( bounds.value(0,1) ); - DatumRange dr= DatumRangeUtil.union( ddX.binStop(ddX.numberOfBins()-1),ddX.binStart(0)); - if (start > dr.max().doubleValue(ddX.getUnits()) ) { + TableDataSet tds = (TableDataSet) ds; + TableDataSet weights = (TableDataSet) ds.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); + if (ddX != null && tds.getXLength() > 0) { + double start = tds.getXTagDouble(0, ddX.getUnits()); + double end = tds.getXTagDouble(tds.getXLength() - 1, ddX.getUnits()); + if (start > ddX.end) { throw new NoDataInIntervalException("data starts after range"); - } else if (end < dr.min().doubleValue(ddX.getUnits())) { + } else if (end < ddX.start) { throw new NoDataInIntervalException("data ends before range"); } } - int nx = (ddX == null ? tds1.length() : ddX.numberOfBins()); - int ny; - if ( ddY == null ) { - if ( SemanticOps.isBundle(ds) && !SemanticOps.isSimpleTableDataSet(ds) ) throw new IllegalArgumentException("not supported, must specify ddY bins"); - ny= tds1.length(0); - } else { - ny= ddY.numberOfBins(); - } + long timer = System.currentTimeMillis(); - if ( ddY==null && rank!=2 ) { - throw new IllegalArgumentException("ddY was null but there was rank 3 dataset"); - } + Units xunits = ddX.getUnits(); - logger.log(Level.FINEST, "Allocating rebinData and rebinWeights: {0} x {1}", new Object[]{nx, ny}); + int nx = (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + int ny = (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); + + logger.finest("Allocating rebinData and rebinWeights: " + nx + " x " + ny); double[][] rebinData = new double[nx][ny]; double[][] rebinWeights = new double[nx][ny]; - // average all the measurements that fall onto the same pixel together. - if ( bundle ) { - averageBundle( tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType ); - zds= SemanticOps.getDependentDataSet(tds1); + average(tds, weights, rebinData, rebinWeights, ddX, ddY); + if (interpolate) { + doBoundaries2RL(tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType); + doBoundaries2TB(tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType); + doCorners(tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType); + } + double[] xTags; + double[] xTagMin; + double[] xTagMax; + + if (ddX != null) { + xTags = ddX.binCenters(); + xTagMin = ddX.binStops(); + xTagMax = ddX.binStarts(); + for (int i = 0; i < tds.getXLength(); i++) { + double xt = tds.getXTagDouble(i, xunits); + int ibin = ddX.whichBin(xt, xunits); + if (ibin > -1 && ibin < nx) { + xTagMin[ibin] = Math.min(xTagMin[ibin], xt); + xTagMax[ibin] = Math.max(xTagMax[ibin], xt); + } + } } else { - average( tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType ); -// try { -// ArrayDataSet tdsc= ArrayDataSet.copy(tds.slice(0)); -// tdsc.putProperty( QDataSet.DEPEND_0, Ops.collapse1((QDataSet)tdsc.property(QDataSet.DEPEND_0))); -// TableUtil.dumpToBinaryStream( (TableDataSet) DataSetAdapter.createLegacyDataSet(tdsc), new FileOutputStream("/tmp/foo.d2s") ); -// } catch ( IOException ex ) { -// ex.printStackTrace(); -// -// } - if (interpolate) { // I think these calculate the interpolated value at the edge. Note there's a bug in here... - doBoundaries2RL(tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType); - doBoundaries2TB(tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType); - doCorners(tds, weights, rebinData, rebinWeights, ddX, ddY, interpolateType); + xTags = new double[nx]; + for (int i = 0; i < nx; i++) { + xTags[i] = tds.getXTagDouble(i, tds.getXUnits()); } - zds= tds1; + xTagMin = xTags; + xTagMax = xTags; } - if (interpolate) { - Datum xTagWidth = getXTagWidth(xunits,ds); - - if ( xTagWidth.value()<0 ) xTagWidth= xTagWidth.multiply(-1); - RankZeroDataSet yTagWidth0; - if ( bundle ) { - yTagWidth0= DataSetUtil.guessCadenceNew( yds, zds ); - } else { - if ( tds.rank()<3 ) { - QDataSet yds1= yds; - if ( yds1.rank()>1 ) yds1= yds1.slice(0); //TODO: rank 2 yds. - yTagWidth0= DataSetUtil.guessCadenceNew( yds1, null ); - } else { - QDataSet yds1= SemanticOps.ytagsDataSet( tds.slice(0) ); - if ( yds1.rank()>1 ) yds1= yds1.slice(0); - yTagWidth0= DataSetUtil.guessCadenceNew( yds1, null ); - for ( int i=1;i1 ) yds1= yds1.slice(0); - yTagWidth0= DataSetUtil.courserCadence( yTagWidth0, DataSetUtil.guessCadenceNew( yds1, null ) ); - } - } + double[][] yTags; + if (ddY != null) { + yTags = new double[][]{ddY.binCenters()}; + } else { + yTags = new double[1][ny]; + for (int j = 0; j < ny; j++) { + yTags[0][j] = tds.getYTagDouble(0, j, tds.getYUnits()); } + } + + Units resultXUnits = ddX == null ? tds.getXUnits() : ddX.getUnits(); + Units resultYUnits = ddY == null ? tds.getYUnits() : ddY.getUnits(); - RankZeroDataSet yTagWidthQ= yTagWidth0; - Datum yTagWidth = yTagWidthQ==null ? null : DataSetUtil.asDatum( yTagWidthQ ); - if ( yTagWidth!=null && yTagWidth.value()<0 ) yTagWidth= yTagWidth.multiply(-1); + if (this.interpolate) { + Datum xTagWidth = (Datum) ds.getProperty("xTagWidth"); + if (xTagWidth == null) { + xTagWidth = DataSetUtil.guessXTagWidth(tds); + } + double xTagWidthDouble = xTagWidth.doubleValue(ddX.getUnits().getOffsetUnits()); + Datum yTagWidth = (Datum) ds.getProperty("yTagWidth"); if (ddX != null) { - fillInterpolateXNew(rebinData, rebinWeights, ddX, xTagWidth, interpolateType); + fillInterpolateX(rebinData, rebinWeights, xTags, xTagMin, xTagMax, xTagWidthDouble, interpolateType); } if (ddY != null) { + /* Note the yTagMin,yTagMax code doesn't work here, because of the + * multiple tables. So here, we'll just up yTagWidth to be twice + * the pixel cadence. When a new data model is introduced, + * this should be revisited. + */ + if (yTagWidth == null && interpolateType == Interpolate.NearestNeighbor) { + yTagWidth = TableUtil.guessYTagWidth(tds); + } fillInterpolateY(rebinData, rebinWeights, ddY, yTagWidth, interpolateType); } } else if (enlargePixels) { enlargePixels(rebinData, rebinWeights); } - // copy the data into rank 2 dataset. - DDataSet result= DDataSet.createRank2( nx, ny ); - DDataSet weightResult= DDataSet.createRank2( nx, ny ); - for ( int i=0; i2 ) { - QDataSet r= DataSetUtil.guessCadenceNew( xds, null ); - Datum rd= DataSetUtil.asDatum(r); - cadence= cadence.gt( rd ) ? cadence : rd; - } - } - if ( cadence.value()<0 ) { - return xunits.createDatum( Double.MAX_VALUE ); - } else { - return cadence; - } - } else { - xds= (QDataSet) tds.property(QDataSet.DEPEND_0); - if ( xds==null ) { - return xunits.createDatum(1); - } else if ( xds.length()>2 ) { - QDataSet r= DataSetUtil.guessCadenceNew( xds, null ); - Datum rd= DataSetUtil.asDatum(r); - return rd; - } else { - return xunits.createDatum( Double.MAX_VALUE ); - } + if (ddX != null) { + properties.put(DataSet.PROPERTY_X_TAG_WIDTH, ddX.binWidthDatum()); } - } - - /** return the cadence of the data. - * @param xds the x tags of the data. - * @param tds1 the data, where we look for fill values. - * @return the cadence of the data (never null) - */ - private static Datum getXTagWidth( QDataSet xds, QDataSet tds1 ) { - Datum xTagWidth; - if ( xds.length()>1 ) { - Datum d= SemanticOps.guessXTagWidth( xds, tds1 ); - if ( d==null ) { - System.err.println("failed to guessXTagWidth"); - Units xunits= SemanticOps.getUnits(xds).getOffsetUnits(); - xTagWidth= xunits.createDatum(Double.MAX_VALUE); - return xTagWidth; - //d= SemanticOps.guessXTagWidth( xds, tds1 ); - } else { - xTagWidth= d; //d.multiply(0.9); - } - } else { - QDataSet xTagWidthDs= (RankZeroDataSet) xds.property( QDataSet.CADENCE ); // note these were once doubles, but this is not supported here. - if (xTagWidthDs!=null) { - xTagWidth= org.virbo.dataset.DataSetUtil.asDatum(xTagWidthDs); - } else { - Units xunits= SemanticOps.getUnits(xds).getOffsetUnits(); - xTagWidth= xunits.createDatum(Double.MAX_VALUE); - } - } - return xTagWidth; - } - - - /** - * return the next or previous closest index. For monotonic datasets, - * this just calls org.virbo.dataset.DataSetUtil.getPreviousIndex or getNextIndex. - * Otherwise, we scan the dataset. - * @param xds the rank 1 dataset to search. - * @param xx the reference value. - * @param sign 1 for next closest index, -1 for the index of the value just less than xx. - * @return -1 if there is no valid data, the index otherwise. - */ - private static int getNextPrevIndex( QDataSet xds, Datum xx, int sign ) { - if ( SemanticOps.isMonotonic(xds) ) { - if ( sign<0 ) { - return org.virbo.dataset.DataSetUtil.getPreviousIndex(xds, xx); - } else { - return org.virbo.dataset.DataSetUtil.getNextIndex(xds, xx); - } - } else { - double best= Double.MAX_VALUE; - int ibest= -1; - double lookFor= xx.doubleValue( SemanticOps.getUnits(xds) ); - QDataSet wds= SemanticOps.weightsDataSet(xds); - for ( int i=0; i0. && check>0 && check "+( dr.width().gt( xTagWidth ) ) ); -// System.err.println( " dr.width()=="+dr.width() ); -// System.err.println( " xtagwidth=="+xTagWidth ); -// } -// dr.width().gt( xTagWidth ); -// } catch ( InconvertibleUnitsException ex ) { -// dr.width().gt( xTagWidth ); -// } - if ( canInterpolate( dr, xTagWidth ) ) { - double alpha = DatumRangeUtil.normalize(dr, xx); - if ( interpolateType==Interpolate.NearestNeighbor || interpolateType==Interpolate.BinXInterpY ) { - alpha= alpha < 0.5 ? 0.0 : 1.0; - } - int ny = ddY == null ? yds.length() : ddY.numberOfBins(); - QDataSet yds1; - if ( yds.rank()==2 ) { - yds1= yds.slice(i0); //TODO: assumes .slice(i0)==.slice(i1) - } else { - yds1= yds; - } - for (int j = 0; j < yds1.length(); j++) { - int jj = ddY == null ? j : ddY.whichBin( yds1.value( j ), yunits ); - if (jj >= 0 && jj < ny) { - if (rebinWeights[ix][jj] > 0.0) { - continue; - } - if ( wds.value( i0, j ) * wds.value( i1, j ) == 0.) { - continue; - } - rebinData[ix][jj] = (1 - alpha) * tds1.value( i0, j ) + - alpha * tds1.value( i1, j ); - rebinWeights[ix][jj] = 1.0; + static void doBoundaries2RL(TableDataSet tds, TableDataSet weights, double[][] rebinData, double[][] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY, Interpolate interpolateType) { + Units yunits = tds.getYUnits(); + Units zunits = tds.getZUnits(); + Units wunits = Units.dimensionless; + TableDataSet wds = WeightsTableDataSet.create(tds); + for (int i = 0; i < 2; i++) { + int ix = i == 0 ? 0 : ddX.numberOfBins() - 1; + Datum xx = i == 0 ? ddX.binCenter(0) : ddX.binCenter(ix); + + int i0 = DataSetUtil.getPreviousColumn(tds, xx); + int i1 = DataSetUtil.getNextColumn(tds, xx); + + int itable = tds.tableOfIndex(i0); + if (itable == tds.tableOfIndex(i1) && (i1 != i0)) { + DatumRange dr = new DatumRange(tds.getXTagDatum(i0), tds.getXTagDatum(i1)); + if (dr.width().gt(DataSetUtil.guessXTagWidth(tds).multiply(0.9))) { + double alpha = DatumRangeUtil.normalize(dr, xx); + if ( interpolateType==Interpolate.NearestNeighbor ) { + alpha= alpha < 0.5 ? 0.0 : 1.0; + } + int ny = ddY == null ? tds.getYLength(itable) : ddY.numberOfBins(); + for (int j = 0; j < tds.getYLength(itable); j++) { + int jj = ddY == null ? j : ddY.whichBin(tds.getYTagDouble(itable, j, yunits), yunits); + if (jj >= 0 && jj < ny) { + if (rebinWeights[ix][jj] > 0.0) { + continue; + } + if (wds.getDouble(i0, j, wunits) * wds.getDouble(i1, j, wunits) == 0.) { + continue; } + rebinData[ix][jj] = (1 - alpha) * tds.getDouble(i0, j, zunits) + + alpha * tds.getDouble(i1, j, zunits); + rebinWeights[ix][jj] = 1.0; } } } @@ -424,61 +226,31 @@ static void doBoundaries2RL( QDataSet tds, QDataSet weights, double[][] rebinDat } - static void doBoundaries2TB( QDataSet tds, QDataSet weights, double[][] rebinData, double[][] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY, Interpolate interpolateType) { + static void doBoundaries2TB(TableDataSet tds, TableDataSet weights, double[][] rebinData, double[][] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY, Interpolate interpolateType) { if (ddY == null) { return; } - for (int itable = 0; itable < tds.length(); itable++) { - - QDataSet tds1= tds.slice(itable); - - QDataSet xds= SemanticOps.xtagsDataSet(tds1); - QDataSet yds= SemanticOps.ytagsDataSet(tds1); - if ( yds.rank()==2 ) { - int islice= yds.length()/2; - if ( yds.length()>0 && yds.length(islice)>0 && yds.value(islice,0)!=yds.value(islice,0) ) { - System.err.println("kludge assumes rank2 yds is repeating values"); - } - - yds= yds.slice(islice); //TODO: kludge assumes that all yds slices are repeated. - } - QDataSet wds= SemanticOps.weightsDataSet(tds1); - - Units yunits = SemanticOps.getUnits(yds); - Units xunits = SemanticOps.getUnits(xds); - + Units yunits = tds.getYUnits(); + Units zunits = tds.getZUnits(); + Units xunits = tds.getXUnits(); + Units wunits = Units.dimensionless; + TableDataSet wds = WeightsTableDataSet.create(tds); + for (int itable = 0; itable < tds.tableCount(); itable++) { for (int i = 0; i < 2; i++) { int iy = i == 0 ? 0 : ddY.numberOfBins() - 1; Datum yy = i == 0 ? ddY.binCenter(0) : ddY.binCenter(iy); - int j0,j1; - if ( SemanticOps.isMonotonic(yds) ) { - j0 = org.virbo.dataset.DataSetUtil.getPreviousIndex( yds, yy ); - j1 = org.virbo.dataset.DataSetUtil.getNextIndex( yds, yy); - } else { - QDataSet myds= Ops.multiply( yds, DataSetUtil.asDataSet(-1) ); - if ( SemanticOps.isMonotonic(myds) ) { - j0 = org.virbo.dataset.DataSetUtil.getPreviousIndex( myds, yy ); - j1 = org.virbo.dataset.DataSetUtil.getNextIndex( myds, yy); - } else { - //fo_k0_ees_1998011_v01.cdf - if ( Ops.total( SemanticOps.weightsDataSet(yds) )==0 ) return; - //throw new IllegalArgumentException("dataset tags must be increasing or decreasing"); - continue; // we just return without interpolating. - } - - } + int j0 = TableUtil.getPreviousRow(tds, itable, yy); + int j1 = TableUtil.getNextRow(tds, itable, yy); if (j1 != j0) { DatumRange dr; - dr = DatumRangeUtil.union( - yunits.createDatum( yds.value(j0) ), - yunits.createDatum( yds.value(j1) ) ); - + dr = new DatumRange(tds.getYTagDatum(itable, j0), tds.getYTagDatum(itable, j1)); + Datum dsWidth = TableUtil.guessYTagWidth(tds, itable); if (ddY.isLog()) { Units u = dr.getUnits(); double d = dr.min().doubleValue(u); @@ -488,21 +260,22 @@ static void doBoundaries2TB( QDataSet tds, QDataSet weights, double[][] rebinDat yy = Units.logERatio.createDatum(Math.log(yy.doubleValue(u) / d)); // TODO: infinity } + DatumRange xdr = new DatumRange(ddX.binCenter(0), ddX.binCenter(ddX.numberOfBins() - 1)); double alpha = DatumRangeUtil.normalize(dr, yy); if ( interpolateType==Interpolate.NearestNeighbor ) { alpha= alpha < 0.5 ? 0.0 : 1.0; } int nx = ddX.numberOfBins(); - for (int ix = 0; ix < tds1.length(); ix++) { - int ii = ddX.whichBin( xds.value(ix), xunits); + for (int ix = tds.tableStart(itable); ix < tds.tableEnd(itable); ix++) { + int ii = ddX.whichBin(tds.getXTagDouble(ix, xunits), xunits); if (ii >= 0 && ii < nx) { if (rebinWeights[ii][iy] > 0.0) { continue; } - if ( wds.value(ix,j0) * wds.value(ix,j1) == 0.) { + if (wds.getDouble(ix, j0, wunits) * wds.getDouble(ix, j1, wunits) == 0.) { continue; } - rebinData[ii][iy] = (1 - alpha) * tds1.value( ix, j0 ) + alpha * tds1.value( ix, j1 ); + rebinData[ii][iy] = (1 - alpha) * tds.getDouble(ix, j0, zunits) + alpha * tds.getDouble(ix, j1, zunits); rebinWeights[ii][iy] = 1.0; } } @@ -511,288 +284,135 @@ static void doBoundaries2TB( QDataSet tds, QDataSet weights, double[][] rebinDat } } - static void doCorners( QDataSet tds, QDataSet weights, double[][] rebinData, double[][] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY, Interpolate interpolateType) { + static void doCorners(TableDataSet tds, TableDataSet weights, double[][] rebinData, double[][] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY, Interpolate interpolateType) { if (ddY == null) { return; } - - for ( int itable=0; itable1 ) { - yds= yds.slice(0); //TODO: kludge for RBSP. Bad CDF file? vap+cdfj:http://emfisis.physics.uiowa.edu/L1/2011/03/03/rbsp-a_HFR-spectra_emfisis-L1_20110303144809_v1.1.1.cdf?HFR_Spectra - } - if ( SemanticOps.isBins(yds) ) { - yds= getRank1Tags( yds ); - } - QDataSet wds= SemanticOps.weightsDataSet(tds1); - - Units yunits = SemanticOps.getUnits(yds); - Units xunits = SemanticOps.getUnits(xds); - - Datum xTagWidth= getXTagWidth( xds, tds1); - - for (int i = 0; i < 2; i++) { - int ix = i == 0 ? 0 : ddX.numberOfBins() - 1; - Datum xx = ddX.binCenter(ix); - - int i0 = getNextPrevIndex( xds, xx, -1 ); - int i1 = getNextPrevIndex( xds, xx, 1 ); - if ( i0==-1 || i1==-1 ) { - continue; - } - if (i0 == i1) { - continue; - } - - DatumRange xdr = DatumRangeUtil.union( - xunits.createDatum( xds.value(i0) ), - xunits.createDatum( xds.value(i1) ) ); - double xalpha = DatumRangeUtil.normalize(xdr, xx); - if (interpolateType == Interpolate.NearestNeighbor || interpolateType==Interpolate.BinXInterpY ) { - xalpha = xalpha < 0.5 ? 0.0 : 1.0; - } - - QDataSet yds1; - for (int j = 0; j < 2; j++) { - if ( yds.rank()==2 ) { - yds1= yds.slice(i0); - } else { - yds1= yds; - } - int iy = j == 0 ? 0 : ddY.numberOfBins() - 1; - Datum yy = ddY.binCenter(iy); - - int j0 = getNextPrevIndex( yds1, yy, -1 ); - int j1 = getNextPrevIndex( yds1, yy, 1 ); - if ( j0==-1 || j1==-1 ) continue; - - if (j0 != j1) { - DatumRange ydr = DatumRangeUtil.union( - yunits.createDatum( yds1.value(j0) ), - yunits.createDatum( yds1.value(j1) ) ); - - if ( canInterpolate( xdr, xTagWidth ) ) { - double yalpha = DatumRangeUtil.normalize(ydr, yy); - if (interpolateType == Interpolate.NearestNeighbor) { - yalpha = yalpha < 0.5 ? 0.0 : 1.0; - } - if (rebinWeights[ix][iy] > 0.0) { - continue; - } - if ( wds.value(i1, j1 ) * - wds.value(i0, j0 ) * - wds.value(i1, j0 ) * - wds.value(i0, j1 ) == 0.) { - continue; - } - rebinData[ix][iy] = - tds1.value(i1, j1 ) * xalpha * yalpha + - tds1.value(i0, j0 ) * (1 - xalpha) * (1 - yalpha) + - tds1.value(i1, j0 ) * xalpha * (1 - yalpha) + - tds1.value(i0, j1 ) * (1 - xalpha) * yalpha; - rebinWeights[ix][iy] = 1.0; - } - } - } + Units yunits = tds.getYUnits(); + Units zunits = tds.getZUnits(); + Units xunits = tds.getXUnits(); + Units wunits = Units.dimensionless; + TableDataSet wds = WeightsTableDataSet.create(tds); + for (int i = 0; i < 2; i++) { + int ix = i == 0 ? 0 : ddX.numberOfBins() - 1; + Datum xx = ddX.binCenter(ix); + int i0 = DataSetUtil.getPreviousColumn(tds, xx); + int i1 = DataSetUtil.getNextColumn(tds, xx); + + int itable = tds.tableOfIndex(i0); + if (itable != tds.tableOfIndex(i1)) { + continue; } - } - } - static void averageBundle( QDataSet tds, QDataSet weights, double[][] rebinData, double[][] rebinWeights, - RebinDescriptor ddX, RebinDescriptor ddY, - Interpolate interpolateType ) { - - QDataSet tds1= tds.slice(0); - QDataSet xds= SemanticOps.xtagsDataSet( tds1 ); - QDataSet yds= SemanticOps.ytagsDataSet( tds1 ); - QDataSet zds= SemanticOps.getDependentDataSet( tds1 ); - - QDataSet wds= SemanticOps.weightsDataSet( zds ); - - Units yunits = SemanticOps.getUnits(yds); - Units xunits = SemanticOps.getUnits(xds); - - QDataSet vyds= SemanticOps.weightsDataSet(yds); - QDataSet vxds= SemanticOps.weightsDataSet(xds); - - double fill= -1e31; - - int nx= ddX.numberOfBins(); - int ny= ddY.numberOfBins(); - - for (int i = 0; i < zds.length(); i++) { - - int ibinx,ibiny; - - if ( vxds.value(i)>0 ) { - ibinx = ddX.whichBin( xds.value(i), xunits ); - } else { + if (i0 == i1) { continue; } - if ( vyds.value(i)>0 ) { - ibiny = ddY.whichBin( yds.value(i), yunits ); - } else { - continue; + DatumRange xdr = new DatumRange(tds.getXTagDatum(i0), tds.getXTagDatum(i1)); + double xalpha = DatumRangeUtil.normalize(xdr, xx); + if (interpolateType == Interpolate.NearestNeighbor) { + xalpha = xalpha < 0.5 ? 0.0 : 1.0; } + for (int j = 0; j < 2; j++) { + int iy = j == 0 ? 0 : ddY.numberOfBins() - 1; + Datum yy = ddY.binCenter(iy); - double z = zds.value( i ); - double w = wds.value( i ); + int j0 = TableUtil.getPreviousRow(tds, itable, yy); + int j1 = TableUtil.getNextRow(tds, itable, yy); - if (ibiny >= 0 && ibiny < ny && ibinx >= 0 && ibinx < nx ) { - if ( interpolateType==Interpolate.NearestNeighbor ) { // propogate fill points through average - if ( w==0 ) { - if ( rebinWeights[ibinx][ibiny]==0 ) { - rebinData[ibinx][ibiny] = fill; - rebinWeights[ibinx][ibiny] = 1; + if (j0 != j1) { + DatumRange ydr = new DatumRange(tds.getYTagDatum(itable, j0), tds.getYTagDatum(itable, j1)); + if (xdr.width().lt(DataSetUtil.guessXTagWidth(tds).multiply(1.1))) { + DatumRange xdr1 = new DatumRange(ddX.binCenter(0), ddX.binCenter(ddX.numberOfBins() - 1)); + double yalpha = DatumRangeUtil.normalize(ydr, yy); + if (interpolateType == Interpolate.NearestNeighbor) { + yalpha = yalpha < 0.5 ? 0.0 : 1.0; } - } else { - if ( rebinWeights[ibinx][ibiny]==1 && rebinData[ibinx][ibiny]==fill ) { - rebinData[ibinx][ibiny] = z; - rebinWeights[ibinx][ibiny] = w; - } else { - rebinData[ibinx][ibiny] += z * w; - rebinWeights[ibinx][ibiny] += w; + if (rebinWeights[ix][iy] > 0.0) { + continue; + } + if (wds.getDouble(i1, j1, wunits) * + wds.getDouble(i0, j0, wunits) * + wds.getDouble(i1, j0, wunits) * + wds.getDouble(i0, j1, wunits) == 0.) { + continue; } + rebinData[ix][iy] = + tds.getDouble(i1, j1, zunits) * xalpha * yalpha + + tds.getDouble(i0, j0, zunits) * (1 - xalpha) * (1 - yalpha) + + tds.getDouble(i1, j0, zunits) * xalpha * (1 - yalpha) + + tds.getDouble(i0, j1, zunits) * (1 - xalpha) * yalpha; + rebinWeights[ix][iy] = 1.0; } - } else { - rebinData[ibinx][ibiny] += z * w; - rebinWeights[ibinx][ibiny] += w; } } } - - multiplyWeights( rebinData, rebinWeights, fill ); - } - - /** - * average data that falls onto the same pixel location. - * @param tds the dataset to average, either rank 2 table or rank 3 array of tables. - * @param weights the weights for each measurement of the dataset. - * @param rebinData output the averages, normalized by the weights - * @param rebinWeights output the weights for each bin. - * @param ddX describes the horizontal bins - * @param ddY describes the vertical bins - * @param interpolateType if NearestNeighbor, then we set weight=1. Why? see http://autoplot.org/developer.spectrogram - */ - static void average( QDataSet tds, QDataSet weights, double[][] rebinData, double[][] rebinWeights, - RebinDescriptor ddX, RebinDescriptor ddY, - Interpolate interpolateType) { + + static void average(TableDataSet tds, TableDataSet weights, double[][] rebinData, double[][] rebinWeights, RebinDescriptor ddX, RebinDescriptor ddY) { + double[] ycoordinate; int nTables; - Units zunits; + Units xUnits, zUnits; int nx, ny; - if ( tds.rank()!=3 ) throw new IllegalArgumentException("rank 3 expected"); - - zunits = SemanticOps.getUnits( tds ); - double fill= zunits.getFillDouble(); - - nx = (ddX == null ? tds.length(0) : ddX.numberOfBins()); - ny = (ddY == null ? tds.length(0,0) : ddY.numberOfBins()); - - nTables = tds.length(); - for (int iTable = 0; iTable < nTables; iTable++) { - QDataSet tds1= tds.slice(iTable); - QDataSet xds= getRank1Tags( SemanticOps.xtagsDataSet( tds1 ) ); - - QDataSet yds= SemanticOps.ytagsDataSet( tds1 ); - if ( yds.length()==1 && yds.rank()==2 && tds1.length()>1 ) { - yds= yds.slice(0); //TODO: kludge for RBSP. Bad CDF file? vap+cdfj:http://emfisis.physics.uiowa.edu/L1/2011/03/03/rbsp-a_HFR-spectra_emfisis-L1_20110303144809_v1.1.1.cdf?HFR_Spectra - } - if ( yds.rank()==2 && SemanticOps.isBins(yds) ) { - yds= getRank1Tags( yds ); - } - QDataSet wds= SemanticOps.weightsDataSet( tds1 ); - - Units yunits = SemanticOps.getUnits(yds); - Units xunits = SemanticOps.getUnits(xds); + xUnits = tds.getXUnits(); + zUnits = tds.getZUnits(); - int[] ibiny = new int[tds1.length(0)]; + nx = (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + ny = (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); - QDataSet vyds= SemanticOps.weightsDataSet(yds); - QDataSet vxds= SemanticOps.weightsDataSet(xds); - if ( yds.rank()==1 ) { - for (int j = 0; j < ibiny.length; j++) { - if (ddY != null) { - if ( vyds.value(j)>0 ) { - ibiny[j] = ddY.whichBin( yds.value(j), yunits ); - } else { - ibiny[j] = -10000; - } - } else { - ibiny[j] = j; - } - } + if (ddY != null) { + ycoordinate = ddY.binCenters(); + } else { + ycoordinate = new double[tds.getYLength(0)]; + for (int j = 0; j < ycoordinate.length; j++) { + ycoordinate[j] = tds.getDouble(0, j, zUnits); } + } - for (int i = 0; i < tds1.length(); i++) { - if ( yds.rank()==2 ) { - for (int j = 0; j < ibiny.length; j++) { - if (ddY != null) { - if ( vyds.value(i,j)>0 ) { - ibiny[j] = ddY.whichBin( yds.value(i,j), yunits ); - } else { - ibiny[j] = -10000; - } - } else { - ibiny[j] = j; - } - } + nTables = tds.tableCount(); + for (int iTable = 0; iTable < nTables; iTable++) { + int[] ibiny = new int[tds.getYLength(iTable)]; + for (int j = 0; j < ibiny.length; j++) { + if (ddY != null) { + ibiny[j] = ddY.whichBin(tds.getYTagDouble(iTable, j, tds.getYUnits()), tds.getYUnits()); + } else { + ibiny[j] = j; } - + } + for (int i = tds.tableStart(iTable); i < tds.tableEnd(iTable); i++) { int ibinx; - if (ddX != null) { - if ( vxds.value(i)>0 ) { - ibinx = ddX.whichBin( xds.value(i), xunits ); - } else { - ibinx = -10000; - } + ibinx = ddX.whichBin(tds.getXTagDouble(i, xUnits), xUnits); } else { ibinx = i; } if (ibinx >= 0 && ibinx < nx) { - for (int j = 0; j < tds1.length(i); j++) { - double z = tds1.value( i, j ); - double w = wds.value( i, j ); + for (int j = 0; j < tds.getYLength(iTable); j++) { + double z = tds.getDouble(i, j, zUnits); + double w = weights == null + ? (zUnits.isFill(z) ? 0. : 1.) + : weights.getDouble(i, j, Units.dimensionless); if (ibiny[j] >= 0 && ibiny[j] < ny) { - if ( interpolateType==Interpolate.NearestNeighbor ) { // propogate fill points through average - if ( w==0 ) { - if ( rebinWeights[ibinx][ibiny[j]]==0 ) { - rebinData[ibinx][ibiny[j]] = fill; - rebinWeights[ibinx][ibiny[j]] = 1; - } - } else { - if ( rebinWeights[ibinx][ibiny[j]]==1 && rebinData[ibinx][ibiny[j]]==fill ) { - rebinData[ibinx][ibiny[j]] = z; - rebinWeights[ibinx][ibiny[j]] = w; - } else { - rebinData[ibinx][ibiny[j]] += z * w; - rebinWeights[ibinx][ibiny[j]] += w; - } - } - } else { - rebinData[ibinx][ibiny[j]] += z * w; - rebinWeights[ibinx][ibiny[j]] += w; - } + rebinData[ibinx][ibiny[j]] += z * w; + rebinWeights[ibinx][ibiny[j]] += w; } } } } } + multiplyWeights(rebinData, rebinWeights, zUnits.getFillDouble()); + } - multiplyWeights( rebinData, rebinWeights, zunits.getFillDouble() ); - + private final static double linearlyInterpolate(int i0, double z0, int i1, double z1, int i) { + double r = ((double) (i - i0)) / (i1 - i0); + return z0 + r * (z1 - z0); } - private static void multiplyWeights(double[][] data, double[][] weights, double fill) { + private final static void multiplyWeights(double[][] data, double[][] weights, double fill) { for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { if (weights[i][j] > 0.0) { @@ -804,9 +424,11 @@ private static void multiplyWeights(double[][] data, double[][] weights, double } } - //still used by AveragePeakTableRebinner - // final double[][] data, final double[][] weights, RebinDescriptor ddY, Datum yTagWidth, Interpolate interpolateType - static void fillInterpolateX(final double[][] data, final double[][] weights, final double[] xTags, double[] xTagMin, double[] xTagMax, final double xSampleWidth, Interpolate interpolateType) { + static void fillInterpolateX( + final double[][] data, final double[][] weights, final double[] xTags, + double[] xTagMin, double[] xTagMax, final double xSampleWidth, + Interpolate interpolateType + ) { final int nx = xTags.length; final int ny = data[0].length; @@ -817,7 +439,7 @@ static void fillInterpolateX(final double[][] data, final double[][] weights, fi for (int j = 0; j < ny; j++) { int ii1 = -1; - int ii2; + int ii2 = -1; for (int i = 0; i < nx; i++) { if (weights[i][j] > 0. && ii1 == (i - 1)) { // ho hum another valid point i1[i] = -1; @@ -856,8 +478,12 @@ static void fillInterpolateX(final double[][] data, final double[][] weights, fi if (interpolateType == Interpolate.NearestNeighbor) { for (int i = 0; i < nx; i++) { - if ( i1[i] > -1 && i2[i] > -1 && Math.abs(xTagMin[i2[i]] - xTagMax[i1[i]]) <= xSampleWidth * 1.5 ) { - int idx; + if (Math.min( + i1[i] == -1 ? Double.MAX_VALUE : (xTags[i] - xTagMin[i1[i]]), + i2[i] == -1 ? Double.MAX_VALUE : (xTagMax[i2[i]] - xTags[i])) < xSampleWidth / 2 + ) { + + int idx = -1; if (i1[i] == -1) { if (i2[i] == -1) { continue; @@ -879,7 +505,7 @@ static void fillInterpolateX(final double[][] data, final double[][] weights, fi } } else { for (int i = 0; i < nx; i++) { - if (i1[i] > -1 && i2[i] > -1 && Math.abs(xTagMin[i2[i]] - xTagMax[i1[i]]) <= xSampleWidth * 1.5) { + if (i1[i] > -1 && i2[i] > -1 && (xTagMin[i2[i]] - xTagMax[i1[i]]) <= xSampleWidth * 1.5) { a2 = ((xTags[i] - xTagMax[i1[i]]) / (xTagMin[i2[i]] - xTags[i1[i]])); a1 = 1. - a2; data[i][j] = data[i1[i]][j] * a1 + data[i2[i]][j] * a2; @@ -891,162 +517,6 @@ static void fillInterpolateX(final double[][] data, final double[][] weights, fi } } - /** - * interpolate the missing pixels by scanning rows, looking for point pairs where weights > 0 and that are acceptably close. - * @param data the data, not scaled by weight. - * @param weights 0 means ignore, positive means valid - * @param ddX - * @param xTagWidth the nominal cadence between measurements. This defines acceptably close, and we apply a fudge factor. - * @param interpolateType if NearestNeighbor, then special actions can occur. - */ - static void fillInterpolateXNew(final double[][] data, final double[][] weights, RebinDescriptor ddX, Datum xTagWidth, Interpolate interpolateType) { - - final int ny = data[0].length; - final int nx = ddX.numberOfBins(); - final int[] i1 = new int[nx]; - final int[] i2 = new int[nx]; - final double[] xTagTemp = new double[ddX.numberOfBins()]; - double a1; - double a2; - - final double[] xTags = ddX.binCenters(); - final Units xTagUnits = ddX.getUnits(); - final boolean log = ddX.isLog(); - - if (log) { - for (int i = 0; i < nx; i++) { - xTagTemp[i] = Math.log(xTags[i]); - } - } else { - System.arraycopy(xTags, 0, xTagTemp, 0, nx); - } - - double xSampleWidth; - double fudge = 1.5 * 0.9; // 0.9 was removed from another code. - - boolean isNN= interpolateType == Interpolate.NearestNeighbor || interpolateType==Interpolate.BinXInterpY; - - if ( isNN ) { - fudge = 1.01; - } - if (xTagWidth == null) { - double d = Double.MAX_VALUE / 4; // avoid roll-over when *1.5 - xSampleWidth = d; - } else { - if (UnitsUtil.isRatiometric(xTagWidth.getUnits())) { - double p = xTagWidth.doubleValue(Units.logERatio); - xSampleWidth = p * fudge; - } else { - double d = xTagWidth.doubleValue(xTagUnits.getOffsetUnits()); - xSampleWidth = d * fudge; - } - } - - double pixelSize= ddX.binWidth(); - xSampleWidth= xSampleWidth+ pixelSize; // there's a bug where two close measurements can fall into bins where the centers are more than xSampleWidth apart, so add a pixel width fuzz here. - - for (int j = 0; j < ny; j++) { - int ii1 = -1; - int ii2; - for (int i = 0; i < nx; i++) { - if (weights[i][j] > 0. && ii1 == (i - 1)) { // ho hum another valid point - i1[i] = -1; - i2[i] = -1; - ii1 = i; - } else if (weights[i][j] > 0. && ii1 == -1) { // first valid point - i1[i] = -1; - i2[i] = -1; - ii1 = i; - if ( isNN ) { - for (int iii = 0; iii < i; iii++) { - i2[iii] = ii1; - } - } - } else if (weights[i][j] > 0. && ii1 < (i - 1)) { // bracketed a gap, interpolate - if ((ii1 > -1)) { // need restriction on Y gap size - i1[i] = -1; - i2[i] = -1; - ii2 = i; - for (int iii = i - 1; iii >= ii1; iii--) { - i1[iii] = ii1; - i2[iii] = ii2; - } - ii1 = i; - } - } else { - i1[i] = -1; - i2[i] = -1; - } - } - if ( isNN && ii1 > -1) { - for (int iii = ii1; iii < nx; iii++) { - i1[iii] = ii1; - } - } - if ( isNN ) { - for (int i = 0; i < nx; i++) { - boolean doInterp; //TODO? Really, this is the name? I think doGrow is better - if ( i1[i]!= -1 && i2[i] != -1) { - boolean doInterpR= ( xTagTemp[i2[i]] - xTagTemp[i] ) < xSampleWidth/2; - doInterp= doInterpR || ( xTagTemp[i] - xTagTemp[i1[i]] ) < xSampleWidth/2; - //doInterp= doInterp || ( xTagTemp[i2[i]]-xTagTemp[i1[i]] ) < xSampleWidth*2; // strange bit of code that is probably wrong. - } else { - //kludge for bug 000321 - //doInterp= Math.min(i1[i] == -1 ? Double.MAX_VALUE : (xTagTemp[i] - xTagTemp[i1[i]]), i2[i] == -1 ? Double.MAX_VALUE : (xTagTemp[i2[i]] - xTagTemp[i])) < xSampleWidth / 2; - //kludge for bug 000321 - if ( i1[i]==-1 && i2[i]==-1 ) { - doInterp= false; - } else { - if ( ddX.isLog() && !UnitsUtil.isRatiometric(xTagWidth.getUnits()) ) { - doInterp= false; - } else { - if ( i1[i]==-1 ) { - doInterp= ( xTagTemp[i2[i]] - xTagTemp[i] ) < xSampleWidth/2; - } else { - doInterp= ( xTagTemp[i] - xTagTemp[i1[i]] ) < xSampleWidth/2; - } - } - } - } - if ( doInterp ) { - int idx; - if (i1[i] == -1) { - idx = i2[i]; - } else if (i2[i] == -1) { - idx = i1[i]; - } else { - a2 = ((xTagTemp[i] - xTagTemp[i1[i]]) / (xTagTemp[i2[i]] - xTagTemp[i1[i]])); - if (a2 < 0.5) { - idx = i1[i]; - } else { - idx = i2[i]; - } - } - data[i][j] = data[idx][j]; - weights[i][j] = weights[idx][j]; - } - } - } else { - for (int i = 0; i < nx; i++) { - if ((i1[i] != -1) && ((xTagTemp[i2[i]] - xTagTemp[i1[i]]) < xSampleWidth || i2[i] - i1[i] == 2)) { //kludge for bug 000321 - a2 = ((xTagTemp[i] - xTagTemp[i1[i]]) / (xTagTemp[i2[i]] - xTagTemp[i1[i]])); - a1 = 1. - a2; - data[i][j] = data[i1[i]][j] * a1 + data[i2[i]][j] * a2; - weights[i][j] = weights[i1[i]][j] * a1 + weights[i2[i]][j] * a2; //approximate - } - } - } - } - } - - /** - * interpolate the missing pixels by scanning columns, looking for point pairs where weights > 0 and that are acceptably close. - * @param data the data, not scaled by weight. - * @param weights 0 means ignore, positive means valid - * @param ddX - * @param xTagWidth the nominal cadence between measurements. This defines acceptably close, and we apply a fudge factor. - * @param interpolateType if NearestNeighbor, then special actions can occur. - */ static void fillInterpolateY(final double[][] data, final double[][] weights, RebinDescriptor ddY, Datum yTagWidth, Interpolate interpolateType) { final int nx = data.length; @@ -1066,17 +536,16 @@ static void fillInterpolateY(final double[][] data, final double[][] weights, Re yTagTemp[j] = Math.log(yTags[j]); } } else { - System.arraycopy(yTags, 0, yTagTemp, 0, ny); + for (int j = 0; j < ny; j++) { + yTagTemp[j] = yTags[j]; + } } - boolean isNN= interpolateType == Interpolate.NearestNeighbor; - double ySampleWidth; - double fudge = 1.5 * 0.9; // 0.9 was removed from another code. - if ( isNN ) { - fudge = 1.01; + double fudge = 1.5; + if (interpolateType == Interpolate.NearestNeighbor) { + fudge = 1.1; } - boolean ySampleWidthRatiometric= false; if (yTagWidth == null) { double d = Double.MAX_VALUE / 4; // avoid roll-over when *1.5 ySampleWidth = d; @@ -1084,39 +553,15 @@ static void fillInterpolateY(final double[][] data, final double[][] weights, Re if (UnitsUtil.isRatiometric(yTagWidth.getUnits())) { double p = yTagWidth.doubleValue(Units.logERatio); ySampleWidth = p * fudge; - ySampleWidthRatiometric= true; } else { double d = yTagWidth.doubleValue(yTagUnits.getOffsetUnits()); ySampleWidth = d * fudge; } } - double pixelSize= ddY.binWidth(); - - final double[] ySampleWidths= new double[ddY.numberOfBins()]; - for ( int j=0; j 0. && ii1 == (j - 1)) { // ho hum another valid point @@ -1128,7 +573,7 @@ static void fillInterpolateY(final double[][] data, final double[][] weights, Re i1[j] = -1; i2[j] = -1; ii1 = j; - if ( isNN ) { + if (interpolateType == Interpolate.NearestNeighbor) { for (int jjj = 0; jjj < j; jjj++) { i2[jjj] = ii1; } @@ -1151,31 +596,19 @@ static void fillInterpolateY(final double[][] data, final double[][] weights, Re i2[j] = -1; } } - if ( isNN && ii1 > -1) { + if (interpolateType == Interpolate.NearestNeighbor && ii1 > -1) { for (int jjj = ii1; jjj < ny; jjj++) { i1[jjj] = ii1; } } - if ( isNN ) { + if (interpolateType == Interpolate.NearestNeighbor) { for (int j = 0; j < ny; j++) { boolean doInterp; if ( i1[j]!= -1 && i2[j] != -1) { - boolean doInterpR= ( yTagTemp[i2[j]] - yTagTemp[j] ) < ySampleWidths[j]; - doInterp= doInterpR || ( yTagTemp[j] - yTagTemp[i1[j]] ) < ySampleWidths[j]; - doInterp= doInterp || ( yTagTemp[i2[j]]-yTagTemp[i1[j]] ) < ySampleWidths[j]; + doInterp= ( yTagTemp[i2[j]]-yTagTemp[i1[j]] ) < ySampleWidth*2; } else { //kludge for bug 000321 - if ( ddY.isLog() && !UnitsUtil.isRatiometric(yTagUnits) ) { - doInterp= false; - } else { - if ( i1[j]==-1 && i2[j]==-1 ) { - doInterp= false; - } else if ( i1[j]==-1 ) { - doInterp= ( yTagTemp[i2[j]] - yTagTemp[j] ) < ySampleWidths[j]/2; - } else { - doInterp= ( yTagTemp[j] - yTagTemp[i1[j]] ) < ySampleWidths[j]/2; - } - } + doInterp= Math.min(i1[j] == -1 ? Double.MAX_VALUE : (yTagTemp[j] - yTagTemp[i1[j]]), i2[j] == -1 ? Double.MAX_VALUE : (yTagTemp[i2[j]] - yTagTemp[j])) < ySampleWidth / 2; } if ( doInterp ) { int idx; @@ -1195,11 +628,14 @@ static void fillInterpolateY(final double[][] data, final double[][] weights, Re weights[i][j] = weights[i][idx]; } + if ( i==1 && j==34 ) { + int jkk=0; + } } } else { - for (int j = 0; j < ny; j++) { //yunits on sample width - if ((i1[j] != -1) && ((yTagTemp[i2[j]] - yTagTemp[i1[j]]) < ySampleWidths[j] || i2[j] - i1[j] == 2)) { //kludge for bug 000321 + for (int j = 0; j < ny; j++) { + if ((i1[j] != -1) && ((yTagTemp[i2[j]] - yTagTemp[i1[j]]) < ySampleWidth || i2[j] - i1[j] == 2)) { //kludge for bug 000321 a2 = ((yTagTemp[j] - yTagTemp[i1[j]]) / (yTagTemp[i2[j]] - yTagTemp[i1[j]])); a1 = 1. - a2; @@ -1251,16 +687,16 @@ private void enlargePixels(double[][] rebinData, double[][] rebinWeights) { } /** - * "Interpolate" here simply means connecting the data points. - * @return true indicates we should connect the data points. + * Getter for property interpolate. + * @return Value of property interpolate. */ public boolean isInterpolate() { return this.interpolate; } /** - * "Interpolate" here simply means connecting the data points. - * @param interpolate true indicates we should connect the data points. + * Setter for property interpolate. + * @param interpolate New value of property interpolate. */ public void setInterpolate(boolean interpolate) { this.interpolate = interpolate; diff --git a/dasCore/src/org/das2/dataset/CacheTag.java b/dasCore/src/org/das2/dataset/CacheTag.java new file mode 100755 index 000000000..f6a8bc28d --- /dev/null +++ b/dasCore/src/org/das2/dataset/CacheTag.java @@ -0,0 +1,116 @@ +package org.das2.dataset; + +import org.das2.datum.DatumRange; +import org.das2.datum.Datum; +import org.das2.datum.DatumUtil; + +/** + * CacheTags are used to represent the coverage of datasets stored in a cache, and are + * the reference used to decide if a cache entry is capable of satisfying a data request. + * + */ +public class CacheTag { + + public static final String INTRINSIC = "intrinsic"; + + DatumRange range; + Datum resolution; + + /** + * Appends two CacheTags, when possible. The new range covers the ranges, and the resolution is the lower of the two. + * @param tag1 a CacheTag + * @param tag2 a CacheTag that is adjacent to tag1 + * @return a CacheTag that covers both CacheTags precisely. + */ + public static CacheTag append( CacheTag tag1, CacheTag tag2 ) { + Datum res; + if ( tag1.resolution==null && tag2.resolution==null ) { + res= null; + } else { + if ( tag1.resolution!=null && tag2.resolution!=null ) { + res= tag1.resolution.gt( tag2.resolution ) ? tag1.resolution : tag2.resolution; + } else { + res= tag1.resolution==null ? tag2.resolution : tag1.resolution; + } + } + + Datum min; + Datum max; + if ( !tag1.range.intersects(tag2.range) ) { + if ( tag2.range.min().lt( tag1.range.min() ) ) { + CacheTag temp= tag1; + tag1= tag2; + tag2= temp; + } + if ( tag1.range.max().lt(tag2.range.min()) ) { + throw new IllegalArgumentException("cache tags cannot be appended, they are not adjacent"); + } + min= tag1.range.min(); + max= tag2.range.max(); + } else { + min= tag1.range.min().lt( tag2.range.min() ) ? tag1.range.min() : tag2.range.min(); + max= tag1.range.max().gt( tag2.range.max() ) ? tag1.range.max() : tag2.range.max(); + } + + DatumRange range= new DatumRange( min, max ); + return new CacheTag( range, res ); + } + + /** + * Constructs a new CacheTag. + * @param start the beginning of the interval. + * @param end the end of the interval. + * @param resolution the highest resolution request that can be provided. + */ + public CacheTag(Datum start, Datum end, Datum resolution) { + this( new DatumRange( start, end ), resolution ); + } + + /** + * Constucts a new CacheTag. + * @param range the interval covered by the CacheTag. + * @param resolution the highest resolution request that can be provided. + */ + public CacheTag( DatumRange range, Datum resolution) { + this.range= range; + this.resolution= resolution; + } + + /** + * Returns a string representation of the object. + * @return a human consumable representation of the cache tag. This should fit on + * one line as it is used to list cache contents. + */ + public String toString() { + return range + " @ " + ( resolution==null ? INTRINSIC : ""+DatumUtil.asOrderOneUnits(resolution) ); + } + + /** + * Indicates if the cache tag the the capability of satifying the given cache tag. If the tag has a lower (courser) resolution and its bounds are within + * this CacheTag. + * @return true if the tag has a lower resolution and its bounds are within + * this CacheTag. + * @param tag a CacheTag to test. + */ + public boolean contains( CacheTag tag ) { + return ( this.range.contains( tag.range ) + && ( this.resolution==null + || ( tag.resolution!=null && this.resolution.le( tag.resolution ) ) + ) ); + } + + /** + * the range covered by the cache tag. + */ + public DatumRange getRange() { + return this.range; + } + + /** + * the resolution of the cache tag, which may be null. + */ + public Datum getResolution() { + return this.resolution; + } +} + diff --git a/dasCore/src/org/das2/dataset/ClippedTableDataSet.java b/dasCore/src/org/das2/dataset/ClippedTableDataSet.java index 6c30eb0c1..bc9b73be1 100755 --- a/dasCore/src/org/das2/dataset/ClippedTableDataSet.java +++ b/dasCore/src/org/das2/dataset/ClippedTableDataSet.java @@ -7,16 +7,16 @@ package org.das2.dataset; import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.datum.DatumVector; import org.das2.datum.Datum; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.SemanticOps; +import java.util.*; /** * * @author Jeremy */ -public class ClippedTableDataSet extends org.virbo.dataset.AbstractDataSet { +public class ClippedTableDataSet implements TableDataSet { /* * clippedTableDataSet @@ -25,49 +25,68 @@ public class ClippedTableDataSet extends org.virbo.dataset.AbstractDataSet { * but not much more. */ - QDataSet source; + TableDataSet source; int xoffset; int xlength; - final void calculateXOffset( Datum xmin, Datum xmax ) { - QDataSet xds= SemanticOps.xtagsDataSet(source); - xoffset= DataSetUtil.getPreviousIndex(xds, xmin); - int ix1= DataSetUtil.getNextIndex(xds, xmax ); + int[] yoffsets; + int[] ylengths; + + int tableOffset; + int tableCount; + + void calculateXOffset( Datum xmin, Datum xmax ) { + xoffset= DataSetUtil.getPreviousColumn(source, xmin); + int ix1= DataSetUtil.getNextColumn(source, xmax ); xlength= ix1- xoffset+1; } - - public ClippedTableDataSet( QDataSet source, Datum xmin, Datum xmax, Datum ymin, Datum ymax ) { + + void calculateTableOffset() { + tableOffset= -99; + for ( int itable=0; itable xoffset ) { + tableOffset= itable; + } + if ( tableOffset!=-99 + && source.tableEnd(itable) >= xoffset+xlength ) { + tableCount= itable - tableOffset + 1; + } + } + } + + void calculateYOffsets( Datum ymin, Datum ymax ) { + yoffsets= new int[tableCount]; + ylengths= new int[tableCount]; + for ( int itable=tableOffset; itable 2 ) { + public ClippedTableDataSet( TableDataSet source, int xoffset, int xlength, + int yoffset, int ylength ) { + if ( source.tableCount() > 1 ) { throw new IllegalArgumentException( "this ClippedTableDataSet constructor requires that there be only one table" ); } - if ( source.length() < xoffset+xlength ) { + if ( source.getXLength() < xoffset+xlength ) { throw new IllegalArgumentException( "xoffset + xlength greater than the number of XTags in source" ); } - if ( source.length(0) < yoffset+ylength ) { + if ( source.getYLength(0) < yoffset+ylength ) { throw new IllegalArgumentException( "yoffset + ylength greater than the number of YTags in source" ); } if ( yoffset<0 || xoffset<0 ) { @@ -76,25 +95,171 @@ public ClippedTableDataSet( QDataSet source, int xoffset, int xlength, int yoffs this.source= source; this.xoffset= xoffset; this.xlength= xlength; + this.tableOffset= 0; + this.tableCount= 1; + this.yoffsets= new int[] { yoffset }; + this.ylengths= new int[] { ylength }; } - - public double value( int i, int j ) { - return source.value( i+xoffset, j ); + + private ClippedTableDataSet( TableDataSet source, int xoffset, int xlength, + int [] yoffsets, int [] ylengths, int tableOffset, int tableCount ) { + if ( source==null ) { + throw new IllegalArgumentException("source is null"); + } + this.source= source; + this.xoffset= xoffset; + this.xlength= xlength; + this.yoffsets= yoffsets; + this.ylengths= ylengths; + this.tableOffset= tableOffset; + this.tableCount= tableCount; } - - @Override - public int rank() { - return 2; + + + public Datum getDatum(int i, int j) { + return source.getDatum( i+xoffset, j+yoffsets[tableOfIndex(i)] ); } - - @Override - public int length() { - return this.xlength; + + public double getDouble(int i, int j, Units units) { + return source.getDouble( i+xoffset, j+yoffsets[tableOfIndex(i)], units ); + } + + public double[] getDoubleScan(int i, Units units) { + int table = tableOfIndex(i); + int yLength = getYLength(table); + double[] array = new double[yLength]; + double[] sourceArray = source.getDoubleScan(i + xoffset, units); + System.arraycopy(sourceArray, yoffsets[table], array, 0, yLength); + return array; + } + + public DatumVector getScan(int i) { + int table = tableOfIndex(i); + int yLength = getYLength(table); + return source.getScan(i+xoffset).getSubVector(yoffsets[table], yoffsets[table] + yLength); + } + + public int getInt(int i, int j, Units units) { + return source.getInt( i+xoffset, j+yoffsets[tableOfIndex(i)], units ); + } + + public DataSet getPlanarView(String planeID) { + TableDataSet sourcePlane= (TableDataSet)source.getPlanarView(planeID); + if (sourcePlane==null) { + return null; + } else { + return new ClippedTableDataSet(sourcePlane, + xoffset,xlength,yoffsets,ylengths,tableOffset,tableCount); + } + } + + public String[] getPlaneIds() { + return source.getPlaneIds(); + } + + public Map getProperties() { + return source.getProperties(); + } + + public Object getProperty( String name ) { + return source.getProperty( name ); + } + + public int getXLength() { + return xlength; + } + + public VectorDataSet getXSlice(int i) { + int itable= source.tableOfIndex(i+xoffset); + DatumRange dr= new DatumRange( source.getYTagDatum(itable,yoffsets[itable]), source.getYTagDatum(itable,yoffsets[itable]+ylengths[itable]) ); + return new ClippedVectorDataSet( source.getXSlice( i+xoffset ), dr ); + } + + public Datum getXTagDatum(int i) { + return source.getXTagDatum( i+xoffset ); + } + + public double getXTagDouble(int i, Units units) { + return source.getXTagDouble( i+xoffset, units ); + } + + public int getXTagInt(int i, Units units) { + return source.getXTagInt( i+xoffset, units ); + } + + public Units getXUnits() { + return source.getXUnits(); + } + + public int getYLength(int table) { + return ylengths[table]; + } + + public VectorDataSet getYSlice(int j, int table) { + return source.getYSlice( j+yoffsets[table], table+tableOffset ); + } + + public Datum getYTagDatum(int table, int j) { + return source.getYTagDatum( table+tableOffset, j+yoffsets[table] ); + } + + public double getYTagDouble(int table, int j, Units units) { + return source.getYTagDouble( table+tableOffset, j+yoffsets[table], units ); + } + + public int getYTagInt(int table, int j, Units units) { + return source.getYTagInt( table+tableOffset, j+yoffsets[table], units ); + } + + public org.das2.datum.Units getYUnits() { + return source.getYUnits(); + } + + public org.das2.datum.Units getZUnits() { + return source.getZUnits(); + } + + public int tableCount() { + return tableCount; + } + + public int tableEnd(int table) { + int i= source.tableEnd(table+tableOffset) - xoffset; + if ( i>getXLength() ) { + return getXLength(); + } else { + return i; + } + } + + public int tableOfIndex(int i) { + return source.tableOfIndex( i+xoffset ) - tableOffset; + } + + public int tableStart(int table) { + int i= source.tableStart(table+tableOffset) - xoffset; + if ( i<0 ) { + return 0; + } else { + return i; + } + } + + public String toString() { + return "ClippedTableDataSet " + TableUtil.toString(this); + } + + public DatumVector getYTags(int table) { + double[] tags = new double[getYLength(table)]; + Units yUnits = getYUnits(); + for (int j = 0; j < tags.length; j++) { + tags[j] = getYTagDouble(table, j, yUnits); + } + return DatumVector.newDatumVector(tags, yUnits); } - @Override - public int length(int i) { - return source.length(i+xoffset); + public Object getProperty(int table, String name) { + return source.getProperty(table, name); } diff --git a/dasCore/src/org/das2/dataset/ConstantDataSetDescriptor.java b/dasCore/src/org/das2/dataset/ConstantDataSetDescriptor.java index 86de8805e..6628d6c4f 100755 --- a/dasCore/src/org/das2/dataset/ConstantDataSetDescriptor.java +++ b/dasCore/src/org/das2/dataset/ConstantDataSetDescriptor.java @@ -65,7 +65,7 @@ public void requestDataSet(Datum start, Datum end, Datum resolution, ProgressMon DataSetUpdateEvent dsue= null; try { DataSet ds= getDataSet(start, end, resolution, monitor); - dsue= new DataSetUpdateEvent( (Object)this, DataSetAdapter.create(ds) ); + dsue= new DataSetUpdateEvent( (Object)this, ds ); fireDataSetUpdateEvent(dsue); } catch ( DasException e ) { dsue= new DataSetUpdateEvent( (Object)this,e); diff --git a/dasCore/src/org/das2/dataset/DataSet.java b/dasCore/src/org/das2/dataset/DataSet.java index e2f6060e1..91c13637b 100644 --- a/dasCore/src/org/das2/dataset/DataSet.java +++ b/dasCore/src/org/das2/dataset/DataSet.java @@ -37,19 +37,7 @@ public interface DataSet { * CacheTag object describing the start, end, and resolution of the dataset. */ final static String PROPERTY_CACHE_TAG= "cacheTag"; - - /** DatumRange describing the range of a dataset in the X dimension */ - final static String PROPERTY_X_CACHE_RNG= "xCacheRange"; - - /** Datum providing the resolution in the X dimension */ - final static String PROPERTY_X_CACHE_RES = "xCacheResolution"; - - /** DatumRange describing the range of a dataset in the X dimension */ - final static String PROPERTY_Y_CACHE_RNG= "yCacheRange"; - - /** Datum providing the resolution in the X dimension */ - final static String PROPERTY_Y_CACHE_RES = "xCacheResolution"; - + /** * Long estimating the size of the dataset in memory. For example, if a dataset is * backed by a local file, then zero for this indicates no penalty for storing this @@ -72,39 +60,16 @@ public interface DataSet { * DatumRange useful for setting scales */ final static String PROPERTY_X_RANGE="xRange"; - - /** Double, used to indicate minimum valid X value */ - final static String PROPERTY_X_VALID_MIN="xValidMin"; - - /** Double, used to indicate maximum valid X value */ - final static String PROPERTY_X_VALID_MAX="xValidMax"; - /** Datum, useful for setting scales */ + /** + * DatumRange useful for setting scales + */ final static String PROPERTY_Y_RANGE="yRange"; - /** Double, used to indicate minimum valid X value */ - final static String PROPERTY_Y_VALID_MIN="yValidMin"; - - /** Double, used to indicate maximum valid X value */ - final static String PROPERTY_Y_VALID_MAX="yValidMax"; - - /** DatumRange useful for setting scales */ + /** + * DatumRange useful for setting scales + */ final static String PROPERTY_Z_RANGE="zRange"; - - /** Double, used to indicate minimum valid X value */ - final static String PROPERTY_Z_VALID_MIN="zValidMin"; - - /** Double, used to indicate maximum valid X value */ - final static String PROPERTY_Z_VALID_MAX="zValidMax"; - - /** Double: Raw value used to indicate fill data. */ - final static String PROPERTY_Y_FILL="yFill"; - - /** Raw value used to indicate fill data. - * Since yscan's are rectangular it's handy to have a fill value to indicate - * gaps in the rectangle - */ - final static String PROPERTY_Z_FILL="zFill"; /** * suggest render method to use. These are @@ -130,39 +95,13 @@ public interface DataSet { final static String PROPERTY_Y_LABEL="yLabel"; final static String PROPERTY_Z_LABEL="zLabel"; - - /** A brief description of the x direction values */ - final static String PROPERTY_X_SUMMARY="xSummary"; - - /** A brief description of the y direction values */ - final static String PROPERTY_Y_SUMMARY="ySummary"; - - /** A brief description of the z direction values */ - final static String PROPERTY_Z_SUMMARY="zSummary"; - - /** A brief description for the entire stream */ - final static String PROPERTY_SUMMARY="summary"; - - /** The format for printing X, Y and Z items, see the Das 2.2.2 (or higher) ICD - * for a list of valid foramt strings - */ - final static String PROPERTY_X_FORMAT="xFormat"; - final static String PROPERTY_Y_FORMAT="yFormat"; - final static String PROPERTY_Z_FORMAT="zFormat"; - + /** - * finally, this data model is done with the addition of title. + * Boolean assuring that the dataset is monotonic in X. This allows + * some optimizations to be made. */ - final static String PROPERTY_TITLE="title"; - - /** Boolean assuring that the dataset is monotonic in X. This allows - * some optimizations to be made. */ final static String PROPERTY_X_MONOTONIC="xMonotonic"; - - /** Boolean assuring that the dataset is monotonic in Y. This allows - * some optimizations to be made. */ - final static String PROPERTY_Y_MONOTONIC="yMonotonic"; - + /** * dataset containing the peaks when available */ @@ -202,7 +141,7 @@ public interface DataSet { Units getYUnits(); /** Returns the value of the x tag at the given index i as a - * Datum. + * Datum. * @param i the index of the requested x tag * @return the value of the x tag at the given index i as a * Datum. @@ -210,7 +149,7 @@ public interface DataSet { Datum getXTagDatum(int i); /** Returns the value of the x tag at the given index i as a - * double in the given units. XTags must be + * double in the given units. XTags must be * monotonically increasing with i. * @return the value of the x tag at the given index i as a * double. @@ -220,7 +159,7 @@ public interface DataSet { double getXTagDouble(int i, Units units); /** Returns the value of the x tag at the given index i as an - * int in the given units. XTags must be + * int in the given units. XTags must be * monotonically increasing with i. * @return the value of the x tag at the given index i as an * int. @@ -243,9 +182,9 @@ public interface DataSet { //TODO: consider throwing IllegalArgumentException if the plane doesn't exist. // we have methods to query for the plane names. DataSet getPlanarView(String planeID); - + /** - * Returns a list of auxiliary planes (e.g. weights, peaks) for the dataset. + * Returns a list of auxillary planes (e.g. weights, peaks) for the dataset. * Note that the default plane, "" may or may not be returned, based on * implementations. */ diff --git a/dasCore/src/org/das2/dataset/DataSetAdapter.java b/dasCore/src/org/das2/dataset/DataSetAdapter.java deleted file mode 100644 index d0bbad034..000000000 --- a/dasCore/src/org/das2/dataset/DataSetAdapter.java +++ /dev/null @@ -1,553 +0,0 @@ -/* - * DataSetAdapter.java - * - * Created on April 2, 2007, 8:49 AM - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ -package org.das2.dataset; - -import java.text.ParseException; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.das2.datum.Datum; -import org.das2.datum.DatumRange; -import org.das2.datum.DatumRangeUtil; -import org.das2.datum.Units; -import org.das2.system.DasLogger; -import org.virbo.dataset.AbstractDataSet; -import org.virbo.dataset.DDataSet; -import org.virbo.dataset.DRank0DataSet; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dsops.Ops; - -/** - * Presents legacy das2 datasets as QDataSets. See also TableDataSetAdapter,VectorDataSetAdapter - * - * @author jbf - */ -public class DataSetAdapter { - - private static final Logger logger = DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG); - - public static final String PROPERTY_SOURCE = "adapterSource"; - - /////////////////////////////////////////////////////////////////////////////////// - // Helper for conversion such as %{xCacheRange} -> %{USER_PROPERTIES.xCacheRange} - protected static Map adaptSubstitutions(Map das2props) { - // Defines a pattern with three subgroups - Pattern ptrn = Pattern.compile("(%\\{)(\\S.*)(\\})"); - - for (Map.Entry e : das2props.entrySet()) { - Object o = e.getValue(); - if (!(o instanceof String)) { - continue; - } - String s = (String) o; - Matcher m = ptrn.matcher(s); - while (m.find()) { - // Group indices are not as expected, 0 = entire match, 1 = 1st group, etc. - if (!m.group(2).contains("USER_PROPERTIES")) { - s = String.format("%sUSER_PROPERTIES.%s%s", s.substring(0, m.end(1)), - s.substring(m.start(2), m.end(2)), - s.substring(m.start(3), s.length())); - m = ptrn.matcher(s); - } - } - e.setValue(s); - } - return das2props; - } - - /** - * Created a new QDataSet given a Das2 DataSet - * - * This function and createLegacyDataSet() are inverses, though a round trip conversion is not guaranteed - * to preserve all properties - * - * @param ds A Das2 Dataset - * @return A new QDataSet - */ - public static AbstractDataSet create(DataSet ds) { - if (ds == null) { - throw new NullPointerException("dataset is null"); - } - - // X-Y Datasets - if (ds instanceof VectorDataSet) { - - if (ds.getPlaneIds().length <= 1) { - //Handle x single y as a simple vector - return new Vector((VectorDataSet) ds); - } else { - //Handle x multi y as a bundle - VectorDataSet vds = (VectorDataSet) ds; - AbstractDataSet bds = (AbstractDataSet) Ops.bundle(null, new Vector(vds)); - String[] planes = ds.getPlaneIds(); - Units unitsY = null; - boolean bCommonYUnits = false; - for (int i = 1; i < planes.length; i++) { - // Arg, everything we want to get at is hidden behind 7 levels of - // interfaces. As a bonus, class names repeat in different packages from - // the same dev group. - org.das2.dataset.AbstractDataSet.ViewDataSet view - = (org.das2.dataset.AbstractDataSet.ViewDataSet) vds.getPlanarView(planes[i]); - if (unitsY == null) { - unitsY = view.getYUnits(); - } else { - bCommonYUnits = (unitsY == view.getYUnits()); - } - - Vector v = new Vector((VectorDataSet) vds.getPlanarView(planes[i]), planes[i]); - v.putProperty(QDataSet.NAME, planes[i]); - Ops.bundle(bds, v); - } - - // Convert Das2 property substitutions to USER_PROPERTIES substitutions - Map dasProps = adaptSubstitutions(vds.getProperties()); - bds.putProperty(QDataSet.USER_PROPERTIES, dasProps); - - bds.putProperty(QDataSet.DEPEND_0, new XTagsDataSet(vds)); - bds.putProperty(QDataSet.TITLE, dasProps.get(DataSet.PROPERTY_TITLE)); - - // If all Y elements of the bundle have the same units, put those units - // on the Y axis, that way something identifies Y. - if (bCommonYUnits) { - bds.putProperty(QDataSet.UNITS, unitsY); - bds.putProperty(QDataSet.LABEL, unitsY.toString()); - } - - // Copy more properties into the overall bundle dataset, wow this really - // needs to be refactored. - bds.putProperty(QDataSet.SCALE_TYPE, vds.getProperty(DataSet.PROPERTY_Y_SCALETYPE)); - DatumRange yRng = (DatumRange) vds.getProperty(DataSet.PROPERTY_Y_RANGE); - if (yRng != null) { - bds.putProperty(QDataSet.TYPICAL_MIN, yRng.min().value()); - bds.putProperty(QDataSet.TYPICAL_MAX, yRng.max().value()); - } - - return DDataSet.copy(bds); - } - } - - // X-YScan Datasets - if (ds instanceof TableDataSet) { - TableDataSet tds = (TableDataSet) ds; - if (tds.tableCount() <= 1) { - return new SimpleTable(tds); - } else { - if (tds instanceof DefaultTableDataSet && tds.tableCount() > tds.getXLength() / 2) { - return ((DefaultTableDataSet) tds).toQDataSet(); - } else { - return new MultipleTable(tds); - } - } - } - - throw new IllegalArgumentException("unsupported dataset type: " + ds.getClass().getName()); - } - - /** - * Created a new Das2 DataSet given a QDataSet - * - * This function and create() are inverses, though a round trip conversion is not guaranteed to preserve - * all properties. Note that not all QDataSets can be represented as Das2 DataSets. If the given QDataSet - * has no Das2 analog, an IllegalArgumentException is thrown. - * - * @param ds A QDataSet - * @return A new Das2 DataSet - */ - public static DataSet createLegacyDataSet(org.virbo.dataset.QDataSet ds) { - if (ds.rank() == 1) { - return VectorDataSetAdapter.create(ds); - } else if (SemanticOps.isBundle(ds)) { - return VectorDataSetAdapter.createFromBundle(ds); - } else if (ds.rank() == 2) { - return TableDataSetAdapter.create(ds); - } else if (ds.rank() == 3) { - return TableDataSetAdapter.create(ds); - } else { - throw new IllegalArgumentException("unsupported rank: " + ds.rank()); - } - } - - /////////////////////////////////////////////////////////////////////////////////// - // Helper dataset holds DEPEND_0 for MultipleTable QDataSets - static class MultiTableXTagsDataSet extends AbstractDataSet { - - DataSet source; - int offset; - int length; - - MultiTableXTagsDataSet(DataSet source, int offset, int length) { - this.source = source; - this.offset = offset; - this.length = length; - properties.put(QDataSet.UNITS, source.getXUnits()); - properties.put(QDataSet.LABEL, source.getProperty(DataSet.PROPERTY_X_LABEL)); - Object o = source.getProperty(DataSet.PROPERTY_X_MONOTONIC); - if (o != null) { - properties.put(QDataSet.MONOTONIC, o); - } - Datum xTagWidth = (Datum) source.getProperty(DataSet.PROPERTY_X_TAG_WIDTH); - if (xTagWidth != null) { - properties.put(QDataSet.CADENCE, org.virbo.dataset.DataSetUtil.asDataSet(xTagWidth)); - } - } - - @Override - public int rank() { - return 1; - } - - @Override - public double value(int i) { - return source.getXTagDouble(i + offset, source.getXUnits()); - } - - @Override - public int length() { - return length; - } - } - - /////////////////////////////////////////////////////////////////////////////////// - // Helper dataset, holds DEPEND_0 for Vector & SimpleTable QDataSets - static class XTagsDataSet extends AbstractDataSet { - - org.das2.dataset.DataSet source; - - XTagsDataSet(org.das2.dataset.DataSet source) { - this.source = source; - properties.put(QDataSet.UNITS, source.getXUnits()); - properties.put(QDataSet.LABEL, source.getProperty(DataSet.PROPERTY_X_LABEL)); - - // QDataSet Cadences are a rank 0 dataset - Datum d = (Datum) source.getProperty(DataSet.PROPERTY_X_TAG_WIDTH); - if (d != null) { - properties.put(QDataSet.CADENCE, DRank0DataSet.create(d)); - } - - Object o = source.getProperty(org.das2.dataset.DataSet.PROPERTY_X_MONOTONIC); - if (o != null) { - properties.put(QDataSet.MONOTONIC, o); - } - } - - @Override - public int rank() { - return 1; - } - - @Override - public double value(int i) { - return source.getXTagDouble(i, source.getXUnits()); - } - - @Override - public int length() { - return source.getXLength(); - } - - } - - /////////////////////////////////////////////////////////////////////////////////// - // Top Level QDataSet for X vs Y data - static class Vector extends AbstractDataSet { - - VectorDataSet source; - - // Time for more ugly hacks: TODO: Convert straight to QDataSet and get - // rid of stupid crap like this. - Vector(VectorDataSet source) { - this(source, null); - } - - private static Object hack(Map m, String k, String id) { - if (id == null) { - return m.get(k); - } else { - return m.get(id + "." + k); - } - } - - // This constructor takes a plane ID so that property values can be gathered. - // It's a dump hack to get around: - // 1. Das2 DataSet objects are immutable - // 2. We are not converting straght to QDataSet - Vector(VectorDataSet source, String sPlaneID) { - super(); - this.source = source; - - //Throw everything including the well-known stuff into user properties - Map dasProps = adaptSubstitutions(source.getProperties()); - properties.put(QDataSet.USER_PROPERTIES, dasProps); - - properties.put(QDataSet.TITLE, hack(dasProps, DataSet.PROPERTY_TITLE, sPlaneID)); - properties.put(QDataSet.UNITS, source.getYUnits()); - properties.put(QDataSet.LABEL, hack(dasProps, DataSet.PROPERTY_Y_LABEL, sPlaneID)); - properties.put(QDataSet.FORMAT, hack(dasProps, DataSet.PROPERTY_Y_FORMAT, sPlaneID)); - properties.put(QDataSet.DEPEND_0, new XTagsDataSet(source)); - properties.put(PROPERTY_SOURCE, source); - - // http://www.sarahandjeremy.net/~jbf/1wire/data/2007/0B000800408DD710.20071201.d2s uses property "valid_range" - String syValid= (String)dasProps.get("valid_range"); - if ( syValid!=null ) { - try { - DatumRange yValid= DatumRangeUtil.parseDatumRange( syValid, source.getYUnits() ); - double val = yValid.min().doubleValue(source.getYUnits()); - properties.put( QDataSet.VALID_MIN, val ); - val = yValid.max().doubleValue(source.getYUnits()); - properties.put( QDataSet.VALID_MAX, val ); - } catch (ParseException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - //New properties after 2014-05-28 Das2 Dev meeting - Datum d = (Datum) hack(dasProps, DataSet.PROPERTY_Y_VALID_MIN, sPlaneID); - if (d != null) { - double val = d.doubleValue(source.getYUnits()); - properties.put(QDataSet.VALID_MIN, val); - } - d = (Datum) hack(dasProps, DataSet.PROPERTY_Y_VALID_MAX, sPlaneID); - if (d != null) { - double val = d.doubleValue(source.getYUnits()); - properties.put(QDataSet.VALID_MAX, val); - } - - properties.put(QDataSet.FILL_VALUE, hack(dasProps, DataSet.PROPERTY_Y_FILL, sPlaneID)); - properties.put(QDataSet.SCALE_TYPE, hack(dasProps, DataSet.PROPERTY_Y_SCALETYPE, sPlaneID)); - properties.put(QDataSet.MONOTONIC, hack(dasProps, DataSet.PROPERTY_Y_MONOTONIC, sPlaneID)); - - //Add this in after next autoplot update - properties.put(QDataSet.DESCRIPTION, hack(dasProps, DataSet.PROPERTY_Y_SUMMARY, sPlaneID)); - - //Let Das2 Streams set a Y-Axis range - DatumRange yRng = (DatumRange) hack(dasProps, DataSet.PROPERTY_Y_RANGE, sPlaneID); - if (yRng != null) { - properties.put(QDataSet.TYPICAL_MIN, yRng.min().value()); - properties.put(QDataSet.TYPICAL_MAX, yRng.max().value()); - } - - d = (Datum) hack(dasProps, DataSet.PROPERTY_Y_TAG_WIDTH, sPlaneID); - if (d != null) { - properties.put(QDataSet.CADENCE, DRank0DataSet.create(d)); - } - } - - @Override - public int rank() { - return 1; - } - - @Override - public double value(int i) { - return source.getDouble(i, source.getYUnits()); - } - - @Override - public int length() { - return source.getXLength(); - } - - } - - /////////////////////////////////////////////////////////////////////////////////// - // Helper Dataset, holds DEPEND_1 for SimpleTable QDataSets - static class YTagsDataSet extends AbstractDataSet { - - TableDataSet source; - int table; - - YTagsDataSet(TableDataSet source, int table) { - this.source = source; - this.table = table; - properties.put(QDataSet.UNITS, source.getYUnits()); - properties.put(QDataSet.LABEL, source.getProperty(DataSet.PROPERTY_Y_LABEL)); - properties.put(QDataSet.SCALE_TYPE, source.getProperty(DataSet.PROPERTY_Y_SCALETYPE)); - - Datum d = (Datum) source.getProperty(DataSet.PROPERTY_Y_TAG_WIDTH); - if (d != null) { - properties.put(QDataSet.CADENCE, DRank0DataSet.create(d)); - } - - DatumRange yRng = (DatumRange) source.getProperty(DataSet.PROPERTY_Y_RANGE); - if (yRng != null) { - properties.put(QDataSet.TYPICAL_MIN, yRng.min().value()); - properties.put(QDataSet.TYPICAL_MAX, yRng.max().value()); - } - - } - - @Override - public int rank() { - return 1; - } - - @Override - public double value(int i) { - return source.getYTagDouble(table, i, source.getYUnits()); - } - - @Override - public int length() { - return source.tableCount() > 0 ? source.getYLength(table) : 99; - } - } - - /////////////////////////////////////////////////////////////////////////////////// - // Toplevel QDataSet for X,Y,Z "grid" data - static class SimpleTable extends AbstractDataSet { - - TableDataSet source; - - SimpleTable(TableDataSet source) { - super(); - if (source.tableCount() > 1) { - throw new IllegalArgumentException("only simple tables are supported"); - } - - this.source = source; - - Map dasProps = adaptSubstitutions(source.getProperties()); - - // Save properterties with value substitution strings in Autoplot Stlye - properties.put(QDataSet.USER_PROPERTIES, dasProps); - - properties.put(QDataSet.UNITS, source.getZUnits()); - properties.put(QDataSet.LABEL, dasProps.get(DataSet.PROPERTY_Z_LABEL)); - properties.put(QDataSet.TITLE, dasProps.get(DataSet.PROPERTY_TITLE)); - QDataSet xtags = new XTagsDataSet(source); - properties.put(QDataSet.DEPEND_0, xtags); - QDataSet ytags = new YTagsDataSet(source, 0); - properties.put(QDataSet.DEPEND_1, ytags); - properties.put(QDataSet.QUBE, Boolean.TRUE); - properties.put(PROPERTY_SOURCE, source); - - //Let Das2 Streams set a Z-Axis range - DatumRange zRng = (DatumRange) dasProps.get(DataSet.PROPERTY_Z_RANGE); - if (zRng != null) { - properties.put(QDataSet.TYPICAL_MIN, zRng.min().value()); - properties.put(QDataSet.TYPICAL_MAX, zRng.max().value()); - } - properties.put(QDataSet.RENDER_TYPE, dasProps.get(DataSet.PROPERTY_RENDERER)); - properties.put(QDataSet.MONOTONIC, dasProps.get(DataSet.PROPERTY_X_MONOTONIC)); - properties.put(QDataSet.FILL_VALUE, dasProps.get(DataSet.PROPERTY_Z_FILL)); - - properties.put(QDataSet.VALID_MIN, dasProps.get(DataSet.PROPERTY_Z_VALID_MIN)); - properties.put(QDataSet.VALID_MAX, dasProps.get(DataSet.PROPERTY_Z_VALID_MAX)); - properties.put(QDataSet.SCALE_TYPE, dasProps.get(DataSet.PROPERTY_Z_SCALETYPE)); - properties.put(QDataSet.LABEL, dasProps.get(DataSet.PROPERTY_Z_LABEL)); - } - - @Override - public int rank() { - return 2; - } - - @Override - public int length(int i) { - return source.getYLength(0); - } - - @Override - public double value(int i, int j) { - return source.getDouble(i, j, source.getZUnits()); - } - - @Override - public int length() { - return source.getXLength(); - } - - } - - /////////////////////////////////////////////////////////////////////////////////// - // Toplevel QDataSet for multiple sets of Z data on an X,Y "grid" - static class MultipleTable extends AbstractDataSet { - - TableDataSet source; - - MultipleTable(TableDataSet source) { - super(); - - this.source = source; - - Map dasProps = adaptSubstitutions(source.getProperties()); - - // Save properterties with value substitution strings in Autoplot Stlye - properties.put(QDataSet.USER_PROPERTIES, dasProps); - - properties.put(QDataSet.JOIN_0, DDataSet.create(new int[0])); - properties.put(QDataSet.UNITS, source.getZUnits()); - properties.put(PROPERTY_SOURCE, source); - properties.put(QDataSet.TITLE, dasProps.get(DataSet.PROPERTY_TITLE)); - - //Let Das2 Streams set Z-Axis properties - DatumRange zRng = (DatumRange) dasProps.get(DataSet.PROPERTY_Z_RANGE); - if (zRng != null) { - properties.put(QDataSet.TYPICAL_MIN, zRng.min().value()); - properties.put(QDataSet.TYPICAL_MAX, zRng.max().value()); - } - properties.put(QDataSet.RENDER_TYPE, dasProps.get(DataSet.PROPERTY_RENDERER)); - properties.put(QDataSet.MONOTONIC, dasProps.get(DataSet.PROPERTY_X_MONOTONIC)); - properties.put(QDataSet.FILL_VALUE, dasProps.get(DataSet.PROPERTY_Z_FILL)); - - properties.put(QDataSet.VALID_MIN, dasProps.get(DataSet.PROPERTY_Z_VALID_MIN)); - properties.put(QDataSet.VALID_MAX, dasProps.get(DataSet.PROPERTY_Z_VALID_MAX)); - properties.put(QDataSet.SCALE_TYPE, dasProps.get(DataSet.PROPERTY_Z_SCALETYPE)); - properties.put(QDataSet.LABEL, dasProps.get(DataSet.PROPERTY_Z_LABEL)); - } - - @Override - public int rank() { - return 3; - } - - @Override - public int length() { - return source.tableCount(); - } - - @Override - public int length(int i) { - return source.tableEnd(i) - source.tableStart(i); - } - - @Override - public int length(int i, int j) { - try { - return source.getYLength(i); - } catch (IndexOutOfBoundsException ex) { - throw ex; - } - } - - @Override - public double value(int i, int j, int k) { - int ts = source.tableStart(i); - try { - return source.getDouble(ts + j, k, source.getZUnits()); - } catch (IndexOutOfBoundsException ex) { - throw ex; - } - } - - @Override - public Object property(String name, int i) { - if (name.equals(QDataSet.DEPEND_0)) { - return new MultiTableXTagsDataSet(source, source.tableStart(i), source.tableEnd(i) - source.tableStart(i)); - } else if (name.equals(QDataSet.DEPEND_1)) { - return new YTagsDataSet(source, i); - } else { - return super.property(name, i); - } - } - } -} diff --git a/dasCore/src/org/das2/dataset/DataSetCache.java b/dasCore/src/org/das2/dataset/DataSetCache.java index 7da8d6d7a..7c060e985 100644 --- a/dasCore/src/org/das2/dataset/DataSetCache.java +++ b/dasCore/src/org/das2/dataset/DataSetCache.java @@ -22,9 +22,6 @@ */ package org.das2.dataset; - -import org.das2.datum.CacheTag; - /** * * @author jbf diff --git a/dasCore/src/org/das2/dataset/DataSetConsumer.java b/dasCore/src/org/das2/dataset/DataSetConsumer.java index fc61a242f..bc9ee0f6a 100644 --- a/dasCore/src/org/das2/dataset/DataSetConsumer.java +++ b/dasCore/src/org/das2/dataset/DataSetConsumer.java @@ -23,12 +23,10 @@ package org.das2.dataset; -import org.virbo.dataset.QDataSet; - /** * * @author jbf */ public interface DataSetConsumer { - public QDataSet getConsumedDataSet(); + public DataSet getConsumedDataSet(); } diff --git a/dasCore/src/org/das2/dataset/DataSetDescriptor.java b/dasCore/src/org/das2/dataset/DataSetDescriptor.java index b07b6f2a7..967a807b4 100755 --- a/dasCore/src/org/das2/dataset/DataSetDescriptor.java +++ b/dasCore/src/org/das2/dataset/DataSetDescriptor.java @@ -21,8 +21,6 @@ */ package org.das2.dataset; -import org.das2.datum.CacheTag; -import java.awt.Graphics2D; import org.das2.components.propertyeditor.Displayable; import org.das2.datum.DatumRange; import org.das2.datum.Units; @@ -57,8 +55,7 @@ * 1 day. Presently, it's implicit that this means to give bin averages of the data. * *

    DataSetDescriptors are identified with a URL-like string: - * {@code http://www-pw.physics.uiowa.edu/das/das2Server?das2_1/cluster/wbd/r_wbd_dsn_cfd&spacecraft%3Dc1%26antenna%3DEy} - *

    + *
    http://www-pw.physics.uiowa.edu/das/das2Server?das2_1/cluster/wbd/r_wbd_dsn_cfd&spacecraft%3Dc1%26antenna%3DEy

    * *

    The protocol of the string indicates how the DataSetDescriptor is to be constructed, and presently * there are: @@ -67,6 +64,7 @@ * class refers to a loadable java class that is an instanceof DataSetDescriptor and * has the method newDataSetDescriptor( Map params ) throws DasException * + *

    * @author jbf */ public abstract class DataSetDescriptor implements Displayable { @@ -83,7 +81,8 @@ protected DataSetDescriptor(final String dataSetID) { } protected DataSetDescriptor() { - this(""); + dataSetCache = DasApplication.getDefaultApplication().getDataSetCache(); + dataSetID = "class:"+this.getClass().getName(); } private static final Logger logger = DasLogger.getLogger(DasLogger.GRAPHICS_LOG); @@ -117,13 +116,13 @@ public void requestDataSet(final Datum start, final Datum end, final Datum resol Runnable request = new Runnable() { public void run() { - logger.info("requestDataSet: " + start + " " + end + " " + resolution); + logger.fine("requestDataSet: " + start + " " + end + " " + resolution); try { DataSet ds = getDataSet(start, end, resolution, monitor); if (ds == null) { throw new NoDataInIntervalException(new DatumRange(start, end).toString()); } - DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)DataSetDescriptor.this, DataSetAdapter.create(ds) ); + DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)DataSetDescriptor.this, ds); dsue.setMonitor(monitor); fireDataSetUpdateEvent(dsue); } catch (DasException e) { @@ -137,7 +136,7 @@ public String toString() { return "loadDataSet " + new DatumRange(start, end); } }; - logger.info("submit data request"); + logger.fine("submit data request"); CacheTag tag = new CacheTag(start, end, resolution); if (dataSetCache.haveStored(this, tag)) { @@ -163,7 +162,7 @@ public void requestDataSet(final Datum start, final Datum end, final Datum resol if (this instanceof ConstantDataSetDescriptor) { try { DataSet ds = getDataSet(null, null, null, null); - DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)this, DataSetAdapter.create(ds) ); + DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)this, ds); dsue.setMonitor(monitor); } catch (DasException e) { DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)DataSetDescriptor.this, e); @@ -174,10 +173,10 @@ public void requestDataSet(final Datum start, final Datum end, final Datum resol Runnable request = new Runnable() { public void run() { - logger.info("request data from dsd: " + start + " " + end + " " + resolution); + logger.fine("request data from dsd: " + start + " " + end + " " + resolution); try { DataSet ds = getDataSet(start, end, resolution, monitor); - DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)DataSetDescriptor.this, DataSetAdapter.create(ds) ); + DataSetUpdateEvent dsue = new DataSetUpdateEvent((Object)DataSetDescriptor.this, ds); dsue.setMonitor(monitor); listener.dataSetUpdated(dsue); } catch (DasException e) { @@ -212,7 +211,7 @@ public DataSet getDataSet(Datum start, Datum end, Datum resolution, ProgressMoni CacheTag tag = null; if (defaultCaching) { tag = new CacheTag(start, end, resolution); - DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG).info("getDataSet " + this + " " + tag); + DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG).fine("getDataSet " + this + " " + tag); } if (defaultCaching && dataSetCache.haveStored(this, tag)) { @@ -293,12 +292,12 @@ public String getDataSetID() { return this.dataSetID; } private static final Pattern CLASS_ID = Pattern.compile("class:([a-zA-Z0-9_\\.]+)(?:\\?(.*))?"); + private static final Pattern EXEC_ID = Pattern.compile("exec:(.+)\\?(.+)"); private static final Pattern NAME_VALUE = Pattern.compile("([_0-9a-zA-Z%+.-]+)=([_0-9a-zA-Z%+.-]+)"); /** * creates a DataSetDescriptor for the given identification string. The identification - * string is a URL-like string, for example - * {@code http://www-pw.physics.uiowa.edu/das/das2Server?das2_1/cluster/wbd/r_wbd_dsn_cfd&spacecraft%3Dc1%26antenna%3DEy} + * string is a URL-like string, for example http://www-pw.physics.uiowa.edu/das/das2Server?das2_1/cluster/wbd/r_wbd_dsn_cfd&spacecraft%3Dc1%26antenna%3DEy * The protocol of the string indicates how the DataSetDescriptor is to be constructed, and presently * there are: *
    @@ -309,15 +308,15 @@ public String getDataSetID() {
          *
    * Note that DataSetDescriptors are stateless, the same DataSetDescriptor object * may be returned to multiple clients. - * @param dataSetID the URL-like identifier. - * @return the DataSetDescriptor for the id. - * @throws org.das2.DasException */ public static DataSetDescriptor create(final String dataSetID) throws DasException { java.util.regex.Matcher classMatcher = CLASS_ID.matcher(dataSetID); + java.util.regex.Matcher execMatcher = EXEC_ID.matcher(dataSetID); DataSetDescriptor result; if (classMatcher.matches()) { result = createFromClassName(dataSetID, classMatcher); + } else if (execMatcher.matches()) { + result = createFromExecutable(dataSetID, execMatcher); } else { try { result = createFromServerAddress(new URL(dataSetID)); @@ -335,6 +334,11 @@ private static DataSetDescriptor createFromServerAddress(final URL url) throws D StreamDescriptor sd = server.getStreamDescriptor(url); return new StreamDataSetDescriptor(sd, server.getStandardDataStreamSource(url)); } + private static DataSetDescriptor createFromExecutable(String dataSetID, Matcher matcher) { + String executable = matcher.group(1); + String[] paramPatterns = matcher.group(2).split("&"); + return new ExecutableDataSetDescriptor(executable, paramPatterns); + } private static DataSetDescriptor createFromClassName(final String dataSetID, final Matcher matcher) throws DasException { try { @@ -388,9 +392,6 @@ public javax.swing.Icon getListIcon() { return null; } - public void drawListIcon( Graphics2D g, int x, int y ) { - } - public String getListLabel() { return this.dataSetID; } diff --git a/dasCore/src/org/das2/dataset/DataSetRebinner.java b/dasCore/src/org/das2/dataset/DataSetRebinner.java index bf595651f..c1f9f30cf 100755 --- a/dasCore/src/org/das2/dataset/DataSetRebinner.java +++ b/dasCore/src/org/das2/dataset/DataSetRebinner.java @@ -23,24 +23,27 @@ package org.das2.dataset; +import java.util.Map; import org.das2.DasException; -import org.virbo.dataset.QDataSet; /** * * @author Edward West */ public interface DataSetRebinner { - - /** - * create a new QDataSet in a rank 2 table with x and y tags described by x and y. - * @param ds The input dataset, either a rank 2 or rank 3 dataset. Note this may include rank 1 dataset and rank 2 bundles at some point. - * @param x describes the column labels. (Note this may become a QDataSet at some point). - * @param y describes the row labels. - * @return a rank 2 QDataSet with the given rows and columns. - * @throws IllegalArgumentException - * @throws DasException - */ - QDataSet rebin( QDataSet ds, RebinDescriptor x, RebinDescriptor y ) throws IllegalArgumentException, DasException; + /** Rebin a dataset into a 2-D grid + * + * Datasets are immutable, so you can't alter them in place + * @param ds The dataset to rebin + * @param x bin size and spacing information for the X direction + * @param y bin size and spacing information for the Y direction + * @param override a map of dataset properties to override, may be null. + * property names and values for this map match those given in DataSet.java + * @return a new dataset + * @throws IllegalArgumentException + * @throws DasException + */ + DataSet rebin(DataSet ds, RebinDescriptor x, RebinDescriptor y, Map override) + throws IllegalArgumentException, DasException; } diff --git a/dasCore/src/org/das2/dataset/DataSetStreamProducer.java b/dasCore/src/org/das2/dataset/DataSetStreamProducer.java index f75b91ca9..07faaf5d3 100644 --- a/dasCore/src/org/das2/dataset/DataSetStreamProducer.java +++ b/dasCore/src/org/das2/dataset/DataSetStreamProducer.java @@ -24,8 +24,8 @@ import java.io.OutputStream; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; +import java.util.Iterator; import java.util.Map; -import java.util.Map.Entry; /** * Configurable class for serializing a DataSet into a das2Stream. This class @@ -75,11 +75,11 @@ private void writeTableDataSetStream( WritableByteChannel out ) { StreamProducer producer = new StreamProducer(out); StreamDescriptor sd = new StreamDescriptor(); - Map properties= tds.getProperties(); + Map properties= tds.getProperties(); if ( properties!=null) { - for ( Entry e: properties.entrySet() ) { - String key= e.getKey(); - sd.setProperty(key, e.getValue() ); + for ( Iterator i= properties.keySet().iterator(); i.hasNext(); ) { + String key= (String)i.next(); + sd.setProperty(key, properties.get(key)); } } @@ -165,11 +165,11 @@ private void writeVectorDataSetStream( WritableByteChannel out ) { StreamProducer producer = new StreamProducer(out); StreamDescriptor sd = new StreamDescriptor(); - Map properties= vds.getProperties(); + Map properties= vds.getProperties(); if ( properties!=null) { - for ( Entry e: properties.entrySet() ) { - String key= e.getKey(); - sd.setProperty(key, e.getValue() ); + for ( Iterator i= properties.keySet().iterator(); i.hasNext(); ) { + String key= (String)i.next(); + sd.setProperty(key, properties.get(key)); } } diff --git a/dasCore/src/org/das2/dataset/DataSetUpdateEvent.java b/dasCore/src/org/das2/dataset/DataSetUpdateEvent.java index 09a2d45d3..2d8577b06 100755 --- a/dasCore/src/org/das2/dataset/DataSetUpdateEvent.java +++ b/dasCore/src/org/das2/dataset/DataSetUpdateEvent.java @@ -8,7 +8,6 @@ import org.das2.event.DasEvent; import org.das2.util.monitor.ProgressMonitor; -import org.virbo.dataset.QDataSet; /** * @@ -16,17 +15,41 @@ */ public class DataSetUpdateEvent extends DasEvent { - private QDataSet dataSet; + private DataSet dataSet; private Exception exception; private ProgressMonitor monitor; + /** + * @deprecated use {link + * #DataSetUpdateEvent(Object)} + */ + public DataSetUpdateEvent(DataSetDescriptor source) { + this((Object)source); + } + + /** + * @deprecated use {link + * #DataSetUpdateEvent(Object,DataSet); + */ + public DataSetUpdateEvent(DataSetDescriptor source, DataSet dataSet) { + this((Object)source,dataSet); + } + + /** + * @deprecated use {link + * #DataSetUpdateEvent(Object,Exception); + */ + public DataSetUpdateEvent(DataSetDescriptor source, Exception exception) { + this((Object)source,exception); + } + public DataSetUpdateEvent( Object source) { // null indicates that the state has changed and the consumer needs to read. super(source); } /** Creates a new instance of DataSetUpdateEvent */ - public DataSetUpdateEvent( Object source, QDataSet dataSet) { + public DataSetUpdateEvent( Object source, DataSet dataSet) { super(source); this.dataSet = dataSet; } @@ -36,7 +59,7 @@ public DataSetUpdateEvent( Object source, Exception exception) { this.exception = exception; } - public QDataSet getDataSet() { + public DataSet getDataSet() { return dataSet; } diff --git a/dasCore/src/org/das2/dataset/DataSetUtil.java b/dasCore/src/org/das2/dataset/DataSetUtil.java index 03cd34e9d..b1a63951b 100755 --- a/dasCore/src/org/das2/dataset/DataSetUtil.java +++ b/dasCore/src/org/das2/dataset/DataSetUtil.java @@ -6,11 +6,13 @@ package org.das2.dataset; -import org.das2.datum.CacheTag; +import org.das2.graph.GraphUtil; +import org.das2.graph.DasPlot; import org.das2.datum.DatumRange; import org.das2.datum.Units; import org.das2.datum.DatumVector; import org.das2.datum.Datum; +import org.das2.util.DasMath; import java.util.*; /** @@ -29,13 +31,7 @@ public static CacheTag guessCacheTag( DataSet ds ) { return new CacheTag( start, end, resolution ); } } - - /** - * returns the xrange of the dataset. This assumes that the xtags - * are monotonic. - * @param ds - * @return - */ + public static DatumRange xRange( DataSet ds ) { int n=ds.getXLength(); return new DatumRange( ds.getXTagDatum(0), ds.getXTagDatum(n-1) ); @@ -48,12 +44,7 @@ private static DatumRange yRangeTDS( TableDataSet ds ) { } else { for ( int i=0; i1 ) { + if ( table.getXLength()>2 ) { Units units= table.getXUnits(); - double min= Math.abs( table.getXTagDouble(1,units) - table.getXTagDouble( 0,units) ); + double min= table.getXTagDouble(1,units) - table.getXTagDouble( 0,units) ; for ( int i=2; i 0) return -(low + 1); // key not found. } + /** Searches the xtags of the specified data set for the specified datum + * using the binary search algorithm. + * + * @param ds the data set to search + * @param datum the key to search for + * @return index of the search datum if it exists as an xtag in the + * data set; otherwise the insertion point. The insertion point is + * is defined as -1.0 if the datum is less that the first xtag + * in the data set, ds.getXLength() if the datum is + * larger than the last xtag, or + * (i + (datum - x0) / (x1 - x0) ) + * where x0 is the largest xtag smaller than datum, + * x1 is the smallest xtag larger than datum, and + * i is the index of x0. + */ + public static double columnFindex( DataSet ds, Datum datum ) { + int result = xTagBinarySearch( ds, datum, 0, ds.getXLength()-1); + + if (result >= -1) { + return result; + } + + result = ~result; + + if (result == ds.getXLength()) { + return result; + } + else { + Units u = ds.getXUnits(); + int lowerIndex = result-1; + double lower = ds.getXTagDouble(lowerIndex, u); + double upper = ds.getXTagDouble(result, u); + double key = datum.doubleValue(u); + return lowerIndex + (key - lower) / (upper - lower); + } + } public static int closestColumn( DataSet table, Datum datum ) { int result= xTagBinarySearch( table, datum, 0, table.getXLength()-1 ); @@ -424,7 +464,7 @@ public static VectorDataSet log10( VectorDataSet ds ) { Units yunits= ds.getYUnits(); Units xunits= ds.getXUnits(); for ( int i=0; i= tableData[0].length) { IndexOutOfBoundsException ioobe = new IndexOutOfBoundsException ("x index is out of bounds: " + i + " xLength: " + getXLength()); @@ -218,6 +225,7 @@ public Datum getDatum(int i, int j) { } } + @Override public DatumVector getScan(int i) { int table = tableOfIndex(i); if (i < 0 || i >= tableData[0].length) { @@ -236,6 +244,7 @@ public DatumVector getScan(int i) { } } + @Override public double getDouble(int i, int j, Units units) { int table; int yLength; @@ -257,6 +266,7 @@ public double getDouble(int i, int j, Units units) { return zUnits[0].getConverter(units).convert(value); } + @Override public double[] getDoubleScan(int i, Units units) { int table = tableOfIndex(i); int yLength = yTags[table].length; @@ -273,10 +283,12 @@ public double[] getDoubleScan(int i, Units units) { return retValues; } + @Override public int getInt(int i, int j, Units units) { return (int)Math.round(getDouble(i, j, units)); } + @Override public DataSet getPlanarView(String planeID) { int planeIndex = -1; for (int index = 0; index < planeIDs.length; index++) { @@ -291,99 +303,25 @@ public DataSet getPlanarView(String planeID) { } } + @Override public String[] getPlaneIds() { String[] result= new String[planeIDs.length]; System.arraycopy( planeIDs, 0, result, 0, planeIDs.length ); return result; } - /** - * this is a highly-interleaved table, and if we sort it out and create a - * optimal QDataSet, it's better. - * @return a QDataSet version of the data - */ - public org.virbo.dataset.AbstractDataSet toQDataSet( ) { - - JoinDataSet result= new JoinDataSet(3); - - Map dasProps = adaptSubstitutions(getProperties()); - - // Save properterties with value substitution strings in Autoplot Stlye - result.putProperty( QDataSet.USER_PROPERTIES, dasProps ); - - Set doneModes= new HashSet(); - int itable=-1; - for ( int i=0; i ); + //return null; + // } + @Override public String[] getPlaneIds() { return new String[0]; } + @Override public Datum getDatum(int i, int j) { + int table = tableOfIndex(i); + int yLength = yTags[table].length; double value = tableData[index][i][j]; return Datum.create(value, zUnits[index]); } + @Override public double getDouble(int i, int j, Units units) { + int table = tableOfIndex(i); + int yLength = yTags[table].length; double value = tableData[index][i][j]; return zUnits[index].getConverter(units).convert(value); } + @Override public double[] getDoubleScan(int i, Units units) { int table = tableOfIndex(i); int yLength = yTags[table].length; @@ -544,6 +504,7 @@ public double[] getDoubleScan(int i, Units units) { return retValues; } + @Override public DatumVector getScan(int i) { int table = tableOfIndex(i); if (i < 0 || i >= tableData[0].length) { @@ -562,70 +523,86 @@ public DatumVector getScan(int i) { } } + @Override public int getInt(int i, int j, Units units) { return (int)Math.round(getDouble(i, j, units)); } + @Override public VectorDataSet getXSlice(int i) { return new XSliceDataSet(this, i); } + @Override public int getYLength(int table) { return DefaultTableDataSet.this.getYLength(table); } + @Override public VectorDataSet getYSlice(int j, int table) { return new YSliceDataSet(this, j, table); } + @Override public Datum getYTagDatum(int table, int j) { return DefaultTableDataSet.this.getYTagDatum(table, j); } + @Override public double getYTagDouble(int table, int j, Units units) { return DefaultTableDataSet.this.getYTagDouble(table, j, units); } + @Override public int getYTagInt(int table, int j, Units units) { return DefaultTableDataSet.this.getYTagInt(table, j, units); } + @Override public Units getZUnits() { return DefaultTableDataSet.this.zUnits[index]; } + @Override public int tableCount() { return DefaultTableDataSet.this.tableCount(); } + @Override public int tableEnd(int table) { return DefaultTableDataSet.this.tableEnd(table); } + @Override public int tableOfIndex(int i) { return DefaultTableDataSet.this.tableOfIndex(i); } + @Override public int tableStart(int table) { return DefaultTableDataSet.this.tableStart(table); } + @Override public Object getProperty(String name) { Object result= DefaultTableDataSet.this.getProperty(planeIDs[index] + "." + name); if ( result==null ) result= DefaultTableDataSet.this.getProperty(name); return result; } + @Override public Object getProperty(int table, String name) { Object result= DefaultTableDataSet.this.getProperty(table,planeIDs[index] + "." + name); if ( result==null ) result= DefaultTableDataSet.this.getProperty(table,name); return result; } + @Override public DatumVector getYTags(int table) { return DefaultTableDataSet.this.getYTags(table); } + @Override public String toString() { return TableUtil.toString(this); } diff --git a/dasCore/src/org/das2/dataset/DefaultVectorDataSet.java b/dasCore/src/org/das2/dataset/DefaultVectorDataSet.java index e147a21f1..c30170c34 100755 --- a/dasCore/src/org/das2/dataset/DefaultVectorDataSet.java +++ b/dasCore/src/org/das2/dataset/DefaultVectorDataSet.java @@ -212,11 +212,13 @@ public String[] getPlaneIds() { } public Object getProperty(String name) { - Object result= DefaultVectorDataSet.this.getProperty(planeIDs[index] + "." + name); - if ( result==null ) { - result= DefaultVectorDataSet.this.getProperty(name); + String planeProp = planeIDs[index] + "." + name; + if (DefaultVectorDataSet.this.hasProperty(name)) { + return DefaultVectorDataSet.this.getProperty(planeProp); + } + else { + return DefaultVectorDataSet.this.getProperty(name); } - return result; } // TODO: this appears to have different logic than in ViewDataSet. This needs to be resolved. diff --git a/dasCore/src/org/das2/dataset/ExecutableDataSetDescriptor.java b/dasCore/src/org/das2/dataset/ExecutableDataSetDescriptor.java new file mode 100644 index 000000000..f8dfed2aa --- /dev/null +++ b/dasCore/src/org/das2/dataset/ExecutableDataSetDescriptor.java @@ -0,0 +1,88 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.das2.dataset; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.das2.DasException; +import org.das2.DasIOException; +import org.das2.client.DataSetStreamHandler; +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.system.DasLogger; +import org.das2.util.DasProgressMonitorInputStream; +import org.das2.util.StreamTool; +import org.das2.util.monitor.ProgressMonitor; + +/** DataSetDescriptor implementation + * + * @author eew + */ +public class ExecutableDataSetDescriptor extends DataSetDescriptor { + + private String exePath; + private String[] commandFmts; + + private static final Logger logger= DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG); + + public ExecutableDataSetDescriptor(String exePath, String[] commandFmts) { + this.exePath = exePath; + this.commandFmts = Arrays.copyOf(commandFmts, commandFmts.length); + } + + @Override + protected DataSet getDataSetImpl(Datum start, Datum end, Datum resolution, ProgressMonitor monitor) throws DasException { + InputStream in; + DataSet result; + + String[] command = new String[commandFmts.length+1]; + command[0] = exePath; + for (int iParam = 0; iParam < commandFmts.length; iParam++) { + String sParam = commandFmts[iParam]; + if (sParam.contains("%{start}")) { + sParam = sParam.replace("%{start}", start.toString()); + } + else if (sParam.contains("%{end}")) { + sParam = sParam.replace("%{end}", end.toString()); + } + else if (sParam.contains("%{resolution}")) { + sParam = sParam.replace("%{resolution}", + Double.toString(resolution.doubleValue(Units.seconds))); + } + command[iParam+1] = sParam; + } + ProcessBuilder builder = new ProcessBuilder(command); + builder.redirectError(ProcessBuilder.Redirect.INHERIT); + try { + Process p = builder.start(); + in = p.getInputStream(); + final DasProgressMonitorInputStream mpin = new DasProgressMonitorInputStream(in, monitor); + ReadableByteChannel channel = Channels.newChannel(mpin); + + DataSetStreamHandler handler = new DataSetStreamHandler(properties, monitor); + + StreamTool.readStream(channel, handler); + return handler.getDataSet(); + + } catch (IOException ex) { + throw new DasIOException(ex); + } + + } + + @Override + public Units getXUnits() { + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff --git a/dasCore/src/org/das2/dataset/GapListDouble.java b/dasCore/src/org/das2/dataset/GapListDouble.java index e052e30e3..f0594e17f 100755 --- a/dasCore/src/org/das2/dataset/GapListDouble.java +++ b/dasCore/src/org/das2/dataset/GapListDouble.java @@ -179,7 +179,7 @@ private static int binarySearch(final double d, final double[] array, final int int low = start; int high = end-1; while (low <= high) { - int mid = (low + high) >>> 1; // findbugs IM_AVERAGE_COMPUTATION_COULD_OVERFLOW shows an unsigned shift should be used. + int mid = (low + high) >> 1; if (array[mid] < d) { low = mid + 1; } diff --git a/dasCore/src/org/das2/dataset/GenericQernalFactory.java b/dasCore/src/org/das2/dataset/GenericQernalFactory.java index 0337a615e..ac900b1bf 100644 --- a/dasCore/src/org/das2/dataset/GenericQernalFactory.java +++ b/dasCore/src/org/das2/dataset/GenericQernalFactory.java @@ -16,7 +16,7 @@ * @author Jeremy */ public class GenericQernalFactory implements QernalTableRebinner.QernalFactory { - static class GenericQernal implements QernalTableRebinner.Qernal { + class GenericQernal implements QernalTableRebinner.Qernal { double[][] qernal; int dx0,dx1; int dy0,dy1; diff --git a/dasCore/src/org/das2/dataset/LanlNNRebinner.java b/dasCore/src/org/das2/dataset/LanlNNRebinner.java deleted file mode 100644 index 774b04808..000000000 --- a/dasCore/src/org/das2/dataset/LanlNNRebinner.java +++ /dev/null @@ -1,320 +0,0 @@ - -package org.das2.dataset; - -import java.util.WeakHashMap; -import org.das2.datum.Units; -import org.das2.datum.UnitsUtil; -import org.das2.DasException; -import org.das2.system.DasLogger; -import java.util.logging.*; -import org.das2.datum.Datum; -import org.das2.datum.DatumRange; -import org.das2.datum.DatumRangeUtil; -import org.das2.datum.UnitsConverter; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.DDataSet; -import org.virbo.dataset.DataSetOps; -import org.virbo.dataset.JoinDataSet; -import org.virbo.dataset.MutablePropertyDataSet; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dsops.Ops; - -/** - * DataSetRebinner for explicitly doing NN rebinning. The AverageTableRebinner had been used for the purpose, and - * there were numerous problems. Also, this looks for BIN_PLUS and BIN_MINUS properties in the dataset. - */ -public class LanlNNRebinner implements DataSetRebinner { - - private static final Logger logger = DasLogger.getLogger(DasLogger.DATA_OPERATIONS_LOG); - - public LanlNNRebinner() { - } - - WeakHashMap yds0c= new WeakHashMap(); - WeakHashMap yds1c= new WeakHashMap(); - WeakHashMap cadence= new WeakHashMap(); - /** - * get cadence that checks for null and returns the pixel cadence in this case. - * @param ds the xtags or ytags - * @param res fall-back cadence, that is the axis resolution - * @return - */ - private QDataSet getCadence( QDataSet ds, Datum res ) { - QDataSet dds= cadence.get(ds); - if ( dds==null && !cadence.containsKey(ds) ) { - dds= DataSetUtil.guessCadenceNew( ds, null ); - cadence.put( ds,dds ); - } - if ( dds==null ) { - return DataSetUtil.asDataSet(res); - } else { - return dds; - } - } - /** - * rebin the data, using the interpolate control to define the interpolation between measurements. Data that fall into the - * same pixel are always averaged in the linear space, regardless of interpolation method. - * @param ds rank 2 table or rank 3 join of tables. - * @param ddX - * @param ddY - * @return - * @throws IllegalArgumentException - * @throws DasException - */ - @Override - public QDataSet rebin( QDataSet ds, RebinDescriptor ddX, RebinDescriptor ddY ) throws IllegalArgumentException, DasException { - logger.entering("org.das2.dataset.LanlNNRebinner", "rebin"); - - if (ds == null) { - throw new NullPointerException("null data set"); - } - if (!( SemanticOps.isTableDataSet(ds) || SemanticOps.isBundle(ds) ) ) { - throw new IllegalArgumentException("Data set must be an instanceof TableDataSet: " + ds.getClass().getName()); - } - - QDataSet tds = (QDataSet) ds; - int rank= tds.rank(); - - if ( rank==2 ) { // make it into a rank 3 table so we are always working with the same scheme. - JoinDataSet tdsx= new JoinDataSet(3); - tdsx.join(tds); - tds= tdsx; - } - - int nx = ddX.numberOfBins(); - int ny = ddY.numberOfBins(); - - DDataSet S= DDataSet.createRank2( nx, ny ); - DDataSet N= DDataSet.createRank2( nx, ny ); - - boolean rs= false; - boolean re= false; - - for ( int itable=0; itable 0 ) { - UnitsConverter xc= xunits.getConverter(ddX.getUnits()); - QDataSet bounds= SemanticOps.bounds(xds); - double start = xc.convert( bounds.value(1,0) ); - double end = xc.convert( bounds.value(1,1) ); - DatumRange dr= DatumRangeUtil.union( ddX.binStop(ddX.numberOfBins()-1),ddX.binStart(0)); - if (start <= dr.max().doubleValue(ddX.getUnits()) ) { - rs= true; - } - if (end >= dr.min().doubleValue(ddX.getUnits())) { - re= true; - } - } - - if ( rank!=2 ) { - throw new IllegalArgumentException("ddY was null but there was rank 3 dataset"); - } - - logger.log(Level.FINEST, "Allocating rebinData and rebinWeights: {0} x {1}", new Object[]{nx, ny}); - - double y0,y1; - int nYData= rank2y ? yds0.length(0) : yds0.length(); - - if ( SemanticOps.isBundle(tds1) && tds1.length(0)==3 && !rank2y && yds0.length()==tds1.length() && xds0.length()==tds1.length() ) { // bug 1160: I think some data could still be mistaken here. - tds1= DataSetOps.unbundle(tds1,tds1.length(0)-1); - weights= DataSetOps.unbundle(weights,weights.length(0)-1); - for ( int i=0; iddX.end ) { // flipped - px0= ddX.whichBin( x1, xunits ); - px1= ddX.whichBin( x0, xunits ); - } else { - px0= ddX.whichBin( x0, xunits ); - px1= ddX.whichBin( x1, xunits ); - } - double wx= 1./((px1-px0+1)); - - int sx0= Math.max( 0, px0 ); - int sx1= Math.min( nx-1, px1 ); - double z= tds1.value( i ); - y0= yds0.value(i); - y1= yds1.value(i); - int py0,py1; - if ( ddY.start>ddY.end ) { // flipped - py0= ddY.whichBin( y1, yunits ); - py1= ddY.whichBin( y0, yunits ); - } else { - py0= ddY.whichBin( y0, yunits ); - py1= ddY.whichBin( y1, yunits ); - } - double wy= 1./((py1-py0+1)); // favor short bins - double w= wx*wy*weights.value(i); - int sy0= Math.max( 0, py0 ); - int sy1= Math.min( ny-1, py1 ); - for ( int k=sx0; k<=sx1; k++ ) { - for ( int l=sy0; l<=sy1; l++ ) { - if ( w>N.value(k,l) ) { - S.putValue(k,l,z*w); - N.putValue(k,l,w); - } - } - } - } - } else { - QDataSet yds0_1=null; - QDataSet yds1_1=null; - if ( rank2y==false ) { - yds0_1= yds0; - yds1_1= yds1; - } - int[] py0s= new int[nYData]; - int[] py1s= new int[nYData]; - double[] wys= new double[nYData]; - for ( int i=0; iddX.end ) { // flipped - px0= ddX.whichBin( x1, xunits ); - px1= ddX.whichBin( x0, xunits ); - } else { - px0= ddX.whichBin( x0, xunits ); - px1= ddX.whichBin( x1, xunits ); - } - double wx= 1./((px1-px0+1)); - - int sx0= Math.max( 0, px0 ); - int sx1= Math.min( nx-1, px1 ); - if ( rank2y ) { - yds0_1= yds0.slice(i); - yds1_1= yds1.slice(i); - } - assert yds0_1!=null; - assert yds1_1!=null; - for ( int j=0; jddY.end ) { // flipped - py0= ddY.whichBin( y1, yunits ); - py1= ddY.whichBin( y0, yunits ); - } else { - py0= ddY.whichBin( y0, yunits ); - py1= ddY.whichBin( y1, yunits ); - } - py0s[j]= py0; - py1s[j]= py1; - double wy= 1./((py1-py0+1)); // favor short bins - wys[j]= wy; - } - } - for ( int j=0; jN.value(k,l) ) { - S.putValue(k,l,z*w); - N.putValue(k,l,w); - } - } - } - } - } - } - } - - if ( !rs ) throw new NoDataInIntervalException("data starts after range"); - if ( !re ) throw new NoDataInIntervalException("data ends before range"); - - MutablePropertyDataSet mds= (MutablePropertyDataSet) Ops.divide( S, N ); - RebinDescriptor.putDepDataSet( ds, mds, ddX, ddY ); - - logger.exiting("org.das2.dataset.LanlNNRebinner", "rebin"); - - return mds; - - } - - private final java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); - - public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { - propertyChangeSupport.addPropertyChangeListener(listener); - } - - public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { - propertyChangeSupport.removePropertyChangeListener(listener); - } -} diff --git a/dasCore/src/org/das2/dataset/LimitCountDataSetCache.java b/dasCore/src/org/das2/dataset/LimitCountDataSetCache.java index 6e8818b1b..9875a510a 100644 --- a/dasCore/src/org/das2/dataset/LimitCountDataSetCache.java +++ b/dasCore/src/org/das2/dataset/LimitCountDataSetCache.java @@ -23,7 +23,6 @@ package org.das2.dataset; -import org.das2.datum.CacheTag; import org.das2.DasApplication; /** * diff --git a/dasCore/src/org/das2/dataset/LimitSizeBytesDataSetCache.java b/dasCore/src/org/das2/dataset/LimitSizeBytesDataSetCache.java index 616f32628..f20406317 100644 --- a/dasCore/src/org/das2/dataset/LimitSizeBytesDataSetCache.java +++ b/dasCore/src/org/das2/dataset/LimitSizeBytesDataSetCache.java @@ -6,7 +6,6 @@ package org.das2.dataset; -import org.das2.datum.CacheTag; import org.das2.datum.Units; import org.das2.datum.Datum; import java.util.*; @@ -33,6 +32,7 @@ public LimitSizeBytesDataSetCache( long totalSizeLimitBytes ) { /* return a list of all the datasets that together cover the cacheTag */ private Entry findStored( DataSetDescriptor dsd, CacheTag cacheTag ) { Entry entry= new Entry( dsd, cacheTag, null ); + List result= new ArrayList(); Entry iHit=null; synchronized (entries) { @@ -53,7 +53,7 @@ boolean haveStoredImpl(DataSetDescriptor dsd, CacheTag cacheTag) { public void reset() { synchronized(entries) { - entries.clear(); + entries.removeAll(entries); this.totalSize= 0; } } diff --git a/dasCore/src/org/das2/dataset/NNQernalFactory.java b/dasCore/src/org/das2/dataset/NNQernalFactory.java index a36d159e0..e39c8730f 100644 --- a/dasCore/src/org/das2/dataset/NNQernalFactory.java +++ b/dasCore/src/org/das2/dataset/NNQernalFactory.java @@ -12,6 +12,7 @@ import org.das2.datum.Datum; import org.das2.datum.Units; import org.das2.datum.UnitsUtil; +import org.das2.util.DasMath; /** * @@ -19,7 +20,7 @@ */ public class NNQernalFactory implements QernalTableRebinner.QernalFactory { - static class NNQernal implements QernalTableRebinner.Qernal { + class NNQernal implements QernalTableRebinner.Qernal { int dx0,dx1; int dy0,dy1; int nx,ny; // number of elements in each dimension @@ -54,7 +55,7 @@ public void apply( int x, int y, double value, double weight, double[][]ss, doub } // special case for 1-pixel Quernal - static class NNQernalOne implements QernalTableRebinner.Qernal { + class NNQernalOne implements QernalTableRebinner.Qernal { int nx, ny; private NNQernalOne( int nx, int ny ) { this.nx= nx; @@ -78,7 +79,7 @@ public Qernal getQernal( RebinDescriptor ddx, RebinDescriptor ddy, Datum xTagWid if (!ddy.isLog() ) throw new IllegalArgumentException("need log axis"); d= ddy.binCenter(0); double f= yTagWidth.doubleValue( Units.log10Ratio ); - i= ddy.whichBin( d.multiply( Math.pow(10,f) ).doubleValue(d.getUnits()), d.getUnits() ); + i= ddy.whichBin( d.multiply( DasMath.exp10(f) ).doubleValue(d.getUnits()), d.getUnits() ); dy0= i/2; dy1= (i+1)/2; } else { diff --git a/dasCore/src/org/das2/dataset/NearestNeighborTableDataSet.java b/dasCore/src/org/das2/dataset/NearestNeighborTableDataSet.java new file mode 100755 index 000000000..3b174863a --- /dev/null +++ b/dasCore/src/org/das2/dataset/NearestNeighborTableDataSet.java @@ -0,0 +1,319 @@ +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import org.das2.datum.UnitsUtil; +import java.util.*; + +public class NearestNeighborTableDataSet implements TableDataSet { + + TableDataSet source; + + Map m_override; /* Have to keep track of your overrides for getProperty */ + + int[] imap; + + int[][] jmap; + + int[] itableMap; + + RebinDescriptor ddX; + + RebinDescriptor ddY; + + NearestNeighborTableDataSet( + TableDataSet source, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) { + imap= new int[ddX.numberOfBins()]; + if ( ddY==null ) { + if ( source.tableCount()>1 ) { + throw new IllegalArgumentException(); + } + jmap= new int[source.tableCount()][source.getYLength(0)]; + } else { + jmap= new int[source.tableCount()][ddY.numberOfBins()]; + } + itableMap= new int[ddX.numberOfBins()]; + + this.ddX= ddX; + this.ddY= ddY; + this.source= source; + + if(override == null) + m_override = new HashMap<>(); + else + m_override = override; + + if ( source.getXLength()==0 ) { + for ( int i=0; i xTagWidth.doubleValue(xunits)/1.90 ) { + imap[i]=-1; + } else { + int itable= source.tableOfIndex(imap[i]); + itableMap[i]= itable; + if ( itable0!=itable ) { + if ( ddY==null ) { + for ( int j=0; j yTagWidth.doubleValue(Units.logERatio)/1.90 ) jmap[itable][j]=-1; + } else { + Datum yclose= source.getYTagDatum( itable, jmap[itable][j] ); + if ( Math.abs( yclose.subtract(yy[j],ddY.getUnits()).doubleValue(yunits)) > yTagWidth.doubleValue(yunits)/1.90 ) { + jmap[itable][j]= -1; + } + } + } + } + itable0= itable; + } + } + } + } + } + + @Override + public Datum getDatum(int i, int j) { + if ( imap[i]!=-1 && jmap[itableMap[i]][j]!=-1 ) { + return source.getDatum(imap[i], jmap[itableMap[i]][j]); + } else { + return source.getZUnits().createDatum(source.getZUnits().getFillDouble()); + } + } + + @Override + public double getDouble(int i, int j, Units units) { + try { + if ( imap[i]!=-1 && jmap[itableMap[i]][j]!=-1 ) { + return source.getDouble(imap[i], jmap[itableMap[i]][j], units); + } else { + return source.getZUnits().getFillDouble(); + } + } catch ( ArrayIndexOutOfBoundsException e ) { + System.err.println("here: "+e); + throw new RuntimeException(e); + } + } + + @Override + public int getInt(int i, int j, Units units) { + if ( imap[i]!=-1 && jmap[itableMap[i]][j]!=-1 ) { + return source.getInt(imap[i], jmap[itableMap[i]][j],units); + } else { + return source.getZUnits().getFillDatum().intValue(source.getZUnits()); + } + } + + @Override + public DataSet getPlanarView(String planeID) { + TableDataSet ds = (TableDataSet)source.getPlanarView(planeID); + if (ds != null) { + return new NearestNeighborTableDataSet(ds,ddX,ddY, null); + } else { + return null; + } + } + + @Override + public String[] getPlaneIds() { + return source.getPlaneIds(); + } + + @Override + public Object getProperty(String name) { + Object ret = m_override.get(name); + if(ret != null) return source.getProperty(name); + return ret; + } + + @Override + public Map getProperties() { + if(m_override.isEmpty()) return source.getProperties(); + + // Fun, now we get to merge + HashMap mRet = new HashMap<>(m_override); + Map srcProps = source.getProperties(); + for(Object key: srcProps.keySet()){ + mRet.put(key, srcProps.get(key)); + } + return mRet; + } + + @Override + public int getXLength() { + return imap.length; + } + + @Override + public VectorDataSet getXSlice(int i) { + return new XSliceDataSet(this,i); + } + + @Override + public VectorDataSet getYSlice(int j, int table) { + return new YSliceDataSet(this, j, table); + } + + @Override + public Datum getXTagDatum(int i) { + return ddX.getUnits().createDatum(getXTagDouble(i,ddX.getUnits())); + } + + @Override + public double getXTagDouble(int i, Units units) { + return ddX.binCenter(i,units); + } + + @Override + public int getXTagInt(int i, Units units) { + return (int)getXTagDouble(i,units); + } + + @Override + public Units getXUnits() { + return ddX.getUnits(); + } + + @Override + public int getYLength(int table) { + if ( ddY==null ) { + return source.getYLength(table); + } else { + return ddY.numberOfBins(); + } + } + + @Override + public Datum getYTagDatum(int table, int j) { + if ( ddY==null ) { + return source.getYTagDatum( table, j ); + } else { + return ddY.getUnits().createDatum(getYTagDouble(table,j,ddY.getUnits())); + } + } + + @Override + public double getYTagDouble(int table, int j, Units units) { + if ( ddY==null ) { + return source.getYTagDouble( table, j, units ); + } else { + return ddY.binCenter(j,units); + } + } + + @Override + public int getYTagInt(int table, int j, Units units) { + return (int)getYTagDouble( table, j, units); + } + + @Override + public Units getYUnits() { + if ( ddY==null ) { + return source.getYUnits(); + } else { + return ddY.getUnits(); + } + } + + @Override + public Units getZUnits() { + return source.getZUnits(); + } + + @Override + public int tableCount() { + return 1; + } + + @Override + public int tableEnd(int table) { + return ddX.numberOfBins(); + } + + @Override + public int tableOfIndex(int i) { + return 0; + } + + @Override + public int tableStart(int table) { + return 0; + } + + @Override + public String toString() { + return "NearestNeighborTableDataSet " + TableUtil.toString(this); + } + + @Override + public double[] getDoubleScan(int i, Units units) { + int yLength = getYLength(tableOfIndex(i)); + double[] array = new double[yLength]; + for (int j = 0; j < yLength; j++) { + array[j] = getDouble(i, j, units); + } + return array; + } + + @Override + public DatumVector getScan(int i) { + Units zUnits = getZUnits(); + return DatumVector.newDatumVector(getDoubleScan(i, zUnits), zUnits); + } + + @Override + public DatumVector getYTags(int table) { + double[] tags = new double[getYLength(table)]; + Units yUnits = getYUnits(); + for (int j = 0; j < tags.length; j++) { + tags[j] = getYTagDouble(table, j, yUnits); + } + return DatumVector.newDatumVector(tags, yUnits); + } + + @Override + public Object getProperty(int table, String name) { + return getProperty(name); + } + +} + diff --git a/dasCore/src/org/das2/dataset/NearestNeighborTableRebinner.java b/dasCore/src/org/das2/dataset/NearestNeighborTableRebinner.java new file mode 100755 index 000000000..2f4673ed7 --- /dev/null +++ b/dasCore/src/org/das2/dataset/NearestNeighborTableRebinner.java @@ -0,0 +1,49 @@ +/* File: TableAverageRebinner.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on November 5, 2003, 10:31 AM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.dataset; + +import java.util.Map; + +/** + * + * @author Jeremy Faden + */ +public class NearestNeighborTableRebinner implements DataSetRebinner { + + /** Creates a new instance of TableAverageRebinner */ + public NearestNeighborTableRebinner() { + } + + @Override + public DataSet rebin( + DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) throws IllegalArgumentException { + if (!(ds instanceof TableDataSet)) { + throw new IllegalArgumentException(); + } + + return new NearestNeighborTableDataSet((TableDataSet)ds, ddX, ddY, override ); + } + +} diff --git a/dasCore/src/org/das2/dataset/NewAverageTableRebinner.java b/dasCore/src/org/das2/dataset/NewAverageTableRebinner.java index e3daab3a9..07ed4f7c0 100644 --- a/dasCore/src/org/das2/dataset/NewAverageTableRebinner.java +++ b/dasCore/src/org/das2/dataset/NewAverageTableRebinner.java @@ -35,7 +35,7 @@ * not thread safe!!! * @author Jeremy Faden */ -public class NewAverageTableRebinner { // implements DataSetRebinner { +public class NewAverageTableRebinner implements DataSetRebinner { private static final Logger logger = DasLogger.getLogger(DasLogger.DATA_OPERATIONS_LOG); @@ -66,11 +66,18 @@ public NewAverageTableRebinner( DataSet ds, RebinDescriptor ddX, RebinDescriptor ny= (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); } - public DataSet rebin(DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY) throws IllegalArgumentException, DasException { + @Override + public DataSet rebin( + DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) throws IllegalArgumentException, DasException { if ( ds!=this.tds ) throw new IllegalArgumentException("already set for another dataset"); if ( ddX!=this.ddX ) throw new IllegalArgumentException("already set for another X rebin descriptor"); if ( ddY!=this.ddY ) throw new IllegalArgumentException("already set for another Y rebin descriptor"); + if(override != null) + throw new UnsupportedOperationException("This rebinner does not "+ + "yet know how to override dataset properties."); + TableDataSet weights = (TableDataSet)tds.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); if (ddX != null && tds.getXLength() > 0) { double start = tds.getXTagDouble(0, ddX.getUnits()); diff --git a/dasCore/src/org/das2/dataset/NoInterpolateQernalFactory.java b/dasCore/src/org/das2/dataset/NoInterpolateQernalFactory.java index 73400c072..28b4e5034 100644 --- a/dasCore/src/org/das2/dataset/NoInterpolateQernalFactory.java +++ b/dasCore/src/org/das2/dataset/NoInterpolateQernalFactory.java @@ -19,7 +19,7 @@ */ public class NoInterpolateQernalFactory implements QernalTableRebinner.QernalFactory { - static class EnlargeQernal implements QernalTableRebinner.Qernal { + class EnlargeQernal implements QernalTableRebinner.Qernal { int dx0,dx1; int dy0,dy1; int nx,ny; // number of elements in each dimension @@ -52,7 +52,7 @@ public void apply( int x, int y, double value, double weight, double[][]ss, doub } // special case for 1-pixel Quernal - static class NoEnlargeQernal implements QernalTableRebinner.Qernal { + class NoEnlargeQernal implements QernalTableRebinner.Qernal { int nx, ny; private NoEnlargeQernal( int nx, int ny ) { this.nx= nx; diff --git a/dasCore/src/org/das2/dataset/NullDataSetCache.java b/dasCore/src/org/das2/dataset/NullDataSetCache.java index 80e7f61ce..76404442b 100644 --- a/dasCore/src/org/das2/dataset/NullDataSetCache.java +++ b/dasCore/src/org/das2/dataset/NullDataSetCache.java @@ -9,8 +9,6 @@ package org.das2.dataset; -import org.das2.datum.CacheTag; - /** * DataSetCache that does no caching at all. This is useful for batch * mode or when dataset caching is undesirable. diff --git a/dasCore/src/org/das2/dataset/PeakTableRebinner.java b/dasCore/src/org/das2/dataset/PeakTableRebinner.java index 38cd89e18..afa1fe07e 100755 --- a/dasCore/src/org/das2/dataset/PeakTableRebinner.java +++ b/dasCore/src/org/das2/dataset/PeakTableRebinner.java @@ -23,10 +23,8 @@ package org.das2.dataset; +import java.util.Map; import org.das2.datum.Units; -import org.virbo.dataset.DDataSet; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; /** * @@ -37,57 +35,81 @@ public class PeakTableRebinner implements DataSetRebinner { public PeakTableRebinner() { } - public QDataSet rebin(QDataSet ds, RebinDescriptor ddX, RebinDescriptor ddY) throws IllegalArgumentException { - if (!(ds.rank()==2 )) { - throw new IllegalArgumentException("dataset must be rank 2"); + + @Override + public DataSet rebin( + DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) throws IllegalArgumentException { + if (!(ds instanceof TableDataSet)) { + throw new IllegalArgumentException(); } + + if(override != null) + throw new UnsupportedOperationException("This rebinner does not "+ + "yet know how to override dataset properties."); + + TableDataSet tds = (TableDataSet)ds; + long timer= System.currentTimeMillis(); - QDataSet tds = ds; - - int nx= (ddX == null ? tds.length() : ddX.numberOfBins()); - int ny= (ddY == null ? tds.length(0) : ddY.numberOfBins()); + int nx= (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + int ny= (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); double[][] rebinData= new double[nx][ny]; + double[][] rebinWeights= new double[nx][ny]; - peaks( tds, rebinData, ddX, ddY); - - double[] dd= new double[nx*ny]; - AveragePeakTableRebinner.flatten( rebinData, dd, 0, nx, ny ); - DDataSet result= DDataSet.wrap( dd, nx, ny ); - org.virbo.dataset.DataSetUtil.copyDimensionProperties( tds, result ); - - return result; + peaks(tds, rebinData, ddX, ddY); + double[] xTags; + if (ddX != null) { + xTags = ddX.binCenters(); + } + else { + xTags = new double[tds.getXLength()]; + for (int i = 0; i < xTags.length; i++) { + xTags[i] = tds.getXTagDouble(i, tds.getXUnits()); + } + } + double[][] yTags; + if (ddY != null) { + yTags = new double[][]{ddY.binCenters()}; + } + else { + yTags = new double[0][tds.getYLength(0)]; + for (int j = 0; j < yTags[0].length; j++) { + yTags[0][j] = tds.getYTagDouble(0, j, tds.getYUnits()); + } + } + double[][][] zValues = {rebinData}; + int[] tableOffsets = {0}; + Units[] zUnits = {tds.getZUnits()}; + String[] planeIDs = {""}; + + return new DefaultTableDataSet(xTags, tds.getXUnits(), yTags, tds.getYUnits(), zValues, zUnits, planeIDs, tableOffsets, java.util.Collections.EMPTY_MAP); } - static void peaks( QDataSet tds, double[][] rebinData, RebinDescriptor ddX, RebinDescriptor ddY) { - - int nx= (ddX == null ? tds.length() : ddX.numberOfBins()); - int ny= (ddY == null ? tds.length(0) : ddY.numberOfBins()); + static void peaks(TableDataSet tds, double[][] rebinData, RebinDescriptor ddX, RebinDescriptor ddY) { + + int nx= (ddX == null ? tds.getXLength() : ddX.numberOfBins()); + int ny= (ddY == null ? tds.getYLength(0) : ddY.numberOfBins()); for (int i = 0; i < rebinData.length; i++) { java.util.Arrays.fill(rebinData[i], Double.NaN); } - QDataSet ytds= SemanticOps.ytagsDataSet(tds); - Units yunits= SemanticOps.getUnits(ytds); - QDataSet xtds= SemanticOps.xtagsDataSet(tds); - Units xunits= SemanticOps.getUnits(xtds); - - int [] ibiny= new int[tds.length(0)]; + int [] ibiny= new int[tds.getYLength(0)]; for (int j=0; j < ibiny.length; j++) { if (ddY != null) { - ibiny[j]= ddY.whichBin( ytds.value(j), yunits ); + ibiny[j]= ddY.whichBin(tds.getYTagDouble(0, j, tds.getYUnits()), tds.getYUnits()); } else { ibiny[j] = j; } } - for (int i=0; i < tds.length(); i++) { + for (int i=0; i < tds.getXLength(); i++) { int ibinx; if (ddX != null) { - ibinx= ddX.whichBin( xtds.value(i), xunits ); + ibinx= ddX.whichBin(tds.getXTagDouble(i, tds.getXUnits()), tds.getXUnits()); } else { ibinx = i; @@ -95,7 +117,7 @@ static void peaks( QDataSet tds, double[][] rebinData, RebinDescriptor ddX, Rebi if (ibinx>=0 && ibinx= 0 && ibiny[j] < ny) { - double value = tds.value(i,j); + double value = tds.getDouble(i, j, tds.getZUnits()); if (Double.isNaN(rebinData[ibinx][ibiny[j]])) { rebinData[ibinx][ibiny[j]] = value; } diff --git a/dasCore/src/org/das2/dataset/QernalTableRebinner.java b/dasCore/src/org/das2/dataset/QernalTableRebinner.java index 3770504c6..456690b1e 100644 --- a/dasCore/src/org/das2/dataset/QernalTableRebinner.java +++ b/dasCore/src/org/das2/dataset/QernalTableRebinner.java @@ -21,13 +21,13 @@ * * @author Jeremy */ -public class QernalTableRebinner { // implements DataSetRebinner { +public class QernalTableRebinner implements DataSetRebinner { - public interface QernalFactory { + interface QernalFactory { Qernal getQernal( RebinDescriptor ddx, RebinDescriptor ddy, Datum xBinWidth, Datum yBinWidth ); } - public interface Qernal { + interface Qernal { void apply( int x, int y, double value, double weight, double[][] s, double[][]w ); } @@ -38,7 +38,9 @@ public QernalTableRebinner( QernalFactory factory ) { this.factory= factory; } - public DataSet rebin(DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY) throws IllegalArgumentException, org.das2.DasException { + public DataSet rebin( + DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY, Map override + ) throws IllegalArgumentException, org.das2.DasException { logger.finest("enter QernalTableRebinner.rebin"); if (ds == null) { @@ -47,6 +49,10 @@ public DataSet rebin(DataSet ds, RebinDescriptor ddX, RebinDescriptor ddY) throw if (!(ds instanceof TableDataSet)) { throw new IllegalArgumentException("Data set must be an instanceof TableDataSet: " + ds.getClass().getName()); } + + if(override != null) + throw new UnsupportedOperationException("This rebinner does not "+ + "yet know how to override dataset properties."); TableDataSet tds = (TableDataSet)ds; TableDataSet weights = (TableDataSet)ds.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); diff --git a/dasCore/src/org/das2/dataset/Rank3TableDataSetAdapter.java b/dasCore/src/org/das2/dataset/Rank3TableDataSetAdapter.java deleted file mode 100755 index c6823be16..000000000 --- a/dasCore/src/org/das2/dataset/Rank3TableDataSetAdapter.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * TableDataSetAdapter.java - * - * Created on January 29, 2007, 9:27 AM - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ -package org.das2.dataset; - -import org.das2.dataset.TableDataSet; -import org.das2.dataset.VectorDataSet; -import org.das2.datum.Datum; -import org.das2.datum.DatumVector; -import org.das2.datum.Units; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import org.virbo.dataset.DataSetOps; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.DatumVectorAdapter; -import org.virbo.dataset.IndexGenDataSet; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.RankZeroDataSet; -import org.virbo.dataset.Slice0DataSet; - -/** - * - * @author jbf - */ -public class Rank3TableDataSetAdapter implements TableDataSet { - - Units xunits, yunits, zunits; - QDataSet x, y, z; - HashMap properties = new HashMap(); - int[] tables; - - HashMap planes; - - public static TableDataSet create(QDataSet z) { - QDataSet xds = (QDataSet) z.property(QDataSet.DEPEND_0); - if (xds == null) { - xds = new IndexGenDataSet(z.length()); - } - QDataSet yds = (QDataSet) z.property(QDataSet.DEPEND_1); - if (yds == null) { - yds = new IndexGenDataSet(z.length(0)); - } - if ( !DataSetUtil.isMonotonic(xds) ) { - QDataSet sort= DataSetOps.sort(xds); - z= DataSetOps.applyIndex( z, 0, sort, false ); - xds= DataSetOps.applyIndex( xds, 0, sort, false ); - } - if ( !DataSetUtil.isMonotonic(yds) ) { - QDataSet sort= DataSetOps.sort(yds); - z= DataSetOps.applyIndex( z, 1, sort, false ); - yds= DataSetOps.applyIndex( yds, 0, sort, false ); - } - return new Rank3TableDataSetAdapter(z, xds, yds); - - } - - /** Creates a new instance of TableDataSetAdapter */ - public Rank3TableDataSetAdapter(QDataSet z, QDataSet x, QDataSet y) { - - planes= new LinkedHashMap(); - planes.put( "", z ); - - xunits = (Units) x.property(QDataSet.UNITS); - yunits = (Units) y.property(QDataSet.UNITS); - zunits = (Units) z.property(QDataSet.UNITS); - if (xunits == null) { - xunits = Units.dimensionless; - } - if (yunits == null) { - yunits = Units.dimensionless; - } - if (zunits == null) { - zunits = Units.dimensionless; - } - this.x = x; - this.y = y; - this.z = z; - - Boolean xMono = (Boolean) x.property(QDataSet.MONOTONIC,0); - if (xMono != null && xMono.booleanValue()) { - properties.put(org.das2.dataset.DataSet.PROPERTY_X_MONOTONIC, Boolean.TRUE); - } - - QDataSet cadence= (QDataSet) x.property( QDataSet.CADENCE,0 ); - if ( cadence!=null ) { - Datum dcadence= DataSetUtil.asDatum(cadence); - properties.put( org.das2.dataset.DataSet.PROPERTY_X_TAG_WIDTH, dcadence ); - } - - cadence = (QDataSet) y.property(QDataSet.CADENCE,0); - if ( cadence!=null ) { - Datum dcadence= DataSetUtil.asDatum(cadence); - properties.put( org.das2.dataset.DataSet.PROPERTY_Y_TAG_WIDTH, dcadence ); - } - - if ( z.property(QDataSet.FILL_VALUE,0) !=null - || z.property(QDataSet.VALID_MIN,0) !=null - || z.property(QDataSet.VALID_MAX,0) !=null ) { - QDataSet wds= DataSetUtil.weightsDataSet(z); - planes.put( org.das2.dataset.DataSet.PROPERTY_PLANE_WEIGHTS, wds ); - //z= DataSetUtil.canonizeFill(z); - } - - tables= new int[ z.length()+1 ]; - for ( int i=1; i<=z.length(); i++ ) { - tables[i]= tables[i-1] + z.length(i-1); - } - } - - public Units getZUnits() { - return zunits; - } - - public Datum getDatum(int i, int j) { - int i0= tableOfIndex(i); - int i1= i - tableStart(i0); - return zunits.createDatum(z.value(i0, i1, j)); - } - - public double getDouble(int i, int j, Units units) { - int i0= tableOfIndex(i); - int i1= i - tableStart(i0); - return zunits.convertDoubleTo(units, z.value(i0, i1, j) ); - } - - - public double[] getDoubleScan(int i, Units units) { - int i0= tableOfIndex(i); - double[] zz = new double[getYLength(i0)]; - for (int j = 0; j < zz.length; j++) { - zz[j] = getDouble( i, j, getZUnits()); - } - return zz; - } - - public DatumVector getScan(int i) { - double[] zz = getDoubleScan(i, getZUnits()); - return DatumVector.newDatumVector(zz, getZUnits()); - } - - public int getInt(int i, int j, Units units) { - return (int) getDouble(i, j, units); - } - - public DatumVector getYTags(int table) { - return DatumVectorAdapter.toDatumVector( new Slice0DataSet(y, table) ); - } - - public Datum getYTagDatum(int table, int j) { - return yunits.createDatum(y.value(table,j)); - } - - public double getYTagDouble(int table, int j, Units units) { - return yunits.convertDoubleTo(units, y.value(table,j)); - } - - public int getYTagInt(int table, int j, Units units) { - return (int) getYTagDouble(table, j, units); - } - - public int getYLength(int table) { - return y.length(table); - } - - public int tableStart(int table) { - return tables[table]; - } - - public int tableEnd(int table) { - return tables[table+1]; - } - - public int tableCount() { - return z.length(); - } - - public int tableOfIndex(int i) { - /*if ( tables.length>10 ) { // this is going to be slow anyway, and the code is untested - int result= Arrays.binarySearch( tables, i); - if ( result<0 ) { - return -1-result; - } else { - return result; - } - } else {*/ - for ( int j=0; j=tables[j] && i= numberOfBins() ) { @@ -150,11 +128,11 @@ public double binStart( int ibin, Units units ) { } } double result= start+((ibin)/(double)(nBin)*(end-start)); - UnitsConverter cu= this.units.getConverter(units); + UnitsConverter uc= this.units.getConverter(units); if ( isLog ) { - return cu.convert(Math.exp(result)); + return uc.convert(Math.exp(result)); } else { - return cu.convert(result); + return uc.convert(result); } } @@ -162,12 +140,6 @@ public Datum binStop( int ibin ) { return Datum.create( binStop( ibin, units ), units ); } - /** - * return the bigger boundary of the bin. - * @param ibin the bin number - * @param units the units for the result. - * @return the bigger boundary of the bin in the desired units. - */ public double binStop( int ibin, Units units ) { if ( this.outOfBoundsAction!=RebinDescriptor.EXTRAPOLATE ) { if ( ibin<0 || ibin >= numberOfBins() ) { @@ -175,18 +147,18 @@ public double binStop( int ibin, Units units ) { } } double result= start+((ibin+1)/(double)(nBin)*(end-start)); - UnitsConverter cu= this.units.getConverter(units); + UnitsConverter uc= this.units.getConverter(units); if ( isLog ) { - return cu.convert(Math.exp(result)); + return uc.convert(Math.exp(result)); } else { - return cu.convert(result); + return uc.convert(result); } } - /** - * return the bin starts of all bins, in units of getUnits() - * @return the bin starts of all bins - */ + /** So what freaking units are the return value in??? + * Anyone? Buler? + * @return a number in who knows what coordinate system + */ public double[] binStarts() { double [] result= new double[nBin]; for (int i=0; igetUnits() - * @return the bin stops of all bins - */ public double[] binStops() { double [] result= new double[nBin]; for (int i=0; i xTagWidth.doubleValue(xunits)/2. ) { + imap[i]=-1; + } + } + return imap; + } + + /* calculates imap when width tags are irregular, and possibly overlapping. + */ + private static int[] calculateImapForWidthTags( DataSet source, DataSet target ) { + int[] imap= new int[target.getXLength()]; + Units xunits= source.getXUnits(); + Units xoffsetUnits= xunits.getOffsetUnits(); + String widthsPlane= "xTagWidth"; + + VectorDataSet widthsDs= (VectorDataSet)source.getPlanarView(widthsPlane); + + int sourceLength= source.getXLength(); + + for ( int i=0; idouble
    with the given units. * @param j index of the x tag for the requested value. * @param i index of the y tag for the requested value. - * @param units the units the returned value should be converted to. + * @param units the units the returned value should be coverted to. * @return the value at index location (i, j) as a double. */ double getDouble(int i, int j, Units units); @@ -70,20 +70,18 @@ public interface TableDataSet extends DataSet { * int with the given units. * @param i index of the x tag for the requested value. * @param j index of the y tag for the requested value. - * @param units the units the returned value should be converted to. + * @param units the units the returned value should be coverted to. * @return the value at index location (i, j) as a int. */ int getInt(int i, int j, Units units); /** Returns the yTags for this data set as a DatumVector - * @param table the table number * @return the yTags for this data set as a DatumVector */ DatumVector getYTags(int table); /** Returns the value of the y tag at the given index j as a - * Datum. - * @param table the table number + * Datum. * @param j the index of the requested y tag * @return the value of the y tag at the given index j as a * Datum. @@ -93,16 +91,15 @@ public interface TableDataSet extends DataSet { /** Returns the value of the y tag at the given index j as a * double in the given units. YTags must be * monotonically increasing with j. - * @param table the table number - * @param units the units of the returned value - * @param j the index of the requested y tag * @return the value of the y tag at the given index j as a * double. + * @param units the units of the returned value + * @param j the index of the requested y tag */ double getYTagDouble(int table, int j, Units units); /** Returns the value of the y tag at the given index j as an - * int in the given units. YTags must be + * int in the given units. YTags must be * monotonically increasing with j. * @return the value of the y tag at the given index j as an * int. diff --git a/dasCore/src/org/das2/dataset/TableDataSetAdapter.java b/dasCore/src/org/das2/dataset/TableDataSetAdapter.java deleted file mode 100644 index d3edee1b0..000000000 --- a/dasCore/src/org/das2/dataset/TableDataSetAdapter.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * TableDataSetAdapter.java - * - * Created on January 29, 2007, 9:27 AM - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ -package org.das2.dataset; - -import org.das2.dataset.TableDataSet; -import org.das2.dataset.VectorDataSet; -import org.das2.datum.Datum; -import org.das2.datum.DatumVector; -import org.das2.datum.Units; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import org.das2.datum.UnitsConverter; -import org.das2.datum.UnitsUtil; -import org.virbo.dataset.ArrayDataSet; -import org.virbo.dataset.DDataSet; -import org.virbo.dataset.DataSetOps; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.DatumVectorAdapter; -import org.virbo.dataset.IndexGenDataSet; -import org.virbo.dataset.JoinDataSet; -import org.virbo.dataset.MutablePropertyDataSet; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.RankZeroDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dataset.TagGenDataSet; -import org.virbo.dataset.WritableDataSet; - -/** - * Adapts QDataSets to legacy das2 TableDataSet. - * See also DataSetAdapter. - * @author jbf - */ -public class TableDataSetAdapter implements TableDataSet { - - Units xunits, yunits, zunits; - QDataSet x, y, z; - HashMap properties = new HashMap(); - HashMap planes; - - public static TableDataSet create(QDataSet z) { - - if (z.rank() == 2) { - QDataSet xds = (QDataSet) z.property(QDataSet.DEPEND_0); - if (xds == null) { - xds = new IndexGenDataSet(z.length()); - } - QDataSet yds = (QDataSet) z.property(QDataSet.DEPEND_1); - if (yds == null) { - if ( z.length()>0 && z.property(QDataSet.DEPEND_0,0)!=null ) { - Units yunits= null; - JoinDataSet jds= new JoinDataSet(2); - for ( int i=0; i(); - planes.put( "", z ); - - xunits = (Units) x.property(QDataSet.UNITS); - yunits = (Units) y.property(QDataSet.UNITS); - zunits = (Units) z.property(QDataSet.UNITS); - if (xunits == null) { - xunits = Units.dimensionless; - } - if (yunits == null) { - yunits = Units.dimensionless; - } - if (zunits == null) { - zunits = Units.dimensionless; - } - this.x = x; - this.y = y; - this.z = z; - - Boolean xMono = (Boolean) x.property(QDataSet.MONOTONIC); - if (xMono != null && xMono ) { - properties.put(org.das2.dataset.DataSet.PROPERTY_X_MONOTONIC, Boolean.TRUE); - } - - QDataSet cadence = (QDataSet) x.property(QDataSet.CADENCE); - if (cadence != null) { - Datum dcadence; - dcadence = DataSetUtil.asDatum(cadence); - properties.put(org.das2.dataset.DataSet.PROPERTY_X_TAG_WIDTH, dcadence); - } - - cadence = (QDataSet) y.property(QDataSet.CADENCE); - if (cadence != null) { - Datum dcadence; - dcadence = DataSetUtil.asDatum(cadence); - properties.put(org.das2.dataset.DataSet.PROPERTY_Y_TAG_WIDTH, dcadence); - } - - if ( z.property(QDataSet.FILL_VALUE) !=null - || z.property(QDataSet.VALID_MIN) !=null - || z.property(QDataSet.VALID_MAX) !=null ) { - //this.z = DataSetUtil.canonizeFill(z); - QDataSet wds= DataSetUtil.weightsDataSet(z); - planes.put( org.das2.dataset.DataSet.PROPERTY_PLANE_WEIGHTS, wds ); - //this.y= DataSetUtil.canonizeFill(y); - } - } - - public Units getZUnits() { - return zunits; - } - - public Datum getDatum(int i, int j) { - return zunits.createDatum(z.value(i, j)); - } - - public double getDouble(int i, int j, Units units) { - return zunits.convertDoubleTo(units, z.value(i, j)); - } - - public double[] getDoubleScan(int i, Units units) { - double[] zz = new double[getYLength(tableOfIndex(i))]; - for (int j = 0; j < zz.length; j++) { - zz[j] = getDouble(i, j, getZUnits()); - } - return zz; - } - - public DatumVector getScan(int i) { - double[] zz = getDoubleScan(i, getZUnits()); - return DatumVector.newDatumVector(zz, getZUnits()); - } - - public int getInt(int i, int j, Units units) { - return (int) getDouble(i, j, units); - } - - public DatumVector getYTags(int table) { - if ( y.rank()==1 ) { - return DatumVectorAdapter.toDatumVector(y); - } else { // if ( y.rank()==2 ) { - return DatumVectorAdapter.toDatumVector( y.slice(table) ); - } - } - - public Datum getYTagDatum(int table, int j) { - if ( y.rank()==1 ) { - if (table > 0) { - throw new IllegalArgumentException("table>0"); - } - return yunits.createDatum(y.value(j)); - } else { - return yunits.createDatum(y.value(table,j)); - } - } - - public double getYTagDouble(int table, int j, Units units) { - if ( y.rank()==1 ) { - if (table > 0) { - throw new IllegalArgumentException("table>0"); - } - return yunits.convertDoubleTo(units, y.value(j)); - } else { - return yunits.convertDoubleTo(units, y.value(table,j)); - } - } - - public int getYTagInt(int table, int j, Units units) { - return (int) getYTagDouble(table, j, units); - } - - public int getYLength(int table) { - if ( y.rank()==1 ) { - return y.length(); - } else { - return y.length(table); - } - } - - public int tableStart(int table) { - if ( y.rank()==1 ) { - return 0; - } else { - return table; - } - } - - public int tableEnd(int table) { - if ( y.rank()==1 ) { - return getXLength(); - } else { - return table+1; - } - } - - public int tableCount() { - if ( y.rank()==1 ) { - return 1; - } else { - return y.length(); // should be the same as x.length() - } - } - - public int tableOfIndex(int i) { - if ( y.rank()==1 ) { - return 0; - } else { - return i; - } - - } - - public VectorDataSet getXSlice(int i) { - if ( y.rank()==1 ) { - return new VectorDataSetAdapter( z.slice(i), y); - } else { - return new VectorDataSetAdapter( z.slice(i), y.slice(i) ); - } - } - - public VectorDataSet getYSlice(int j, int table) { - if ( y.rank()==1 ) { - return new VectorDataSetAdapter(DataSetOps.slice1(z, j), x); - } else { - // warning: this assumes qube... - return new VectorDataSetAdapter(DataSetOps.slice1(z, j), x); - } - } - - public Object getProperty(String name) { - Object result = properties.get(name); - return (result != null) ? result : z.property(name); - } - - public Object getProperty(int table, String name) { - return getProperty(name); - } - - public Map getProperties() { - Map result = new HashMap(); - result.put(QDataSet.UNITS, null); - Map m = new HashMap(DataSetUtil.getProperties(z, result)); - m.putAll(properties); - return m; - } - - public Units getXUnits() { - return xunits; - } - - public Units getYUnits() { - return yunits; - } - - public Datum getXTagDatum(int i) { - return xunits.createDatum(x.value(i)); - } - - public double getXTagDouble(int i, Units units) { - return xunits.convertDoubleTo(units, x.value(i)); - } - - public int getXTagInt(int i, Units units) { - return (int) xunits.convertDoubleTo(units, x.value(i)); - } - - public int getXLength() { - return x.length(); - } - - public org.das2.dataset.DataSet getPlanarView(String planeID) { - if ( planeID.equals("") ) return this; - if ( planes.containsKey(planeID) ) { - return new TableDataSetAdapter( (QDataSet)planes.get(planeID), x, y ); - } - return null; - } - - public String[] getPlaneIds() { - return planes.keySet().toArray(new String[planes.keySet().size()] ); - } - - public String toString() { - return DataSetUtil.toString(this.z); - } -} diff --git a/dasCore/src/org/das2/dataset/TableDataSetBuilder.java b/dasCore/src/org/das2/dataset/TableDataSetBuilder.java index a87ae831a..5cb67c9e3 100755 --- a/dasCore/src/org/das2/dataset/TableDataSetBuilder.java +++ b/dasCore/src/org/das2/dataset/TableDataSetBuilder.java @@ -23,40 +23,31 @@ package org.das2.dataset; -import org.das2.datum.CacheTag; import org.das2.datum.Units; import org.das2.datum.DatumVector; import org.das2.datum.Datum; import java.util.*; -import java.util.Map.Entry; -/** Handles 1-N planes of table data. - * - * Update Note: Historically by default there was an un-named plane, that was always - * present in the builder. I removed that so that all planes could have a - * name, it probably breaks lots of stuff so extensive tests are needed - * before this change is committed. -cwp 2014-09-22 - * +/** Build an X-Y data grid from input vectors. + * Here's an example of using this class. + *
    + * {@code
    + *		TableDataSet
    + * }
    + *
    * @author Edward West */ public class TableDataSetBuilder { private static final double[] EMPTY = new double[0]; - private List xTags = new ArrayList(); - boolean monotonic= true; + private GapListDouble xTags = new GapListDouble(); private List zValues = new ArrayList(); - // No default plane - // private List planeIDs = new ArrayList(); { - // planeIDs.add(""); - // } - - private List planeIDs = new ArrayList(); - - private Units xUnits = Units.dimensionless; + private List planeIDs = new ArrayList(); + private Units xUnits = Units.dimensionless; private Units yUnits = Units.dimensionless; private Map zUnitsMap = new HashMap(); @@ -64,33 +55,46 @@ public class TableDataSetBuilder { private SortedSet yTagSet = new TreeSet(new DoubleArrayComparator()); private Map properties = new HashMap(); - private List tableProperties= new ArrayList(); + private List tableProperties= new ArrayList<>(); - /** Creates a new instance of TableDataSetBuilder with a default plane - * A single plane with the empty string as it's name is defined. - * - * @param xUnits Units for the X-axis data - * @param yUnits Units for the Y-axis data - * @param zUnits Units for the Z-axis data - */ + /** Creates a new instance of TableDataSetBuilder + * This version assigns a 0-length string to the name of the first plane, and + * assumes that the zUnits are dimensionless. + */ + public TableDataSetBuilder( Units xUnits, Units yUnits) { + setXUnits(xUnits); + setYUnits(yUnits); + + zUnitsMap.put("", Units.dimensionless); + planeIDs.add(""); + } + + /** Creates a new instance of TableDataSetBuilder + * This version assigns a 0-length string to the name of the first plane. + */ public TableDataSetBuilder( Units xUnits, Units yUnits, Units zUnits ) { - this(xUnits, yUnits, zUnits, ""); + setXUnits(xUnits); + setYUnits(yUnits); + + zUnitsMap.put("", zUnits); + planeIDs.add(""); } - /** Creates a new instance of TableDataSetBuilder with a named default plane + /** Create a new instance of TableDataSetBuilder * - * @param xUnits Units for the X-axis data - * @param yUnits Units for the Y-axis data - * @param zUnits Units for the Z-axis data - * @param name The name for data plane at index 0, may be the empty string "". + * @param xUnits The X Axis Units + * @param yUnits The Y Axis Units + * @param zUnits The Z Axis Units + * @param sPlane0Name A name for the 0th plane of the dataset */ - public TableDataSetBuilder( Units xUnits, Units yUnits, Units zUnits, String name ) { - - setXUnits(xUnits); - setYUnits(yUnits); - setZUnits(zUnits, name); - planeIDs.add(name); - } + public TableDataSetBuilder( Units xUnits, Units yUnits, Units zUnits, String sPlane0Name ) + { + setXUnits(xUnits); + setYUnits(yUnits); + zUnitsMap = new HashMap(); + zUnitsMap.put(sPlane0Name, zUnits); + planeIDs.add(sPlane0Name); + } public void setProperty(String name, Object value) { properties.put(name, value); @@ -126,11 +130,18 @@ public void insertYScan( Datum x, DatumVector y, DatumVector z, String planeId ) insertYScan( x, y, new DatumVector[]{z}, new String[]{ planeId } ); } + private int count = 0; + public void insertYScan(Datum xTag, DatumVector yTags, DatumVector[] scans, String[] planeIDs) { double x = xTag.doubleValue(xUnits); double[] y = yTags.toDoubleArray(yUnits); - int insertionIndex = xTags.size(); - if ( xTags.size()>0 && ((Double)xTags.get(xTags.size()-1))>x ) monotonic=false; + int insertionIndex = xTags.indexOf(x); + for (int i = 0; i < scans.length; i++) { + if (yTags.getLength() != scans[i].getLength()) { + IllegalArgumentException iae = new IllegalArgumentException + ("Scan length must equal yTags length"); + } + } if (yTagSet.contains(y)) { y = (double[])yTagSet.tailSet(y).iterator().next(); } @@ -145,9 +156,6 @@ public void insertYScan(Datum xTag, DatumVector yTags, DatumVector[] scans, Stri for (int i = 0; i < planeIDs.length; i++) { String planeID = planeIDs[i]; Units zUnits = (Units)zUnitsMap.get(planeID); - if ( zUnits==null && i==0 ) { - zUnits = (Units)zUnitsMap.get(""); - } if (zUnits == null) { zUnits = Units.dimensionless; addPlane(planeID, zUnits); @@ -161,27 +169,25 @@ public void insertYScan(Datum xTag, DatumVector yTags, DatumVector[] scans, Stri } - private void appendProperties( Map properties ) { - //TODO: this needs to be verified after findbugs refactoring - Map sproperties= properties; - for ( Entry e : sproperties.entrySet() ) { - Object key= e.getKey(); + private void appendProperties( Map properties ) { + for ( Iterator i=properties.keySet().iterator(); i.hasNext(); ) { + Object key= i.next(); if ( this.properties.containsKey(key) ) { if ( key.equals( DataSet.PROPERTY_SIZE_BYTES ) ) { // this is reset anyway } else if ( key.equals( DataSet.PROPERTY_CACHE_TAG ) ) { CacheTag tag= (CacheTag)this.properties.get(key); - CacheTag appendTag= (CacheTag)e.getValue(); + CacheTag appendTag= (CacheTag)properties.get(key); try { this.properties.put( key, CacheTag.append( tag, appendTag ) ); - } catch ( IllegalArgumentException ex ) { - System.err.println("ignoring unequal property: append: "+key+"="+e.getValue()+" to "+tag ); + } catch ( IllegalArgumentException e ) { + System.err.println("ignoring unequal property: append: "+key+"="+properties.get(key)+" to "+this.properties.get(key ) ); } - } else if ( !this.properties.get(key).equals(e.getValue()) ) { - System.err.println( "ignoring unequal property: append: "+key+"="+e.getValue()+" to "+this.properties.get(key ) ); + } else if ( !this.properties.get(key).equals(properties.get(key)) ) { + System.err.println( "ignoring unequal property: append: "+key+"="+properties.get(key)+" to "+this.properties.get(key ) ); } } else { - this.properties.put( key, e.getValue() ); + this.properties.put( key, properties.get(key) ); } } } @@ -220,9 +226,9 @@ private DatumVector getZScanDatumVector(TableDataSet tds, int table, int i) { } double[] scan = new double[tds.getYLength(table)]; for (int j = 0; j < tds.getYLength(table); j++) { - scan[j] = tds.getDouble(i, j, yUnits); + scan[j] = tds.getDouble(i, j, tds.getZUnits()); } - return DatumVector.newDatumVector(scan, yUnits); + return DatumVector.newDatumVector(scan, tds.getZUnits()); } public void setXUnits(Units units) { @@ -239,7 +245,12 @@ public void setYUnits(Units units) { yUnits = units; } + /** Set the default Z-Units for the first plane in the dataset. + * + * @param units + */ public void setZUnits(Units units) { + setZUnits(units, ""); } @@ -251,6 +262,7 @@ public void setZUnits(Units units, String planeID) { } public String toString() { + int index = 0; return "TableDataSetBuilder ["+xTags.size()+" xtags, "+getTableCount(zValues)+"tables]"; } @@ -259,12 +271,8 @@ public TableDataSet toTableDataSet() { int[] tableOffsets = getTableOffsets(zValues, count); double[][] collapsedYTags = collapseYTags(zValues, count); double[][][] collapsedZValues = collapseZValues(zValues, planeIDs, zUnitsMap); - double[] collapsedXTags= collapseXTags(xTags,xTags.size()); Units[] zUnitsArray = getUnitsArray(planeIDs, zUnitsMap); - if ( monotonic ) { - properties.put( DataSet.PROPERTY_X_MONOTONIC, Boolean.TRUE ); - } - return new DefaultTableDataSet(collapsedXTags, xUnits, + return new DefaultTableDataSet(xTags.toArray(), xUnits, collapsedYTags, yUnits, collapsedZValues, zUnitsArray, (String[])planeIDs.toArray(new String[planeIDs.size()]), tableOffsets, @@ -276,9 +284,39 @@ public int getXLength() { } public double getXTag(int i) { - return (Double)xTags.get(i); + return xTags.get(i); } - + + private static double[] insert(double[] array, double value, int index) { + double[] result = new double[array.length + 1]; + System.arraycopy(array, 0, result, 0, index); + result[index] = value; + System.arraycopy(array, index, result, index + 1, array.length - index); + return result; + } + + private static double[][] insert(double[][] array, double[] values, int index) { + double[][] result = new double[array.length + 1][]; + System.arraycopy(array, 0, result, 0, index); + result[index] = values; + System.arraycopy(array, index, result, index + 1, array.length - index); + return result; + } + + private static String toString(double[] array) { + return toString(array, 0, array.length); + } + + private static String toString(double[] array, int startIndex, int endIndex) { + if (array.length == 0) return "[]"; + StringBuffer buffer = new StringBuffer("["); + for (int i = startIndex; i < endIndex-1; i++) { + buffer.append(array[i]).append(", "); + } + buffer.append(array[endIndex - 1]).append(']'); + return buffer.toString(); + } + private static int getTableCount(List list) { int count = 0; double[] previous = null; @@ -323,17 +361,6 @@ private static double[][] collapseYTags(List list, int count) { } return result; } - - private static double[] collapseXTags( List list, int count ) { - int index = 0; - list.size(); - double[] result = new double[count]; - for ( int i=0; i 0.0) { } - private static class MultiYScan { + private class MultiYScan { private HashMap map = new HashMap(); double[] yTags; public void put(String name, double[] scan) { diff --git a/dasCore/src/org/das2/dataset/TableDataSetGridder.java b/dasCore/src/org/das2/dataset/TableDataSetGridder.java new file mode 100644 index 000000000..94385b0ed --- /dev/null +++ b/dasCore/src/org/das2/dataset/TableDataSetGridder.java @@ -0,0 +1,107 @@ +/* + * TableDataSetGridder.java + * + * Created on July 15, 2005, 4:56 PM + * + * + */ + +package org.das2.dataset; + +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.datum.UnitsUtil; +import org.das2.util.DasMath; + +/** + * calculate TableDataSets with tables that are gridded in linear or log space. + * @author Jeremy + */ +public class TableDataSetGridder { + + private static Datum yTagGcd( TableDataSet tds, int itable, Datum error ) { + Units units= tds.getYUnits(); + double[] ytag= tds.getYTags(itable).toDoubleArray(units); + double gcd= DasMath.gcd( ytag, error.doubleValue( units.getOffsetUnits() ) ); + return units.getOffsetUnits().createDatum( gcd ); + } + + private static Datum yTagGcdLog( TableDataSet tds, int itable, Datum error ) { + Units units= tds.getYUnits(); + double[] ytag= new double[ tds.getYLength(itable) -1 ] ; + + if ( ! UnitsUtil.isRatiometric(error.getUnits()) ) throw new IllegalArgumentException("error units must be ratiometric"); + for ( int i=0; i1 ) throw new IllegalArgumentException("only simple tables for now"); + + int itable=0; + Datum xTagGcd= xTagGcd( tds, itable, xerror ); + Datum yTagGcd= yTagGcdLog( tds, itable, yerror ); + + Units xunits= tds.getXUnits(); + Units yunits= tds.getYUnits(); + + double tagWidth= DataSetUtil.guessXTagWidth(tds).doubleValue(xunits.getOffsetUnits()); + double dx= xTagGcd.doubleValue(xunits.getOffsetUnits()); + double xbase= tds.getXTagDouble( tds.tableStart(itable), xunits ) -tagWidth/2 - dx / 2.; + + int nx= (int)(( tds.getXTagDouble(tds.tableEnd(itable)-1, xunits ) + tagWidth/2 - xbase ) / dx + 1 ); + + int[] imap= new int[nx]; + + + for ( int i=tds.tableStart(itable); i(-(insertion point) - 1). (See Arrays.binarySearch) + */ + public static int yTagBinarySearch( TableDataSet ds, int table, Datum datum, int low, int high ) { + Units units= datum.getUnits(); + double key= datum.doubleValue(units); + while (low <= high) { + int mid = (low + high) >> 1; + double midVal = ds.getYTagDouble(table,mid,units); + int cmp; + if (midVal < key) { + cmp = -1; // Neither val is NaN, thisVal is smaller + } else if (midVal > key) { + cmp = 1; // Neither val is NaN, thisVal is larger + } else { + long midBits = Double.doubleToLongBits(midVal); + long keyBits = Double.doubleToLongBits(key); + cmp = (midBits == keyBits ? 0 : // Values are equal + (midBits < keyBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN) + 1)); // (0.0, -0.0) or (NaN, !NaN) + } + + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid - 1; + else + return mid; // key found + } + return -(low + 1); // key not found. + } + + /** Searches the ytags of the specified table of the specified data set for + * the specified datum using the binary search algorithm. + * + * @param ds the data set to search + * @param table the table to search + * @param datum the key to search for + * @return index of the search datum if it exists as an ytag in the + * data set; otherwise the insertion point. The insertion point is + * is defined as -1.0 if the datum is less that the first ytag + * in the data set, ds.getYLength(table) if the datum + * is larger than the last ytag, or + * (i + (datum - y0) / (y1 - y0) ) + * where y0 is the largest ytag smaller than datum, + * y1 is the smallest ytag larger than datum, and + * i is the index of y0. + */ + public static double rowFindex( TableDataSet ds, int table, Datum datum ) { + int result = yTagBinarySearch( ds, table, datum, 0, ds.getXLength()-1); + Units u = datum.getUnits(); + + if (result >= -1) { + return result; + } + + result = ~result; + + if (result == ds.getYLength(table)) { + return result; + } + else { + int lowerIndex = result-1; + double lower = ds.getYTagDouble(table, lowerIndex, u); + double upper = ds.getYTagDouble(table, result, u); + double key = datum.doubleValue(u); + return lowerIndex + (key - lower) / (upper - lower); + } + } + public static Datum closestDatum( TableDataSet table, Datum x, Datum y ) { int i= DataSetUtil.closestColumn( table, x ); int j= closestRow( table, table.tableOfIndex(i), y ); @@ -91,8 +161,8 @@ public static Datum guessYTagWidth( TableDataSet table ) { * is returned. monotonically decreasing is handled, in which case a positive tag cadence * is returned. * @param table - * @param itable the table index. - * @return the nominal cadence of the tags. + * @param the table index. + * @return the norminal cadence of the tags. */ public static Datum guessYTagWidth( TableDataSet table, int itable ) { // cheat and check for logarithmic scale. If logarithmic, then return YTagWidth as percent. @@ -105,13 +175,9 @@ public static Datum guessYTagWidth( TableDataSet table, int itable ) { double t= y0; y0= y1; y1= t; } if ( cycles > 10. ) { - return Units.log10Ratio.createDatum( Math.log10(y1/y0) ); + return Units.log10Ratio.createDatum( DasMath.log10(y1/y0) ); } else { - if ( (yn-y0)/n > (y1-y0) ) { - return table.getYUnits().createDatum((yn-y0)/n); // the average is bigger than the first. maybe return the last. - } else { - return table.getYUnits().createDatum(y1-y0); - } + return table.getYUnits().createDatum(y1-y0); } } public static double tableMax( TableDataSet tds, Units units ) { @@ -212,10 +278,9 @@ public static void dumpToAsciiStream( TableDataSet tds, Datum xmin, Datum xmax, } pout.println(" />"); - StringBuilder yTagsString= new StringBuilder( ); - yTagsString.append( tds.getYTagDatum(0,0) ); + String yTagsString= ""+tds.getYTagDatum(0,0); for ( int j=1; j"); pout.print(""); @@ -255,29 +320,26 @@ public static void dumpToAsciiStream( TableDataSet tds, OutputStream out ) { } public static void dumpToAsciiStream(TableDataSet tds, WritableByteChannel out) { - dumpToDas2Stream( tds, out, true, true ); + dumpToDas2Stream( tds, out, true ); + } + + public static void dumpToDas2Stream( TableDataSet tds, OutputStream out, boolean asciiTransferTypes ) { + dumpToDas2Stream(tds, Channels.newChannel(out), asciiTransferTypes ); } public static void dumpToBinaryStream( TableDataSet tds, OutputStream out ) { - dumpToDas2Stream(tds, Channels.newChannel(out), false, true ); + dumpToDas2Stream(tds, Channels.newChannel(out), false ); } - - /** - * write the data to a das2Stream - * @param tds - * @param out - * @param asciiTransferTypes - * @param sendStreamDescriptor if false, then don't send the stream and don't close. - */ - public static void dumpToDas2Stream( TableDataSet tds, WritableByteChannel out, boolean asciiTransferTypes, boolean sendStreamDescriptor ) { + + private static void dumpToDas2Stream( TableDataSet tds, WritableByteChannel out, boolean asciiTransferTypes ) { try { StreamProducer producer = new StreamProducer(out); StreamDescriptor sd = new StreamDescriptor(); - Map properties= tds.getProperties(); - for ( Entry e: properties.entrySet() ) { - String key= e.getKey(); - sd.setProperty(key, e.getValue() ); + Map properties= tds.getProperties(); + for ( Iterator i= properties.keySet().iterator(); i.hasNext(); ) { + String key= (String)i.next(); + sd.setProperty(key, properties.get(key)); } DataTransferType zTransferType; @@ -295,7 +357,7 @@ public static void dumpToDas2Stream( TableDataSet tds, WritableByteChannel out, xTransferType= DataTransferType.getByName("sun_real8"); } - if ( sendStreamDescriptor ) producer.streamDescriptor(sd); + producer.streamDescriptor(sd); DatumVector[] zValues = new DatumVector[1]; for (int table = 0; table < tds.tableCount(); table++) { StreamXDescriptor xDescriptor = new StreamXDescriptor(); @@ -315,7 +377,7 @@ public static void dumpToDas2Stream( TableDataSet tds, WritableByteChannel out, producer.packet(pd, xTag, zValues); } } - if ( sendStreamDescriptor ) producer.streamClosed(sd); + producer.streamClosed(sd); } catch (StreamException se) { throw new RuntimeException(se); } diff --git a/dasCore/src/org/das2/dataset/TagMapTableDataSet.java b/dasCore/src/org/das2/dataset/TagMapTableDataSet.java new file mode 100644 index 000000000..36540e846 --- /dev/null +++ b/dasCore/src/org/das2/dataset/TagMapTableDataSet.java @@ -0,0 +1,203 @@ +package org.das2.dataset; + +import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import java.util.*; + +public class TagMapTableDataSet implements TableDataSet { + + TableDataSet source; + + // maps from TagMapTDS to source TDS + int[] imap; + + // maps from TagMapTDS to source TDS + int[][] jmap; + + int[] itableMap; + + int tableCount; + int[] tableStart; + int[] tableEnd; + + TagMapTableDataSet( TableDataSet source, int[] imap, int[][]jmap, int[] itableMap ) { + this.imap= imap; + this.jmap= jmap; + this.itableMap= itableMap; + + int itable=itableMap[0]; + + ArrayList tableStartList= new ArrayList(); + ArrayList tableEndList= new ArrayList(); + tableStartList.add(new Integer(0)); + + for ( int i=1; iitable ) { + tableStartList.add( new Integer(i) ); + tableEndList.add( new Integer(i) ); + } + } + tableEndList.add( new Integer(itableMap.length) ); + + tableCount= tableEndList.size(); + tableStart= new int[tableCount]; + tableEnd= new int[tableCount]; + for ( int i=0; i planes; - - HashMap properties= new HashMap(); - - public static VectorDataSet create( QDataSet y ) { - QDataSet xds= SemanticOps.xtagsDataSet(y); - // convert to us2000 for legacy server - Units xunits= SemanticOps.getUnits( xds ); - if ( UnitsUtil.isTimeLocation(xunits) ) { - UnitsConverter uc= UnitsConverter.getConverter( xunits, Units.us2000 ); - ArrayDataSet xx= ArrayDataSet.copy(xds); - for ( int i=0; i(); - planes.put( "", y ); - - for ( int i=0; i properties= vds.getProperties(); + Map properties= vds.getProperties(); if ( properties!=null) { - for ( Entry e: properties.entrySet() ) { - String key= e.getKey(); - sd.setProperty(key, e.getValue() ); + for ( Iterator i= properties.keySet().iterator(); i.hasNext(); ) { + String key= (String)i.next(); + sd.setProperty(key, properties.get(key)); } } @@ -208,7 +198,7 @@ public static void dumpToDas2Stream( VectorDataSet vds, WritableByteChannel out, yTransferType= DataTransferType.getByName("sun_real4"); } - if ( sendStreamDescriptor ) producer.streamDescriptor(sd); + producer.streamDescriptor(sd); StreamXDescriptor xDescriptor = new StreamXDescriptor(); xDescriptor.setUnits(vds.getXUnits()); @@ -237,7 +227,7 @@ public static void dumpToDas2Stream( VectorDataSet vds, WritableByteChannel out, } producer.packet(pd, xTag, yValues); } - if ( sendStreamDescriptor ) producer.streamClosed(sd); + producer.streamClosed(sd); } catch (StreamException se) { throw new RuntimeException(se); } @@ -275,170 +265,4 @@ public static VectorDataSet finiteDerivative( VectorDataSet ds, int n ) { return builder.toVectorDataSet(); } - - /** - * return a converter for differences. If dst units are specified, - * then explicitly this is the target. - * @param src - * @param dst - * @return - */ - private static UnitsConverter getDifferencesConverter( Units unitsOut, Units unitsIn, Units dstUnits ) { - UnitsConverter xuc; - if ( dstUnits!=null ) { - xuc= unitsOut.getConverter( dstUnits ); - } else { - xuc= unitsOut.getConverter( unitsIn.getOffsetUnits() ); - } - return xuc; - } - - /** - * produce a simpler version of the dataset by averaging adjacent data. - * code taken from org.das2.graph.GraphUtil.reducePath. Adjacent points are - * averaged together until a point is found that is not in the bin, and then - * a new bin is started. The bin's lower bounds are integer multiples - * of xLimit and yLimit. - * - * If yLimit is null, then averaging is done for all points in the x bin, - * regardless of how close they are in Y. This is similarly true when - * xLimit is null. - * - * xLimit and yLimit are rank 0 datasets, so that they can indicate that binning - * should be done in log space rather than linear. In this case, a SCALE_TYPE - * for the dataset should be "log" and its unit should be convertible to - * Units.logERatio (for example, Units.log10Ratio or Units.percentIncrease). - * Note when either is log, then averaging is done in the log space. - * - * @param xds the x tags - * @param ds the y tags - * @param start first index. - * @param finish last (non-inclusive) index. - * @param xLimit the size of the bins or null to indicate no limit. - * @param yLimit the size of the bins or null to indicate no limit. - * @return - */ - public static QDataSet reduce2D( QDataSet xds, QDataSet ds, int start, int finish, Datum xLimit, Datum yLimit ) { - - double x0 = Float.MAX_VALUE; - double y0 = Float.MAX_VALUE; - double sx0 = 0; - double sy0 = 0; - double nn0 = 0; - double ax0 = Float.NaN; - double ay0 = Float.NaN; // last averaged location - - QDataSet wds= SemanticOps.weightsDataSet(ds); - - if ( xds.rank()==2 && xds.property( QDataSet.BINS_1 )!=null ) { - xds= Ops.reduceMean(xds,1); - } - - final Units xunits= SemanticOps.getUnits(xds); - final Units yunits= SemanticOps.getUnits(ds); - - DataSetBuilder builder= new DataSetBuilder(1,1000); - DataSetBuilder xbuilder= new DataSetBuilder(1,1000); - DataSetBuilder wbuilder= new DataSetBuilder(1,1000); - - boolean xlog= xLimit!=null && UnitsUtil.isRatiometric( xLimit.getUnits() ); - boolean ylog= yLimit!=null && UnitsUtil.isRatiometric( yLimit.getUnits() ); - - UnitsConverter uc; - double dxLimit, dyLimit; // x and y bin sizes in dataset offset units. - if ( xLimit!=null ) { - uc= getDifferencesConverter( xLimit.getUnits(), xunits.getOffsetUnits(), xlog ? Units.logERatio : null ); - dxLimit = uc.convert( xLimit.doubleValue(xLimit.getUnits()) ); - } else { - dxLimit= Double.MAX_VALUE; - } - if ( yLimit!=null ) { - uc= getDifferencesConverter( yLimit.getUnits(), yunits.getOffsetUnits(), ylog ? Units.logERatio : null ); - dyLimit = uc.convert( yLimit.doubleValue(yLimit.getUnits()) ); - } else { - dyLimit= Double.MAX_VALUE; - } - - int points = 0; - int inCount = 0; - - int i=start; - - while ( i0 ) { - ax0 = sx0 / nn0; - ay0 = sy0 / nn0; - - builder.putValue( points, ylog ? Math.exp(ay0) : ay0 ); - xbuilder.putValue( points, xlog ? Math.exp(ax0) : ax0 ); - wbuilder.putValue( points, nn0 ); - - points++; - } - - i++; - - x0 = dxLimit * ( 0.5 + (int) Math.floor(p0/dxLimit) ); - y0 = dyLimit * ( 0.5 + (int) Math.floor(p1/dyLimit) ); - sx0 = p0*ww; - sy0 = p1*ww; - nn0 = ww; - } - - if ( nn0>0 ) { - ax0 = sx0 / nn0; - ay0 = sy0 / nn0; - - builder.putValue( points, ylog ? Math.exp(ay0) : ay0 ); - xbuilder.putValue( points, xlog ? Math.exp(ax0) : ax0 ); - wbuilder.putValue( points, nn0 ); - - points++; - } - - DDataSet xdsr= xbuilder.getDataSet(); - org.virbo.dataset.DataSetUtil.putProperties( org.virbo.dataset.DataSetUtil.getDimensionProperties(xds,null), xdsr ); - xdsr.putProperty( QDataSet.CADENCE, null ); - - DDataSet ydsr= builder.getDataSet(); - org.virbo.dataset.DataSetUtil.putProperties( org.virbo.dataset.DataSetUtil.getDimensionProperties(ds,null), ydsr ); - ydsr.putProperty( QDataSet.CADENCE, null ); - - ydsr.putProperty( QDataSet.DEPEND_0, xdsr ); - ydsr.putProperty( QDataSet.WEIGHTS, wbuilder.getDataSet() ); - - return ydsr; - - } - } \ No newline at end of file diff --git a/dasCore/src/org/das2/dataset/WeightsVectorDataSet.java b/dasCore/src/org/das2/dataset/WeightsVectorDataSet.java deleted file mode 100644 index 6fc4a0d90..000000000 --- a/dasCore/src/org/das2/dataset/WeightsVectorDataSet.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.dataset; - -import java.util.HashMap; -import org.das2.datum.Datum; -import org.das2.datum.Units; - -/** - * DataSet with non-zero values when the source data is valid. - * @author jbf - */ -public class WeightsVectorDataSet implements VectorDataSet { - - VectorDataSet source; - Units sourceUnits; - - public static VectorDataSet create( VectorDataSet source ) { - if ( source.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS)!=null ) { - return (VectorDataSet)source.getPlanarView(DataSet.PROPERTY_PLANE_WEIGHTS); - } else { - return new WeightsVectorDataSet( source ); - } - } - - private WeightsVectorDataSet( VectorDataSet source ) { - this.source= source; - this.sourceUnits= source.getYUnits(); - } - - public DataSet getPlanarView(String planeID) { - return this; - } - - public String[] getPlaneIds() { - return new String[] { "" }; - } - - public java.util.Map getProperties() { - return new HashMap(); - } - - public Object getProperty(String name) { - return null; - } - - public int getXLength() { - return source.getXLength(); - } - - public org.das2.datum.Datum getXTagDatum(int i) { - return source.getXTagDatum(i); - } - - public double getXTagDouble(int i, org.das2.datum.Units units) { - return source.getXTagDouble( i, units ); - } - - public int getXTagInt(int i, org.das2.datum.Units units) { - return source.getXTagInt(i, units); - } - - public org.das2.datum.Units getXUnits() { - return source.getXUnits(); - } - - public org.das2.datum.Units getYUnits() { - return Units.dimensionless; - } - - public Datum getDatum(int i) { - return Units.dimensionless.createDatum( getDouble(i,Units.dimensionless ) ); - } - - public double getDouble(int i, Units units) { - return ( sourceUnits.isFill(source.getDouble( i, sourceUnits ) ) ) ? 0.0 : 1.0; - } - - public int getInt(int i, Units units) { - return (int)getDouble(i, units); - } - - -} diff --git a/dasCore/src/org/das2/dataset/test/BigVectorDataSet.java b/dasCore/src/org/das2/dataset/test/BigVectorDataSet.java index 3b9512a15..f67ff0081 100644 --- a/dasCore/src/org/das2/dataset/test/BigVectorDataSet.java +++ b/dasCore/src/org/das2/dataset/test/BigVectorDataSet.java @@ -10,8 +10,6 @@ import org.das2.datum.Units; import org.das2.util.monitor.ProgressMonitor; import java.util.Random; -import org.virbo.dataset.QDataSet; -import org.virbo.dsutil.DataSetBuilder; /** * @@ -19,7 +17,7 @@ */ public class BigVectorDataSet { - public static QDataSet getDataSet( int size, ProgressMonitor mon ) { + public static VectorDataSet getDataSet( int size, ProgressMonitor mon ) { double dsize= (double)size; System.err.println("enter getDataSet"); @@ -27,26 +25,18 @@ public static QDataSet getDataSet( int size, ProgressMonitor mon ) { Random random = new Random(0); - DataSetBuilder vbd = new DataSetBuilder(1,100); - DataSetBuilder xbd = new DataSetBuilder(1,100); - + VectorDataSetBuilder vbd = new VectorDataSetBuilder(Units.dimensionless, Units.dimensionless); double y = 0; for (int i = 0; i < size; i += 1) { y += random.nextDouble() - 0.5; if (i % 100 == 10) { - vbd.putValue( i, Units.dimensionless.getFillDouble()); - xbd.putValue( i, i/dsize ); + vbd.insertY( i / dsize, Units.dimensionless.getFillDouble()); } else { - vbd.putValue( i, y); - xbd.putValue( i, i/dsize ); + vbd.insertY( i / dsize, y); } } - xbd.putProperty( QDataSet.MONOTONIC, Boolean.TRUE ); - vbd.putProperty( QDataSet.DEPEND_0, xbd.getDataSet() ); - vbd.putProperty( QDataSet.FILL_VALUE, Units.dimensionless.getFillDouble() ); - - QDataSet vds = vbd.getDataSet(); - + vbd.setProperty(DataSet.PROPERTY_X_MONOTONIC, Boolean.TRUE); + VectorDataSet vds = vbd.toVectorDataSet(); System.err.println("done getDataSet in " + (System.currentTimeMillis() - t0) + " ms"); return vds; } diff --git a/dasCore/src/org/das2/dataset/test/MendelbrotDataLoader.java b/dasCore/src/org/das2/dataset/test/MendelbrotDataLoader.java index 3cc5e13c3..e0c6aba3e 100644 --- a/dasCore/src/org/das2/dataset/test/MendelbrotDataLoader.java +++ b/dasCore/src/org/das2/dataset/test/MendelbrotDataLoader.java @@ -20,9 +20,6 @@ import org.das2.dataset.DataSetDescriptor; import org.das2.dataset.RebinDescriptor; import org.das2.dataset.WritableTableDataSet; -import org.virbo.dataset.DDataSet; -import org.virbo.dataset.QDataSet; -import org.virbo.dsops.Ops; /** @@ -45,7 +42,7 @@ public MendelbrotDataLoader( Renderer r ) { super(r); } - private float punktfarbe(double xwert, double ywert) {// color value from 0.0 to 1.0 by iterations + private final float punktfarbe(double xwert, double ywert) {// color value from 0.0 to 1.0 by iterations double r = 0.0, i = 0.0, m = 0.0; int j = 0; final int MAX=limit; @@ -57,7 +54,7 @@ private float punktfarbe(double xwert, double ywert) {// color value from 0.0 to r = m + xwert; } if ( j==MAX ) j=0; - return (float)j / (float)MAX; + return (float)j; } public void update( ) { @@ -106,8 +103,8 @@ public void update( ) { public void run( ) { try { logger.fine( "calculate dataset for "+taskDescription ); - QDataSet result= getDataSet( xRebinDescriptor, yRebinDescriptor, getMonitor(taskDescription) , taskDescription ); - System.err.println( result.property("TaskDescription") ); + DataSet result= getDataSet( xRebinDescriptor, yRebinDescriptor, getMonitor(taskDescription) , taskDescription ); + System.err.println( result.getProperty("TaskDescription") ); getRenderer().setDataSet( result ); completedRequest= currentRequest; logger.fine( "completed "+taskDescription ); @@ -122,7 +119,7 @@ public void run( ) { } } - private QDataSet getDataSet( RebinDescriptor ddx, RebinDescriptor ddy, ProgressMonitor monitor, String desc) throws DasException { + private DataSet getDataSet( RebinDescriptor ddx, RebinDescriptor ddy, ProgressMonitor monitor, String desc) throws DasException { double xstart, xend, xresolution; xstart= ddx.binCenter(0, Units.dimensionless ); @@ -136,25 +133,36 @@ private QDataSet getDataSet( RebinDescriptor ddx, RebinDescriptor ddy, ProgressM int ny= (int)(1.5+((yend-ystart)/yresolution)); int nx= (int)(1.5+((xend-xstart)/xresolution)); - - DDataSet result= DDataSet.createRank2(nx, ny); - + + WritableTableDataSet result= WritableTableDataSet.newSimple( nx, Units.dimensionless, ny, Units.dimensionless, Units.dimensionless ); + + double[][] z= new double[nx][ny]; + monitor.setTaskSize(ny); monitor.started(); for ( int iy=0; iy Units.microseconds and getBasis() -> "since 2000-01-01T00:00". + * + * @author jbf + */ +public class Basis { + + public static final Basis fahrenheit= new Basis( "fahrenheit", "fahrenheit", Basis.physicalZero, 255.370, Units.celciusDegrees ); + public static final Basis kelvin= new Basis( "kelvin", "kelvin", Basis.physicalZero, 0, Units.celciusDegrees ); + public static final Basis centigrade= new Basis( "centigrade", "centigrade", Basis.physicalZero, 273.15, Units.celciusDegrees ); + + public static final Basis since2000= new Basis( "since2000", "since 2000-01-01T00:00Z", null, 0, null ); + public static final Basis since2010= new Basis( "since2010", "since 2010-01-01T00:00Z", since2000, 315619200., Units.seconds ); + public static final Basis since1980= new Basis( "since1980", "since 1980-01-01T00:00Z", since2000, -631152000., Units.seconds ); + public static final Basis since1970= new Basis( "since1970", "since 1970-01-01T00:00Z", since2000, -938044800., Units.seconds ); + public static final Basis since1958= new Basis( "since1958", "since 1958-01-01T00:00Z", since2000, -1325376000., Units.seconds ); + public static final Basis modifiedJulian= new Basis( "modifiedJulian", "since 1858-11-17T00:00Z", since2000, 4453401600., Units.seconds ); + public static final Basis since0000= new Basis( "since0000", "since 01-Jan-0000T00:00Z", since2000, 63113904000., Units.seconds ); + + /** + * special basis representing physical zero for all combinations of physical units. + */ + public static final Basis physicalZero= new Basis( "", "physical zero", null, 0, null ); + + private IdentityHashMap bases; + + private String id; + private String description; + private Basis parent; + + public Basis( String id, String description, Basis parent, double d, Units offsetUnits ) { + this.id= id; + this.description= description; + this.parent= parent; + if ( parent!=null ) { + parent.bases.put( this, offsetUnits.createDatum(d) ); + } else { + bases= new IdentityHashMap(); + } + } + + /** + * return the location of this basis in given basis, in the given units. + * @param basis + * @param u + * @return + */ + double getOffset( Basis basis, Units u ) { + if ( parent==null ) { + return bases.get(basis).doubleValue(u); + } else { + double d0= parent.bases.get(this).doubleValue(u); + double d1= parent.bases.get(basis).doubleValue(u); + return d0 - d1; + } + } + + /** + * specify offset to another basis. Register to + * @param toBasis + * @param d + * @param u + */ + public void registerConverter( Basis toBasis, double d, Units u ) { + bases.put( toBasis, u.createDatum(d) ); + } + + + public static void main( String[] args ) { + Basis since2010= new Basis( "since2010", "since 2010-01-01T00:00Z", Basis.since2000, 315619200000000.0, Units.microseconds ); + Basis since2011= new Basis( "since2011", "since 2011-01-01T00:00Z", Basis.since2000, 347155200000000.0, Units.microseconds ); + System.err.println( since2011.getOffset(since2010, Units.days )); + + System.err.println( centigrade.getOffset( fahrenheit, Units.fahrenheitDegrees ) ); + + } +} diff --git a/dasCore/src/org/das2/datum/BestFormatter.txt b/dasCore/src/org/das2/datum/BestFormatter.txt new file mode 100644 index 000000000..56219da55 --- /dev/null +++ b/dasCore/src/org/das2/datum/BestFormatter.txt @@ -0,0 +1,37 @@ +Best Formatter + +Purpose: for a group of numbers, select a formatter that correctly and + efficiently represents each number of the set. + +Cases: + 1. linearly spaced. + 2. logarithmically spaced. + 3. arbitary set of similar magnitude + 4. arbitary set of differing magnitudes + 3a. similar, large magnitude + 3b. similar, order-one magnitude + +Is (1) an instance of (3), and (2) an instance of (4)? + +Case 3b solution: + Find the greatest common divisor of the numbers, and let this +set the number of fractional decimal places. + +Case 3a solution: + Identify the base exponent to make each number order one, specifically +so the mantissas of the numbers vary from 1-100. Then use gcd to determine +number of digimal places, and express each number with the common exponent +pulled out. + +Case 4 solution: +??? + + + +Case 3 examples: +dv= DatumVector.newDatumVector( new double[] { 100000, 125000, 105000 }, Units.dimensionless ); + +Case 4 examples: +dv= DatumVector.newDatumVector( new double[] { 0.002, 2.001, 105.001 }, Units.dimensionless ); +dv= DatumVector.newDatumVector( new double[] { 1e3, 1e6, 1e9, 2.345e7 }, Units.dimensionless ); +dv= DatumVector.newDatumVector( new double[] { 0.001, 1.0, 1000.00 }, Units.dimensionless ); diff --git a/dasCore/src/org/das2/datum/CalendarTime.java b/dasCore/src/org/das2/datum/CalendarTime.java new file mode 100644 index 000000000..db6161068 --- /dev/null +++ b/dasCore/src/org/das2/datum/CalendarTime.java @@ -0,0 +1,1016 @@ +package org.das2.datum; + +import java.text.ParseException; +import java.util.Calendar; +import java.util.StringTokenizer; +import java.util.TimeZone; + +/** Represents a point in time, over thousands of years to nano-second resolution. + * The Gegorian calendar is extended in both directions, which makes little sense + * for dates prior to it's adoption. + * + * @author ljg, eew, jbf, cwp + */ +public class CalendarTime implements Comparable{ + + /** The time point's year number. + * Note: that year 1 BC is represented year 0 in this field. + */ + protected int m_nYear; + + /** The time point's month of year, normalized range is 1 to 12 */ + protected int m_nMonth; + + /** The time point's day of month, normalized range is 1 up to 31 + * depending on the month rValue. + */ + protected int m_nDom; + + // Cash the day of year calculation after a normalize. + protected int m_nDoy; + + /** The time point's hour of day, normalized range is 0 to 23 */ + protected int m_nHour; + + /** The time point's minute of hour, normalized range is 0 to 59 */ + protected int m_nMinute; + + /** The time point's second of minute, normalized range is 0 to 59. + * Note that leap seconds are not handled by this class, though it + * wouldn't be hard to do so. + */ + protected int m_nSecond; + + /** The time point's nanosecond of second, normalized range is 0 to 999,999,999 */ + protected long m_nNanoSecond; + + + //////////////////////////////////////////////////////////////////////////////////// + /** Empty constructor */ + + public CalendarTime(){ + m_nYear = 1; + m_nMonth = 1; + m_nDom = 1; + m_nDoy = 1; + m_nHour = 0; + m_nMinute = 0; + m_nSecond = 0; + m_nNanoSecond = 0; + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Static method to create a calender time set to now */ + static public CalendarTime now(){ + + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + + + CalendarTime ct = new CalendarTime(); + ct.m_nYear = cal.get(Calendar.YEAR); + ct.m_nMonth = cal.get(Calendar.MONTH) + 1; + ct.m_nDom = cal.get(Calendar.DAY_OF_MONTH); + ct.m_nDoy = cal.get(Calendar.DAY_OF_YEAR); + ct.m_nHour = cal.get(Calendar.HOUR_OF_DAY); + ct.m_nMinute = cal.get(Calendar.MINUTE); + ct.m_nSecond = cal.get(Calendar.SECOND); + ct.m_nNanoSecond = cal.get(Calendar.MILLISECOND) * 1000000L; + + return ct; + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Tuple constructor + * @param lFields an array of integer fields. Values are assumed to be in largest + * time span to smallest time span. The array may be null. Up to 7 items will + * be used from the array in the order: year, month, day, hour, min, sec, + * nanosecond. + */ + public CalendarTime(int... lFields){ + m_nYear = 1; + m_nMonth = 1; + m_nDom = 1; + m_nDoy = 1; + m_nHour = 0; + m_nMinute = 0; + m_nSecond = 0; + m_nNanoSecond = 0; + + if(lFields == null) return; + if(lFields.length > 0) m_nYear = lFields[0]; + if(lFields.length > 1) m_nMonth = lFields[1]; + if(lFields.length > 2) m_nDom = lFields[2]; + if(lFields.length > 3) m_nHour = lFields[3]; + if(lFields.length > 4) m_nMinute = lFields[4]; + if(lFields.length > 5) m_nSecond = lFields[5]; + if(lFields.length > 6) m_nNanoSecond = lFields[6]; + + normalize(); + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Copy constructor */ + public CalendarTime(CalendarTime other){ + m_nYear = other.m_nYear; + m_nMonth = other.m_nMonth; + m_nDom = other.m_nDom; + m_nDoy = other.m_nDoy; + m_nHour = other.m_nHour; + m_nMinute = other.m_nMinute; + m_nSecond = other.m_nSecond; + m_nNanoSecond = other.m_nNanoSecond; + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Constructing a calendar time from a time string. + * Taken from Larry Granroth's Das1 lib. + */ + public CalendarTime(String s) throws ParseException{ + + final int DATE = 0; + final int YEAR = 1; + final int MONTH = 2; + final int DAY = 3; + final int HOUR = 4; + final int MINUTE = 5; + final int SECOND = 6; + + final String DELIMITERS = " \t/-:,_;\r\n"; + final String PDSDELIMITERS = " \t/-T:,_;\r\n"; + + final String[] months = { + "january", "febuary", "march", "april", "may", "june", + "july", "august", "september", "october", "november", "december" + }; + final String[] mons = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + // Starting values for this object, does not default to current year. + m_nYear = 0; + m_nMonth = 0; + m_nDom = 0; + m_nDoy = 0; + m_nHour = 0; + m_nMinute = 0; + m_nSecond = 0; + m_nNanoSecond = 0; + + String[] lToks = new String[10]; + + int[] format = new int[7]; + + int ptr; + + int hold; + + int tokIndex; + + /* handle ISO8601 time format */ + String delimiters = DELIMITERS; + int iC; + if((iC = s.indexOf((int) 'Z')) != -1){ + s = s.substring(0, iC); + } + int end_of_date = s.indexOf((int) 'T'); + if(end_of_date != -1){ + iC = end_of_date - 1; + if(Character.isDigit(s.charAt(iC))){ + delimiters = PDSDELIMITERS; + } + else{ + end_of_date = -1; + } + } + + /* if not PDS then count out 3 non-space delimiters */ + int nTokens = 0; + if(end_of_date == -1){ + nTokens = 0; + int nLen = s.length(); + for(int i = 0; i < nLen; i++){ + if((iC = (delimiters.substring(2)).indexOf(s.charAt(i))) != -1){ + nTokens++; + } + if(nTokens == 3){ + end_of_date = i; + break; + } + } + } + + /* tokenize the time string */ + StringTokenizer st = new StringTokenizer(s, delimiters); + + if(!st.hasMoreTokens()) + throw new java.text.ParseException("No tokens in '" + s + "'", 0); + + for(nTokens = 0; nTokens < 10 && st.hasMoreTokens(); nTokens++) + lToks[nTokens] = st.nextToken(); + + boolean[] lWant = new boolean[]{false, false, false, false, false, false, false}; + lWant[DATE] = lWant[YEAR] = lWant[MONTH] = lWant[DAY] = true; + hold = 0; + + tokIndex = -1; + + // The big token parser loop, each iteration handles one token from the input string + for(int i = 0; i < nTokens; i++){ + tokIndex = s.indexOf(lToks[i], tokIndex + 1); + if((end_of_date != -1) && lWant[DATE] && tokIndex > end_of_date){ + lWant[DATE] = false; + lWant[HOUR] = lWant[MINUTE] = lWant[SECOND] = true; + } + + int nTokLen = lToks[i].length(); + double rValue; + + // skip 3-digit day-of-year values in parenthesis + if ((nTokLen == 5) && lToks[i].startsWith("(") && lToks[i].endsWith(")")) { + try{ + rValue = Double.parseDouble(lToks[i].substring(1, nTokLen-2)); + } + catch(NumberFormatException e){ + throw new ParseException("Error in token '"+lToks[i]+"'", 1); + } + if ((rValue > 0) && (rValue < 367)) continue; + } + + try{ + rValue = Double.parseDouble(lToks[i]); + } + catch(NumberFormatException e){ + if(nTokLen < 3 || !lWant[DATE]){ + throw new ParseException("Error at token '"+lToks[i]+"' in '"+s+"'", 0); + } + for(int j = 0; j < 12; j++){ + if(lToks[i].equalsIgnoreCase(months[j]) || + lToks[i].equalsIgnoreCase(mons[j])) { + m_nMonth = j + 1; + lWant[MONTH] = false; + if(hold > 0){ + if(m_nDom > 0) + throw new ParseException("Ambiguous dates in token '" + lToks[i] + + "' in '" + s + "'", 0); + m_nDom = hold; + hold = 0; + lWant[DAY] = false; + } + break; + } + } + if(lWant[MONTH]) + throw new ParseException("Error at token '"+lToks[i]+"' in '"+s+"'", 0); + continue; + } + + if(Math.IEEEremainder(rValue, 1.0) != 0.0){ + if(lWant[SECOND]){ + //Round normally to nearest nanosecond + long nTmp = Math.round( rValue * 1.0e+9); + m_nSecond = (int) ((long)nTmp / 1000000000L); + m_nNanoSecond = (long)nTmp % 1000000000L; + break; + } + else{ + throw new ParseException("Error at token '"+lToks[i]+"' in '"+s+"'", 0); + } + } + + int number = (int) rValue; + if(number < 0){ + throw new ParseException("Error at token '"+lToks[i]+"' in '"+s+"'", 0); + } + + if(lWant[DATE]){ + + if(number == 0){ + throw new ParseException("m,d, or y can't be 0 in '" + s + "'", 0); + } + + if(number >= 10000000 && lWant[YEAR]){ // %Y%m%d + m_nYear = number / 10000; + lWant[YEAR] = false; + m_nMonth = number / 100 % 100; + lWant[MONTH] = false; + m_nDom = number % 100; + m_nDoy = 0; + lWant[DAY] = false; + } + else if(number >= 1000000 && lWant[YEAR]){ //%Y%j + m_nYear = number / 1000; + lWant[YEAR] = false; + m_nDoy = number % 1000; + m_nMonth = 0; + lWant[MONTH] = false; + lWant[DAY] = false; + + } + else if(number > 31){ + + if(lWant[YEAR]){ + m_nYear = number; + if(m_nYear < 1000){ + m_nYear += 1900; + } + lWant[YEAR] = false; + } + else if(lWant[MONTH]){ + lWant[MONTH] = false; + m_nMonth = 0; + m_nDoy = number; + lWant[DAY] = false; + } + else{ + throw new ParseException("Error at token '"+lToks[i]+"' in '"+s+"'", 0); + } + + } + else if(number > 12){ + + if(lWant[DAY]){ + if(hold > 0){ + m_nMonth = hold; + lWant[MONTH] = false; + } + if(nTokLen == 3){ + if(m_nMonth > 0){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nDoy = number; + m_nDom = 0; + lWant[MONTH] = false; + } + else{ + m_nDom = number; + } + lWant[DAY] = false; + } + else{ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + + } + else if(!lWant[MONTH]){ + + if(m_nMonth > 0){ + m_nDom = number; + m_nDoy = 0; + } + else{ + m_nDoy = number; + m_nDom = 0; + } + lWant[DAY] = false; + + } + else if(!lWant[DAY]){ + + if(m_nDoy > 0){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nMonth = number; + lWant[MONTH] = false; + + } + else if(!lWant[YEAR]){ + + if(nTokLen == 3){ + if(m_nMonth > 0){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nDoy = number; + m_nDom = 0; + lWant[DAY] = false; + } + else{ + if(m_nDoy > 0){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nMonth = number; + if(hold > 0){ + m_nDom = hold; + lWant[DAY] = false; + } + } + lWant[MONTH] = false; + + } + else if(hold > 0){ + + m_nMonth = hold; + hold = 0; + lWant[MONTH] = false; + m_nDom = number; + lWant[DAY] = false; + + } + else{ + hold = number; + } + + if(!(lWant[YEAR] || lWant[MONTH] || lWant[DAY])){ + lWant[DATE] = false; + lWant[HOUR] = lWant[MINUTE] = lWant[SECOND] = true; + } + + } + else if(lWant[HOUR]){ + + if(nTokLen == 4){ + hold = number / 100; + // TODO: handle times like Jan-1-2001T24:00 --> Jan-2-2001T00:00, for ease of modifying times + if(hold > 23){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nHour = hold; + hold = number % 100; + if(hold > 59){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nMinute = hold; + lWant[MINUTE] = false; + } + else{ + if(number > 23){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nHour = number; + } + lWant[HOUR] = false; + + } + else if(lWant[MINUTE]){ + // TODO: handle times like 0:90 --> 1:30, for ease of modifying times + if(number > 59){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nMinute = number; + lWant[MINUTE] = false; + + } + else if(lWant[SECOND]){ + + if(number > 61){ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + m_nSecond = number; + lWant[SECOND] = false; + + } + else{ + throw new ParseException("Error at token '" + lToks[i] + "' in '" + s + "'", 0); + } + + } + // End of token parsing loop + + if(m_nMonth > 12){ + throw new ParseException("Month is greater than 12 in '" + s + "'", 0); + } + if(m_nMonth > 0 && m_nDom <= 0){ + m_nDom = 1; + } + + int iLeap = ((m_nYear % 4) != 0 ? 0 : ((m_nYear % 100) > 0 ? 1 : ((m_nYear % 400) > 0 ? 0 : 1))); + + if((m_nMonth > 0) && (m_nDom > 0) && (m_nDoy == 0)){ + if(m_nDom > TimeUtil.daysInMonth[iLeap][m_nMonth]){ + throw new java.text.ParseException("day of month too high in '" + s + "'", 0); + } + m_nDoy = TimeUtil.dayOffset[iLeap][m_nMonth] + m_nDom; + } + else if((m_nDoy > 0) && (m_nMonth == 0) && (m_nDom == 0)){ + if(m_nDoy > (365 + iLeap)){ + throw new java.text.ParseException("day of year too high in '" + s + "'", 0); + } + int i = 2; + while(i < 14 && m_nDoy > TimeUtil.dayOffset[iLeap][i]) i++; + i--; + m_nMonth = i; + m_nDom = m_nDoy - TimeUtil.dayOffset[iLeap][i]; + } + else{ + if(m_nMonth == 0){ + m_nMonth = 1; + } + m_nDom = 1; + } + + // Okay, hit nomalize, looking at the code above, we know that the seconds + // field can be way over value, others may be too. + normalize(); + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Construct from a datum. + * splits the time location datum into y,m,d,etc components. Note that + * seconds is a double, and micros will be 0. + * + * @param datum with time location units + */ + public CalendarTime(Datum datum){ + double microseconds = TimeUtil.getMicroSecondsSinceMidnight(datum); + Datum sansMicros = datum.subtract(microseconds, Units.microseconds); + + int jd = TimeUtil.getJulianDay(sansMicros); + if(jd < 0) + throw new IllegalArgumentException("julian day is negative."); + + int[] lDate = TimeUtil.julianToGregorian(jd); + m_nYear = lDate[0]; + m_nMonth = lDate[1]; + m_nDom = lDate[2]; + m_nNanoSecond = Math.round( microseconds * 1000); + normalize(); + } + + //////////////////////////////////////////////////////////////////////////////////// + @Override + public String toString(){ + return m_nYear + "/" + m_nMonth + "/" + m_nDom + " " + m_nHour + ":" + m_nMinute + ":" + m_nSecond + + "." + m_nNanoSecond; + } + + public boolean isLeapYear(){ return TimeUtil.isLeapYear(m_nYear); } + + @Override + public int compareTo(CalendarTime o){ + if(m_nYear != o.m_nYear) return m_nYear - o.m_nYear; + if(m_nMonth != o.m_nMonth) return m_nMonth - o.m_nMonth; + if(m_nDom != o.m_nDom) return m_nDom - o.m_nDom; + if(m_nHour != o.m_nHour) return m_nHour - o.m_nHour; + if(m_nMinute != o.m_nMinute) return m_nMinute - o.m_nMinute; + if(m_nSecond != o.m_nSecond) return m_nSecond - o.m_nSecond; + if(m_nNanoSecond < o.m_nNanoSecond) return -1; + if(m_nNanoSecond > o.m_nNanoSecond) return 1; + + return 0; + } + + @Override + public boolean equals(Object o){ + if(! (o instanceof CalendarTime) ) return false; + + CalendarTime ctO = (CalendarTime)o; + + if(m_nYear != ctO.m_nYear) return false; + if(m_nMonth != ctO.m_nMonth) return false; + if(m_nDom != ctO.m_nDom) return false; + if(m_nHour != ctO.m_nHour) return false; + if(m_nMinute != ctO.m_nMinute) return false; + if(m_nSecond != ctO.m_nSecond) return false; + if(m_nNanoSecond != ctO.m_nNanoSecond) return false; + + return true; + } + + @Override + public int hashCode(){ + int hash = 7; + hash = 71 * hash + this.m_nYear; + hash = 71 * hash + this.m_nMonth; + hash = 71 * hash + this.m_nDom; + hash = 71 * hash + this.m_nHour; + hash = 71 * hash + this.m_nMinute; + hash = 71 * hash + this.m_nSecond; + hash = 71 * hash + (int) (this.m_nNanoSecond ^ (this.m_nNanoSecond >>> 32)); + return hash; + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Resolution flags to use when requesting time point as a string */ + public static enum Resolution { + YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISEC, MICROSEC, NANOSEC + }; + + //////////////////////////////////////////////////////////////////////////////////// + /** Produce an ISO 8601 Date-Time string with up-to nanosecond resolution. + * The primary ISO format uses YYYY-MM-DD style dates. + * + * @param res The resolution of the returned string. + * @return + */ + public String toISO8601(Resolution res){ + switch(res){ + case YEAR: + return String.format("%04d", m_nYear); + case MONTH: + return String.format("%04d-%02d", m_nYear, m_nMonth); + case DAY: + return String.format("%04d-%02d-%02d", m_nYear, m_nMonth, m_nDom); + case HOUR: + return String.format("%04d-%02d-%02dT%02d", m_nYear, m_nMonth, m_nDom, m_nHour); + case MINUTE: + return String.format("%04d-%02d-%02dT%02d:%02d", m_nYear, m_nMonth, m_nDom, m_nHour, m_nMinute); + case SECOND: + return String.format("%04d-%02d-%02dT%02d:%02d:%02d", m_nYear, m_nMonth, m_nDom, m_nHour, + m_nMinute, m_nSecond); + case MILLISEC: + // Let string.format handle rounding for me. + return String.format("%04d-%02d-%02dT%02d:%02d:%02d.%03.0f", m_nYear, m_nMonth, m_nDom, + m_nHour, m_nMinute, m_nSecond, m_nNanoSecond / 1000000.0); + case MICROSEC: + // Let string.format handle rounding for me. + return String.format("%04d-%02d-%02dT%02d:%02d:%02d.%06.0f", m_nYear, m_nMonth, m_nDom, + m_nHour, m_nMinute, m_nSecond, m_nNanoSecond / 1000.0); + case NANOSEC: + // Let string.format handle rounding for me. + return String.format("%04d-%02d-%02dT%02d:%02d:%02d.%09d", m_nYear, m_nMonth, m_nDom, + m_nHour, m_nMinute, m_nSecond, m_nNanoSecond); + } + return null; // added to make the compilier happy. + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Produce an alternate ISO 8601 Date-Time string with up-to nanosecond resolution. + * The alternate format ISO format uses ordinal day-of-year style dates, i.e. + * YYYY-DDD. + * + * @param res The resolution of the returned string. + * @return + */ + public String toAltISO8601(Resolution res){ + switch(res){ + case YEAR: + return String.format("%04d", m_nYear); + case MONTH: + throw new IllegalArgumentException("Alternate ISO time point format doesn't " + + "contain a month number."); + case DAY: + return String.format("%04d-%03d", m_nYear, m_nDoy); + case HOUR: + return String.format("%04d-%03dT%02d", m_nYear, m_nDoy, m_nHour); + case MINUTE: + return String.format("%04d-%03dT%02d:%02d", m_nYear, m_nDoy, m_nHour, m_nMinute); + case SECOND: + return String.format("%04d-%03dT%02d:%02d:%02d", m_nYear, m_nDoy, m_nHour, + m_nMinute, m_nSecond); + case MILLISEC: + // Let string.format handle rounding for me. + return String.format("%04d-%03dT%02d:%02d:%02d.%03.0f", m_nYear, m_nDoy, m_nHour, + m_nMinute, m_nSecond, m_nNanoSecond / 1000000.0); + case MICROSEC: + // Let string.format handle rounding for me. + return String.format("%04d-%03dT%02d:%02d:%02d.%06.0f", m_nYear, m_nDoy, m_nHour, + m_nMinute, m_nSecond, m_nNanoSecond / 1000.0); + case NANOSEC: + // Let string.format handle rounding for me. + return String.format("%04d-%03dT%02d:%02d:%02d.%09d", m_nYear, m_nDoy, m_nHour, + m_nMinute, m_nSecond, m_nNanoSecond); + } + return null; // added to make the compilier happy. + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Normalize date and time components for the Gregorian calendar ignoring leap seconds + * + * From Larry Granroth's original Das1 libs. + */ + private void normalize(){ + + // month is required input -- first adjust month + if( m_nMonth > 12 || m_nMonth < 1){ + // temporarily make month zero-based + m_nMonth--; + m_nYear += m_nMonth / 12; + m_nMonth %= 12; + if(m_nMonth < 0){ + m_nMonth += 12; + m_nYear--; + } + m_nMonth++; + } + + // index for leap year + int iLeap = TimeUtil.isLeapYear(m_nYear)?1:0; + + // day of year is output only -- calculate it + m_nDoy = TimeUtil.dayOffset[iLeap][m_nMonth] + m_nDom; + + // now adjust other items . . . + + // New addition, handle nanoseconds + if(m_nNanoSecond >= 1000000000 || m_nNanoSecond < 0){ + m_nSecond += m_nNanoSecond / 1000000000; + m_nNanoSecond = m_nNanoSecond % 1000000000; + if(m_nNanoSecond < 0){ + m_nNanoSecond += 1000000000; + m_nSecond--; + } + } + + // again, we're ignoring leap seconds + if( m_nSecond >= 60 || m_nSecond < 0){ + m_nMinute += m_nSecond / 60; + m_nSecond = m_nSecond % 60; + if(m_nSecond < 0){ + m_nSecond += 60; + m_nMinute--; + } + } + + if(m_nMinute >= 60 || m_nMinute < 0){ + m_nHour += m_nMinute / 60; + m_nMinute %= 60; + if(m_nMinute < 0){ + m_nMinute += 60; + m_nHour--; + } + } + + if(m_nHour >= 24 ||m_nHour < 0){ + m_nDoy += m_nHour / 24; + m_nHour %= 24; + if(m_nHour < 0){ + m_nHour += 24; + m_nDoy--; + } + } + + /* final adjustments for year and day of year */ + int ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + if(m_nDoy > ndays || m_nDoy < 1){ + while(m_nDoy > ndays){ + m_nYear++; + m_nDoy -= ndays; + ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + } + while(m_nDoy < 1){ + m_nYear--; + ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + m_nDoy += ndays; + } + } + + /* and finally convert day of year back to month and day */ + iLeap = TimeUtil.isLeapYear(m_nYear)?1:0; + while(m_nDoy <= TimeUtil.dayOffset[iLeap][m_nMonth]){ + m_nMonth--; + } + while(m_nDoy > TimeUtil.dayOffset[iLeap][m_nMonth + 1]){ + m_nMonth++; + } + m_nDom = m_nDoy - TimeUtil.dayOffset[iLeap][m_nMonth]; + } + + //////////////////////////////////////////////////////////////////////////////////// + + /** Set the year field. Use set() if you have multiple fields to set. */ + public void setYear(int nYear){ + m_nYear = nYear; + normalize(); + } + /** Set the month field. Use set() if you have multiple fields to set. */ + public void setMonth(int nMonth){ + m_nMonth = nMonth; + normalize(); + } + /** Set the day of month field. Use set() if you have multiple fields to set. */ + public void setDay(int nDay){ + m_nDom = nDay; + normalize(); + } + + /** Set the day of year, and recompute the month and day of month. + * @param nDoy the new day of year. + */ + public void setDayOfYear(int nDoy){ + m_nDoy = nDoy; + + /* final adjustments for year and day of year */ + int ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + if(m_nDoy > ndays || m_nDoy < 1){ + while(m_nDoy > ndays){ + m_nYear++; + m_nDoy -= ndays; + ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + } + while(m_nDoy < 1){ + m_nYear--; + ndays = TimeUtil.isLeapYear(m_nYear) ? 366 : 365; + m_nDoy += ndays; + } + } + + /* and finally convert day of year back to month and day */ + int iLeap = TimeUtil.isLeapYear(m_nYear)?1:0; + while(m_nDoy <= TimeUtil.dayOffset[iLeap][m_nMonth]){ + m_nMonth--; + } + while(m_nDoy > TimeUtil.dayOffset[iLeap][m_nMonth + 1]){ + m_nMonth++; + } + m_nDom = m_nDoy - TimeUtil.dayOffset[iLeap][m_nMonth]; + } + + /** Set the hour field. Use set() if you have multiple fields to set. */ + public void setHour(int nHour){ + m_nHour = nHour; + normalize(); + } + /** Set the minute field. Use set() if you have multiple fields to set. */ + public void setMinute(int nMinute){ + m_nMinute = nMinute; + normalize(); + } + /** Set the second field. Use set() if you have multiple fields to set. */ + public void setSecond(int nSecond){ + m_nSecond = nSecond; + normalize(); + } + /** Set the nanosecond field. Use set() if you have multiple fields to set. */ + public void setNanoSecond(long nNano){ + m_nNanoSecond = nNano; + normalize(); + } + + /** Set (upto) all fields of a calendar time + * + * @param lFields An array of 1 to 7 items whose values will be assigned to the + * year, month, day, hour, minute, second and nanosecond respectively. Also + * integers can be specified one at a time in var-args fashion + */ + public void set(int... lFields){ + + if(lFields.length < 1) return; + + // Cool case fall through (good idea Ed!) + switch(lFields.length){ + default: m_nNanoSecond = lFields[6]; + case 6: m_nSecond = lFields[5]; + case 5: m_nMinute = lFields[4]; + case 4: m_nHour = lFields[3]; + case 3: m_nDom = lFields[2]; + case 2: m_nMonth = lFields[1]; + case 1: m_nYear = lFields[0]; + } + + normalize(); + } + + ///////////////////////////////////////////////////////////////////////////////////// + + public int year(){return m_nYear;} + public int month(){return m_nMonth;} + public int day(){return m_nDom;} + public int dayOfYear(){ return m_nDoy; } + public int hour(){return m_nHour;} + public int minute(){return m_nMinute;} + public int second(){return m_nSecond;} + public int nanosecond(){return (int)m_nNanoSecond;} + + public int[] get(){ + return new int[]{m_nYear, m_nMonth, m_nDom, m_nHour, m_nMinute, m_nSecond, + (int)m_nNanoSecond}; + } + + + //////////////////////////////////////////////////////////////////////////////////// + + /** Used to step add to a calendar time by 1 or more integer units. */ + public enum Step { + YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, NANOSEC, HALF_YEAR, QUARTER, + MILLISEC, MICROSEC; + + public static Step HigerStep(Step step){ + switch(step){ + case DAY: return MONTH; + case HOUR: return DAY; + case MINUTE: return HOUR; + case SECOND: return MINUTE; + case MILLISEC: return SECOND; + case MICROSEC: return MILLISEC; + case NANOSEC: return MICROSEC; + default: return YEAR; + } + } + + public static Step LowerStep(Step step){ + switch(step){ + case YEAR: return MONTH; + case MONTH: return DAY; + case DAY: return HOUR; + case HOUR: return MINUTE; + case MINUTE: return SECOND; + case SECOND: return MILLISEC; + case MILLISEC: return MICROSEC; + default: return NANOSEC; + } + } + } + + /** A convenience method for handling multiple steps at once. + * + * @param steps An array of upto 7 items, each one will step succeedingly smaller + * time fields if present. So the array items are taken to be: + * [year, month, day, hour, minute, second, nanosecond]. + * + * @return A new calendar time stepped as specified. + */ + public CalendarTime step(int lSteps[]){ + CalendarTime ct = new CalendarTime(this); + + Step fields[] = {Step.YEAR, Step.MONTH, Step.DAY, Step.HOUR, Step.MINUTE, + Step.SECOND, Step.NANOSEC}; + for(int i = 0; i < 7; i++){ + if((lSteps.length > i)&&(lSteps[i] != 0)) + ct = ct.step(fields[i], lSteps[i]); + } + + return ct; + } + + //////////////////////////////////////////////////////////////////////////////////// + /** Introduced as a way to increase the efficiency of the time axis tick calculation. + * Step to the next higher ordinal. If the calendar time is already at the ordinal, + * then step by one unit. + * + * @param field The time field to change. Integers for this value are defined in + * TimeUtil + * @param steps number of positive or negative steps to take + * @return + */ + public CalendarTime step(Step field, int steps) { + + CalendarTime ct = new CalendarTime(this); + + if(steps == 0) return ct; + + // First change the relavent field + switch(field){ + case NANOSEC: ct.m_nNanoSecond += steps; break; + case MICROSEC: ct.m_nNanoSecond += 1000*steps; break; + case MILLISEC: ct.m_nNanoSecond += 1000000*steps; break; + case SECOND: ct.m_nSecond += steps; break; + case MINUTE: ct.m_nMinute += steps; break; + case HOUR: ct.m_nHour += steps; break; + case DAY: ct.m_nDom += steps; break; + case MONTH: ct.m_nMonth += steps; break; + case QUARTER: ct.m_nMonth += steps*3; break; + case HALF_YEAR: ct.m_nMonth += steps*6; break; + case YEAR: ct.m_nYear += steps*1; break; + default: + throw new IllegalArgumentException("Unknown time field designator: "+field); + } + + + // Handle zeroing out lower level fields (case fall throught can be handy) + switch(field){ + case YEAR: + ct.m_nMonth = 1; + case HALF_YEAR: + //Map months to a 0-1 half year scale + double dHalfYears = (ct.m_nMonth - 1)/6.0; + ct.m_nMonth = (((int)dHalfYears) * 6) + 1; //Truncates towards zero + case QUARTER: + //Map months to a 0-3 quarterly scale + double dQuarters = (ct.m_nMonth - 1)/3.0; + ct.m_nMonth = (((int)dQuarters) * 3) + 1; //Truncates towards zero + case MONTH: + ct.m_nDom = 1; + case DAY: + ct.m_nHour = 0; + case HOUR: + ct.m_nMinute = 0; + case MINUTE: + ct.m_nSecond = 0; + case MILLISEC: + ct.m_nNanoSecond = (ct.m_nNanoSecond / 1000000) * 1000000; + case MICROSEC: + ct.m_nNanoSecond = (ct.m_nNanoSecond / 1000) * 1000; + } + + ct.normalize(); + return ct; + } + + /** Special handler for changing the nanoseconds, as this field is a long */ + public CalendarTime stepNano(long steps) { + CalendarTime ct = new CalendarTime(this); + ct.m_nNanoSecond += steps; + ct.normalize(); + return ct; + } + + + /////////////////////////////////////////////////////////////////////////////////// + /** Get a time datum in us2000 units. + * + * @return A datum whose value is the number of milliseconds since midnight + * 2000-01-01, ignoring leap seconds. + */ + public Datum toDatum(){ + int jd = 367 * m_nYear - 7 * (m_nYear + (m_nMonth + 9) / 12) / 4 + - 3 * ((m_nYear + (m_nMonth - 9) / 7) / 100 + 1) / 4 + + 275 * m_nMonth / 9 + m_nDom + 1721029; + + double us2000 = (jd - 2451545) * 86400e6; // TODO: leap seconds + + return Datum.create(m_nHour*3600.0e6 + m_nMinute*60e6 + m_nSecond*1e6 + + m_nNanoSecond/1000 + us2000, Units.us2000); + } + + +} diff --git a/dasCore/src/org/das2/datum/Datum.java b/dasCore/src/org/das2/datum/Datum.java new file mode 100644 index 000000000..f88f28def --- /dev/null +++ b/dasCore/src/org/das2/datum/Datum.java @@ -0,0 +1,576 @@ +/* File: Datum.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import org.das2.datum.format.DatumFormatter; + + +/** + *

    A Datum is a number in the context of a Unit, for example "15 microseconds.". + * A Datum object has methods for formatting itself as a String, representing + * itsself as a double in the context of another Unit, and mathematical + * operators that allow simple calculations to be made at the physical quantities. + * Also a Datum's precision can be limited to improve formatting.

    + *

    + * @author jbf + */ +public class Datum implements Comparable { + + private Units units; + private Number value; + private double resolution; + private DatumFormatter formatter; + + /** + * class backing Datums with a double. + */ + public static class Double extends Datum { + + Double( Number value, Units units ) { + super( value, units, 0. ); + } + + Double( double value, Units units ) { + super( new java.lang.Double(value), units, 0. ); + } + Double( double value ) { + super( new java.lang.Double(value), Units.dimensionless, 0. ); + } + Double( double value, Units units, double resolution ) { + super( new java.lang.Double(value), units, units.getDatumFormatterFactory().defaultFormatter(), resolution ); + } + + } + + private Datum(Number value, Units units, double resolution ) { + this( value, units, units.getDatumFormatterFactory().defaultFormatter(), resolution ); + } + + private Datum(Number value, Units units, DatumFormatter formatter, double resolution ) { + if ( value==null ) throw new IllegalArgumentException("value is null"); + this.value = value; + this.units = units; + this.resolution= resolution; + this.formatter = formatter; + } + + /** + * returns the datum's double value. This protected method allows subclasses and classes + * within the package to peek at the double value. + * + * @return the double value of the datum in the context of its units. + */ + protected double doubleValue() { + return this.getValue().doubleValue(); + } + + /** + * returns a double representing the datum in the context of units. + * + * @param units the Units in which the double should be returned + * @return a double in the context of the provided units. + */ + public double doubleValue(Units units) { + if ( units!=getUnits() ) { + return getUnits().getConverter(units).convert(this.getValue()).doubleValue(); + } else { + return this.getValue().doubleValue(); + } + } + + /** + * returns the resolution (or precision) of the datum. This is metadata for the datum, used + * primarily to limit the number of decimal places displayed in a string representation, + * but operators like add and multiply will propogate errors through the calculation. + * + * @param units the Units in which the double resolution should be returned. Note + * the units must be convertable to this.getUnits().getOffsetUnits(). + * @return the double resolution of the datum in the context of units. + */ + public double getResolution( Units units ) { + Units offsetUnits= getUnits().getOffsetUnits(); + if ( units!=offsetUnits ) { + return offsetUnits.getConverter(units).convert(this.resolution); + } else { + return this.resolution; + } + } + + /** + * returns the datum's int value. This protected method allows subclasses and classes + * within the package to peek at the value as an integer. (The intent was that a + * Datum might be backed by an integer instead of a double, so that numerical + * round-off issues can be avoided.) + * + * @return the integer value of the datum in the context of its units. + */ + protected int intValue() { + return this.getValue().intValue(); + } + + /** + * returns a int representing the datum in the context of units. + * + * @param units the Units in which the int should be returned + * @return a double in the context of the provided units. + */ + public int intValue(Units units) { + if ( units!=getUnits() ) { + return getUnits().getConverter(units).convert(this.getValue()).intValue(); + } else { + return this.getValue().intValue(); + } + } + + /** + * returns the datum's units. For example, UT times might have the units + * Units.us2000. + * + * @return the datum's units. + */ + public Units getUnits() { + return this.units; + } + + /** + * returns the Number representing the datum's location in the space indentified by its units. + * This protected method allows subclasses and classes + * within the package to peek at the value. (The intent was that a + * Datum might be backed by an integer, float, or double, depending on the application.) + * @return a Number in the context of the provided units. + */ + protected Number getValue() { + return this.value; + } + + /** + * convenience method for checking to see if a datum is a fill datum. + * @return true if the value is fill as defined by the Datum's units. + */ + public boolean isFill() { + return getUnits().isFill(getValue()); + } + + /** + * returns a Datum whose value is the sum of this and datum, in this.getUnits(). + * @return a Datum that is the sum of the two values in this Datum's units. + * @param datum Datum to add, that is convertable to this.getUnits(). + */ + public Datum add( Datum datum ) { + Datum result= add( datum.getValue(), datum.getUnits() ); + result.resolution= Math.sqrt( datum.resolution * datum.resolution + this.resolution * this.resolution ); + return result; + } + + /** + * returns a Datum whose value is the sum of this and value in + * the context of units, in this.getUnits(). + * @param value a Number to add in the context of units. + * @param units units defining the context of value. There should be a converter from + * units to this Datum's units. + * @return value Datum that is the sum of the two values in this Datum's units. + */ + public Datum add( Number value, Units units ) { return getUnits().add( getValue(), value, units ); } + + /** + * returns a Datum whose value is the sum of this and value in + * the context of units, in this.getUnits(). + * @param d a Number to add in the context of units. + * @param units units defining the context of value. There should be a converter from + * units to this Datum's units. + * @return value Datum that is the sum of the two values in this Datum's units. + */ + public Datum add( double d, Units units ) { return add( new java.lang.Double(d), units ); } + + /** + * returns a Datum whose value is the difference of this and value. + * The returned Datum will have units according to the type of units subtracted. + * For example, "1979-01-02T00:00" - "1979-01-01T00:00" = "24 hours" (this datum's unit's offset units), + * while "1979-01-02T00:00" - "1 hour" = "1979-01-01T23:00" (this datum's units.) + * + * Note also the resolution of the result is calculated. + * + * @return a Datum that is the sum of the two values in this Datum's units. + * @param datum Datum to add, that is convertable to this.getUnits() or offset units. + */ + public Datum subtract( Datum datum ) { + Datum result= subtract( datum.getValue(), datum.getUnits() ); + result.resolution= Math.sqrt( datum.resolution * datum.resolution + this.resolution * this.resolution ); + return result; + } + + /** + * returns a Datum whose value is the difference of this and value in + * the context of units. + * The returned Datum will have units according to the type of units subtracted. + * For example, "1979-01-02T00:00" - "1979-01-01T00:00" = "24 hours" (this datum's unit's offset units), + * while "1979-01-02T00:00" - "1 hour" = "1979-01-01T23:00" (this datum's units.) + * + * @param a a Number to add in the context of units. + * @param units units defining the context of value. There should be a converter from + * units to this Datum's units or offset units. + * @return value Datum that is the difference of the two values in this Datum's units. + */ + public Datum subtract( Number a, Units units ) { + Datum result= getUnits().subtract( getValue(), a, units ); + return result; + } + + /** + * returns a Datum whose value is the difference of this and value in + * the context of units. + * The returned Datum will have units according to the type of units subtracted. + * For example, "1979-01-02T00:00" - "1979-01-01T00:00" = "24 hours" (this datum's unit's offset units), + * while "1979-01-02T00:00" - "1 hour" = "1979-01-01T23:00" (this datum's units.) + * + * @param d a Number to add in the context of units. + * @param units units defining the context of value. There should be a converter from + * units to this Datum's units or offset units. + * @return value Datum that is the difference of the two values in this Datum's units. + */ + public Datum subtract( double d, Units units ) { return subtract( new java.lang.Double(d), units ); } + + private static double relativeErrorMult( double x, double dx, double y, double dy ) { + return Math.sqrt( dx/x * dx/x + dy/y * dy/y ); + } + + /** + * divide this by the datum a. Currently, only division is only supported:

    +     *   between convertable units, resulting in a Units.dimensionless quantity, or
    +     *   by a Units.dimensionless quantity, and a datum with this datum's units is returned.
    + * This may change, as a generic SI units class is planned. + * + * @param a the datum divisor. + * @return the quotient. + */ + public Datum divide( Datum a ) { + Datum result= divide( a.getValue(), a.getUnits() ); + result.resolution= Math.abs( result.doubleValue() ) * relativeErrorMult( doubleValue(), resolution, a.doubleValue(), a.resolution ); + return result; + } + + /** + * divide this by the Number provided in the context of units. Currently, only division is only supported:
    +     *   between convertable units, resulting in a Units.dimensionless quantity, or
    +     *   by a Units.dimensionless quantity, and a datum with this datum's units is returned.
    + * This may change, as a generic SI units class is planned. + * @param a the magnitude of the divisor. + * @param units the units of the divisor. + * @return the quotient. + */ + public Datum divide( Number a, Units units ) { return getUnits().divide( getValue(), a, units ); } + + /** + * divide this by the dimensionless double. + * @param d the magnitude of the divisor. + * @return the quotient. + */ + public Datum divide( double d ) { return divide( new java.lang.Double(d), Units.dimensionless ); } + + /** + * multiply this by the datum a. Currently, only multiplication is only supported:
    +     *   by a dimensionless datum, or when this is dimensionless.
    +     * This may change, as a generic SI units class is planned.
    +     *
    +     * This should also throw an IllegalArgumentException if the units are LocationUnits (e.g. UT time), but doesn't.  This may
    +     * change.
    +     *   
    +     * @param a the datum to multiply
    +     * @return the product.
    +     */
    +    public Datum multiply( Datum a ) { 
    +        Datum result= multiply( a.getValue(), a.getUnits() );         
    +        result.resolution= result.doubleValue() * relativeErrorMult( doubleValue(), resolution, a.doubleValue(), a.resolution );
    +        return result;
    +    }
    +    
    +    /**
    +     * multiply this by the Number provided in the context of units.  Currently, only multiplication is only supported:
    +     *   by a dimensionless datum, or when this is dimensionless.
    +     * This may change, as a generic SI units class is planned.
    +     *
    +     * This should also throw an IllegalArgumentException if the units are LocationUnits (e.g. UT time), but doesn't.  This may
    +     * change.
    +     *
    +     * @param a the magnitude of the multiplier.
    +     * @param units the units of the multiplier.
    +     * @return the product.
    +     */
    +    public Datum multiply( Number a, Units units ) { return getUnits().multiply( getValue(), a, units ); }
    +    
    +    /**
    +     * multiply by a dimensionless number.
    +     *
    +     * This should also throw an IllegalArgumentException if the units are LocationUnits (e.g. UT time), but doesn't.  This may
    +     * change.
    +     *
    +     * @param d the multiplier.
    +     * @return the product.
    +     */
    +    public Datum multiply( double d ) {  return multiply( new java.lang.Double(d), Units.dimensionless ); }
    +    
    +    /**
    +     * creates an equivalent datum using a different unit.  For example,
    +     *  x= Datum.create( 5, Units.seconds );
    +     *  System.err.println( x.convertTo( Units.seconds ) );
    +     * 
    +     * @param units the new Datum's units
    +     * @throws java.lang.IllegalArgumentException if the datum cannot be converted to the given units.
    +     * @return a datum with the new units, that is equal to the original datum.
    +     */
    +    public Datum convertTo( Units units ) throws IllegalArgumentException {
    +        UnitsConverter muc= this.units.getConverter(units);
    +        Datum result= units.createDatum( muc.convert( this.getValue() ) );
    +        if ( this.resolution!=0. ) {
    +            muc= this.units.getOffsetUnits().getConverter(units.getOffsetUnits());
    +            result.resolution= muc.convert(this.resolution);
    +        }
    +        return result;
    +    }
    +    
    +    /**
    +     * returns a hashcode that is a function of the value and the units.
    +     * @return a hashcode for the datum
    +     */
    +    public int hashCode() {
    +        long bits = (long) getValue().hashCode();
    +        int doubleHash= (int)(bits ^ (bits >>> 32));
    +        int unitsHash= units.hashCode();
    +        return doubleHash ^ unitsHash;
    +    }
    +    
    +    /**
    +     * returns true if the two datums are equal.  That is, their double values are equal when converted to the same units.
    +     * @param a the Object to compare to.
    +     * @throws java.lang.IllegalArgumentException if the Object is not a datum or the units are not convertable.
    +     * @return true if the datums are equal.
    +     */
    +    public boolean equals( Object a ) throws IllegalArgumentException {
    +        return ( a!=null && (a instanceof Datum) && this.equals( (Datum)a ) );
    +    }
    +    
    +    /**
    +     * returns true if the two datums are equal.  That is, their double values are equal when converted to the same units.
    +     * @param a the datum to compare
    +     * @throws java.lang.IllegalArgumentException if the units are not convertable.
    +     * @return true if the datums are equal.
    +     */
    +    public boolean equals( Datum a ) throws IllegalArgumentException {
    +        return ( a!=null && this.getUnits().isConvertableTo( a.getUnits() ) && this.compareTo(a)==0 );
    +    }
    +    
    +    /**
    +     * returns true if this is less than a.
    +     * @param a a datum convertable to this Datum's units.
    +     * @throws java.lang.IllegalArgumentException if the two don't have convertable units.
    +     * @return true if this is less than a.
    +     */
    +    public boolean lt( Datum a ) throws IllegalArgumentException {
    +        return (this.compareTo(a)<0);
    +    }
    +    
    +    /**
    +     * returns true if this is greater than a.
    +     * @param a a datum convertable to this Datum's units.
    +     * @throws java.lang.IllegalArgumentException if the two don't have convertable units.
    +     * @return true if this is greater than a.
    +     */
    +    public boolean gt( Datum a ) throws IllegalArgumentException {
    +        return (this.compareTo(a)>0);
    +    }
    +    
    +    /**
    +     * returns true if this is less than or equal to a.
    +     * @param a a datum convertable to this Datum's units.
    +     * @throws java.lang.IllegalArgumentException if the two don't have convertable units.
    +     * @return true if this is less than or equal to a.
    +     */
    +    public boolean le( Datum a ) throws IllegalArgumentException {
    +        return (this.compareTo(a)<=0);
    +    }
    +    
    +    /**
    +     * returns true if this is greater than or equal to a.
    +     * @param a a datum convertable to this Datum's units.
    +     * @throws java.lang.IllegalArgumentException if the two don't have convertable units.
    +     * @return true if this is greater than or equal to a.
    +     */
    +    public boolean ge( Datum a ) throws IllegalArgumentException {
    +        return (this.compareTo(a)>=0);
    +    }
    +    
    +    /**
    +     * compare this to another datum.
    +     * @return an int <0 if this comes before Datum a in this Datum's units space,
    +     * 0 if they are equal, and >0 otherwise.
    +     * @param a the Datum to compare this datum to.
    +     * @throws IllegalArgumentException if a is not convertable to this Datum's
    +     * units.
    +     */
    +    public int compareTo( Datum a ) throws IllegalArgumentException {
    +        if ( this.units != a.units ) {
    +            a= a.convertTo(this.units);
    +        }
    +        
    +        double d= this.getValue().doubleValue() - a.getValue().doubleValue();
    +        
    +        if (d==0.) {
    +            return 0;
    +        } else if ( d<0. ) {
    +            return -1;
    +        } else {
    +            return 1;
    +        }
    +    }
    +    
    +    /**
    +     * returns true if the value is non NaN.
    +     * @return true if the value is non NaN.
    +     * @deprecated Use isFinite instead, or getValue.
    +     */
    +    public boolean isValid() {
    +        return (value.doubleValue()!=java.lang.Double.NaN);
    +    }
    +    
    +    /**
    +     * returns true if the value is finite, that is not INFINITY or NaN.
    +     * @return true if the value is finite, that is not INFINITY or NaN.
    +     */
    +    public boolean isFinite() {
    +        return ( value.doubleValue()!=java.lang.Double.POSITIVE_INFINITY )
    +        && ( value.doubleValue()!=java.lang.Double.NEGATIVE_INFINITY )
    +        && ( value.doubleValue()!=java.lang.Double.NaN );
    +    }
    +    
    +    /**
    +     * returns a human readable String representation of the Datum, which should also be parseable with
    +     * Units.parse()
    +     * @return a human readable String representation of the Datum, which should also be parseable with
    +     * Units.parse()
    +     */
    +    public String toString() {
    +        if (formatter==null) {
    +            return units.getDatumFormatterFactory().defaultFormatter().format(this);
    +        } else {
    +            return formatter.format(this);
    +        }
    +    }
    +    
    +    /**
    +     * convenient method for creating a dimensionless Datum with the given value.
    +     * @param value the magnitude of the datum.
    +     * @return a dimensionless Datum with the given value.
    +     */
    +    public static Datum create(double value) {
    +        return Units.dimensionless.createDatum(value);
    +    }
    +    
    +    /**
    +     * creates a datum with the given units and value, for example,
    +     * Datum.create( 54, Units.milliseconds )
    +     * @param value the magnitude of the datum.
    +     * @param units the units of the datum.
    +     * @return a Datum with the given units and value.
    +     */
    +    public static Datum create( double value, Units units ) {
    +        return units.createDatum( value );
    +    }
    +    
    +    /**
    +     * Returns a Datum with a specific DatumFormatter attached to
    +     * it.  This was was used to limit resolution before limited resolution
    +     * Datums were introduced.
    +     *
    +     * @param value the magnitude of the datum.
    +     * @param units the units of the datum.
    +     * @param formatter the DatumFormatter that should be used to format this datum, which will be
    +     *   returned by getFormatter().
    +     * @return a Datum with the given units and value, that should return the given formatter when asked.  
    +     */
    +    public static Datum create( double value, Units units, DatumFormatter formatter ) {
    +        Datum result= create( value, units);
    +        result.formatter= formatter;
    +        return result;
    +    }
    +    
    +    /**
    +     * Returns a Datum with the given value and limited to the given resolution.
    +     * When formatted, the formatter should use this resolution to limit the 
    +     * precision displayed.
    +     * @param value the magnitude of the datum, or value to be interpreted in the context of units.
    +     * @param units the units of the datum.
    +     * @param resolution the limit to which the datum's precision is known.
    +     * @return a Datum with the given units and value.
    +     */
    +    public static Datum create( double value, Units units, double resolution ) {
    +        Datum result= units.createDatum( value, resolution );
    +        result.formatter= units.getDatumFormatterFactory().defaultFormatter();
    +        return result;
    +    }
    +    
    +    /**
    +     * Returns a Datum with the given value and limited to the given resolution.
    +     * When formatted, the formatter should use this resolution to limit the 
    +     * precision displayed.
    +     * @param value the magnitude of the datum, or value to be interpreted in the context of units.
    +     * @param units the units of the datum.
    +     * @param resolution the limit to which the datum's precision is known.
    +     * @param formatter the DatumFormatter that should be used to format this datum, which will be
    +     *   returned by getFormatter().
    +     * @return a Datum with the given units and value.
    +     */
    +    public static Datum create( double value, Units units, double resolution, DatumFormatter formatter ) {
    +        Datum result= units.createDatum( value, resolution );
    +        result.formatter= formatter;
    +        return result;
    +    }
    +    
    +    /**
    +     * creates a dimensionless datum backed by an int.
    +     * @return a dimensionless Datum with the given value.
    +     * @param value the magnitude of the dimensionless datum.
    +     */
    +    public static Datum create( int value ) {
    +        return Units.dimensionless.createDatum( value );
    +    }
    +    
    +    /**
    +     * creates a datum backed by an int with the given units.
    +     * @return a Datum with the given units and value.
    +     * @param value the magnitude of the datum
    +     * @param units the units of the datum
    +     */
    +    public static Datum create( int value, Units units ) {
    +        return units.createDatum( value );
    +    }
    +    
    +    /**
    +     * returns a formatter suitable for formatting this datum as a string.
    +     * @return a formatter to be used to format this Datum into a String.
    +     */
    +    public DatumFormatter getFormatter() {
    +        return this.formatter;
    +    }
    +    
    +}
    diff --git a/dasCore/src/org/das2/datum/DatumRange.java b/dasCore/src/org/das2/datum/DatumRange.java
    new file mode 100644
    index 000000000..6e57b9b9f
    --- /dev/null
    +++ b/dasCore/src/org/das2/datum/DatumRange.java
    @@ -0,0 +1,293 @@
    +package org.das2.datum;
    +
    +/**
    + * A DatumRange is provided as a means to carry an ordered pair of Datums
    + * representing an interval.  This sort of data structure comes up often in
    + * processing, and it's useful to define once and get the operators
    + * implemented correctly.  Consider using this object whenever you see
    + * pairs of Datums in interfaces and codes (e.g. tbegin,tend), they are probably
    + * a DatumRange!
    + */
    +
    +public class DatumRange implements Comparable {
    +    
    +    Datum s1;
    +    Datum s2;
    +    
    +    /**
    +     * Creates valid DatumRange from two Datums.
    +     * @param s1 the start or smaller value of the range.
    +     * @param s2 the stop or bigger value of the range.
    +     */
    +    public DatumRange(Datum s1, Datum s2) {
    +        if ( s2.lt(s1) ) {
    +            throw new IllegalArgumentException( "s2 [0,1).include(2)->[0,2)  (note this is exclusive of 2 since it's the end).
    +     * [0,1).include(-1)->[-1,1).
    +     * [0,1).include(0.5)->[0,1]  (and returns the same DatumRange object)
    +     * 
    + * Also, including a fill Datum returns the same DatumRange as well. + */ + public DatumRange include(Datum d) { + if ( d.isFill() ) return this; + if ( this.contains(d) || this.max().equals(d) ) return this; + Datum min= ( this.min().le(d) ? this.min() : d ); + Datum max= ( this.max().ge(d) ? this.max() : d ); + return new DatumRange( min, max ); + } + + /** + * return the units of the DatumRange. + */ + public Units getUnits() { + return this.s1.getUnits(); + } + + /** + * creates a new DatumRange object with the range specified in the space + * identified by units. Note that min must be <= max. + */ + public static DatumRange newDatumRange(double min, double max, Units units) { + return new DatumRange( Datum.create(min,units), Datum.create(max,units) ); + } + +} + diff --git a/dasCore/src/org/das2/datum/DatumRangeUtil.java b/dasCore/src/org/das2/datum/DatumRangeUtil.java new file mode 100644 index 000000000..04a2fc6ba --- /dev/null +++ b/dasCore/src/org/das2/datum/DatumRangeUtil.java @@ -0,0 +1,1067 @@ +/* + * DatumRangeUtil.java + * + * Created on September 16, 2004, 2:35 PM + */ + +package org.das2.datum; + +import org.das2.util.DasMath; +import org.das2.system.DasLogger; +import java.text.*; +import java.util.*; +import java.util.logging.*; +import java.util.regex.*; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.TimeDatumFormatter; + +/** + * + * @author Jeremy + */ +public class DatumRangeUtil { + + private static final int DATEFORMAT_USA= 1; + private static final int DATEFORMAT_EUROPE= 0; + private static final int DATEFORMAT_YYYY_DDD= 2; + + private static final boolean DEBUG=false; + + + // this pattern is always a year + private static boolean isYear( String string ) { + return string.length()==4 && Pattern.matches("\\d{4}",string); + } + + // this pattern is always a day of year + private static boolean isDayOfYear( String string ) { + return string.length()==3 && Pattern.matches("\\d{3}",string); + } + + private static int monthNumber( String string ) throws ParseException { + if ( Pattern.matches("\\d+", string) ) { + return parseInt(string); + } else { + int month= monthNameNumber(string); + if ( month==-1 ) throw new ParseException("hoping for month at, got "+string, 0); + return month; + } + } + + private static int monthNameNumber( String string ) { + if ( string.length() < 3 ) return -1; + String[] monthNames= new String[] { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; + string= string.substring(0,3).toLowerCase(); + int r=-1; + for ( int i=0; i 100 ) { + return year; + } else { + if ( year < 70 ) { + return 2000+year; + } else { + return 1900+year; + } + } + } + + public static class DateDescriptor { + String date; + String year; + String month; + String day; + String delim; + int dateformat; + } + + private int stregex( String string, String regex ) { + Matcher matcher= Pattern.compile(regex).matcher(string); + if ( matcher.find() ) { + return matcher.start(); + } else { + return -1; + } + } + + private static void caldat( int julday, DateDescriptor dateDescriptor ) { + int jalpha, j1, j2, j3, j4, j5; + + jalpha = (int)(((double)(julday - 1867216) - 0.25)/36524.25); + j1 = julday + 1 + jalpha - jalpha/4; + j2 = j1 + 1524; + j3 = 6680 + (int)(((j2-2439870)-122.1)/365.25); + j4 = 365*j3 + j3/4; + j5 = (int)((j2-j4)/30.6001); + + int day = j2 - j4 - (int)(30.6001*j5); + int month = j5-1; + month = ((month - 1) % 12) + 1; + int year = j3 - 4715; + year = year - (month > 2 ? 1 : 0); + year = year - (year <= 0 ? 1 : 0); + + dateDescriptor.day= ""+day; + dateDescriptor.month= ""+month; + dateDescriptor.year= ""+year; + + } + + private static int julday( int month, int day, int year ) { + int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - + 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + + 275 * month / 9 + day + 1721029; + return jd; + } + + private static void printGroups( Matcher matcher ) { + for ( int i=0; i<=matcher.groupCount(); i++ ) { + System.out.println(" "+i+": "+matcher.group(i) ); + } + System.out.println(" " ); + } + + private static int parseInt( String s ) throws ParseException { + try { + return Integer.parseInt(s); + } catch ( NumberFormatException e ) { + throw new ParseException( "failed attempt to parse int in "+s, 0 ); + } + } + + /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + //;; + //;; papco_parse_timerange, string -> timeRange + //;; + //;; parses a timerange from a string. Valid strings include: + // ;; "2001" + // ;; "2001-2004" + // ;; "2003-004" + // ;; "12/31/2001" + // ;; "Jan 2001" + // ;; "Jan-Feb 2004" + // ;; "2004-004 - 2003-007" + // ;; "JAN to MAR 2004" + // ;; "2004/feb-2004/mar" + // ;; "2004/004-008 + // ;; "1979-03-01T20:58:45.000Z span 17.5 s" + // ;; keeps track of format(e.g. %Y-%j) for debugging, and perhaps to reserialize + // ;; + // ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // if an element is trivially identifiable, as in "mar", then it is required + // that the corresponding range element be of the same format or not specified. + */ + + static class TimeRangeParser { + String token; + String delim=""; + + String string; + int ipos; + + final int YEAR=0; + final int MONTH=1; + final int DAY=2; + final int HOUR=3; + final int MINUTE=4; + final int SECOND=5; + final int NANO=6; + + final int STATE_OPEN=89; + final int STATE_TS1TIME=90; + final int STATE_TS2TIME=91; + + int state= STATE_OPEN; + + String delimRegEx= "\\s|-|/|\\.|:|to|through|span|T|Z|\u2013|,"; + Pattern delimPattern= Pattern.compile( delimRegEx ); + int[] ts1= new int[] { -1, -1, -1, -1, -1, -1, -1 }; + int[] ts2= new int[] { -1, -1, -1, -1, -1, -1, -1 }; + int[] ts= null; + + boolean beforeTo; + + private Pattern yyyymmddPattern= Pattern.compile("((\\d{4})(\\d{2})(\\d{2}))( |to|t|-|$)"); + + /* groups= group numbers: { year, month, day, delim } (0 is all) */ + private boolean tryPattern( Pattern regex, String string, int[] groups, DateDescriptor dateDescriptor ) throws ParseException { + Matcher matcher= regex.matcher( string.toLowerCase() ); + if ( matcher.find() && matcher.start()==0 ) { + // printGroups(matcher); + int posDate= matcher.start(); + int length= matcher.end()-matcher.start(); + dateDescriptor.delim= matcher.group(groups[3]); + dateDescriptor.date= string.substring( matcher.start(), matcher.end()-dateDescriptor.delim.length() ); + String month; + String day; + String year; + dateDescriptor.day= matcher.group(groups[2]); + dateDescriptor.month= matcher.group(groups[1]); + dateDescriptor.year= matcher.group(groups[0]); + return true; + } else { + return false; + } + } + + public boolean isTime( String string, int[] timearr ) throws ParseException { + Matcher m; + Pattern hhmmssmmPattern= Pattern.compile( "(\\d+):(\\d\\d+):(\\d\\d+).(\\d+) )" ); + Pattern hhmmssPattern= Pattern.compile( "(\\d+):(\\d\\d+):(\\d\\d+)" ); + Pattern hhmmPattern= Pattern.compile( "(\\d+):(\\d\\d+)" ); + Pattern hhPattern= Pattern.compile( "(\\d+):" ); + + if ( (m=hhmmssmmPattern.matcher(string)).matches() ) { + timearr[HOUR]= Integer.parseInt( m.group(1) ); + timearr[MINUTE]= Integer.parseInt( m.group(2) ); + timearr[SECOND]= Integer.parseInt( m.group(3) ); + timearr[NANO]= (int)( Integer.parseInt( m.group(4) ) * ( 100000 / DasMath.exp10( m.group(4).length() ) )); + throw new RuntimeException("working on this"); + } else if (( m=hhmmssPattern.matcher(string)).matches() ) { + } else if (( m=hhmmPattern.matcher(string)).matches() ) { + } else if (( m=hhPattern.matcher(string)).matches() ) { + } + return false; + } + + public boolean isDate( String string, DateDescriptor dateDescriptor ) throws ParseException { + // this is introduced because mm/dd/yy is so ambiguous, the parser + // has trouble with these dates. Check for these as a group. + + if ( string.length()<6 ) return false; + + int[] groups; + String dateDelimRegex= "( |to|t|-)"; + String yearRegex= "(\\d{2}(\\d{2})?)"; // t lower case because tryPattern folds case + + if ( tryPattern( yyyymmddPattern, string, new int[] { 2,3,4,5 }, dateDescriptor ) ) { + dateDescriptor.dateformat= DATEFORMAT_USA; + return true; + } + + String delim; + + String delims="(/|\\.|-| )"; + Matcher matcher= Pattern.compile(delims).matcher(string); + + if ( matcher.find() ) { + int posDelim= matcher.start(); + delim= string.substring(matcher.start(),matcher.end()); + } else { + return false; + } + + if ( delim.equals(".") ) { + delim="\\."; + } + + String monthNameRegex= "(jan[a-z]*|feb[a-z]*|mar[a-z]*|apr[a-z]*|may|june?|july?|aug[a-z]*|sep[a-z]*|oct[a-z]*|nov[a-z]*|dec[a-z]*)"; + String monthRegex= "((\\d?\\d)|"+monthNameRegex+")"; + String dayRegex= "(\\d?\\d)"; + + String euroDateRegex; + + if ( delim.equals("\\.") ) { + euroDateRegex= "(" + dayRegex + delim + monthRegex + delim + yearRegex + dateDelimRegex + ")"; + groups= new int [] { 6, 3, 2, 8 }; + } else { + euroDateRegex= "(" + dayRegex + delim + monthNameRegex + delim + yearRegex + dateDelimRegex + ")"; + groups= new int [] { 4, 3, 2, 6 }; + } + if ( tryPattern( Pattern.compile( euroDateRegex ), string, groups, dateDescriptor ) ) { + dateDescriptor.dateformat= DATEFORMAT_EUROPE; + return true; + } + + String usaDateRegex= monthRegex + delim + dayRegex + delim + yearRegex + dateDelimRegex ; + if ( tryPattern( Pattern.compile( usaDateRegex ), string, new int[] { 5,1,4,7 }, dateDescriptor ) ) { + dateDescriptor.dateformat= DATEFORMAT_USA; + return true; + } + + // only works for four-digit years + String lastDateRegex= "(\\d{4})" + delim + monthRegex + delim + dayRegex + dateDelimRegex; + if ( tryPattern( Pattern.compile( lastDateRegex ), string, new int[] { 1,2,5,6 }, dateDescriptor ) ) { + dateDescriptor.dateformat= DATEFORMAT_USA; + return true; + } + + String doyRegex= "(\\d{3})"; + String dateRegex= doyRegex+"(-|/)" + yearRegex + dateDelimRegex; + + if ( tryPattern( Pattern.compile( dateRegex ), string, new int[] { 3,1,1,5 }, dateDescriptor ) ) { + int doy= parseInt(dateDescriptor.day); + if ( doy>366 ) return false; + int year= parseInt(dateDescriptor.year); + caldat( julday( 12, 31, year-1 ) + doy, dateDescriptor ); + dateDescriptor.dateformat= DATEFORMAT_YYYY_DDD; + + return true; + } + + dateRegex= yearRegex +"(-|/)" + doyRegex + dateDelimRegex; + if ( tryPattern( Pattern.compile( dateRegex ), string, new int[] { 1,4,4,5 }, dateDescriptor ) ) { + int doy= parseInt(dateDescriptor.day); + if ( doy>366 ) return false; + int year= parseInt(dateDescriptor.year); + caldat( julday( 12, 31, year-1 ) + doy, dateDescriptor ); + dateDescriptor.dateformat= DATEFORMAT_YYYY_DDD; + + return true; + } + return false; + } + + + private void nextToken( ) { + Matcher matcher= delimPattern.matcher( string.substring(ipos) ); + if ( matcher.find() ) { + int r= matcher.start(); + int length= matcher.end()-matcher.start(); + token= string.substring( ipos, ipos+r ); + delim= string.substring( ipos+r, ipos+r+length ); + ipos= ipos + r + length; + } else { + token= string.substring(ipos); + delim= ""; + ipos= string.length(); + } + } + + private void setBeforeTo( boolean v ) { + beforeTo= v; + if ( beforeTo ) { + ts= ts1; + } else { + ts= ts2; + } + } + + /* identify and make the "to" delimiter unambiguous */ + public String normalizeTo( String s ) throws ParseException { + + int minusCount= 0; + for ( int i=0; i2 ) { + result= ss[0]; + for ( int i=1; i 0 ) { + ArrayList unload; + String formatUn; + int idx=0; + + if ( beforeToUnresolved.size() < afterToUnresolved.size() ) { + if ( beforeToUnresolved.size()>0 ) { + for ( int i=0; i0 ) { + for ( int i=0; i=0; i-- ) { + while ( ts[lsd]!=-1 && lsd>0 ) lsd--; + if ( ts[lsd]!=-1 ) { + throw new ParseException( "can't resolve these tokens in \""+stringIn+"\": "+unload+ " ("+format+")", 0 ); + } + ts[lsd]= parseInt((String)unload.get(i)); + String[] s= format.split(formatUn+(i+1)); + format= s[0]+formatCodes[lsd]+s[1]; + } + + } // unresolved entities + + { + StringBuffer stringBuffer= new StringBuffer("ts1: "); + for ( int i=0; i<7; i++ ) stringBuffer.append(""+ts1[i]+" "); + logger.fine( stringBuffer.toString() ); + stringBuffer= new StringBuffer("ts2: "); + for ( int i=0; i<7; i++ ) stringBuffer.append(""+ts2[i]+" "); + logger.fine( stringBuffer.toString() ); + logger.fine( format ); + } + + /* contextual fill. Copy over digits that were specified in one time but + * not the other. + */ + for ( int i=YEAR; i<=DAY; i++ ) { + if ( ts2[i] == -1 && ts1[i] != -1 ) ts2[i]= ts1[i]; + if ( ts1[i] == -1 && ts2[i] != -1 ) ts1[i]= ts2[i]; + } + + int i= NANO; + int[] implicit_timearr= new int[] { -1, 1, 1, 0, 0, 0, 0 }; + int ts1lsd= -1; + int ts2lsd= -1; + while (i>=0) { + if ( ts2[i] != -1 && ts2lsd == -1 ) ts2lsd=i; + if ( ts2lsd == -1 ) ts2[i]= implicit_timearr[i]; + if ( ts2[i] == -1 && ts2lsd != -1 ) { + throw new ParseException("not specified in stop time: "+digitIdentifiers[i]+" in "+stringIn+" ("+format+")",ipos); + } + if ( ts1[i] != -1 && ts1lsd == -1 ) ts1lsd=i; + if ( ts1lsd == -1 ) ts1[i]= implicit_timearr[i]; + if ( ts1[i] == -1 && ts1lsd != -1 ) { + throw new ParseException("not specified in start time:"+digitIdentifiers[i]+" in "+stringIn+" ("+format+")",ipos); + } + i= i-1; + } + + + if ( ts1lsd != ts2lsd && ( ts1lsd3; idigit-- ) { + if ( arr[idigit]>0 ) break; + } + stopRes= Math.max( stopRes, idigit ); + } + + int[] arr= TimeUtil.toTimeArray(time); + if ( stopRes>3 ) { + timeString+= ( arr[4] < 10 ? "0" : "" ) + arr[4]; + if ( stopRes>4 ) { + int second= arr[5]; + timeString+=":"+ ( second < 10 ? "0" : "" ) + second; + if ( stopRes>5 ) { + int millis= arr[6]; + DecimalFormat nf= new DecimalFormat("000"); + timeString+="."+nf.format(millis); + if ( stopRes>6 ) { + int micros= arr[7]; + timeString+=nf.format(micros); + } + } + } + } + + return timeString; + } + + public static String formatTimeRange( DatumRange self ) { + + String[] monthStr= new String[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + double seconds= self.width().doubleValue(Units.seconds); + + CalendarTime ts1= new CalendarTime(self.min()); + CalendarTime ts2= new CalendarTime(self.max()); + + // ts1= [ year1, month1, dom1, hour1, minute1, second1, nanos1 ] + // ts2= [ year2, month2, dom2, hour2, minute2, second2, nanos2 ] + + boolean isMidnight1= TimeUtil.getSecondsSinceMidnight( self.min() ) == 0.; + boolean isMidnight2= TimeUtil.getSecondsSinceMidnight( self.max() ) == 0.; + + boolean isMonthBoundry1= isMidnight1 && ts1.m_nDom == 1; + boolean isMonthBoundry2= isMidnight2 && ts2.m_nDom == 1; + + boolean isYearBoundry1= isMonthBoundry1 && ts1.m_nMonth == 1; + boolean isYearBoundry2= isMonthBoundry2 && ts2.m_nMonth == 1; + + //String toDelim= " \u2013 "; + String toDelim= " through "; + if ( isYearBoundry1 && isYearBoundry2 ) { // no need to indicate month + if ( ts2.m_nYear-ts1.m_nYear == 1 ) { + return "" + ts1.m_nYear; + } else { + return "" + ts1.m_nYear + toDelim + (ts2.m_nYear-1); + } + } else if ( isMonthBoundry1 && isMonthBoundry2 ) { // no need to indicate day of month + if ( ts2.m_nMonth == 1 ) { + ts2.m_nMonth=13; + ts2.m_nYear--; + } + if ( ts2.m_nYear == ts1.m_nYear ) { + if ( ts2.m_nMonth-ts1.m_nMonth == 1 ) { + return monthStr[ts1.m_nMonth-1] + " " + ts1.m_nYear; + } else { + return monthStr[ts1.m_nMonth-1]+ toDelim + monthStr[ts2.m_nMonth-1-1] + " " + ts1.m_nYear; + } + } else { + return monthStr[ts1.m_nMonth-1] + " " + ts1.m_nYear + toDelim + + monthStr[ts2.m_nMonth-1-1] + " " + ts2.m_nYear; + } + } + + if ( isMidnight1 && isMidnight2 ) { // no need to indicate HH:MM + if ( TimeUtil.getJulianDay( self.max() ) - TimeUtil.getJulianDay( self.min() ) == 1 ) { + return TimeDatumFormatter.DAYS.format( self.min() ); + } else { + Datum endtime= self.max().subtract( Datum.create( 1, Units.days ) ); + return TimeDatumFormatter.DAYS.format( self.min() ) + toDelim + + TimeDatumFormatter.DAYS.format( endtime ); + } + + } else { + DatumFormatter timeOfDayFormatter; + + if ( seconds<1. ) timeOfDayFormatter= TimeDatumFormatter.MILLISECONDS; + else if ( seconds<60. ) timeOfDayFormatter= TimeDatumFormatter.MILLISECONDS; + else if ( seconds<3600. ) timeOfDayFormatter= TimeDatumFormatter.SECONDS; + else timeOfDayFormatter= TimeDatumFormatter.MINUTES; + + int maxDay= TimeUtil.getJulianDay(self.max()); + if ( TimeUtil.getSecondsSinceMidnight(self.max())==0 ) maxDay--; // want to have 24:00, not 00:00 + if ( maxDay== TimeUtil.getJulianDay(self.min()) ) { + return TimeDatumFormatter.DAYS.format(self.min()) + + " " + efficientTime( self.min(), self.max(), self ) + + " to " + efficientTime( self.max(), self.min(), self ); + } else { + String t1str= efficientTime( self.min(), self.max(), self ); + String t2str= efficientTime( self.max(), self.min(), self ); + return TimeDatumFormatter.DAYS.format( self.min() ) + " " + t1str + + " to " + TimeDatumFormatter.DAYS.format( self.max() ) + " " + t2str; + } + } + } + + /** + * return a list of DatumRanges that together cover the space identified + * by bounds. The list should contain one DatumRange that is equal to + * element, which should define the phase and period of the list elements. + * For example, + *
     DatumRange bounds= DatumRangeUtil.parseTimeRangeValid( '2006' );
    +     * DatumRange first= DatumRangeUtil.parseTimeRangeValid( 'Jan 2006' );
    +     * List list= generateList( bounds, first );
    + * Note the procedure calls element.previous until bound.min() is contained, + * then calls bound.max until bound.max() is contained. + * + * @param bounds range to be covered. + * @param element range defining the width and phase of each list DatumRange. + * + */ + public static List generateList( DatumRange bounds, DatumRange element ) { + + ArrayList result= new ArrayList(); + DatumRange dr= element; + while ( dr.max().gt(bounds.min()) ) { + result.add(0,dr); + dr= dr.previous(); + } + dr= element.next(); + while( dr.min().lt(bounds.max() ) ) { + result.add(dr); + dr= dr.next(); + } + return result; + } + + + public static DatumRange newDimensionless(double lower, double upper) { + return new DatumRange( Datum.create(lower), Datum.create(upper) ); + } + + public static DatumRange parseDatumRange( String str, Units units ) throws ParseException { + if ( units instanceof TimeLocationUnits ) { + return parseTimeRange( str ); + } else { + // consider Patterns -- dash not handled because of negative sign. + String[] ss= str.split("to"); + if ( ss.length==1 ) { + ss= str.split("\u2013"); + } + if ( ss.length != 2 ) { + if ( ss.length==3 ) { + ss[0]= "-"+ss[1]; + ss[1]= ss[2]; + } else { + throw new IllegalArgumentException("failed to parse: "+str); + } + } + + // TODO: handle "124.0 to 140.0 kHz" when units= Units.hertz + Datum d2; + try { + d2= DatumUtil.parse(ss[1]); + if ( d2.getUnits()==Units.dimensionless ) d2= units.parse( ss[1] ); + } catch ( ParseException e ) { + d2= units.parse( ss[1] ); + } + Datum d1= d2.getUnits().parse( ss[0] ); + + if ( d1.getUnits().isConvertableTo(units) ) { + return new DatumRange( d1.convertTo(units), d2.convertTo(units) ); + } else { + throw new ParseException( "Can't convert parsed unit ("+d1.getUnits()+") to "+units, 0 ); + } + } + } + + public static DatumRange parseDatumRange( String str, DatumRange orig ) throws ParseException { + return parseDatumRange( str, orig.getUnits() ); + } + + /** + * returns DatumRange relative to this, where 0. is the minimum, and 1. is the maximum. + * For example rescale(1,2) is scanNext, rescale(0.5,1.5) is zoomOut. + * @param dr a DatumRange with nonzero width. + * @param min the new min normalized with respect to this range. 0. is this range's min, 1 is this range's max, 0 is + * min-width. + * @param max the new max with normalized wrt this range. 0. is this range's min, 1 is this range's max, 0 is + * min-width. + * @return new DatumRange. + */ + public static DatumRange rescale( DatumRange dr, double min, double max ) { + Datum w= dr.width(); + if ( !w.isFinite() ) { + throw new RuntimeException("width is not finite"); + } + if ( w.doubleValue( w.getUnits() )==0. ) { + // condition that might cause an infinate loop! For now let's check for this and throw RuntimeException. + throw new RuntimeException("width is zero!"); + } + return new DatumRange( dr.min().add( w.multiply(min) ), dr.min().add( w.multiply(max) ) ); + } + + /** + * returns DatumRange relative to this, where 0. is the minimum, and 1. is the maximum, but the + * scaling is done in the log space. + * For example, rescaleLog( [0.1,1.0], -1, 2 )-> [ 0.01, 10.0 ] + * @param dr a DatumRange with nonzero width. + * @param min the new min normalized with respect to this range. 0. is this range's min, 1 is this range's max, 0 is + * min-width. + * @param max the new max with normalized wrt this range. 0. is this range's min, 1 is this range's max, 0 is + * min-width. + * @return new DatumRange. + */ + public static DatumRange rescaleLog( DatumRange dr, double min, double max ) { + Units u= dr.getUnits(); + double s1= DasMath.log10( dr.min().doubleValue(u) ); + double s2= DasMath.log10( dr.max().doubleValue(u) ); + double w= s2 - s1; + if ( w==0. ) { + // condition that might cause an infinate loop! For now let's check for this and throw RuntimeException. + throw new RuntimeException("width is zero!"); + } + s2= DasMath.exp10( s1 + max * w ); // danger + s1= DasMath.exp10( s1 + min * w ); + return new DatumRange( s1, s2, u ); + } + + /** + * returns the position within dr, where 0. is the dr.min(), and 1. is dr.max() + * @param dr a datum range with non-zero width. + * @param d a datum to normalize with respect to the range. + * @return a double indicating the normalized datum. + */ + public static double normalize( DatumRange dr, Datum d ) { + return d.subtract(dr.min()).divide(dr.width()).doubleValue(Units.dimensionless); + } + + /** + * returns the position within dr, where 0. is the dr.min(), and 1. is dr.max() + * @param dr a datum range with non-zero width. + * @param d a datum to normalize with respect to the range. + * @return a double indicating the normalized datum. + */ + public static double normalizeLog( DatumRange dr, Datum d ) { + Units u= dr.getUnits(); + double d0= Math.log( dr.min().doubleValue( u ) ); + double d1= Math.log( dr.max().doubleValue( u ) ); + double dd= Math.log( d.doubleValue( u ) ); + return (dd-d0) / ( d1-d0 ); + } + + /** + * Like DatumRange.intesects, but returns a zero-width range when the two do + * not intersect. When they do not intersect, the min or max of the first range + * is returned, depending on whether or not the second range is above or below + * the first range. Often this allows for simpler code. + * @see DatumRange.intersection. + */ + public static DatumRange sloppyIntersection( DatumRange range, DatumRange include ) { + Units units= range.getUnits(); + double s11= range.min().doubleValue(units); + double s12= include.min().doubleValue(units); + double s21= range.max().doubleValue(units); + if ( range.intersects(include) ) { + double s1= Math.max( s11, s12 ); + double s22= include.max().doubleValue(units); + double s2= Math.min( s21, s22 ); + return new DatumRange( s1, s2, units ); + } else { + if ( s11 + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import org.das2.system.DasLogger; +import org.das2.util.DasMath; +import java.text.*; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.DatumFormatterFactory; +import org.das2.datum.format.DefaultDatumFormatterFactory; +import org.das2.datum.format.EnumerationDatumFormatterFactory; +import org.das2.datum.format.ExponentialDatumFormatter; +import org.das2.datum.format.TimeDatumFormatter; + +/** + * + * @author Edward West + */ +public final class DatumUtil { + + /** Creates a new instance of DatumUtil */ + private DatumUtil() { + } + + public static DatumFormatter bestFormatter( DatumVector datums ) { + double[] array; + Units units; + + if ( datums.getUnits() instanceof EnumerationUnits ) { + return EnumerationDatumFormatterFactory.getInstance().defaultFormatter(); + } + + if ( datums.getUnits() instanceof TimeLocationUnits ) { + Datum t1= datums.get(0); + int nticks= datums.getLength(); + Datum t2= datums.get(nticks-1); + return DatumUtil.bestTimeFormatter(t1,t2,nticks-1); + } + + if ( datums.getUnits() instanceof LocationUnits ) { + array= new double[ datums.getLength() ]; + units= ((LocationUnits)datums.get(0).getUnits()).getOffsetUnits(); + array[0]= 0.; + for ( int i=1; i(gcd*0.1) ) { // don't look at fuzzy zero + int ee= (int)Math.floor(0.05+DasMath.log10(Math.abs(d))); + if ( ee(discernable*0.1) ) { // don't look at fuzzy zero + int ee= (int)Math.floor(0.05+DasMath.log10(Math.abs(d))); + if ( ee 60 ) { + return DefaultDatumFormatterFactory.getInstance().defaultFormatter(); + } else if ( smallestExp < -3 || smallestExp > 3 ) { + return new ExponentialDatumFormatter( smallestExp - (-1*fracDigits) +1 , smallestExp ); + } else { + int nFraction= -1 * (int)Math.floor(0.05+DasMath.log10(discernable)); + nFraction= nFraction<0 ? 0 : nFraction; + String formatString = zeros(nFraction); + return factory.newFormatter(formatString); + } + } + catch (java.text.ParseException pe) { + Logger logger = DasLogger.getLogger(); + //Should not happen under normal circumstances, so bail. + RuntimeException re = new RuntimeException(pe); + logger.log(Level.SEVERE, pe.getMessage(), re); + throw re; + } + } + + private static String exp(int power) { + StringBuffer buffer = new StringBuffer(power+4); + for (int i = 0; i < power - 1; i++) { + buffer.append('#'); + } + buffer.append("0.#E0"); + return buffer.toString(); + } + + private static final String zeros100= "0.00000000000000000000" + + "0000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000"; + public static String zeros(int count) { + if ( count < 0 ) return "0"; + else if ( count <= 100 ) return zeros100.substring(0,count+2); + else { + StringBuffer buff = new StringBuffer(count+2).append("0."); + for (int index = 0; index < count; index++) { + buff.append('0'); + } + return buff.toString(); + } + } + + public static DatumFormatter bestTimeFormatter(Datum minimum, Datum maximum, int nsteps) { + double secondsPerStep = maximum.subtract(minimum).doubleValue(Units.seconds) / ( nsteps ); + double daysPerStep= secondsPerStep/86400; + if (secondsPerStep < 1.) { + return TimeDatumFormatter.MILLISECONDS; + } + else if (secondsPerStep < 60.) { + return TimeDatumFormatter.SECONDS; + } + else if (secondsPerStep < 3600.) { + return TimeDatumFormatter.MINUTES; + } + else if (secondsPerStep < 86400. ) { + return TimeDatumFormatter.HOURS; + } + else if ( secondsPerStep < 28*86400.0 ) { + return TimeDatumFormatter.DAYS; + } + else if ( secondsPerStep < 31557600.0 ) { + return TimeDatumFormatter.MONTHS; + } + else { + return TimeDatumFormatter.YEARS; + } + } + + /** + * attempt to parse the string as a datum. Note that if the + * units aren't specified, then of course the Datum will be + * assumed to be dimensionless. + * @throws ParseException when the double can't be parsed or the units aren't recognized. + */ + public static Datum parse(java.lang.String s) throws ParseException { + String[] ss= s.trim().split("\\s"); + Units units; + double value; + if ( ss.length==1 ) { + units= Units.dimensionless; + } else { + try { + units= Units.lookupUnits(ss[1]); + } catch ( IllegalArgumentException e ) { + throw new ParseException( e.getMessage(), 0 ); + } + } + return Datum.create( Double.parseDouble(ss[0]), units ); + } + + public static Datum parseValid(java.lang.String s) { + try { + return parse( s ); + } catch ( ParseException e ) { + throw new RuntimeException(e); + } + } + + public static Datum createValid( String s ) { + return Datum.create( Double.parseDouble(s), Units.dimensionless ); + } + + public static double[] doubleValues( Datum[] datums, Units units ) { + double[] result= new double[datums.length]; + for (int j=0; j 20) + score = 20/nn; + else + score = nn; + + if (score > bestScore) { + bestScore = score; + bestDatum = dd; + } + } + return bestDatum; + } + + +} diff --git a/dasCore/src/org/das2/datum/DatumVector.java b/dasCore/src/org/das2/datum/DatumVector.java new file mode 100644 index 000000000..93ab70b45 --- /dev/null +++ b/dasCore/src/org/das2/datum/DatumVector.java @@ -0,0 +1,190 @@ +/* File: DatumVector.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on February 18, 2004, 10:40 AM + * by Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +/** + * + * @author eew + */ +public final class DatumVector { + + private final Units units; + private final Object store; + private final double resolution; + private final int offset; + private final int length; + + /** T0DO: check offset and length for out of bounds condition */ + private DatumVector(double[] array, int offset, int length, Units units) { + this(array, offset, length, units, 0.0, true); + } + + /** T0DO: check offset and length for out of bounds condition */ + private DatumVector(double[] array, int offset, int length, Units units, double resolution, boolean copy) { + // + if (array == null) { + throw new NullPointerException("array is null"); + } + if (units == null) { + throw new NullPointerException("units is null"); + } + // + if (copy) { + this.store = new double[length]; + for (int i = 0; i < length; i++) { + ((double[])store)[i] = array[offset + i]; + } + offset = 0; + } + else { + this.store = array; + } + this.offset = offset; + this.units = units; + this.resolution= resolution; + this.length = length; + } + + /** T0DO: check start and end for out of bounds condition */ + public DatumVector getSubVector(int start, int end) { + if (start == 0 && end == length) { + return this; + } + else return new DatumVector((double[])store, offset + start, end - start, units, resolution, false); + } + + public Datum get(int index) { + return Datum.create( ((double[])store)[index + offset], units, resolution ); + } + + public Units getUnits() { + return this.units; + } + + public double doubleValue(int index, Units toUnits) { + return units.convertDoubleTo(toUnits, ((double[])store)[index + offset]); + } + + public double[] toDoubleArray(Units units) { + return toDoubleArray(null, units); + } + + public double[] toDoubleArray(double[] array, Units units) { + if (array == null || array.length < length) { + array = new double[length]; + } + if (units == this.units) { + System.arraycopy(store, offset, array, 0, length); + } + else { + double[] store = (double[])this.store; + for (int i = 0; i < length; i++) { + array[i] = this.units.convertDoubleTo(units, store[i]); + } + } + return array; + } + + public static DatumVector newDatumVector(Datum[] array, Units units) { + double[] dArray = new double[array.length]; + for (int i = 0; i < array.length; i++) { + dArray[i] = array[i].doubleValue(units); + } + return newDatumVector(dArray, units); + } + + public static DatumVector newDatumVector(double[] array, Units units) { + return newDatumVector(array, 0, array.length, units); + } + + public static DatumVector newDatumVector(double[] array, double resolution, Units units) { + return new DatumVector( array, 0, array.length, units, resolution, true ); + } + + public static DatumVector newDatumVector(double[] array, int offset, int length, Units units) { + return new DatumVector(array, offset, length, units); + } + + public int getLength() { + return length; + } + + public DatumVector add( Datum d ) { + double[] dd= new double[getLength()]; + Units newUnits; + if ( d.getUnits() instanceof LocationUnits ) { + newUnits= d.getUnits(); + for ( int i=0; i0 ) result.append(", "); + Datum d= get(i); + result.append(d.getFormatter().format(d,units)); + } + if ( getLength()>4 ) result.append(", ..."); + result.append(" "+units.toString()+" ]"); + return result.toString(); + } +} diff --git a/dasCore/src/org/das2/datum/EnumerationUnits.java b/dasCore/src/org/das2/datum/EnumerationUnits.java new file mode 100755 index 000000000..3449b2158 --- /dev/null +++ b/dasCore/src/org/das2/datum/EnumerationUnits.java @@ -0,0 +1,228 @@ +/* File: EnumerationUnits.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.datum; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.das2.datum.format.DatumFormatterFactory; +import org.das2.datum.format.EnumerationDatumFormatterFactory; + +/** + * Units class for mapping arbitary objects to Datums. Nothing about the contract + * for a Datum requires that they correspond to physical quanities, and we can + * assign a mapping from numbers to objects using this class. This allows + * information such as "Cluster 1" or "Spin Flip" to be encoded. + * + * This is used to model ordinal or nominal data, as described in + * http://en.wikipedia.org/wiki/Level_of_measurement + * + * @author Jeremy + */ +public class EnumerationUnits extends Units { + + private HashMap ordinals; // maps from ordinal to Datum.Integer + private int highestOrdinal; // highest ordinal for each Units type + private HashMap objects; // maps from object to Datum.Integer + private HashMap invObjects; // maps from Datum.Integer to object + public static HashMap unitsInstances; + + public EnumerationUnits(String id) { + this(id, ""); + } + + public EnumerationUnits(String id, String description) { + super(id, description); + highestOrdinal = 0; + ordinals = new HashMap(); + objects = new HashMap(); + invObjects = new HashMap(); + } + + public static Datum createDatumAndUnits(Object object) { + return create(object).createDatum(object); + } + + /** + * creates the datum, explicitly setting the ordinal. Use with caution. + * @param ival + * @param sval + * @throws IllegalArgumentException if this ordinal is already taken by a different value. + */ + public Datum createDatum(int ival, Object object) { + if (objects.containsKey(object)) { + return objects.get(object); + } else { + if (highestOrdinal < ival) { + highestOrdinal = ival; + } + Integer ordinal = new Integer(ival); + Datum result = new Datum.Double(ordinal, this); + if ( ordinals.containsKey(ordinal) ) { + Datum d= ordinals.get(ordinal); + if ( ! invObjects.get( d ).equals(object) ) { + throw new IllegalArgumentException("value already exists for this ordinal!"); + } + } + ordinals.put(ordinal, result); + invObjects.put(result, object); + objects.put(object, result); + return result; + } + + } + + public DatumVector createDatumVector(Object[] objects) { + double[] doubles = new double[objects.length]; + for (int i = 0; i < objects.length; i++) { + doubles[i] = createDatum(objects[i]).doubleValue(this); + } + return DatumVector.newDatumVector(doubles, this); + } + + public synchronized Datum createDatum(Object object) { + if (objects.containsKey(object)) { + return objects.get(object); + } else { + highestOrdinal++; + Integer ordinal = new Integer(highestOrdinal); + Datum result = new Datum.Double(ordinal, this); + ordinals.put(ordinal, result); + invObjects.put(result, object); + objects.put(object, result); + return result; + } + } + + /** + * provides access to map of all values. + * @return + */ + public Map getValues() { + return Collections.unmodifiableMap(ordinals); + } + + public Datum createDatum(int value) { + Integer key = new Integer(value); + if (ordinals.containsKey(key)) { + return ordinals.get(key); + } else { + throw new IllegalArgumentException("No Datum exists for this ordinal: " + value); + } + } + + public Datum createDatum(long value) { + return createDatum((int) value); + } + + public Datum createDatum(Number value) { + return createDatum(value.intValue()); + } + + public Object getObject(Datum datum) { + if (invObjects.containsKey(datum)) { + return invObjects.get(datum); + } else { + throw new IllegalArgumentException("This Datum doesn't map back to an object! This shouldn't happen!"); + } + } + + public static synchronized EnumerationUnits create(Object o) { + if (unitsInstances == null) + unitsInstances = new HashMap(); + Class c = o.getClass(); + if (unitsInstances.containsKey(c)) { + return unitsInstances.get(c); + } else { + Units u= null; + try { + u= Units.getByName(c.toString() + "Unit"); + } catch ( IllegalArgumentException ex ) { + EnumerationUnits result = new EnumerationUnits(c.toString() + "Unit"); + unitsInstances.put(c, result); + return result; + } + if ( u instanceof EnumerationUnits ) { + return (EnumerationUnits)u; + } else { + throw new IllegalArgumentException("unit already exists: "+u); + } + } + } + + public Datum createDatum(double d) { + return createDatum((int) d); + } + + public Datum createDatum(double d, double resolution) { + return createDatum((int) d); + } + + public DatumFormatterFactory getDatumFormatterFactory() { + return EnumerationDatumFormatterFactory.getInstance(); + } + + public Datum subtract(Number a, Number b, Units bUnits) { + throw new IllegalArgumentException("subtract on EnumerationUnit"); + } + + public Datum add(Number a, Number b, Units bUnits) { + throw new IllegalArgumentException("add on EnumerationUnit"); + } + + public Datum divide(Number a, Number b, Units bUnits) { + throw new IllegalArgumentException("divide on EnumerationUnit"); + } + + public Datum multiply(Number a, Number b, Units bUnits) { + throw new IllegalArgumentException("multiply on EnumerationUnit"); + } + + public Datum parse(String s) throws java.text.ParseException { + Datum result = null; + for (Iterator i = objects.keySet().iterator(); i.hasNext();) { + Object key = i.next(); + Object value = objects.get(key); + if (key.toString().equals(s)) { // if the look the same, they are the same + if (result == null) { + result = (Datum) objects.get(key); + } else { + throw new IllegalStateException("Multiple Objects' string representations match"); + } + } + } + if (result == null) { + throw new java.text.ParseException("no objects match \"" + s + "\"", 0); + } + return result; + } + + public int getHighestOrdinal() { + return this.highestOrdinal; + } + + public String toString() { + return this.getId() + "(ordinal)"; + } +} diff --git a/dasCore/src/org/das2/datum/InconvertibleUnitsException.java b/dasCore/src/org/das2/datum/InconvertibleUnitsException.java new file mode 100644 index 000000000..214e90c77 --- /dev/null +++ b/dasCore/src/org/das2/datum/InconvertibleUnitsException.java @@ -0,0 +1,25 @@ +/* + * InconvertibleUnitsException.java + * + * Created on December 1, 2007, 6:31 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.datum; + +/** + * introduced so that clients can more precisely catch this exception. + * @author jbf + */ +public class InconvertibleUnitsException extends IllegalArgumentException { + + /** Creates a new instance of InconvertibleUnitsException */ + public InconvertibleUnitsException( Units fromUnits, Units toUnits ) { + super( ( fromUnits==Units.dimensionless ? "(dimensionless)" : fromUnits.toString() ) + + " -> " + + ( toUnits==Units.dimensionless ? "(dimensionless)" : toUnits.toString() ) ) ; + } + +} diff --git a/dasCore/src/org/das2/datum/LocationUnits.java b/dasCore/src/org/das2/datum/LocationUnits.java new file mode 100644 index 000000000..ddf15e443 --- /dev/null +++ b/dasCore/src/org/das2/datum/LocationUnits.java @@ -0,0 +1,95 @@ +/* File: LocationUnits.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +/** + * + * @author jbf + */ +public class LocationUnits extends NumberUnits { + + Units offsetUnits; + Basis basis; + + /** Creates a new instance of LocationUnit */ + public LocationUnits( String id, String description, Units offsetUnits, Basis basis ) { + super( id, description ); + this.offsetUnits= offsetUnits; + this.basis= basis; + } + + /** + * return the physical units of the basis vector, such as "microseconds" or "degrees" + * @return + */ + @Override + public Units getOffsetUnits() { + return this.offsetUnits; + } + + /** + * return the basis for the unit, such as "since 2000-01-01T00:00Z" or "north of Earth's equator" + * @return + */ + @Override + public Basis getBasis() { + return this.basis; + } + + public Datum add(Number a, Number b, Units bUnits) { + if ( bUnits instanceof LocationUnits ) { + throw new IllegalArgumentException("You can't add "+this+" to "+bUnits+", they both identify a location in a space"); + } else { + Units offsetUnits= getOffsetUnits(); + if ( bUnits!=offsetUnits) { + UnitsConverter uc= Units.getConverter( bUnits, offsetUnits ); + b= uc.convert(b); + } + return createDatum( add( a, b ) ); + } + } + + public Datum divide(Number a, Number b, Units bUnits) { + throw new IllegalArgumentException("multiplication of locationUnits"); + } + + public Datum multiply(Number a, Number b, Units bUnits) { + throw new IllegalArgumentException("division of locationUnits"); + } + + public Datum subtract( Number a, Number b, Units bUnits) { + if ( bUnits instanceof LocationUnits ) { + if ( this != bUnits ) { + b= bUnits.getConverter(this).convert(b); + } + return getOffsetUnits().createDatum(subtract( a, b )); + } else { + if ( bUnits != getOffsetUnits()) { + b= bUnits.getConverter( getOffsetUnits() ).convert(b); + } + return createDatum( subtract( a, b ) ); + } + } + +} diff --git a/dasCore/src/org/das2/datum/MonthDatumRange.java b/dasCore/src/org/das2/datum/MonthDatumRange.java new file mode 100644 index 000000000..e544eca32 --- /dev/null +++ b/dasCore/src/org/das2/datum/MonthDatumRange.java @@ -0,0 +1,82 @@ +/* + * MonthDatumRange.java + * + * Created on November 15, 2004, 4:28 PM + */ + +package org.das2.datum; + +/** + * + * @author Jeremy + */ +public class MonthDatumRange extends DatumRange { + + int width; + int widthDigit; + int[] start; + int[] end; + + public MonthDatumRange( int[] start, int[] end ) { + super( TimeUtil.toDatum( start ), + TimeUtil.toDatum( end ) ); + widthDigit= -1; + int[] widthArr= new int[7]; + boolean haveNonZeroDigit= false; + for ( int i=0; i<7; i++ ) { + widthArr[i]= end[i]-start[i]; + } + while( widthArr[1]<0 ) { + widthArr[1]+= 12; + widthArr[0]--; + } + for ( int i=0; i<7; i++ ) { + if ( widthArr[i]!=0 ) { + if ( widthDigit!=-1 ) { + throw new IllegalArgumentException("MonthDatumRange must only vary in month or year, not both"); + } else { + widthDigit=i; + width= widthArr[widthDigit]; + } + } + } + this.start= start; + this.end= end; + } + + public DatumRange next() { + int[] end1= new int[7]; + for ( int i=0; i<7; i++ ) { + end1[i]= this.end[i]; + } + end1[widthDigit]= end1[widthDigit]+this.width; + switch ( widthDigit ) { + case 1: while( end1[1]>12 ) { + end1[1]-= 12; + end1[0]++; + } + case 0: break; + default: throw new IllegalArgumentException("not implemented"); + } + return new MonthDatumRange( this.end, end1 ); + } + + public DatumRange previous() { + int[] start1= new int[7]; + for ( int i=0; i<7; i++ ) { + start1[i]= this.start[i]; + } + start1[widthDigit]= start1[widthDigit]-this.width; + switch ( widthDigit ) { + case 1: while( start1[1]<1 ) { + start1[1]+= 12; + start1[0]--; + } + case 0: break; + default: throw new IllegalArgumentException("not implemented"); + } + + return new MonthDatumRange( start1, this.start ); + } + +} diff --git a/dasCore/src/org/das2/datum/NumberUnits.java b/dasCore/src/org/das2/datum/NumberUnits.java new file mode 100755 index 000000000..c9f06e1e1 --- /dev/null +++ b/dasCore/src/org/das2/datum/NumberUnits.java @@ -0,0 +1,276 @@ +/* File: Units.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import org.das2.util.DasMath; +import java.math.*; +import java.text.ParseException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.das2.datum.format.DefaultDatumFormatterFactory; +import org.das2.datum.format.DatumFormatterFactory; + +/** + * + * @author jbf + */ +public class NumberUnits extends Units { + + public NumberUnits(String id) { + this(id,""); + } + + public NumberUnits(String id, String description) { + super(id,description); + } + + public Datum createDatum( double value ) { + return new Datum.Double( value, this, 0. ); + } + + public Datum createDatum( double value, double resolution ) { + return new Datum.Double( value, this, resolution ); + } + + public Datum createDatum( int value ) { + return new Datum.Double( value, this ); + } + + public Datum createDatum( long value ) { + return new Datum.Double( value, this ); + } + + public Datum createDatum( Number value ) { + return new Datum.Double( value, this ); + } + + + public DatumFormatterFactory getDatumFormatterFactory() { + return DefaultDatumFormatterFactory.getInstance(); + } + + /* + * @returns double[2], [0] is number, [1] is the resolution + */ + private double[] parseDecimal( String s ) { + s= s.trim(); + BigDecimal bd= new BigDecimal(s); + + if ( bd.scale()>0 ) { + double resolution= DasMath.exp10( -1*bd.scale() ); + return new double[] { Double.parseDouble(s), resolution }; + } else { + int ie= s.indexOf( 'E' ); + if ( ie==-1 ) ie= s.indexOf('e'); + String mant; + if ( ie==-1 ) { + int id= s.indexOf('.'); + double[] dd= new double[2]; + dd[0]= Double.parseDouble(s); + if ( id==-1 ) { + dd[1]= 1.; + } else { + int scale= s.length()-id-1; + dd[1]= DasMath.exp10(-1*scale); + } + return dd; + } else { + mant= s.substring(0,ie); + double[] dd= parseDecimal( mant ); + double exp= DasMath.exp10( Double.parseDouble( s.substring(ie+1) ) ); + dd[0]= dd[0] * exp; + dd[1]= dd[1] * exp; + return dd; + } + } + } + + // note + and - are left out because of ambiguity with sign. + private static Pattern expressionPattern= Pattern.compile( "(.+)([\\*/])(.+)" ); + + private Datum parseExpression( String s ) throws ParseException { + Matcher m= expressionPattern.matcher(s); + if ( !m.matches() ) throw new IllegalArgumentException("not an expression"); + String operator= m.group(2); + Datum operand1; + try { + operand1= Units.dimensionless.parse( m.group(1) ); + } catch ( IllegalArgumentException e ) { + operand1= this.parse( m.group(1) ); + } + Datum operand2; + try { + operand2= Units.dimensionless.parse( m.group(3) ); + } catch ( IllegalArgumentException e ) { + operand2= this.parse( m.group(3) ); + } + Datum result; + if ( operator.equals("*") ) { + result= operand1.multiply(operand2); + } else if ( operator.equals("/") ) { + result= operand1.divide(operand2); + } else { + throw new IllegalArgumentException("Bad operator: "+operator+" of expression "+s); + } + return result; + } + + /* + * parse the string in the context of this. If units are not + * specified, then assume units are this. Otherwise, parse the + * unit and attempt to convert to this before creating the unit. + */ + public Datum parse(String s) throws ParseException { + expressionPattern= Pattern.compile( "(.+)([\\*/])(.+)" ); + if ( expressionPattern.matcher(s).matches() ) { + Datum result= parseExpression( s ); + if ( result.getUnits()==Units.dimensionless ) { + result= this.createDatum( result.doubleValue() ); + } else { + // throw exception if it's not convertable + result= result.convertTo(this); + } + return result; + } else { + try { + s= s.trim(); + if ( s.endsWith(this.getId()) ) { + s= s.substring(0,s.length()-this.getId().length()); + } + String[] ss= s.split("\\s+"); + double[] dd= parseDecimal(ss[0]); + if ( ss.length==1 ) { + return Datum.create( dd[0], this, dd[1] ); + } else { + String unitsString= ss[1]; + for ( int i=2; i + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import org.das2.datum.Units; + +/** + * + * @author jbf + */ +public class TimeContext { + + public static TimeContext MILLISECONDS= new TimeContext("milliseconds",1/86400000.); + public static TimeContext SECONDS= new TimeContext("seconds", 1/86400.); + public static TimeContext MINUTES = new TimeContext("minutes", 1/1440.); + public static TimeContext HOURS = new TimeContext("hours",1/24.); + public static TimeContext DAYS = new TimeContext("days",1); + public static TimeContext WEEKS = new TimeContext("weeks",7); + public static TimeContext MONTHS = new TimeContext("months",30); + public static TimeContext YEARS = new TimeContext("years",365); + public static TimeContext DECADES = new TimeContext("decades",3650); + + String s; + double ordinal; + + public TimeContext(String s, double ordinal ) { + this.s= s; + this.ordinal= ordinal; + } + + public boolean gt( TimeContext tc ) { + return ordinal>tc.ordinal; + } + + public boolean le( TimeContext tc ) { + return ordinal<=tc.ordinal; + } + + public String toString() { + return s; + } + + public static TimeContext getContext( Datum t1, Datum t2) { + TimeContext context; + double seconds= t2.subtract(t1).doubleValue(Units.seconds); + if (seconds<1) { context=MILLISECONDS; } + else if (seconds<60) { context=SECONDS; } + else if (seconds<3600) { context=MINUTES; } + else if (seconds<86400) { context=HOURS; } + else if (seconds<=864000) { context=DAYS; } + else { context=DAYS; } + return context; + } + +} diff --git a/dasCore/src/org/das2/datum/TimeLocationUnits.java b/dasCore/src/org/das2/datum/TimeLocationUnits.java new file mode 100644 index 000000000..8b3ad6d51 --- /dev/null +++ b/dasCore/src/org/das2/datum/TimeLocationUnits.java @@ -0,0 +1,58 @@ +/* File: TimeLocationUnits.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import org.das2.datum.format.DatumFormatterFactory; +import org.das2.datum.format.TimeDatumFormatterFactory; + +/** + * + * @author jbf + */ +public class TimeLocationUnits extends LocationUnits { + + /* TimeLocationUnits class is introduced because it is often necessary to + * easily identify a time quantity, for instance when deciding whether to + * use a timeAxis or not. (TimeAxis is no longer a class, but we use a + * special tickV for time units.) + */ + + public TimeLocationUnits( String id, String description, Units offsetUnits, Basis basis ) { + super(id,description,offsetUnits,basis); + } + + public DatumFormatterFactory getDatumFormatterFactory() { + return TimeDatumFormatterFactory.getInstance(); + } + + public Datum parse(String s) throws java.text.ParseException { + CalendarTime ct = new CalendarTime(s); + return ct.toDatum(); + } + + public String getTimeZone() { + return "UT"; + } + +} diff --git a/dasCore/src/org/das2/datum/TimeUtil.java b/dasCore/src/org/das2/datum/TimeUtil.java new file mode 100755 index 000000000..df962f885 --- /dev/null +++ b/dasCore/src/org/das2/datum/TimeUtil.java @@ -0,0 +1,400 @@ +/* File: TimeUtil.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 22, 2003, 11:00 AM by Jeremy Faden + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import java.text.ParseException; +import java.util.Map; +import java.util.HashMap; +import org.das2.datum.format.TimeDatumFormatter; + +/** + * Various time utilities + * @author jbf + */ +public final class TimeUtil { + + private TimeUtil() { + } + + // One of the few times package private is useful + final static int[][] daysInMonth = { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0} + }; + + // One of the few times package private is useful + final static int[][] dayOffset = { + {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} + }; + + public static int daysInMonth(int month, int year) { + return daysInMonth[isLeapYear(year)?1:0][month]; + } + + public static int julday( int month, int day, int year ) { + int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - + 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + + 275 * month / 9 + day + 1721029; + return jd; + } + + public static int dayOfYear( int month, int day, int year ) { + return day + dayOffset[isLeapYear(year)?1:0][month]; + } + + // intruduced to aid in debugging + public static class TimeDigit{ + CalendarTime.Step ordinal; // YEAR, MONTH, etc. + String label; + int divisions; // approximate + private static Map digits = + new HashMap(); + @Override + public String toString(){ + return label; + } + private TimeDigit(CalendarTime.Step ordinal, String label, int divisions){ + this.ordinal = ordinal; + this.label = label; + this.divisions = divisions; + digits.put(ordinal, this); + } + public CalendarTime.Step getOrdinal(){ + return ordinal; + } + public int divisions(){ + return divisions; + } + public static TimeDigit fromOrdinal(CalendarTime.Step ordinal){ + return digits.get(ordinal); + } + } + + public static final TimeDigit TD_YEAR = new TimeDigit( CalendarTime.Step.YEAR, "YEAR", 12 ); + public static final TimeDigit TD_MONTH = new TimeDigit( CalendarTime.Step.MONTH, "MONTH", 30 ); + public static final TimeDigit TD_DAY = new TimeDigit( CalendarTime.Step.DAY, "DAY", 24 ); + public static final TimeDigit TD_HOUR = new TimeDigit( CalendarTime.Step.HOUR, "HOUR", 60 ); + public static final TimeDigit TD_MINUTE = new TimeDigit( CalendarTime.Step.MINUTE, "MINUTE", 60 ); + public static final TimeDigit TD_SECOND = new TimeDigit( CalendarTime.Step.SECOND, "SECOND", 1000 ); + public static final TimeDigit TD_MILLI= new TimeDigit( CalendarTime.Step.MILLISEC, "MILLISECONDS", 1000 ); + public static final TimeDigit TD_MICRO = new TimeDigit( CalendarTime.Step.MICROSEC, "MICROSECONDS", 1000 ); + public static final TimeDigit TD_NANO = new TimeDigit( CalendarTime.Step.NANOSEC, "NANOSECONDS", 1000 ); + + public static double getSecondsSinceMidnight(Datum datum) { + double xx= datum.doubleValue(Units.t2000); + if (xx<0) { + xx= xx % 86400; + if (xx==0) { + return 0; + } else { + return 86400+xx; + } + } else { + return xx % 86400; + } + } + + public static double getMicroSecondsSinceMidnight(Datum datum) { + double xx= datum.doubleValue( Units.us2000 ); + if (xx<0) { + xx= xx % 86400e6; + if (xx==0) { + return 0; + } else { + return 86400e6+xx; + } + } else { + return xx % 86400e6; + } + } + + /** + * return the the integer number of days that have elapsed since roughly Monday, January 1, 4713 BC. Julian Day + * is defined as starting at noon UT, here is is defined starting at midnight. + * @param datum + * @return + */ + public static int getJulianDay( Datum datum ) { + double xx= datum.doubleValue(Units.mj1958); + return (int)Math.floor( xx ) + 2436205; + } + + /** + *Break the Julian day apart into month, day year. This is based on + *http://en.wikipedia.org/wiki/Julian_day (GNU Public License), and + *was introduced when toTimeStruct failed when the year was 1886. + *@param julian the (integer) number of days that have elapsed since the initial epoch at noon Universal Time (UT) Monday, January 1, 4713 BC + *@return a CalendarTime with the month, day and year fields set. + */ + public static int[] julianToGregorian( int julian ) { + + int[] lRet = {0,0,0}; + + int j = julian + 32044; + int g = j / 146097; + int dg = j % 146097; + int c = (dg / 36524 + 1) * 3 / 4; + int dc = dg - c * 36524; + int b = dc / 1461; + int db = dc % 1461; + int a = (db / 365 + 1) * 3 / 4; + int da = db - a * 365; + int y = g * 400 + c * 100 + b * 4 + a; + int m = (da * 5 + 308) / 153 - 2; + int d = da - (m + 4) * 153 / 5 + 122; + int Y = y - 4800 + (m + 2) / 12; + int M = (m + 2) % 12 + 1; + int D = d + 1; + + lRet[0] = Y; + lRet[1] = M; + lRet[2] = D; + return lRet; + } + + /** Here millis are the number of milliseconds after the second, and micros are + * the number of micro seconds after the millisecond not the second. + * + * returns int[] { year, month, day, hour, minute, second, millis, micros } + */ + public static int[] toTimeArray( Datum time ) { + + CalendarTime ts= new CalendarTime( time ); + int millis = (int) (ts.m_nNanoSecond / 1000000); + int micros = (int) ((ts.m_nNanoSecond % 1000000)/ 1000); + + return new int[] { ts.m_nYear, ts.m_nMonth, ts.m_nDom, ts.m_nHour, ts.m_nMinute, ts.m_nSecond, + millis, micros }; + } + + public static Datum toDatum( int[] timeArray ) { + int year = timeArray[0]; + int month = timeArray[1]; + int day = timeArray[2]; + if ( timeArray[1]<1 ) { + throw new IllegalArgumentException(""); + } + int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - + 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + + 275 * month / 9 + day + 1721029; + int hour = (int)timeArray[3]; + int minute = (int)timeArray[4]; + double seconds = timeArray[5] + hour*(float)3600.0 + minute*(float)60.0 + timeArray[6]/1e9; + double us2000= UnitsConverter.getConverter(Units.mj1958,Units.us2000).convert(( jd - 2436205 ) + seconds / 86400. ); + return Datum.create( us2000, Units.us2000 ); + } + + /** Is year a leap year. + * Warning: Not tested for years prior to 1. + */ + public static boolean isLeapYear( int year ) { + if(year % 4 != 0) return false; + if(year % 400 == 0) return true; + if(year % 100 == 0) return false; + else return true; + } + + + public static Datum next( TimeDigit td, int count, Datum datum ) { + if ( td==TD_NANO ) throw new IllegalArgumentException("not supported nanos"); + CalendarTime ct = new CalendarTime(datum).step(td.getOrdinal(), count); + Datum result= ct.toDatum(); + return result; + } + + public static Datum next(CalendarTime.Step step, Datum datum ) { + CalendarTime ct = new CalendarTime(datum).step(step, 1); + return ct.toDatum(); + } + + + /** step down the previous ordinal. If the datum is already at an ordinal + * boundry, then step down by one ordinal. + * @param step + * @param datum + * @return + */ + public static Datum prev(CalendarTime.Step step, Datum datum ) { + CalendarTime ct= new CalendarTime(datum).step(step, -1); + return ct.toDatum(); + } + + public static Datum now() { + double us2000= ( System.currentTimeMillis() - 946684800e3 ) * 1000; + return Units.us2000.createDatum(us2000); + } + + /** + * @param year the year + * @param month the month + * @param day the day of month, unless month==0, then day is day of year. + * @param hour additional hours + * @param minute additional minutes + * @param second additional seconds + * @param units the Units in which to return the result. + * @return a double in the context of units. + */ + public static double convert(int year, int month, int day, int hour, int minute, + double second, TimeLocationUnits units) { + // if month==0, then day is doy (day of year). + int jd; + if ( month>0 ) { + jd = julday(month,day,year); + } else { + // if month==0 then day is doy + int month1= 1; + int day1= 1; + jd = julday(month1,day1,year); + jd+= ( day - 1 ); + } + + second+= hour*3600.0 + minute*60.0; + + double us2000 = (jd-2451545)*86400000000. + second * 1000000; + + if ( units==Units.us2000 ) { + return us2000; + } else { + return Units.us2000.convertDoubleTo(units, us2000); + } + } + + private final static String[] mons= { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + /** + * returns 1..12 for the English month name + * + * @throws ParseException if the name isn't recognized + */ + public static int monthNumber( String s ) throws ParseException { + s= s.substring(0,3); + for ( int i=0; i<12; i++ ) { + if ( s.equalsIgnoreCase( mons[i] ) ) return i+1; + } + throw new ParseException("Unable to parse month", 0 ); + } + + /** + * returns "Jan", "Feb", ... for month number (1..12). + * @param mon integer from 1 to 12. + * @return three character English month name. + */ + public static String monthNameAbbrev( int mon ) { + if ( mon<1 || mon>12 ) throw new IllegalArgumentException("invalid month number: "+mon); + return mons[mon-1]; + } + + /** Creates a datum from a string + * @param s + * @throws ParseException + * @return + */ + public static Datum create(String s) throws java.text.ParseException { + CalendarTime ts= new CalendarTime(s); + return ts.toDatum(); + } + + /** creates a Datum from a string which is known to contain + * a valid time format. Throws a RuntimeException if the + * string is not valid. + * @param validString + * @return + */ + public static Datum createValid(String validString ) { + try { + return create( validString ); + } catch ( java.text.ParseException ex ) { + throw new RuntimeException( ex ); + } + } + + public static boolean isValidTime( String string ) { + try { + create( string ); + return true; + } catch ( java.text.ParseException ex ) { + return false; + } + } + + public static Datum prevMidnight(Datum datum) { + //return datum.subtract(getMicroSecondsSinceMidnight(datum), Units.microseconds); + return datum.subtract(getSecondsSinceMidnight(datum), Units.seconds); + } + + /** + * returns the next midnight, or this datum if we are already on midnight. + * @param datum + * @return + */ + public static Datum nextMidnight( Datum datum ) { + CalendarTime ct = new CalendarTime(datum).step(CalendarTime.Step.DAY, 1); + return ct.toDatum(); + } + /** + * creates a Datum representing the time given in integer years, months, ..., seconds, nanoseconds. The year + * must be at least 1960, and must be a four-digit year. A double in Units.us2000 is used to represent the + * Datum, so resolution will drop as the year drops away from 2000. + * + * @param year four digit year >= 1960. + * @param month integer month, 1..12. + * @param day integer day of month. + * @param hour additional hours + * @param minute additional minutes + * @param second additional seconds + * @param nano additional nanoseconds + * @return a Datum with units Units.us2000. + */ + public static Datum createTimeDatum( int year, int month, int day, int hour, int minute, int second, int nano ) { + //if ( year<1960 ) throw new IllegalArgumentException("year must not be < 1960, and no 2 digit years (year="+year+")"); + if ( year<100 ) throw new IllegalArgumentException("year must not be < 100, and no 2 digit years (year="+year+")"); + int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - + 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + + 275 * month / 9 + day + 1721029; + double microseconds = second*1e6 + hour*3600e6 + minute*60e6 + nano/1e3; + double us2000= UnitsConverter.getConverter(Units.mj1958,Units.us2000).convert(( jd - 2436205 )) + microseconds; + return Datum.create( us2000, Units.us2000 ); + } + + public static void main(String[] args) throws Exception { + System.out.println( TimeUtil.now() ); + System.out.println( Datum.create( TimeUtil.convert(2000,1,2, 0, 0, 0, Units.us2000 ), Units.us2000 )); + Datum x=create( "2000-1-1 0:00:33.45" ); + System.out.println( x ); + + CalendarTime ts= new CalendarTime(x); + System.out.println( ts.toDatum() ); + + TimeDatumFormatter tf = TimeDatumFormatter.DEFAULT; + + for ( int i=0; i<44; i++ ) { + System.out.println(tf.format(x)+"\t"+(long)x.doubleValue(Units.us2000)); + x= TimeUtil.prev(CalendarTime.Step.SECOND,x); + } + } + +} diff --git a/dasCore/src/org/das2/datum/Units.java b/dasCore/src/org/das2/datum/Units.java new file mode 100755 index 000000000..87b2a196f --- /dev/null +++ b/dasCore/src/org/das2/datum/Units.java @@ -0,0 +1,821 @@ +/* File: Units.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.das2.datum.format.DatumFormatterFactory; + +/** + * Class for indicating physical units, and other random units. + * @author jbf + */ +public abstract class Units { + + private static final Logger logger= Logger.getLogger("datum.units"); + + private static Map unitsMap = new HashMap(); + + public static final Units dimensionless= new NumberUnits("","dimensionless quantities"); + + public static final Units radians= new NumberUnits("radian"); + public static final Units degrees= new NumberUnits("degrees"); + public static final Units deg= new NumberUnits("deg"); + static { + degrees.registerConverter(radians, new UnitsConverter.ScaleOffset(Math.PI/180.0,0.0) ); + degrees.registerConverter(deg, UnitsConverter.IDENTITY); + } + + /** + * + */ + public static final Units rgbColor= new NumberUnits("rgbColor","256*256*red+256*green+blue"); + + /** + * this is left in in case legacy code needs to see the conversion from dB to dimensionless offset. + */ + private static final class dBConverter extends UnitsConverter { + @Override + public double convert(double value) { + return 10 * Math.log10(value); + } + @Override + public UnitsConverter getInverse() { + if (inverse == null) { + inverse = new UnitsConverter() { + @Override + public double convert(double value) { + return Math.pow(10.0, value / 10.0); + } + @Override + public UnitsConverter getInverse() { + return dBConverter.this; + } + }; + } + return inverse; + } + } + + public static final Units celciusDegrees= new NumberUnits("celcius degrees"); // disambiguate from "deg C" which is the temperature scale + public static final Units fahrenheitDegrees= new NumberUnits("fahrenheit degrees"); // disambiguate from "deg F" which is the temperature scale + + public static final Units hours= new NumberUnits("hr"); + public static final Units minutes= new NumberUnits("min"); + public static final Units seconds= new NumberUnits("s"); + public static final Units seconds2= new NumberUnits("sec"); + //public static final Units seconds3= new NumberUnits("seconds"); // note s was not convertible to seconds. + public static final Units milliseconds= new NumberUnits("ms","milliseconds"); + public static final Units milliseconds2= new NumberUnits("msec"); + public static final Units microseconds= new NumberUnits("microseconds"); + public static final Units microseconds2= new NumberUnits("\u00B5s"); + + public static final Units nanoseconds= new NumberUnits("nanoseconds"); + public static final Units ns= new NumberUnits("ns","nanoseconds"); + public static final Units picoseconds= new NumberUnits("picoseconds"); + public static final Units days= new NumberUnits("days"); + static { + seconds.registerConverter(milliseconds, UnitsConverter.MILLI); + seconds.registerConverter(microseconds, UnitsConverter.MICRO); + seconds.registerConverter(nanoseconds,UnitsConverter.NANO); + seconds.registerConverter(ns,UnitsConverter.NANO); + nanoseconds.registerConverter( ns, UnitsConverter.IDENTITY ); + seconds.registerConverter(picoseconds,UnitsConverter.PICO); + seconds.registerConverter(seconds2,UnitsConverter.IDENTITY); + microseconds.registerConverter(nanoseconds, UnitsConverter.MILLI); // to support time formatting, often from us2000 to microseconds offset. + microseconds.registerConverter(microseconds2, UnitsConverter.IDENTITY); + milliseconds.registerConverter(milliseconds2, UnitsConverter.IDENTITY); + hours.registerConverter(seconds, new UnitsConverter.ScaleOffset( 3600.,0.0)); + minutes.registerConverter(seconds, new UnitsConverter.ScaleOffset( 60.,0.0)); + days.registerConverter(seconds, new UnitsConverter.ScaleOffset(8.64e4, 0.0)); + } + + public static final Units bytesPerSecond= new NumberUnits("bytes/s"); + public static final Units kiloBytesPerSecond= new NumberUnits("KBytes/s"); + public static final Units bytes= new NumberUnits( "bytes" ); + public static final Units kiloBytes= new NumberUnits( "KBytes" ); + static { + bytesPerSecond.registerConverter( kiloBytesPerSecond, UnitsConverter.KILO ); + bytes.registerConverter( kiloBytes, UnitsConverter.KILO ); + } + + public static final Units hertz= new NumberUnits("Hz"); + public static final Units kiloHertz = new NumberUnits("kHz"); // I verified that this should be lower case k. I wonder why... + public static final Units megaHertz = new NumberUnits("MHz"); + public static final Units gigaHertz = new NumberUnits("GHz"); + static { + hertz.registerConverter(kiloHertz, UnitsConverter.KILO); + hertz.registerConverter(megaHertz, UnitsConverter.MEGA); + hertz.registerConverter(gigaHertz, UnitsConverter.GIGA); + } + + public static final Units eV= new NumberUnits("eV"); + public static final Units ev= new NumberUnits("ev"); // Mike at LANL had run into these... + public static final Units keV= new NumberUnits("keV"); + public static final Units MeV= new NumberUnits("MeV"); + static { + eV.registerConverter(Units.ev, UnitsConverter.IDENTITY); + eV.registerConverter(Units.keV, UnitsConverter.KILO); + eV.registerConverter(Units.MeV, UnitsConverter.MEGA); + } + + /** + * 1 / cm3 + */ + public static final Units pcm3= new NumberUnits("cm!a-3!n"); + + public static final Units kelvin= new NumberUnits("K"); + public static final Units cmps= new NumberUnits("cm/s"); + + public static final Units cm_2s_1keV_1= new NumberUnits( "cm!U-2!N s!U-1!N keV!U-1!N" ); + public static final Units cm_2s_1MeV_1= new NumberUnits( "cm!U-2!N s!U-1!N MeV!U-1!N" ); + static { + cm_2s_1keV_1.registerConverter( Units.cm_2s_1MeV_1, UnitsConverter.KILO ); + } + /** + * Volts 2 m-2 Hz-1 + */ + public static final Units v2pm2Hz= new NumberUnits("V!a2!nm!a-2!nHz!a-1"); + static { + unitsMap.put("V**2 m**-2 Hz**-1", v2pm2Hz); + } + public static final Units V_per_m = new NumberUnits("V m**-1"); + static { + unitsMap.put("V/m", V_per_m); + } + + /** + * Watts / m2 + */ + public static final Units wpm2= new NumberUnits("W/m!a-2!n"); + public static final Units W_per_m2_Hz = new NumberUnits("W m**-2 Hz**-1"); + + + public static final Units meters = new NumberUnits("m"); + public static final Units millimeters = new NumberUnits("mm"); + public static final Units centimeters = new NumberUnits("cm"); + public static final Units kiloMeters = new NumberUnits("km"); + public static final Units inches = new NumberUnits("inch"); + public static final Units typographicPoints = new NumberUnits("points"); + static { + meters.registerConverter(kiloMeters, UnitsConverter.KILO); + meters.registerConverter(centimeters, UnitsConverter.CENTI ); + meters.registerConverter(millimeters, UnitsConverter.MILLI ); + inches.registerConverter( meters, new UnitsConverter.ScaleOffset(0.0254,0.0) ); + inches.registerConverter( typographicPoints, new UnitsConverter.ScaleOffset(72,0.0) ); + } + + /**** begin of LocationUnits. These must be defined after the physical units to support Basis. ****/ + + public static final Units centigrade= new LocationUnits( "centigrade", "centigrade", Units.celciusDegrees, Basis.centigrade ); + public static final Units fahrenheitScale= new LocationUnits("deg F", "deg F", Units.fahrenheitDegrees, Basis.fahrenheit ); + + static { + centigrade.registerConverter(fahrenheitScale, new UnitsConverter.ScaleOffset(1.8, 32)); + celciusDegrees.registerConverter(fahrenheitDegrees, new UnitsConverter.ScaleOffset(1.8,0) ); + } + + /** + * Microseconds since midnight Jan 1, 2000, excluding those within a leap second. Differences across leap + * second boundaries do not represent the number of microseconds elapsed. + */ + public static final TimeLocationUnits us2000= new TimeLocationUnits("us2000", "Microseconds since midnight Jan 1, 2000.", + Units.microseconds, Basis.since2000); + + /** + * Microseconds since midnight Jan 1, 1980, excluding those within a leap second. + */ + public static final TimeLocationUnits us1980= new TimeLocationUnits("us1980", "Microseconds since midnight Jan 1, 1980.", + Units.microseconds, Basis.since1980 ); + + /** + * Seconds since midnight Jan 1, 2010, excluding leap seconds. + */ + public static final TimeLocationUnits t2010= new TimeLocationUnits("t2010","Seconds since midnight Jan 1, 2010.", + Units.seconds, Basis.since2010 ); + + /** + * Seconds since midnight Jan 1, 2000, excluding leap seconds. + */ + public static final TimeLocationUnits t2000= new TimeLocationUnits("t2000","Seconds since midnight Jan 1, 2000.", + Units.seconds, Basis.since2000 ); + + /** + * seconds since midnight Jan 1, 1970, excluding leap seconds. + */ + public static final TimeLocationUnits t1970= new TimeLocationUnits("t1970","Seconds since midnight Jan 1, 1970", + Units.seconds, Basis.since1970 ); + + /** + * milliseconds since midnight Jan 1, 1970, excluding leap seconds. + */ + public static final TimeLocationUnits ms1970= new TimeLocationUnits("ms1970","Milliseconds since midnight Jan 1, 1970", + Units.milliseconds, Basis.since1970 ); + + /** + * roughly days since noon on some day in 1958, Julian - 2436204.5 to be more precise. + */ + public static final TimeLocationUnits mj1958= new TimeLocationUnits("mj1958","Julian - 2436204.5", + Units.days, Basis.since1958 ); + + /** + * The Modified Julian Day (MJD) is the number of days (with decimal fraction of the day) that have elapsed since midnight at the beginning of Wednesday November 17, 1858. + * Julian - 2400000.5 + */ + public static final TimeLocationUnits mjd= new TimeLocationUnits("mjd", "days since midnight November 17, 1858.", + Units.days , Basis.modifiedJulian ); + + static { + ((Units)t2000).registerConverter(us2000, UnitsConverter.MICRO); + ((Units)us1980).registerConverter(us2000, new UnitsConverter.ScaleOffset(1.0, -631152000000000L ) ); + ((Units)t2000).registerConverter(t1970, new UnitsConverter.ScaleOffset(1.0, 9.466848e8)); + ((Units)t1970).registerConverter(ms1970, UnitsConverter.MILLI ); + ((Units)t2000).registerConverter(t2010, new UnitsConverter.ScaleOffset(1.0, -3.1561920e+8 )); + ((Units)t2000).registerConverter(mj1958, new UnitsConverter.ScaleOffset(1.0/8.64e4, 15340 )); + ((Units)t2000).registerConverter(mjd, new UnitsConverter.ScaleOffset(1.0/8.64e4, 51544 )); + } + + /**** ratiometric units ***********/ + + public static final Units percent= new NumberUnits("%",""); + + /** + * Define a set of units to describe ratiometric (logarithmic) spacing. Note that Units.percent + * is no longer the defacto ratiometric spacing, and Units.percentIncrease takes its place. + * Note the log10Ratio is the preferred method for expressing spacing, but all are convertible + * See logERatio, log10Ratio and google for "fold change." + */ + + /* percentIncrease is defined as ( b-a )*100. / a. So { 1,2,4,8 } has a spacing of 100 % diff. */ + public static final Units dB = new NumberUnits("dB","decibels"); + public static final Units ampRatio= new NumberUnits("ampratio","amplitude ratio"); + public static final Units percentIncrease= new NumberUnits("% diff","Special dimensionless number, useful for expressing on logarithmic scale. 100% indicates a doubling"); + public static final Units log10Ratio= new NumberUnits("log10Ratio", "Special dimensionless number, useful for expressing distances on a log10 scale" ); + public static final Units logERatio= new NumberUnits("logERatio", "Special dimensionless number, useful for expressing distances on a logE scale" ); + private static class PercentRatioConverter extends UnitsConverter { + @Override + public double convert(double value) { + return ( Math.exp(value) - 1.0 ) * 100; + } + @Override + public UnitsConverter getInverse() { + if (inverse == null) { + inverse = new UnitsConverter() { + @Override + public double convert(double value) { + return Math.log( value / 100 + 1. ); + } + @Override + public UnitsConverter getInverse() { + return PercentRatioConverter.this; + } + }; + } + return inverse; + } + } + + /** + * see http://en.wikipedia.org/wiki/Decibel + */ + private static class AmpRatioConverter extends UnitsConverter { + @Override + public double convert(double value) { + return ( Math.pow(10,value/20.) ); + } + @Override + public UnitsConverter getInverse() { + if (inverse == null) { + inverse = new UnitsConverter() { + @Override + public double convert(double value) { + return 20 * Math.log10( value ); + } + @Override + public UnitsConverter getInverse() { + return AmpRatioConverter.this; + } + }; + } + return inverse; + } + } + + static { + log10Ratio.registerConverter( logERatio, new UnitsConverter.ScaleOffset( Math.log(10), 0. ) ); + logERatio.registerConverter( percentIncrease, new PercentRatioConverter() ); + dB.registerConverter( log10Ratio, new UnitsConverter.ScaleOffset( 10, 0 ) ); + dB.registerConverter( ampRatio, new AmpRatioConverter() ); + } + + /* static { + unitsMap.put("mj1958", Units.mj1958); + unitsMap.put("t1970", Units.t1970); + unitsMap.put("t2000", Units.t2000); + unitsMap.put("us2000", Units.us2000); + unitsMap.put("seconds", Units.seconds); + unitsMap.put("s", Units.seconds); + unitsMap.put("days", Units.days); + unitsMap.put("microseconds", Units.microseconds); + unitsMap.put("", Units.dimensionless); + unitsMap.put("dB", Units.dB); + + unitsMap.put("Hz", Units.hertz); + unitsMap.put("kHz", Units.kiloHertz); + unitsMap.put("MHz", Units.megaHertz); + }*/ + + private String id; + private String description; + private final Map conversionMap = new ConcurrentHashMap(); + + protected Units( String id ) { + this( id, "" ); + }; + + protected Units( String id, String description ) { + this.id= id; + this.description= description; + unitsMap.put( id, this ); + }; + + /** + * get the id uniquely identifying the units. Note the id may contain + * special tokens, like "since" for time locations. + * @return the id. + */ + public String getId() { + return this.id; + } + + /** + * register a converter between the units. Note these converters can be + * changed together to derive conversions. (A to B, B to C defines A to C.) + * @param toUnits the target units + * @param converter the converter that goes from this unit to target units. + */ + public void registerConverter(Units toUnits, UnitsConverter converter) { + conversionMap.put(toUnits, converter); + UnitsConverter inverse = (UnitsConverter)toUnits.conversionMap.get(this); + if (inverse == null || inverse.getInverse() != converter) { + toUnits.registerConverter(this, converter.getInverse()); + } + } + + /** + * return the units to which this unit is convertible. + * @return the units to which this unit is convertible. + */ + public Units[] getConvertableUnits() { + Set result= new HashSet(); + LinkedList queue = new LinkedList(); + queue.add(this); + while (!queue.isEmpty()) { + Units current = (Units)queue.removeFirst(); + for (Map.Entry entry : current.conversionMap.entrySet()) { + Units next = (Units)entry.getKey(); + if (!result.contains(next)) { + queue.add(next); + result.add(next); + } + } + } + return (Units[])result.toArray( new Units[result.size()] ); + } + + /** + * return true if the unit can be converted to toUnits. + * @deprecated use isConvertibleTo (which does not contain spelling error) + * @param toUnits Units object. + * @return true if the unit can be converted to toUnits. + */ + public boolean isConvertableTo( Units toUnits ) { + UnitsConverter result= getConverterInternal(this, toUnits); + return result!=null; + } + + /** + * return true if the unit can be converted to toUnits. + * @param toUnits Units object. + * @return true if the unit can be converted to toUnits. + */ + public boolean isConvertibleTo( Units toUnits ) { + UnitsConverter result= getConverterInternal(this, toUnits); + return result!=null; + } + + /** + * lookup the UnitsConverter object that takes numbers from fromUnits to toUnits. + * This will chain together UnitsConverters registered via units.registerConverter. + * @param fromUnits units instance that is the source units. + * @param toUnits units instance that is the target units. + * @return UnitsConverter object + * @throws InconvertibleUnitsException when the conversion is not possible. + */ + public static UnitsConverter getConverter( final Units fromUnits, final Units toUnits ) { + logger.log(Level.FINER, "getConverter( {0} to {1} )", new Object[]{fromUnits, toUnits}); //TODO: THIS IS CALLED WITH EVERY REPAINT!!! + UnitsConverter result= getConverterInternal(fromUnits, toUnits); + if ( result==null ) { + throw new InconvertibleUnitsException( fromUnits, toUnits ); + } + return result; + } + + /** + * lookup the UnitsConverter object that takes numbers from fromUnits to toUnits. + * This will chain together UnitsConverters registered via units.registerConverter. + * @param fromUnits + * @param toUnits + * @return UnitsConverter object + * @throws InconvertibleUnitsException when the conversion is not possible. + */ + private static UnitsConverter getConverterInternal( final Units fromUnits, final Units toUnits ) { + logger.log(Level.FINE, "fromUnits={0} {1} toUnits={2} {3}", new Object[]{fromUnits,fromUnits.hashCode(), toUnits,toUnits.hashCode()}); + if (fromUnits == toUnits) { + return UnitsConverter.IDENTITY; + } + + UnitsConverter o = fromUnits.conversionMap.get(toUnits); + if ( o != null) { + return o; + } + + Map visited = new HashMap(); + visited.put(fromUnits, null); + LinkedList queue = new LinkedList(); + queue.add(fromUnits); + while (!queue.isEmpty()) { + Units current = (Units)queue.removeFirst(); + for ( Map.Entry entry : current.conversionMap.entrySet() ) { + Units next = (Units)entry.getKey(); + if (!visited.containsKey(next)) { + visited.put(next, current); + queue.add(next); + if (next == toUnits) { + logger.log(Level.FINE, "build conversion from {0} to {1}", new Object[]{fromUnits, toUnits}); + return buildConversion(fromUnits, toUnits, visited); + } + } + } + } + return null; + } + + private static UnitsConverter buildConversion(Units fromUnits, Units toUnits, Map parentMap) { + ArrayList list = new ArrayList(); + Units current = toUnits; + while (current != null) { + list.add(current); + current = (Units)parentMap.get(current); + } + UnitsConverter converter = UnitsConverter.IDENTITY; + for (int i = list.size() - 1; i > 0; i--) { + Units a = (Units)list.get(i); + Units b = (Units)list.get(i - 1); + UnitsConverter c = (UnitsConverter)a.conversionMap.get(b); + converter = converter.append(c); + } + fromUnits.registerConverter(toUnits, converter); + return converter; + } + + /** + * Get the converter that goes from this Unit to toUnits. E.g. + * Units.meters.getConverter(Units.centimeters) yields a converter that + * multiplies by 100. + * @param toUnits + * @return a converter from this unit to toUnits. + * @throws IllegalArgumentException if conversion between units is not possible + */ + public UnitsConverter getConverter( Units toUnits ) { + return getConverter( this, toUnits ); + } + + /** + * convert the double in this units' space to toUnits' space. + * @param toUnits the units. + * @param value the value in toUnits. + * @return the double in the new units system. + */ + public double convertDoubleTo( Units toUnits, double value ) { + if ( this==toUnits ) { + return value; + } else { + return getConverter(this,toUnits).convert(value); + } + } + + @Override + public String toString() { + return id; + } + + /** + * return the units from the Basis for the unit, such as "seconds" in + * "seconds since midnight, Jan 1, 1970" + * @return this units offsets. + */ + public Units getOffsetUnits() { + return this; + } + + /** + * return the Basis which defines the meaning of zero and the direction of positive values, such as + * "since midnight, Jan 1, 1970" + * @return the Basis object, which simply identifies a basis. + */ + public Basis getBasis() { + return Basis.physicalZero; + } + + public abstract Datum createDatum( double value ); + public abstract Datum createDatum( int value ); + public abstract Datum createDatum( long value ); + public abstract Datum createDatum( Number value ); + + public abstract Datum createDatum( double value, double resolution ); + + private final static double FILL_DOUBLE= -1e31; + private final static float FILL_FLOAT= -1e31f; + private final static int FILL_INT= Integer.MAX_VALUE; + private final static long FILL_LONG= Long.MAX_VALUE; + + public double getFillDouble() { return FILL_DOUBLE; } + public float getFillFloat() { return FILL_FLOAT; } + public int getFillInt() { return FILL_INT; } + public long getFillLong() { return FILL_LONG; } + public Datum getFillDatum() { return this.createDatum(FILL_DOUBLE); } + + public boolean isFill( double value ) { return valueFILL_DOUBLE/10 ; + } + + public abstract DatumFormatterFactory getDatumFormatterFactory(); + + public abstract Datum parse(String s) throws ParseException; + public String format( Datum datum ) { + return getDatumFormatterFactory().defaultFormatter().format(datum); + } + public String grannyFormat( Datum datum ) { + return getDatumFormatterFactory().defaultFormatter().grannyFormat(datum); + } + + public abstract Datum add( Number a, Number b, Units bUnits ); + public abstract Datum subtract( Number a, Number b, Units bUnits ); + public abstract Datum multiply( Number a, Number b, Units bUnits ); + public abstract Datum divide( Number a, Number b, Units bUnits ); + + /** + * return all the known units. + * @return list of all the known units. + */ + public static List getAllUnits() { + return new ArrayList(unitsMap.keySet()); + } + + /** + * returns a Units object with the given string representation that is stored in the unitsMap. + * + * @param s units identifier + * @return units object + * @throws IllegalArgumentException if the unit is not recognized. + */ + public static Units getByName(String s) { + Units units = (Units)unitsMap.get(s); + if (units == null) { + throw new IllegalArgumentException("Unrecognized units: "+s); + } else return units; + } + + /** + * return canonical das2 unit for colloquial time. + * @param s string containing time unit like s, sec, millisec, etc. + * @return + */ + public static Units lookupTimeLengthUnit(String s) throws ParseException { + s= s.toLowerCase().trim(); + if ( s.startsWith("sec") || s.equals("s") ) { + return Units.seconds; + } else if ( s.startsWith("ms") || s.startsWith("millisec") || s.startsWith("milliseconds") ) { + return Units.milliseconds; + } else if ( s.equals("hr") || s.startsWith("hour") ) { + return Units.hours; + } else if ( s.equals("mn") || s.startsWith("min") ) { + return Units.minutes; + } else if ( s.startsWith("us") || s.startsWith("\u00B5s" ) || s.startsWith("micros")) { + return Units.microseconds; + } else if ( s.startsWith("ns") || s.startsWith("nanos" ) ) { + return Units.nanoseconds; + } else if ( s.startsWith("d") ) { //TODO: yikes... + return Units.days; + } else { + throw new ParseException("failed to identify unit: "+s,0); + } + } + + /** + * lookupUnits canonical units object, or allocate one. If one is + * allocated, then parse for "<unit> since <datum>" and add conversion to + * "microseconds since 2000-001T00:00." Note leap seconds are ignored! + * @param base the base time, for example 2000-001T00:00. + * @param offsetUnits the offset units for example microseconds. Positive values of the units will be since the base time. + * @return the unit. + */ + public static synchronized Units lookupTimeUnits( Datum base, Units offsetUnits ) { + Units result; + String canonicalName = "" + offsetUnits + " since "+ base; + try { + result= Units.getByName(canonicalName); + return result; + } catch ( IllegalArgumentException ex ) { + Basis basis= new Basis( "since "+ base, "since "+ base, Basis.since2000, base.doubleValue(Units.us2000), Units.us2000.getOffsetUnits() ); + result= new TimeLocationUnits( canonicalName, canonicalName, offsetUnits, basis ); + result.registerConverter( Units.us2000, + new UnitsConverter.ScaleOffset( + offsetUnits.convertDoubleTo(Units.microseconds, 1.0), + base.doubleValue(Units.us2000) ) ); + return result; + } + } + + /** + * lookupUnits canonical units object, or allocate one. If one is + * allocated, then parse for "<unit> since <datum>" and add conversion to + * "microseconds since 2000-001T00:00" (us2000). Note leap seconds are ignored + * in the returned units, so each day is 86400 seconds long, and differences in + * times should not include leap seconds. Note this contains a few kludges + * as this for datasets encountered by Autoplot. + * @param units string like "microseconds since 2000-001T00:00" which will be the id. + * @return a units object that implements. + * @throws java.text.ParseException if the time cannot be parsed, etc. + */ + public static synchronized Units lookupTimeUnits( String units ) throws ParseException { + + Units result; + + //see if it's already registered. + try { + result= Units.getByName(units); + return result; + } catch ( IllegalArgumentException ex ) { + //do nothing until later + } + + if(units.trim().equalsIgnoreCase("UTC")) return us2000; + + String[] ss= units.split("since"); + Units offsetUnits= lookupTimeLengthUnit(ss[0]); + Datum datum; + + if ( ss[1].equals(" 1-1-1 00:00:00" ) ) { // make this into something that won't crash. + //datum= Units.mj1958.createDatum(-714779); + ss[1]= "1901-01-01 00:00:00"; // /media/mini/data.backup/examples/netcdf/sst.ltm.1961-1990.nc + } + if ( ss[1].contains("1970-01-01 00:00:00.0 0:00") ) { + ss[1]= "1970-01-01 00:00:00"; + } + if ( ss[1].endsWith(" UTC") ) { // http://www.ngdc.noaa.gov/stp/satellite/dmsp/f16/ssj/2011/01/f16_20110101_ssj.h5?TIME + ss[1]= ss[1].substring(0,ss[1].length()-4); + } + datum= TimeUtil.create(ss[1]); + return lookupTimeUnits( datum, offsetUnits ); + } + + /** + * lookupUnits canonical units object, or allocate one. + * Examples include: + * "nT" where it's already allocated, + * "apples" where it allocates a new one, and + * "seconds since 2011-12-21T00:00" where it uses lookupTimeUnits. + * @param sunits string identifier. + * @return canonical units object. + */ + public static synchronized Units lookupUnits(String sunits) { + Units result; + sunits= sunits.trim(); + try { + result= Units.getByName(sunits); + + } catch ( IllegalArgumentException ex ) { + if ( sunits.contains(" since ") || sunits.equalsIgnoreCase("UTC") ) { + try { + result = lookupTimeUnits(sunits); + } catch (ParseException ex1) { + result= new NumberUnits( sunits ); + } + } else if ( sunits.equals("sec") ) { // begin, giant table of kludges + result= Units.seconds; + } else if ( sunits.equals("msec") ) { // CDF + result= Units.milliseconds; + } else if ( sunits.contains("(All Qs)")) { //themis files have this annotation on the units. Register a converter. TODO: solve this in a nice way. The problem is I wouldn't want to assume nT(s) doesn't mean nT * sec. + result= new NumberUnits( sunits ); + Units targetUnits= lookupUnits( sunits.replace("(All Qs)","").trim() ); + result.registerConverter( targetUnits, UnitsConverter.IDENTITY ); + } else { + Pattern multPattern= Pattern.compile("([.0-9]+)\\s*([a-zA-Z]+)"); + Matcher m= multPattern.matcher(sunits); + if ( m.matches() ) { // kludge for ge_k0_mgf which has "0.1nT" for units. We register a converter when we see these. Note this is going to need more attention + try { + Units convTo; + convTo = lookupUnits(m.group(2)); + if ( convTo!=null ) { + double fact= Double.parseDouble(m.group(1)); + result= new NumberUnits( sunits ); + result.registerConverter( convTo, new UnitsConverter.ScaleOffset(fact,0.0) ); + } else { + result= lookupUnits(sunits); + } + } catch ( NumberFormatException ex2 ) { + result= lookupUnits(sunits); + } + } else { + result= new NumberUnits( sunits ); + } + } + } + + // look to see if there is a standard unit for this and register a converter if so. E.g. [ms]<-->ms + String stdunits= sunits; + if ( stdunits.startsWith("[") && stdunits.endsWith("]") ) { // we can't just pop these off. Hudson has case where this causes problems. We need to make units in vap files canonical as well. + stdunits= stdunits.substring(1,stdunits.length()-1); + } + if ( stdunits.startsWith("(") && stdunits.endsWith(")") ) { // often units get [] or () put around them. Pop these off. + stdunits= stdunits.substring(1,stdunits.length()-1); + } + if ( !stdunits.equals(sunits) ) { + Units stdUnit= lookupUnits(stdunits); // we need to register "foo" when "[foo]" so that order doesn't matter. + if ( !stdUnit.isConvertibleTo(result) ) { + logger.log(Level.FINE, "registering identity converter {0} -> {1}", new Object[]{stdUnit, result}); + stdUnit.registerConverter( result, UnitsConverter.IDENTITY ); + stdUnit.getConverter(result); + } + } + return result; + } + + public static void main( String[] args ) throws java.text.ParseException { + //Datum ratio = Datum.create(100); + Datum ratio = Units.ampRatio.createDatum(100); + Datum db = ratio.convertTo(dB); + System.out.println("ratio: " + ratio); + System.out.println("dB: " + db); + + Datum Hz = Datum.create(1000000.0, hertz); + Datum kHz = Hz.convertTo(kiloHertz); + Datum MHz = kHz.convertTo(megaHertz); + System.out.println("Hz: " + Hz); + System.out.println("kHz: " + kHz); + System.out.println("MHz: " + MHz); + + System.err.println( Units.ms1970.createDatum(1000) ); + } +} diff --git a/dasCore/src/org/das2/datum/UnitsConverter.java b/dasCore/src/org/das2/datum/UnitsConverter.java new file mode 100644 index 000000000..cf44b15ba --- /dev/null +++ b/dasCore/src/org/das2/datum/UnitsConverter.java @@ -0,0 +1,227 @@ +/* File: UnitsConverter.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum; + +/** + * + * @author jbf + */ +public abstract class UnitsConverter { + + public static final UnitsConverter IDENTITY = new UnitsConverter() { + public UnitsConverter getInverse() { + return this; + } + public double convert(double value) { + return value; + } + public String toString() { + return "IDENTITY UnitsConverter"; + } + }; + + public static final UnitsConverter TERA = new ScaleOffset(1e-12, 0.0); + public static final UnitsConverter GIGA = new ScaleOffset(1e-9, 0.0); + public static final UnitsConverter MEGA = new ScaleOffset(1e-6, 0.0); + public static final UnitsConverter KILO = new ScaleOffset(1e-3, 0.0); + public static final UnitsConverter MILLI = new ScaleOffset(1e3, 0.0); + public static final UnitsConverter CENTI = new ScaleOffset(1e2, 0.0); + public static final UnitsConverter MICRO = new ScaleOffset(1e6, 0.0); + public static final UnitsConverter NANO = new ScaleOffset(1e9, 0.0); + public static final UnitsConverter PICO = new ScaleOffset(1e12, 0.0); + + protected UnitsConverter inverse; + + protected UnitsConverter() { + } + + protected UnitsConverter(UnitsConverter inverse) { + this.inverse = inverse; + } + + public abstract UnitsConverter getInverse(); + + public abstract double convert(double value); + + public Number convert( Number number ) { + double value = number.doubleValue(); + value = convert(value); + if (number instanceof Integer) { + return new Integer((int)value); + } + else if (number instanceof Long) { + return new Long((long)value); + } + else { + return new Double(value); + } + } + + public UnitsConverter append(UnitsConverter that) { + return new Appended(this, that); + } + + public static class ScaleOffset extends UnitsConverter { + private final double offset; + private final double scale; + private final int hashCode; + + /** + * Creates a new UnitsConverter.ScaleOffset. This + * converter multiplies by scale and adds offset, so + * offset is in the target Units. For example, + * deg C to deg F would be + *
    new UnitsConverter.ScaleOffset( 9./5, 32 )
    . + * + */ + public ScaleOffset(double scale, double offset) { + this(scale, offset, null); + } + + private ScaleOffset(double scale, double offset, UnitsConverter inverse) { + super(inverse); + this.scale = scale; + this.offset = offset; + hashCode = computeHashCode(); + } + + private int computeHashCode() { + long scaleBits = Double.doubleToLongBits(scale); + long offsetBits = Double.doubleToLongBits(offset); + long code = (11 * 13 * 13) + (13 * scaleBits) + offsetBits; + int a = (int)(code >> 32); + int b = (int)(0xFFFFFFFFL & code); + return a + b; + } + + public UnitsConverter getInverse() { + if (((UnitsConverter)this).inverse == null) { + ((UnitsConverter)this).inverse = new ScaleOffset(1.0 / scale, -(offset / scale), this); + } + return ((UnitsConverter)this).inverse; + } + + public double convert( double value ) { + return scale * value + offset; + } + + public UnitsConverter append(UnitsConverter that) { + if (this.equals(IDENTITY)) { + return that; + } + else if (that.equals(IDENTITY)) { + return this; + } + else if (that instanceof ScaleOffset) { + ScaleOffset so = (ScaleOffset)that; + double aScale = this.scale * so.scale; + double aOffset = this.offset * so.scale + so.offset; + return new ScaleOffset(aScale, aOffset); + } + else { + return super.append(that); + } + } + + public boolean equals(Object o) { + if (!(o instanceof ScaleOffset)) { + return false; + } + ScaleOffset that = (ScaleOffset)o; + return this.scale == that.scale && this.offset == that.offset; + } + + public String toString() { + return getClass().getName() + "[scale=" + scale + ",offset=" + offset + "]"; + } + + public int hashCode() { + return hashCode; + } + + } + + public static class Appended extends UnitsConverter{ + + UnitsConverter[] converters; + + public Appended(UnitsConverter uc1, UnitsConverter uc2) { + UnitsConverter[] a1 = ucToArray(uc1); + UnitsConverter[] a2 = ucToArray(uc2); + converters = new UnitsConverter[a1.length + a2.length]; + for (int i = 0; i < a1.length; i++) { + converters[i] = a1[i]; + } + for (int i = 0; i < a2.length; i++) { + converters[i + a1.length] = a2[i]; + } + } + + private Appended(UnitsConverter[] array, UnitsConverter inverse) { + super(inverse); + converters = array; + } + + public double convert(double value) { + for (int i = 0; i < converters.length; i++) { + value = converters[i].convert(value); + } + return value; + } + + public Number convert(Number value) { + for (int i = 0; i < converters.length; i++) { + value = converters[i].convert(value); + } + return value; + } + + public UnitsConverter getInverse() { + if (inverse == null) { + int length = converters.length; + UnitsConverter[] inverseArray = new UnitsConverter[length]; + for (int i = 0; i < length; i++) { + inverseArray[i] = converters[length - i - 1].getInverse(); + } + inverse = new Appended(inverseArray, this); + } + return inverse; + } + + private static UnitsConverter[] ucToArray(UnitsConverter uc) { + if (uc instanceof Appended) { + return ((Appended)uc).converters; + } + else { + return new UnitsConverter[] {uc}; + } + } + + } + + public static UnitsConverter getConverter(Units fromUnits, Units toUnits) { + return Units.getConverter(fromUnits,toUnits); + } + +} diff --git a/dasCore/src/org/das2/datum/UnitsUtil.java b/dasCore/src/org/das2/datum/UnitsUtil.java new file mode 100644 index 000000000..69abf1d73 --- /dev/null +++ b/dasCore/src/org/das2/datum/UnitsUtil.java @@ -0,0 +1,153 @@ +/* + * UnitsUtil.java + * + * Created on December 1, 2004, 10:25 PM + */ + +package org.das2.datum; + +/** + * + * @author Jeremy + */ +public class UnitsUtil { + + /** + * returns true if the unit is used to measure distance in a logarithmic + * space, such as decibels or percent increase. Note Units.dimensionless + * are not considered ratiometric. (Of course, all ratiometic + * units are dimensionless...) + */ + public static final boolean isRatiometric( Units unit ) { + return unit!=Units.dimensionless && unit.isConvertableTo(Units.logERatio); + } + + /** + * returns true if the unit describes a location in time, as in us2000. + */ + public static final boolean isTimeLocation( Units unit ) { + return unit.isConvertableTo(Units.us2000); + } + + /** + * returns true if the unit is a ratio measurement, meaning there is a physical zero + * and you can make meaningful ratios between arbitary numbers. All operations + * like add, multiply and divide are allowed. (What about negative numbers? We + * need a statistician!) + * Examples include "5 km" or "0.2/cc" and "15 counts" + * See http://en.wikipedia.org/wiki/Level_of_measurement + * @param unit + * @return + */ + public static final boolean isRatioMeasurement( Units unit ) { + return !(unit instanceof EnumerationUnits) && unit.getOffsetUnits()==unit; + } + + /** + * returns true if the unit is a interval measurement, meaning the choice of + * zero is arbitrary. Subtraction and comparison are allowed, but addition, + * multiplication and division are invalid operators. + * Examples include "2008-04-09T14:27:00Z" and 15 deg W Longitude. + * See http://en.wikipedia.org/wiki/Level_of_measurement + * @param unit + * @return + */ + public static final boolean isIntervalMeasurement( Units unit ) { + return !(unit instanceof EnumerationUnits) && unit.getOffsetUnits()!=unit; + } + /** + * returns true if the unit is nominal, meaning that Datums with this unit + * can only be equal or not equal. Currently all nominal data is stored + * as ordinal data, so this always returns false. + * Examples include "Iowa City", and "Voyager 1". + * See http://en.wikipedia.org/wiki/Level_of_measurement + * @param unit + * @return true if the unit is nominal. + */ + public static final boolean isNominalMeasurement( Units unit ) { + return false; + } + + /** + * returns true if the unit is ordinal, meaning that Datums with this unit + * can only be equal or not equal, or compared. subtract, add, multiply, + * divide are invalid. + * Examples include energy bin labels and quality measures. + * See http://en.wikipedia.org/wiki/Level_of_measurement + * @param unit + * @return true if the unit is ordinal. + */ + public static final boolean isOrdinalMeasurement( Units unit ) { + return unit instanceof EnumerationUnits; + } + + /** + * returns the unit whose product with the parameter unit is + * unity. + */ + public static Units getInverseUnit( Units unit ) { + if ( unit==Units.seconds ) { + return Units.hertz; + } else if ( unit==Units.hertz ) { + return Units.seconds; + } else if ( unit==Units.dimensionless ) { + return Units.dimensionless; + } else if ( unit==Units.milliseconds ) { + return Units.kiloHertz; + } else if ( unit==Units.microseconds ) { + return Units.megaHertz; + } else { + throw new IllegalArgumentException( "units not supported: "+unit ); + } + } + + /** + * Special division operation that either does the Datum division if + * possible, or returns the division of the magitude parts of the + * Datums plus the unit names "A/B", suitable for human consumption. + */ + public static String divideToString( Datum aDatum, Datum bDatum ) { + try { + Datum result= divide( aDatum, bDatum ); + return String.valueOf(result); + } catch ( IllegalArgumentException e ) { + Units aUnits= aDatum.getUnits(); + Units bUnits= bDatum.getUnits(); + double a= aDatum.doubleValue(aUnits); + double b= bDatum.doubleValue(bUnits); + return ""+(a/b)+" "+aUnits+" / " +bUnits; + } + } + + /** + * attempt to perform the division of two Datums by looking for + * convertable units or dimensionless. + */ + public static Datum divide( Datum aDatum, Datum bDatum ) { + Units bUnits= bDatum.getUnits(); + Units aUnits= aDatum.getUnits(); + + Units bInvUnits; + try{ + bInvUnits= getInverseUnit(bUnits); + } catch ( IllegalArgumentException e ) { + bInvUnits= null; + } + + double a= aDatum.doubleValue(aUnits); + double b= bDatum.doubleValue(bUnits); + + if ( bUnits==Units.dimensionless ) { + return aUnits.createDatum( a/b ); + } else if ( aUnits==Units.dimensionless ) { + return bInvUnits.createDatum(a/b); + } else { + if ( !bUnits.isConvertableTo(aUnits) ) { + throw new IllegalArgumentException("unable to calculate, b units not convertable to a"); + } else { + UnitsConverter uc= bUnits.getConverter(aUnits); + return Units.dimensionless.createDatum( a / uc.convert(b) ); + } + } + } +} diff --git a/dasCore/src/org/das2/datum/format/DatumFormatter.java b/dasCore/src/org/das2/datum/format/DatumFormatter.java new file mode 100644 index 000000000..9b0047534 --- /dev/null +++ b/dasCore/src/org/das2/datum/format/DatumFormatter.java @@ -0,0 +1,99 @@ +/* File: DatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 24, 2003, 4:45 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumVector; +import org.das2.datum.Units; + +/** Formats Datum objects for printing and parses strings to Datum objects. + * + * @author Edward West + */ +public abstract class DatumFormatter { + //TODO: consider the following api: + /* + * String format( Datum datum ) returns fully-qualified datum e.g. "12 days" + * String format( Datum datum, Units units ) returns formatted in the context of units (e.g.for axis) + * + * we've considered this and it needs to be implemented. + */ + + /** Available for use by subclasses */ + protected DatumFormatter() {} + + /* + * format the Datum so that it is understood out-of-context. For + * example, "4.5 seconds" + */ + public abstract String format( Datum datum ); + + /* + * format the Datum in the context of a given unit. For example, + * "4.5". It is acceptable to return the fully-qualified Datum, which + * is also the default class behavior. This will give somewhat undesirable + * results on axes. + */ + public String format( Datum datum, Units units ) { + return format( datum ); + } + + + + /** Returns the datum formatted as a String with special formatting + * characters. As with format, this should be out-of-context and should + * be tagged with the Units. + * + * The default implementation just returns the result of + * {@link #format(org.das2.datum.Datum)} + */ + public String grannyFormat(Datum datum) { + return format(datum); + } + + + /** formats the Datum in the context of the units. + */ + public String grannyFormat( Datum datum, Units units ) { + return format( datum, units ); + } + + /** + * format the set of Datums using a consistent and optimized format. + * First introduced to support DasAxis, where tighter coupling between + * the two is required to efficiently provide context. + * @param datums + * @param context visible range, context should be provided. + * @return + */ + public String[] axisFormat( DatumVector datums, DatumRange context ) { + String [] result= new String[datums.getLength()]; + for ( int i=0; i + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +/** + * + * @author Edward West + */ +public abstract class DatumFormatterFactory { + + /** provided for use by subclasses */ + protected DatumFormatterFactory() {} + + public abstract DatumFormatter newFormatter(String format) throws java.text.ParseException; + + public abstract DatumFormatter defaultFormatter(); +} diff --git a/dasCore/src/org/das2/datum/format/DefaultDatumFormatter.java b/dasCore/src/org/das2/datum/format/DefaultDatumFormatter.java new file mode 100644 index 000000000..52edbe79d --- /dev/null +++ b/dasCore/src/org/das2/datum/format/DefaultDatumFormatter.java @@ -0,0 +1,185 @@ +/* File: DefaultDatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 1, 2003, 4:45 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.das2.datum.format; + +import org.das2.util.NumberFormatUtil; +import org.das2.util.DasMath; + +import java.text.*; +import java.util.Locale; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumVector; +import org.das2.datum.Units; + +/** Formats Datum objects for printing and parses strings to Datum objects. + * + * @author Edward West + */ +public class DefaultDatumFormatter extends DatumFormatter { + + private String formatString; + private NumberFormat format; + + /** Available for use by subclasses */ + protected DefaultDatumFormatter() { + } + + /** Creates a new instance of DatumFormatter */ + public DefaultDatumFormatter(String formatString) throws ParseException { + if (formatString.equals("")) { + this.formatString = ""; + format = null; + } else { + this.formatString = formatString; + format = NumberFormatUtil.getDecimalFormat(formatString); + } + } + + public String format(Datum datum) { + return format(datum, datum.getUnits()) + " " + datum.getUnits(); + } + + public String format(Datum datum, Units units) { + double d = datum.doubleValue(units); + if (Double.isInfinite(d) || Double.isNaN(d)) { + return "" + d; + } + String result; + if (format == null) { + double resolution = datum.getResolution(units.getOffsetUnits()); + result = formatLimitedResolution(d, resolution); + } else { + result = format.format(datum.doubleValue(units)); + } + return result; + } + + @Override + public String grannyFormat(Datum datum, Units units) { + String formt = format(datum, units); + if (formt.indexOf("E") != -1) { + int iE = formt.indexOf("E"); + StringBuffer granny = new StringBuffer(formt.length() + 4); + String mant = formt.substring(0, iE); + if (Double.parseDouble(mant) != 1.0) { + granny.append(mant).append("\u00d7"); + } + granny.append("10").append("!A").append(formt.substring(iE + 1)).append("!N"); + formt = granny.toString(); + } + return formt; + } + + @Override + public String[] axisFormat(DatumVector datums, DatumRange context ) { + Units units= context.getUnits(); + String[] result = new String[datums.getLength()]; + for (int i = 0; i < result.length; i++) { + result[i] = format(datums.get(i), units); + } + boolean hasMant = false; + for (int i = 0; i < result.length; i++) { + String res1 = result[i]; + if (res1.indexOf("E") != -1) { + int iE = res1.indexOf("E"); + String mant = res1.substring(0, iE); + if (Double.parseDouble(mant) != 1.0) { + hasMant = true; + } + } + } + for (int i = 0; i < result.length; i++) { + String res1 = result[i]; + if (res1.indexOf("E") != -1) { + int iE = res1.indexOf("E"); + StringBuffer granny = new StringBuffer(res1.length() + 4); + String mant = res1.substring(0, iE); + + if (hasMant) { + granny.append(mant).append("\u00d7"); + } + + granny.append("10").append("!A").append(res1.substring(iE + 1)).append("!N"); + result[i] = granny.toString(); + } + + } + return result; + } + + public String grannyFormat(Datum datum) { + return grannyFormat(datum, datum.getUnits()) + " " + datum.getUnits(); + } + + public String toString() { + return formatString; + } + + private String formatLimitedResolution(double d, double resolution) { + String result; + if (resolution == 0. && Double.toString(d).length() > 7) { + // make the default resolution be 0.01%. + resolution = d / 10000; + } + if (resolution > 0) { + // 28 --> scale = -1 + // 2.8 --> scale = 0 + int scale = (int) Math.ceil(-1 * DasMath.log10(resolution) - 0.00001); + int exp; + if (d != 0.) { + exp = (int) DasMath.log10(Math.abs(d)); + } else { + exp = 0; + } + if (scale >= 0) { + DecimalFormat f; + if (exp <= -5 || exp >= 5) { + f = NumberFormatUtil.getDecimalFormat("0E0"); + f.setMinimumFractionDigits(scale + exp - 1); + f.setMaximumFractionDigits(scale + exp - 1); + } else { + f = NumberFormatUtil.getDecimalFormat("0"); + f.setMinimumFractionDigits(scale); + f.setMaximumFractionDigits(scale); + } + result = f.format(d); + } else { + double round = DasMath.exp10(-1 * scale); + d = Math.round(d / round) * round; + DecimalFormat f; + if (exp <= -5 || exp >= 5) { + f = NumberFormatUtil.getDecimalFormat("0E0"); + f.setMinimumFractionDigits(scale + exp + 1); + f.setMaximumFractionDigits(scale + exp + 1); + } else { + f = NumberFormatUtil.getDecimalFormat("0"); + } + result = f.format(d); + } + } else { + result = Double.toString(d); + } + return result; + } +} diff --git a/dasCore/src/org/das2/datum/format/DefaultDatumFormatterFactory.java b/dasCore/src/org/das2/datum/format/DefaultDatumFormatterFactory.java new file mode 100644 index 000000000..4566068a1 --- /dev/null +++ b/dasCore/src/org/das2/datum/format/DefaultDatumFormatterFactory.java @@ -0,0 +1,61 @@ +/* File: DatumFormatterFactory.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 25, 2003, 3:35 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +/** + * + * @author Edward West + */ +public final class DefaultDatumFormatterFactory extends DatumFormatterFactory { + + private static DatumFormatterFactory factory; + + /** provided for use by subclasses */ + protected DefaultDatumFormatterFactory() { + } + + public DatumFormatter newFormatter(String format) throws java.text.ParseException { + return new DefaultDatumFormatter(format); + } + + /** Get an instance of this factory. */ + public static DatumFormatterFactory getInstance() { + //This isn't thread safe, but who cares. Instances are small and + //functionally identical. + if (factory == null) { + factory = new DefaultDatumFormatterFactory(); + } + return factory; + } + + public DatumFormatter defaultFormatter() { + try { + return newFormatter(""); + } + catch (java.text.ParseException pe) { + throw new RuntimeException(pe); + } + } + +} diff --git a/dasCore/src/org/das2/datum/format/EnumerationDatumFormatter.java b/dasCore/src/org/das2/datum/format/EnumerationDatumFormatter.java new file mode 100644 index 000000000..d9cb23c6c --- /dev/null +++ b/dasCore/src/org/das2/datum/format/EnumerationDatumFormatter.java @@ -0,0 +1,47 @@ +/* File: EnumerationDatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 29, 2003, 4:34 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +import org.das2.datum.Datum; +import org.das2.datum.EnumerationUnits; + +/** + * + * @author Edward West + */ +public class EnumerationDatumFormatter extends DatumFormatter { + + /** Creates a new instance of EnumerationDatumFormatter */ + public EnumerationDatumFormatter() { + } + + public String toString() { + return getClass().getName(); + } + + public String format(Datum datum) { + return ((EnumerationUnits)datum.getUnits()).getObject(datum).toString(); + } + +} diff --git a/dasCore/src/org/das2/datum/format/EnumerationDatumFormatterFactory.java b/dasCore/src/org/das2/datum/format/EnumerationDatumFormatterFactory.java new file mode 100644 index 000000000..5d3c88370 --- /dev/null +++ b/dasCore/src/org/das2/datum/format/EnumerationDatumFormatterFactory.java @@ -0,0 +1,51 @@ +/* File: EnumerationDatumFormatterFactory.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 29, 2003, 4:34 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +/** + * + * @author Edward West + */ +public class EnumerationDatumFormatterFactory extends DatumFormatterFactory { + + private final static EnumerationDatumFormatterFactory INSTANCE + = new EnumerationDatumFormatterFactory(); + + /** Creates a new instance of EnumerationDatumFormatterFactory */ + private EnumerationDatumFormatterFactory() { + } + + public DatumFormatter defaultFormatter() { + return new EnumerationDatumFormatter(); + } + + public DatumFormatter newFormatter(String format) throws java.text.ParseException { + return defaultFormatter(); + } + + public static EnumerationDatumFormatterFactory getInstance() { + return INSTANCE; + } + +} diff --git a/dasCore/src/org/das2/datum/format/ExponentialDatumFormatter.java b/dasCore/src/org/das2/datum/format/ExponentialDatumFormatter.java new file mode 100644 index 000000000..6ed5ea25d --- /dev/null +++ b/dasCore/src/org/das2/datum/format/ExponentialDatumFormatter.java @@ -0,0 +1,107 @@ +/* File: DefaultDatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 1, 2003, 4:45 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +import org.das2.util.NumberFormatUtil; +import org.das2.util.DasMath; + +import java.text.*; +import org.das2.datum.Datum; +import org.das2.datum.Units; + +/** Formats Datums forcing a given exponent and number of decimal places. + * This is useful for axes where each of the labels should have the same + * exponent. Zero is treated specially, just "0" is returned. This helps + * one to quickly identify zero on the axis. + * + * @author Jeremy Faden + */ +public class ExponentialDatumFormatter extends DatumFormatter { + + private DecimalFormat format; + + int digits; + int exponent; + NumberFormat mantFormat; + String mantFormatString; + + /* print with digits in the mantissa, use exponent for the exponent */ + /* mEe */ + public ExponentialDatumFormatter(int digits, int exponent) { + this.digits= digits; + this.exponent= exponent; + StringBuffer buff = new StringBuffer(digits+2).append("0"); + if ( digits>1 ) buff.append('.'); + for (int i = 1; i< digits; i++) { + buff.append('0'); + } + mantFormatString= buff.toString(); + this.mantFormat= NumberFormatUtil.getDecimalFormat( buff.toString() ); + } + + public String format(Datum datum) { + return format( datum, datum.getUnits() ) + " " + datum.getUnits(); + } + + public String format( Datum datum, Units units ) { + double x= datum.doubleValue(datum.getUnits()); + if ( x == 0. ) return "0."; + double exp= DasMath.exp10(exponent); + double mant= x/exp; + double tenToN= DasMath.exp10(digits); + mant= Math.round( mant * tenToN ) / tenToN; + return mantFormat.format(mant)+"E"+exponent; + } + + public String grannyFormat( Datum datum, Units units ) { + String format= format(datum,units); + if ( format.indexOf("E")!=-1 ) { + int iE= format.indexOf("E"); + StringBuffer granny = new StringBuffer(format.length() + 4); + String mant= format.substring(0,iE); + granny.append(mant).append("\u00d7"); + granny.append("10").append("!A").append(format.substring(iE+1)).append("!N"); + format = granny.toString(); + } + return format; + } + + public String grannyFormat( Datum datum ) { + String format= format(datum,datum.getUnits()); + if ( format.indexOf("E")!=-1 ) { + int iE= format.indexOf("E"); + StringBuffer granny = new StringBuffer(format.length() + 4); + String mant= format.substring(0,iE); + granny.append(mant).append("\u00d7"); + granny.append("10").append("!A").append(format.substring(iE+1)).append("!N"); + format = granny.toString(); + } + return format + " " +datum.getUnits(); + } + + public String toString() { + return mantFormatString + "E"+exponent; + } + +} diff --git a/dasCore/src/org/das2/datum/format/LatinPrefixDatumFormatter.java b/dasCore/src/org/das2/datum/format/LatinPrefixDatumFormatter.java new file mode 100644 index 000000000..60c643c0a --- /dev/null +++ b/dasCore/src/org/das2/datum/format/LatinPrefixDatumFormatter.java @@ -0,0 +1,98 @@ +/* File: DefaultDatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on October 1, 2003, 4:45 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +import org.das2.util.NumberFormatUtil; +import org.das2.util.DasMath; + +import java.text.*; +import java.util.Locale; +import org.das2.datum.Datum; +import org.das2.datum.Units; + +/** Formats Datums using K and M, etc labels and a specified precision. + * + * @author Jeremy Faden + */ +public class LatinPrefixDatumFormatter extends DatumFormatter { + + private DecimalFormat format; + + int digits; + int exponent; + + /* print with digits in the mantissa */ + /* m.mmK */ + public LatinPrefixDatumFormatter( int digits ) { + this.digits= digits; + } + + public String format(Datum datum) { + return format( datum, datum.getUnits() ) + " " + datum.getUnits(); + } + + public String format( Datum datum, Units units ) { + double x= datum.doubleValue(units); + if ( x == 0. ) return "0."; + int exponent= (int) DasMath.log10(1.000001*Math.abs(x)) / 3 * 3; + + String expString; + switch (exponent) { + case -18: expString="a"; break; + case -15: expString="f"; break; + case -12: expString="p"; break; + case -9: expString="n";break; + case -6: expString="\u03BC";break; // micro + case -3: expString="m";break; + case 0: expString="";break; + case 3: expString="k";break; + case 6: expString="M";break; + case 9: expString="G";break; + case 12: expString="T"; break; + default: expString=""; exponent=0; break; + } + + int sign= x < 0 ? -1 : 1; + + double exp= DasMath.exp10(exponent); + double mant= x / exp; + + int mantFracDigits= digits - (int)DasMath.log10(mant); + + StringBuffer buff = new StringBuffer(digits+2).append("0"); + if ( digits>1 ) buff.append('.'); + for (int i = 0; i< mantFracDigits; i++) { + buff.append('0'); + } + String mantFormatString= buff.toString(); + NumberFormat mantFormat= NumberFormatUtil.getDecimalFormat(buff.toString()); + + return mantFormat.format(mant) + expString; + } + + public String toString() { + return "EngineeringFormatter("+digits+" sig fig)"; + } + +} diff --git a/dasCore/src/org/das2/datum/format/TimeDatumFormatter.java b/dasCore/src/org/das2/datum/format/TimeDatumFormatter.java new file mode 100644 index 000000000..6d55255c2 --- /dev/null +++ b/dasCore/src/org/das2/datum/format/TimeDatumFormatter.java @@ -0,0 +1,350 @@ +/* File: TimeDatumFormatter.java + * Copyright (C) 2002-2003 The University of Iowa + * + * Created on September 25, 2003, 1:47 PM + * by Edward West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +import java.text.*; +import java.util.regex.*; +import org.das2.datum.CalendarTime; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.TimeUtil; + +/** + * + * @author Edward West + */ +public class TimeDatumFormatter extends DatumFormatter { + + /** Private constants for referencing an array of timestamp fields */ + private static final int YEAR_FIELD_INDEX = 0; + private static final int MONTH_FIELD_INDEX = 1; + private static final int DAY_FIELD_INDEX = 2; + private static final int DOY_FIELD_INDEX = 3; + private static final int HOUR_FIELD_INDEX = 4; + private static final int MINUTE_FIELD_INDEX = 5; + private static final int SECONDS_FIELD_INDEX = 6; + private static final int TIMESTAMP_FIELD_COUNT = 7; + + /** + * yyyy-MM-dd'T'HH:mm:ss.SSS'Z + */ + public static final TimeDatumFormatter DEFAULT; + /** + * yyyy-MM-dd + */ + public static final TimeDatumFormatter DAYS; + /** + * yyyy + */ + public static final TimeDatumFormatter YEARS; + /** + * yyyy-MM + */ + public static final TimeDatumFormatter MONTHS; + /** + * yyyy-MM-dd HH:'00' + */ + public static final TimeDatumFormatter HOURS; + /** + * HH:mm + */ + public static final TimeDatumFormatter MINUTES; + /** + * HH:mm:ss + */ + public static final TimeDatumFormatter SECONDS; + /** + * HH:mm:ss.SSS + */ + public static final TimeDatumFormatter MILLISECONDS; + /** + * HH:mm:ss.SSSSSS + */ + public static final TimeDatumFormatter MICROSECONDS; + /** + * HH:mm:ss.SSSSSSSSS + */ + public static final TimeDatumFormatter NANOSECONDS; + + //Initialize final constants + static { + try { + DEFAULT = new TimeDatumFormatter("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + YEARS= new TimeDatumFormatter("yyyy"); + MONTHS= new TimeDatumFormatter("yyyy-MM"); + DAYS = new TimeDatumFormatter("yyyy-MM-dd"); + HOURS = new TimeDatumFormatter("yyyy-MM-dd HH:'00'"); + MINUTES = new TimeDatumFormatter("HH:mm"); + SECONDS = new TimeDatumFormatter("HH:mm:ss"); + MILLISECONDS = new TimeDatumFormatter("HH:mm:ss.SSS"); + MICROSECONDS = new TimeDatumFormatter("HH:mm:ss.SSSSSS"); + NANOSECONDS = new TimeDatumFormatter("HH:mm:ss.SSSSSSSSS"); + } catch (ParseException pe) { + throw new RuntimeException(pe); + } + } + + private String formatString; + + private MessageFormat format; + + private int[] scaleSeconds; + + /** Creates a new instance of TimeDatumFormatter */ + public TimeDatumFormatter(String formatString) throws ParseException { + this.formatString = formatString; + if ( formatString.contains( "%" ) ) { + format = new MessageFormat(parseTimeFormatStringPercent(formatString)); + } else { + format = new MessageFormat(parseTimeFormatString(formatString)); + } + } + + /** + * returns a TimeDatumFormatter suitable for the specified scale and context. + * Context may be null to indicate that the formatted string will be interpreted + * outside of any context. + * @param scale the length we wish to represent, such as TimeUtil.HOUR + * @param context the context for the formatter, or null if the formatted string + * will be interpreted outside of any context. + * @throws IllegalArgumentException if the scale is TimeUtil.NANOS or is not found in TimeUtil. + */ + public static TimeDatumFormatter formatterForScale(CalendarTime.Step scale, DatumRange context ) { + try { + if ( context!=null ) { + switch ( scale ) { + case YEAR: return YEARS; + case MONTH: return MONTHS; + case DAY: return DAYS; + case HOUR: return MINUTES; + case MINUTE: return MINUTES; + case SECOND: return SECONDS; + case MILLISEC: return MILLISECONDS; + case MICROSEC: return MICROSECONDS; + case NANOSEC: return NANOSECONDS; + default: throw new IllegalArgumentException("unsupported scale: "+scale); + } + } else { + switch ( scale ) { + case YEAR: return YEARS; + case MONTH: return MONTHS; + case DAY: return DAYS; + case HOUR: return HOURS; + case MINUTE: return new TimeDatumFormatter("yyyy-MM-dd HH:mm"); + case SECOND: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss"); + case MILLISEC: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss.SSS"); + case MICROSEC: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss.SSSSSS"); + case NANOSEC: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"); + default: throw new IllegalArgumentException("unsupported scale: "+scale); + } + } + } catch ( ParseException e ) { + throw new RuntimeException(e); + } + } + + public String toString() { + return formatString; + } + + public String format(Datum datum) { + if ( datum.isFill() ) return "fill"; + CalendarTime ct = new CalendarTime(datum); + Number[] array = timeStructToArray(ct); + return format.format(array); + } + + protected Format getFormat() { + return format; + } + + protected String parseTimeFormatString(String input) throws ParseException { + final String formatPattern = "(([yMDdHmsS])\\2*)"; + final String delimiterPattern = "([-/:.,_ \t]+)"; + final String literalPattern = "('(?:[^']|'')*')"; + Pattern token = Pattern.compile( + formatPattern + "|" + delimiterPattern + "|" + literalPattern + ); + int from = 0; + StringBuffer formatString = new StringBuffer(); + Matcher matcher = token.matcher(input); + while (matcher.find(from)) { + int start = matcher.start(); + if (start > from) { + char[] dots = new char[start + 1]; + java.util.Arrays.fill(dots, from, start, '.'); + dots[from] = '^'; + dots[start] = '^'; + StringBuffer errorString = new StringBuffer("Unrecognized sub-pattern\n"); + errorString.append(input).append("\n"); + errorString.append(dots); + throw new ParseException(errorString.toString(), from); + } + String format = matcher.group(1); + String delimiter = matcher.group(3); + String literal = matcher.group(4); + if (format != null) { + switch (format.charAt(0)) { + case 'y': { + appendSubFormat(formatString, YEAR_FIELD_INDEX, format.length()); + } break; + case 'M': { + appendSubFormat(formatString, MONTH_FIELD_INDEX, format.length()); + } break; + case 'D': { + appendSubFormat(formatString, DOY_FIELD_INDEX, format.length()); + } break; + case 'd': { + appendSubFormat(formatString, DAY_FIELD_INDEX, format.length()); + } break; + case 'H': { + appendSubFormat(formatString, HOUR_FIELD_INDEX, format.length()); + } break; + case 'm': { + appendSubFormat(formatString, MINUTE_FIELD_INDEX, format.length()); + } break; + case 's': { + appendSubFormat(formatString, SECONDS_FIELD_INDEX, format.length()); + }break; + case 'S': { + int digitCount = format.length(); + int fieldIndex = addScaleFactor(digitCount); + appendSubFormat(formatString, fieldIndex, digitCount); + } + break; + default: break; + } + } else if (delimiter != null) { + formatString.append(delimiter); + } else if (literal != null) { + literal = literal.substring(1, literal.length() - 1); + literal = literal.replaceAll("''", "'"); + formatString.append(literal); + } + from = matcher.end(); + } + return formatString.toString(); + } + + /** + * create the message format, based on %Y, %m, %d format specification. + * @param input + * @return + * @throws java.text.ParseException + */ + protected String parseTimeFormatStringPercent(String format) throws ParseException { + StringBuffer formatString= new StringBuffer(); + String[] ss= format.split("%"); + formatString.append(ss[0]); + int offset= ss[0].length(); + + for ( int i=1; i + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.datum.format; + +/** + * + * @author Edward West + */ +public class TimeDatumFormatterFactory extends DatumFormatterFactory { + + private static TimeDatumFormatterFactory factory; + + /** Creates a new instance of TimeDatumFormatterFactory */ + protected TimeDatumFormatterFactory() {} + + public DatumFormatter defaultFormatter() { + return TimeDatumFormatter.DEFAULT; + } + + public DatumFormatter newFormatter(String format) throws java.text.ParseException { + return new TimeDatumFormatter(format); + } + + /** Get an instance of this factory. */ + public static TimeDatumFormatterFactory getInstance() { + //This isn't thread safe, but who cares. Instances are small and + //functionally identical. + if (factory == null) { + factory = new TimeDatumFormatterFactory(); + } + return factory; + } + +} diff --git a/dasCore/src/org/das2/datum/format/package.html b/dasCore/src/org/das2/datum/format/package.html new file mode 100644 index 000000000..0197255f2 --- /dev/null +++ b/dasCore/src/org/das2/datum/format/package.html @@ -0,0 +1,19 @@ + +

    + Classes for formatting a Datum to a String. The DatumFormatter +base class identifies four methods: format(Datum), format( Datum, Units ), +grannyFormat( Datum ), and grannyFormat( Datum, Units ). format(Datum) +should return a string accurately representing the datum out-of-context, +generally meaning the datum's units are displayed along with the double +value. format(Datum,Units) means that the string will be used in the context +of the given units. For example, say you want to output an ascii table +with column headers. Each column header indicates a title and a unit, for +example, "delay(sec)." Then to format a datum for displaying in this column +format( datum, Units.seconds ) would return an appropriate number. The +grannyFormat methods work the same way, except the formatter may return +a "granny string" (see GrannyTextRenderer) like "cm!e-3!n" +

    format(Datum) is the +only abstract class, and by default all other methods simply return the result +of the format(Datum) method.

    + + \ No newline at end of file diff --git a/dasCore/src/org/das2/datum/package.html b/dasCore/src/org/das2/datum/package.html new file mode 100644 index 000000000..295af20da --- /dev/null +++ b/dasCore/src/org/das2/datum/package.html @@ -0,0 +1,18 @@ + +

    Provides classes representing physical quanties. The Units class +defines an enumeration of physical units such as Units.hertz and Units.microseconds. It +also contains a convention for identifying locations in time with units like +Units.t1970, the number of microseconds elapsed since midnight, January 1, 1970. +The Units class also contains a registry of Units and the conversions between them. +

    +

    A Datum is a number in the context of a Unit, for example "15 microseconds.". + A Datum object has methods for formatting itself as a String, representing + itsself as a double in the context of another Unit, and mathematical +operators that allow simple calculations to be made at the physical quantities. +Also a Datum's precision can be limited to improve formatting. +

    +

    A DatumRange is a pair of Datums that identify a range in the physical space +of a Unit. An example of a formatted DatumRange is "January, 2005" or "0 to 50Hz."

    +

    Also there are utility classes for parsing and formatting times.

    + +

    \ No newline at end of file diff --git a/dasCore/src/org/das2/datum/swing/DatumJFormatterFactory.java b/dasCore/src/org/das2/datum/swing/DatumJFormatterFactory.java new file mode 100644 index 000000000..35fdd878a --- /dev/null +++ b/dasCore/src/org/das2/datum/swing/DatumJFormatterFactory.java @@ -0,0 +1,69 @@ +/* + * DatumJFormatter.java + * + * Created on June 12, 2007, 9:11 AM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package org.das2.datum.swing; + +import java.text.ParseException; +import javax.swing.JFormattedTextField; +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.DefaultDatumFormatterFactory; + +/** + * + * @author eew + */ +public class DatumJFormatterFactory extends JFormattedTextField.AbstractFormatterFactory { + + DatumFormatter format; + SwingFormatter wrapper; + Units units; + Units implicitUnits = Units.dimensionless; + + public DatumJFormatterFactory() { + this(DefaultDatumFormatterFactory.getInstance().defaultFormatter()); + } + + /** Creates a new instance of DatumJFormatter */ + public DatumJFormatterFactory(DatumFormatter format) { + this.format = format; + this.wrapper = new SwingFormatter(); + } + + public void explicitUnits(Units u) { + units = u; + } + + public JFormattedTextField.AbstractFormatter getFormatter(JFormattedTextField tf) { + return wrapper; + } + + private class SwingFormatter extends JFormattedTextField.AbstractFormatter { + public Object stringToValue(String text) throws ParseException { + Units u = units != null ? units : implicitUnits; + return u.parse(text); + } + + public String valueToString(Object value) throws ParseException { + Datum datum = (Datum)value; + if (datum == null) { + throw new ParseException("Null values not allowed.", -1); + } + implicitUnits = datum.getUnits(); + if (units != null) { + return format.format(datum, units); + } + else { + return format.format(datum); + } + } + } + +} diff --git a/dasCore/src/org/das2/datum/swing/SwingDatumFormatter.java b/dasCore/src/org/das2/datum/swing/SwingDatumFormatter.java new file mode 100644 index 000000000..4f1d4c217 --- /dev/null +++ b/dasCore/src/org/das2/datum/swing/SwingDatumFormatter.java @@ -0,0 +1,61 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.datum.swing; + +import org.das2.datum.Datum; +import org.das2.datum.Units; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.DefaultDatumFormatterFactory; +import java.text.ParseException; +import javax.swing.JFormattedTextField.AbstractFormatter; +import javax.swing.JFormattedTextField.AbstractFormatterFactory; +import javax.swing.text.DefaultFormatterFactory; + +/** + * + * @author eew + */ +public class SwingDatumFormatter extends AbstractFormatter { + + private Units units; + + private DatumFormatter formatter; + + public SwingDatumFormatter() { + formatter = DefaultDatumFormatterFactory.getInstance().defaultFormatter(); + } + + @Override + public Object stringToValue(String text) throws ParseException { + if (units == null) { + units = Units.dimensionless; + } + return units.parse(text); + } + + @Override + public String valueToString(Object value) throws ParseException { + Datum d = (Datum)value; + if (d == null) { + return ""; + } + units = d.getUnits(); + return formatter.format(d, units); + } + + public Units getUnits() { + return units; + } + + public void setUnits(Units units) { + this.units = units; + } + + public static final AbstractFormatterFactory newFactory() { + return new DefaultFormatterFactory(new SwingDatumFormatter()); + } + +} diff --git a/dasCore/src/org/das2/event/AbstractDragRenderer.java b/dasCore/src/org/das2/event/AbstractDragRenderer.java deleted file mode 100644 index 261087959..000000000 --- a/dasCore/src/org/das2/event/AbstractDragRenderer.java +++ /dev/null @@ -1,72 +0,0 @@ -/* File: EmptyDragRenderer.java - * Copyright (C) 2002-2013 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.event; - -import java.awt.Graphics; -import java.awt.Point; - -/** - * Do-nothing drag renderer and extension point for other DragRenderers. - * @author jbf - */ -public abstract class AbstractDragRenderer implements DragRenderer -{ - - public AbstractDragRenderer(){} - - /** - * return the event for the gesture. A mouse box event is returned. - * @param source - * @param p1 - * @param p2 - * @param isModified - * @return - */ - public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { - return new MouseBoxEvent( source, p1, p2, isModified ); - } - - /** - * this is not used, and is left over from an old version of the library. - * @param g - */ - public void clear(Graphics g) { - } - - /** - * indicates that MM.mousePointSelected() should called as new mouse events - * come in. - */ - public boolean isPointSelection() { - return true; - } - - /** - * range selection events should be fired during drag. - */ - public boolean isUpdatingDragSelection() { - return false; - } - -} diff --git a/dasCore/src/org/das2/event/AngleSelectionDragRenderer.java b/dasCore/src/org/das2/event/AngleSelectionDragRenderer.java new file mode 100644 index 000000000..ff1edb801 --- /dev/null +++ b/dasCore/src/org/das2/event/AngleSelectionDragRenderer.java @@ -0,0 +1,41 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.das2.event; + +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; + +/** + * + * @author jbf + */ +public class AngleSelectionDragRenderer implements DragRenderer{ + + public Rectangle[] renderDrag(Graphics g, Point p1, Point p2) { + g.drawLine( p1.x, p1.y, p2.x, p2.y ); + Rectangle r= new Rectangle(p1); + r.add(p2); + return new Rectangle[] { r }; + } + + public void clear(Graphics g) { + + } + + public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { + return new MouseBoxEvent( source, p1, p2, isModified ); + } + + public boolean isPointSelection() { + return false; + } + + public boolean isUpdatingDragSelection() { + return true; + } + +} diff --git a/dasCore/src/org/das2/event/AngleSlicerMouseModule.java b/dasCore/src/org/das2/event/AngleSlicerMouseModule.java new file mode 100644 index 000000000..72fb3827d --- /dev/null +++ b/dasCore/src/org/das2/event/AngleSlicerMouseModule.java @@ -0,0 +1,83 @@ +/* File: HorizontalSlicerMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.DataSetConsumer; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; +import org.das2.graph.Renderer; +/** + * + * @author jbf + */ +public class AngleSlicerMouseModule extends MouseModule { + + private DasAxis xaxis; + private DasAxis yaxis; + + private TableDataSetConsumer dataSetConsumer; + + private DataPointSelectionEvent de; + + private javax.swing.event.EventListenerList listenerList = null; + + public AngleSlicerMouseModule( DasPlot parent, TableDataSetConsumer dataSetConsumer, DasAxis xaxis, DasAxis yaxis) { + this( parent, (DataSetConsumer)dataSetConsumer, xaxis, yaxis ); + } + + protected AngleSlicerMouseModule(DasPlot parent, DataSetConsumer dataSetConsumer, DasAxis xaxis, DasAxis yaxis) { + super( parent, new AngleSelectionDragRenderer(), "Angle Slice" ); + + if (!(dataSetConsumer instanceof TableDataSetConsumer)) { + throw new IllegalArgumentException("dataSetConsumer must be an XTaggedYScanDataSetConsumer"); + } + this.dataSetConsumer= ( TableDataSetConsumer)dataSetConsumer; + this.xaxis= xaxis; + this.yaxis= yaxis; + this.de= new DataPointSelectionEvent(this,null,null); + + } + + public static AngleSlicerMouseModule create(DasPlot parent) { + DasAxis xaxis= parent.getXAxis(); + DasAxis yaxis= parent.getYAxis(); + return new AngleSlicerMouseModule(parent,parent,xaxis,yaxis); + } + + public static AngleSlicerMouseModule create(Renderer renderer) + { + DasPlot parent= renderer.getParent(); + DasAxis xaxis= parent.getXAxis(); + DasAxis yaxis= parent.getYAxis(); + return new AngleSlicerMouseModule(parent,renderer,xaxis,yaxis); + } + + @Override + public void mouseRangeSelected(MouseDragEvent e0) { + MouseBoxEvent e= (MouseBoxEvent)e0; + + } + +} diff --git a/dasCore/src/org/das2/event/AnnotatorMouseModule.java b/dasCore/src/org/das2/event/AnnotatorMouseModule.java index 31e541709..f9a04be3a 100644 --- a/dasCore/src/org/das2/event/AnnotatorMouseModule.java +++ b/dasCore/src/org/das2/event/AnnotatorMouseModule.java @@ -13,16 +13,14 @@ import org.das2.graph.DasRow; /** - * Draw a box and add an annotation. + * * @author Jeremy */ public class AnnotatorMouseModule extends MouseModule { DasCanvas canvas; - /** Creates a new instance of AnnotatorMouseModule - * @param parent typically a plot. - */ + /** Creates a new instance of AnnotatorMouseModule */ public AnnotatorMouseModule( DasCanvasComponent parent ) { super( parent, new BoxRenderer(parent), "Annotate" ); this.canvas= (DasCanvas)parent.getParent(); @@ -32,15 +30,10 @@ public DasCanvas getCanvas() { return canvas; } - @Override public void mouseRangeSelected(MouseDragEvent e) { super.mouseRangeSelected(e); System.out.println(e); - if ( !( e instanceof MouseBoxEvent ) ) { - logger.warning("Expected MouseBoxEvent"); - return; - } MouseBoxEvent me= (MouseBoxEvent) e; double n; diff --git a/dasCore/src/org/das2/event/BoxRangeSelectorMouseModule.java b/dasCore/src/org/das2/event/BoxRangeSelectorMouseModule.java index 09aa8aaab..6b2cff539 100755 --- a/dasCore/src/org/das2/event/BoxRangeSelectorMouseModule.java +++ b/dasCore/src/org/das2/event/BoxRangeSelectorMouseModule.java @@ -72,8 +72,8 @@ public void mouseRangeSelected(MouseDragEvent e0) { Datum xMin = xAxis.invTransform(e.getXMinimum()); Datum xMax = xAxis.invTransform(e.getXMaximum()); - Datum yMin = yAxis.invTransform(e.getYMinimum()); - Datum yMax = yAxis.invTransform(e.getYMaximum()); + Datum yMin = yAxis.invTransform(e.getYMaximum()); + Datum yMax = yAxis.invTransform(e.getYMinimum()); BoxSelectionEvent evt = new BoxSelectionEvent(this, new DatumRange( xMin, xMax ), new DatumRange( yMin, yMax) ); if (consumer != null) { evt.setDataSet(consumer.getConsumedDataSet()); diff --git a/dasCore/src/org/das2/event/BoxSelectionEvent.java b/dasCore/src/org/das2/event/BoxSelectionEvent.java index d77503747..f2ed22a2e 100755 --- a/dasCore/src/org/das2/event/BoxSelectionEvent.java +++ b/dasCore/src/org/das2/event/BoxSelectionEvent.java @@ -23,17 +23,16 @@ package org.das2.event; - +import org.das2.dataset.DataSet; import org.das2.datum.Datum; import org.das2.datum.DatumRange; import java.util.HashMap; -import org.virbo.dataset.QDataSet; /** - * This is the range analog to the DataPointSelectionEvent. The - * DataPointSelectionEvent is a point, and this is a box. + * This is the range anolog to the DataPointSelectionEvent. The DPSE is a point, + * and this is a box. * - * Note that it's acceptable to have null xrange and yrange, so that the same + * Note that it's acceptible to have null xrange and yrange, so that the same * code can support a variety of applications. It's left to the programmer to * see that these are used consistently. * @@ -45,26 +44,21 @@ public class BoxSelectionEvent extends DasEvent { private DatumRange yrange; private Datum finishx, finishy; private Datum startx, starty; - private QDataSet ds; + private DataSet ds; private HashMap planes; - + /** - * create the BoxSelectionEvent with additional planes of data. - * @param source the object creating this event, for example the BoxSelectorMouseModule. - * @param xrange the horizontal range - * @param yrange the vertical range + * @deprecated use BoxSelectionEvent( Object, DatumRange, DatumRange ); */ + public BoxSelectionEvent(Object source, org.das2.datum.Datum xMin, org.das2.datum.Datum xMax, org.das2.datum.Datum yMin, org.das2.datum.Datum yMax) { + this( source, xMin.le(xMax) ? new DatumRange( xMin, xMax ) : new DatumRange( xMax, xMin ), + yMin.le(yMax) ? new DatumRange( yMin, yMax ) : new DatumRange( yMax, yMin ) ); + } + public BoxSelectionEvent( Object source, DatumRange xrange, DatumRange yrange ) { this( source, xrange, yrange, null ); } - /** - * create the BoxSelectionEvent with additional planes of data. - * @param source the object creating this event, for example the BoxSelectorMouseModule. - * @param xrange the horizontal range - * @param yrange the vertical range - * @param planes a map from String to Object containing arbitrary metadata. - */ public BoxSelectionEvent( Object source, DatumRange xrange, DatumRange yrange, HashMap planes ) { super( source ); this.xrange= xrange; @@ -72,90 +66,72 @@ public BoxSelectionEvent( Object source, DatumRange xrange, DatumRange yrange, H this.planes= planes; } - /** - * set the end coordinates of the mouse release. - * @param x the release coordinate X - * @param y the release coordinate Y - */ public void setFinish( Datum x, Datum y ) { this.finishx = x; this.finishy = y; } - /** - * get the X coordinate of the mouse button release - * @return the release coordinate X - */ public Datum getFinishX() { return this.finishx; } - /** - * get the Y coordinate of the mouse button release - * @return the release coordinate Y - */ public Datum getFinishY() { return this.finishy; } - /** - * set the coordinates of the mouse button press - * @param x the x coordinate - * @param y the y coordinate - */ public void setStart( Datum x, Datum y ) { this.startx = x; this.starty = y; } - - /** - * get the X coordinate or the mouse button press - * @return the X coordinate or the mouse button press - */ + public Datum getStartX() { return this.startx; } - /** - * get the Y coordinate or the mouse button press - * @return the Y coordinate or the mouse button press - */ public Datum getStartY() { return this.starty; } + + /** + * @deprecated use getXRange().min(); + */ + public org.das2.datum.Datum getXMinimum() { + if ( xrange!=null ) return xrange.min(); else return null; + } /** - * get the X data range of the gesture - * @return the X data range of the gesture + * @deprecated use getXRange().max(); */ - public DatumRange getXRange() { - return xrange; + public org.das2.datum.Datum getXMaximum() { + if ( xrange!=null ) return xrange.max(); else return null; } /** - * get the Y data range of the gesture - * @return the Y data range of the gesture + * @deprecated use getYRange().min(); + */ + public org.das2.datum.Datum getYMinimum() { + if ( yrange!=null ) return yrange.min(); else return null; + } + + /** + * @deprecated use getYRange().max(); */ + public org.das2.datum.Datum getYMaximum() { + if ( yrange!=null ) return yrange.max(); else return null; + } + + public DatumRange getXRange() { + return xrange; + } + public DatumRange getYRange() { return yrange; } - /** - * get the data attached to the plane name. This allows applications - * to attach additional data to the event. For example, the BoxSelectorMouseModule - * attaches the key pressed. - * - * @param plane, e.g. 'keyChar' - * @return the value associated with the plane, or null. - */ public Object getPlane( String plane ) { return planes==null ? null : planes.get(plane); } - /** - * return the list of additional data planes attached to this event. - * @return - */ public String[] getPlaneIds() { if ( planes==null ) { return new String[0]; @@ -164,23 +140,14 @@ public String[] getPlaneIds() { } } - /** - * attach a dataset to this event. - * @param ds a dataset for this event. - */ - public void setDataSet(QDataSet ds) { + public void setDataSet(DataSet ds) { this.ds = ds; } - /** - * get the dataset attached to this event. - * @return the dataset attached to this event. - */ - public QDataSet getDataSet() { + public DataSet getDataSet() { return ds; } - @Override public String toString() { return "[BoxSelectionEvent x: "+xrange+", y: "+yrange+"]"; } diff --git a/dasCore/src/org/das2/event/BoxSelectorMouseModule.java b/dasCore/src/org/das2/event/BoxSelectorMouseModule.java index 9fc96cd23..f18e545f3 100644 --- a/dasCore/src/org/das2/event/BoxSelectorMouseModule.java +++ b/dasCore/src/org/das2/event/BoxSelectorMouseModule.java @@ -7,7 +7,6 @@ */ package org.das2.event; -import java.util.logging.Level; import org.das2.dataset.DataSetConsumer; import org.das2.datum.Datum; import org.das2.datum.DatumRange; @@ -27,13 +26,10 @@ * crosshair. * * Three properties control when BoxSelectionEvents are to be fired: - *
      - *
    • dragEvents, as the mouse is dragged, - *
    • keyEvents, when a key is pressed. (The key is the "keyChar" plane of the event) - *
    • releaseEvents, when the mouse is released. (false by default) - *
    - * This is intended to be used as a base class for other slicers which need a - * range to be selected in X, Y, or both. + * dragEvents as the mouse is dragged, + * keyEvents when a key is pressed. (The key is the "keyChar" plane of the event) + * releaseEvents when the mouse is released. (false by default) + * * @see BoxRenderer * @author Jeremy */ @@ -41,31 +37,16 @@ public class BoxSelectorMouseModule extends MouseModule { DasAxis xaxis, yaxis; DataSetConsumer dataSetConsumer; - javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList(); + javax.swing.event.EventListenerList listenerList = null; MouseDragEvent lastMouseEvent; /** when true, box selections are remembered, and tweaks to corners are allowed. */ boolean tweakable = false; BoxSelectionEvent lastSelectionEvent = null; - /** - * create a BoxSelectorMouseModule - * @param parent the plot component - * @param label the label for this mouseModule. - * @return new BoxSelectorMouseModule - */ public static BoxSelectorMouseModule create( DasPlot parent, String label ) { - return new BoxSelectorMouseModule( parent, parent.getXAxis(), parent.getYAxis(), null, new BoxRenderer(parent,true), label ); + return new BoxSelectorMouseModule( parent, parent.getXAxis(), parent.getYAxis(), null, new BoxRenderer(parent), label ); } - /** - * create a new BoxSelectorMouseModule - * @param parent the parent component - * @param xAxis - * @param yAxis - * @param consumer used by some subclasses, such as CutoffMouseModule. - * @param dragRenderer the drag renderer to use, typically a BoxRenderer but needn't be. - * @param label the label for this mouseModule. - */ public BoxSelectorMouseModule(DasCanvasComponent parent, DasAxis xAxis, DasAxis yAxis, DataSetConsumer consumer, DragRenderer dragRenderer, String label) { @@ -88,7 +69,7 @@ private Datum[] checkTweak(Point p) { double nx = DatumRangeUtil.normalize(lastSelectionEvent.getXRange(), xaxis.invTransform(p.getX())); double ny = DatumRangeUtil.normalize(lastSelectionEvent.getYRange(), yaxis.invTransform(p.getY())); - logger.log(Level.FINE, "{0} {1}", new Object[]{nx, ny}); + System.err.println("" + nx + " " + ny); Datum otherx = null; Datum othery = null; @@ -192,7 +173,6 @@ private BoxSelectionEvent getBoxSelectionEvent(MouseDragEvent mde) { return evt; } - @Override public void mouseRangeSelected(MouseDragEvent e) { lastMouseEvent = e; if (keyEvents) { @@ -203,31 +183,32 @@ public void mouseRangeSelected(MouseDragEvent e) { } } - @Override public void keyPressed(KeyEvent e) { + int keyCode = e.getKeyCode(); if (lastMouseEvent != null) { BoxSelectionEvent dpse = getBoxSelectionEvent(lastMouseEvent); HashMap planes = new HashMap(); planes.put("keyChar", String.valueOf(e.getKeyChar())); - BoxSelectionEvent dpse2 = new BoxSelectionEvent(this, dpse.getXRange(), dpse.getYRange(), planes); - dpse2.setStart( dpse.getStartX(), dpse.getStartY() ); - dpse2.setFinish( dpse.getFinishX(), dpse.getFinishY() ); - fireBoxSelectionListenerBoxSelected(dpse2); + dpse = new BoxSelectionEvent(this, dpse.getXRange(), dpse.getYRange(), planes); + fireBoxSelectionListenerBoxSelected(dpse); } } /** Registers BoxSelectionListener to receive events. * @param listener The listener to register. */ - public void addBoxSelectionListener(org.das2.event.BoxSelectionListener listener) { + public synchronized void addBoxSelectionListener(org.das2.event.BoxSelectionListener listener) { + if (listenerList == null) { + listenerList = new javax.swing.event.EventListenerList(); + } listenerList.add(org.das2.event.BoxSelectionListener.class, listener); } /** Removes BoxSelectionListener from the list of listeners. * @param listener The listener to remove. */ - public void removeBoxSelectionListener(org.das2.event.BoxSelectionListener listener) { + public synchronized void removeBoxSelectionListener(org.das2.event.BoxSelectionListener listener) { listenerList.remove(org.das2.event.BoxSelectionListener.class, listener); } @@ -236,15 +217,16 @@ public void removeBoxSelectionListener(org.das2.event.BoxSelectionListener liste * @param event The event to be fired */ protected void fireBoxSelectionListenerBoxSelected(BoxSelectionEvent event) { - Object[] listeners; - listeners = listenerList.getListenerList(); + if (listenerList == null) { + return; + } + Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == org.das2.event.BoxSelectionListener.class) { ((org.das2.event.BoxSelectionListener) listeners[i + 1]).BoxSelected(event); } } } - /** * Holds value of property dragEvents. */ @@ -265,7 +247,6 @@ public boolean isDragEvents() { public void setDragEvents(boolean dragEvents) { this.dragEvents = dragEvents; } - /** * Holds value of property keyEvents. */ @@ -276,6 +257,7 @@ public void setDragEvents(boolean dragEvents) { * @return Value of property keyEvents. */ public boolean isKeyEvents() { + return this.keyEvents; } @@ -287,15 +269,10 @@ public void setKeyEvents(boolean keyEvents) { this.keyEvents = keyEvents; } - @Override public void mouseReleased(java.awt.event.MouseEvent e) { super.mouseReleased(e); if (releaseEvents) { - if ( lastMouseEvent!=null ) { - fireBoxSelectionListenerBoxSelected(getBoxSelectionEvent(lastMouseEvent)); - } else { - logger.fine("no mouse event to fire"); - } + fireBoxSelectionListenerBoxSelected(getBoxSelectionEvent(lastMouseEvent)); } } /** @@ -308,6 +285,7 @@ public void mouseReleased(java.awt.event.MouseEvent e) { * @return Value of property releaseEvents. */ public boolean isReleaseEvents() { + return this.releaseEvents; } @@ -316,6 +294,7 @@ public boolean isReleaseEvents() { * @param releaseEvents New value of property releaseEvents. */ public void setReleaseEvents(boolean releaseEvents) { + this.releaseEvents = releaseEvents; } } diff --git a/dasCore/src/org/das2/event/BoxZoomDialog.form b/dasCore/src/org/das2/event/BoxZoomDialog.form new file mode 100644 index 000000000..7f2e3f840 --- /dev/null +++ b/dasCore/src/org/das2/event/BoxZoomDialog.form @@ -0,0 +1,165 @@ + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dasCore/src/org/das2/event/BoxZoomDialog.java b/dasCore/src/org/das2/event/BoxZoomDialog.java new file mode 100644 index 000000000..635067620 --- /dev/null +++ b/dasCore/src/org/das2/event/BoxZoomDialog.java @@ -0,0 +1,229 @@ +/* + * BoxZoomDialog.java + * + * Created on May 16, 2007, 7:36 AM + */ + +package org.das2.event; + +import javax.swing.JOptionPane; + +/** + * + * @author jbf + */ +public class BoxZoomDialog extends javax.swing.JPanel { + + BoxZoomMouseModule module; + + /** Creates new form BoxZoomDialog */ + public BoxZoomDialog( BoxZoomMouseModule module ) { + initComponents(); + this.module= module; + zoomYButton.setAction( module.getZoomYAction() ); + zoomXButton.setAction( module.getZoomXAction() ); + zoomBoxButton.setAction( module.getZoomBoxAction() ); + } + + String getXRange( ) { + return xRangeTextField.getText(); + } + + String getYRange( ) { + return yRangeTextField.getText(); + } + + public boolean isAutoBoxZoom() { + return autoCheckBox.isSelected(); + } + + public boolean isConstrainProportions() { + return constrainProportionsCheckBox.isSelected(); + } + + public boolean isDisablePopup() { + return disablePopupCheckBox.isSelected(); + } + + public void setDisablePopup( boolean v ) { + disablePopupCheckBox.setSelected(v); + } + + public void setConstrainProportions( boolean v ) { + constrainProportionsCheckBox.setSelected(v); + } + + public void setAutoBoxZoom( boolean v ) { + autoCheckBox.setSelected(v); + } + + public void setXRange( String s ) { + this.xRangeTextField.setText(s); + } + + public void setYRange( String s ) { + this.yRangeTextField.setText(s); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + jLabel1 = new javax.swing.JLabel(); + xRangeTextField = new javax.swing.JTextField(); + jLabel2 = new javax.swing.JLabel(); + yRangeTextField = new javax.swing.JTextField(); + zoomYButton = new javax.swing.JButton(); + zoomBoxButton = new javax.swing.JButton(); + zoomXButton = new javax.swing.JButton(); + autoCheckBox = new javax.swing.JCheckBox(); + disablePopupCheckBox = new javax.swing.JCheckBox(); + constrainProportionsCheckBox = new javax.swing.JCheckBox(); + + jLabel1.setText("X:"); + + jLabel2.setText("Y:"); + + zoomYButton.setText("z
    o
    o
    m
    Y"); + zoomYButton.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + zoomBoxButton.setText("Zoom
    Box"); + + zoomXButton.setText("zoom X"); + + autoCheckBox.setText("auto box zoom"); + autoCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + autoCheckBox.setMargin(new java.awt.Insets(0, 0, 0, 0)); + autoCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + autoCheckBoxActionPerformed(evt); + } + }); + + disablePopupCheckBox.setText("disable popup"); + disablePopupCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + disablePopupCheckBox.setMargin(new java.awt.Insets(0, 0, 0, 0)); + disablePopupCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + disablePopupCheckBoxActionPerformed(evt); + } + }); + + constrainProportionsCheckBox.setText("constrain proportions"); + constrainProportionsCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + constrainProportionsCheckBox.setMargin(new java.awt.Insets(0, 0, 0, 0)); + constrainProportionsCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + constrainProportionsCheckBoxActionPerformed(evt); + } + }); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(jLabel1) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(xRangeTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 155, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(layout.createSequentialGroup() + .add(jLabel2) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(yRangeTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(layout.createSequentialGroup() + .add(10, 10, 10) + .add(zoomYButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 21, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(zoomXButton) + .add(zoomBoxButton, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 109, Short.MAX_VALUE))) + .add(constrainProportionsCheckBox) + .add(autoCheckBox) + .add(layout.createSequentialGroup() + .add(17, 17, 17) + .add(disablePopupCheckBox))))) + .addContainerGap()) + ); + + layout.linkSize(new java.awt.Component[] {xRangeTextField, yRangeTextField}, org.jdesktop.layout.GroupLayout.HORIZONTAL); + + layout.linkSize(new java.awt.Component[] {zoomBoxButton, zoomXButton}, org.jdesktop.layout.GroupLayout.HORIZONTAL); + + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(jLabel1) + .add(xRangeTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(jLabel2) + .add(yRangeTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(zoomBoxButton) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(zoomXButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 31, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(zoomYButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 80, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(constrainProportionsCheckBox) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(autoCheckBox) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(disablePopupCheckBox) + .addContainerGap()) + ); + }//
    //GEN-END:initComponents + + private void disablePopupCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_disablePopupCheckBoxActionPerformed + if ( disablePopupCheckBox.isSelected() ) { + int result= JOptionPane.showConfirmDialog(this, + "Hitting OK will disable this popup and will perform the zoom. " + + "Popup may be re-enabled via the plot's property editor.", + "hiding box zoom popup", + JOptionPane.OK_CANCEL_OPTION ); + if ( result==JOptionPane.OK_OPTION ) { + module.setAutoUpdate(true); + module.zoomBox(); + this.module.dialog.setVisible(false); + } else { + disablePopupCheckBox.setSelected(false); + } + } + module.guiChanged(); + }//GEN-LAST:event_disablePopupCheckBoxActionPerformed + + private void constrainProportionsCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_constrainProportionsCheckBoxActionPerformed + module.guiChanged(); + }//GEN-LAST:event_constrainProportionsCheckBoxActionPerformed + + private void autoCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_autoCheckBoxActionPerformed + disablePopupCheckBox.setEnabled( autoCheckBox.isSelected() ); + if ( !autoCheckBox.isSelected() ) disablePopupCheckBox.setSelected(false); + module.guiChanged(); + }//GEN-LAST:event_autoCheckBoxActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox autoCheckBox; + private javax.swing.JCheckBox constrainProportionsCheckBox; + private javax.swing.JCheckBox disablePopupCheckBox; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JTextField xRangeTextField; + private javax.swing.JTextField yRangeTextField; + private javax.swing.JButton zoomBoxButton; + private javax.swing.JButton zoomXButton; + private javax.swing.JButton zoomYButton; + // End of variables declaration//GEN-END:variables + +} diff --git a/dasCore/src/org/das2/event/BoxZoomGesturesRenderer.java b/dasCore/src/org/das2/event/BoxZoomGesturesRenderer.java deleted file mode 100644 index 3e49f4260..000000000 --- a/dasCore/src/org/das2/event/BoxZoomGesturesRenderer.java +++ /dev/null @@ -1,140 +0,0 @@ -/* File: BoxRenderer.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package org.das2.event; - -import org.das2.graph.DasCanvasComponent; - -import java.awt.*; - -/** - * BoxZoom renderer that shows delegation to X and Y axis zooms. - * @author eew - */ -public class BoxZoomGesturesRenderer extends BoxRenderer { - - GesturesRenderer gr; - DragRenderer hr,vr; - - protected enum Type { BOX, XAXIS, YAXIS }; - - public BoxZoomGesturesRenderer(DasCanvasComponent parent) { - super(parent); - gr= new GesturesRenderer(parent); - hr= new HorizontalRangeGesturesRenderer(parent); - vr= new VerticalRangeGesturesRenderer(parent); - } - - public void clear(Graphics g) { - parent.paintImmediately(dirtyBounds); - } - - protected static Type idType( DasCanvasComponent parent, Point p1, Point p2 ) { - int ymax= Math.max( p1.y, p2.y ); - int ymin= Math.min( p1.y, p2.y ); - int xmax= Math.max( p1.x, p2.x ); - int xmin= Math.min( p1.x, p2.x ); - - double boxAspect= ( ymax - ymin ) / (float)( xmax - xmin ); - boolean edgeY= ymax>parent.getRow().getDMaximum() || yminparent.getColumn().getDMaximum() || xmin
    z
    o
    o
    m
    Y
    ") { + public void actionPerformed( ActionEvent e ) { + if ( yrange!=null ) yAxis.setDatumRange(yrange); } - dr= maybeRound( yAxis, dr ); - // check bounds are still finite - if ( ! DatumRangeUtil.isAcceptable(dr, yAxis.isLog() ) ) { - dr= null; + }; + } + + Action getZoomXAction() { + return new AbstractAction("zoom X") { + public void actionPerformed( ActionEvent e ) { + if ( xrange!=null ) xAxis.setDatumRange(xrange); } - ydrnew= dr; - } - - if ( axisIsAdjustable(xAxis) && xdrnew==null ) return; - if ( axisIsAdjustable(yAxis) && ydrnew==null ) return; - - if ( axisIsAdjustable(xAxis) ) xAxis.setDatumRange(xdrnew); - if ( axisIsAdjustable(yAxis) ) yAxis.setDatumRange(ydrnew); - - super.mouseWheelMoved(e); + }; } - + + Action getZoomBoxAction() { + return new AbstractAction("
    Zoom
    Box
    ") { + public void actionPerformed( ActionEvent e ) { + zoomBox(); + } + }; + } + + protected void zoomBox() { + if ( yrange!=null ) yAxis.setDatumRange(yrange); + if ( xrange!=null ) xAxis.setDatumRange(xrange); + } + @Override public void mouseRangeSelected(MouseDragEvent e0) { if ( e0 instanceof MouseBoxEvent ) { @@ -171,34 +101,7 @@ public void mouseRangeSelected(MouseDragEvent e0) { xrange= GraphUtil.invTransformRange( xAxis, e.getXMinimum(), e.getXMaximum() ); yrange= GraphUtil.invTransformRange( yAxis, e.getYMinimum(), e.getYMaximum() ); - - double boxAspect= ( e.getYMaximum() - e.getYMinimum() ) / (float)( e.getXMaximum() - e.getXMinimum() ); - boolean edgeY= e.getYMaximum()>yAxis.getRow().getDMaximum() || e.getYMinimum()xAxis.getColumn().getDMaximum() || e.getXMinimum()10 && edgeX ) ) { - xrange= xAxis.getDatumRange(); - } - //boxes along axes must only zoom along that axes. The intent might have been to start the box on the axis instead of the plot. - if ( edgeY && boxAspect<0.2 ) { - yrange= yAxis.getDatumRange(); - } - if ( edgeX && boxAspect>5 ) { - xrange= xAxis.getDatumRange(); - } - if ( constrainProportions ) { double aspect= yAxis.getHeight() / (double)xAxis.getWidth(); DatumRange mx= new DatumRange( e.getXMinimum(), e.getXMaximum(), Units.dimensionless ); @@ -212,12 +115,21 @@ public void mouseRangeSelected(MouseDragEvent e0) { my= DatumRangeUtil.rescale(my, 0.5-f/2, 0.5+f/2 ); } xrange= GraphUtil.invTransformRange( xAxis, mx.min().doubleValue(Units.dimensionless), - mx.max().doubleValue(Units.dimensionless) ); + mx.max().doubleValue(Units.dimensionless) ); yrange= GraphUtil.invTransformRange( yAxis, my.max().doubleValue(Units.dimensionless), - my.min().doubleValue(Units.dimensionless) ); - } + my.min().doubleValue(Units.dimensionless) ); + } else { + xrange= GraphUtil.invTransformRange( xAxis, e.getXMinimum(), e.getXMaximum() ); + yrange= GraphUtil.invTransformRange( yAxis, e.getYMaximum(), e.getYMinimum() ); + } - zoomBox(); + if ( ! autoUpdate ) { + getDialog(); + bzdialog.setXRange( xrange.toString() ); + bzdialog.setYRange(yrange.toString()); + } else { + zoomBox(); + } } else if ( e0.isGesture() ) { if ( e0.getGesture()==Gesture.ZOOMOUT ) { @@ -247,6 +159,7 @@ public boolean isAutoUpdate() { * @param autoUpdate New value of property autoUpdate. */ public void setAutoUpdate(boolean autoUpdate) { + if ( bzdialog!=null ) bzdialog.setAutoBoxZoom( autoUpdate ); this.autoUpdate = autoUpdate; } @@ -263,7 +176,34 @@ public boolean isConstrainProportions() { * @param constrainProportions New value of property constrainProportions. */ public void setConstrainProportions(boolean constrainProportions) { + if ( bzdialog!=null ) bzdialog.setConstrainProportions(constrainProportions); this.constrainProportions = constrainProportions; } - + + /** + * Holds value of property popupDisabled. + */ + private boolean popupDisabled; + + /** + * Getter for property popupDisabled. + * @return Value of property popupDisabled. + */ + public boolean isPopupDisabled() { + return this.popupDisabled; + } + + /** + * Setter for property popupDisabled. + * @param popupDisabled New value of property popupDisabled. + */ + public void setPopupDisabled(boolean popupDisabled) { + if ( bzdialog!=null ) bzdialog.setDisablePopup(popupDisabled); + this.popupDisabled = popupDisabled; + } + + + + + } diff --git a/dasCore/src/org/das2/event/ColorBarRepaletteMouseModule.java b/dasCore/src/org/das2/event/ColorBarRepaletteMouseModule.java new file mode 100644 index 000000000..15bc4b104 --- /dev/null +++ b/dasCore/src/org/das2/event/ColorBarRepaletteMouseModule.java @@ -0,0 +1,114 @@ +/* File: VerticalRangeSelectorMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumRangeUtil; +import org.das2.graph.DasColorBar; +import org.das2.graph.DasRow; +import org.das2.graph.Renderer; +import java.awt.event.MouseEvent; +import javax.swing.event.EventListenerList; + +/** + * + * @author jbf + */ +public class ColorBarRepaletteMouseModule extends MouseModule { + + DasColorBar colorBar; + Renderer parent; + DatumRange range0; + boolean animated0; + + /** Utility field used by event firing mechanism. */ + private EventListenerList listenerList = null; + + public String getLabel() { return "Repalette"; }; + + public ColorBarRepaletteMouseModule( Renderer parent, DasColorBar colorBar ) { + if (colorBar.isHorizontal()) { + throw new IllegalArgumentException("Axis orientation is not vertical"); + } + this.parent= parent; + // this.dragRenderer= (DragRenderer)HorizontalRangeRenderer.renderer; + this.dragRenderer= new HorizontalSliceSelectionRenderer(parent.getParent()); + this.colorBar= colorBar; + } + + private void setColorBar( int y ) { + DatumRange dr; + DasRow row= colorBar.getRow(); + double alpha= ( row.getDMaximum() - y ) / (1.*row.getHeight()); + dr= DatumRangeUtil.rescale(range0, 0, alpha ); + colorBar.setDatumRange(dr); + parent.update(); + } + + public void mouseReleased( MouseEvent e ) { + colorBar.setAnimated(animated0); + } + + public void mousePointSelected(MousePointSelectionEvent e) { + setColorBar( e.y ); + } + + /** Registers DataRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new EventListenerList(); + } + listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Removes DataRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { + ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); + } + } + } + + public void mousePressed(java.awt.event.MouseEvent e) { + super.mousePressed(e); + animated0= colorBar.isAnimated(); + colorBar.setAnimated(false); + range0= colorBar.getDatumRange(); + } + +} diff --git a/dasCore/src/org/das2/event/CrossHairMouseModule.java b/dasCore/src/org/das2/event/CrossHairMouseModule.java index 3978894e5..52817ac3a 100755 --- a/dasCore/src/org/das2/event/CrossHairMouseModule.java +++ b/dasCore/src/org/das2/event/CrossHairMouseModule.java @@ -22,36 +22,38 @@ */ package org.das2.event; -import java.awt.Toolkit; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.ClipboardOwner; -import java.awt.datatransfer.StringSelection; -import java.awt.datatransfer.Transferable; -import java.awt.event.KeyEvent; +import org.das2.dataset.DataSet; import org.das2.graph.DasAxis; import org.das2.graph.DasPlot; import org.das2.graph.Renderer; -import org.virbo.dataset.QDataSet; + /** * * @author Owner */ public class CrossHairMouseModule extends MouseModule { + + public static abstract class InfoItem { + public abstract void label(org.das2.datum.Datum x, org.das2.datum.Datum y, StringBuilder out); + public org.das2.datum.Datum[] snap(org.das2.datum.Datum x, org.das2.datum.Datum y) { + return null; + } + } private DasAxis xaxis; private DasAxis yaxis; private DasPlot plot; - + protected DataPointSelectionEvent de; org.das2.dataset.DataSetConsumer dataSetConsumer; /** Utility field used by event firing mechanism. */ - private javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList(); + private javax.swing.event.EventListenerList listenerList = null; public CrossHairMouseModule(DasPlot parent, DasAxis xaxis, DasAxis yaxis) { - this( parent, null, xaxis, yaxis ); + this( parent, parent, xaxis, yaxis ); } public CrossHairMouseModule( DasPlot parent, org.das2.dataset.DataSetConsumer dataSetConsumer, DasAxis xAxis, DasAxis yAxis ) { @@ -68,9 +70,23 @@ public static CrossHairMouseModule create( DasPlot parent ) { DasAxis yaxis= null; return new CrossHairMouseModule(parent,null,xaxis,yaxis); } + + public void addInfoItem(InfoItem i) { + DragRenderer dr = getDragRenderer(); + if (dr instanceof CrossHairRenderer) { + ((CrossHairRenderer)dr).addInfoItem(i); + } + } + + public void removeInfoItem(InfoItem i) { + DragRenderer dr = getDragRenderer(); + if (dr instanceof CrossHairRenderer) { + ((CrossHairRenderer)dr).removeInfoItem(i); + } + } - private QDataSet getContextDataSet() { - QDataSet ds; + private DataSet getContextDataSet() { + DataSet ds; if ( dataSetConsumer!=null ) { ds = dataSetConsumer.getConsumedDataSet(); } else { @@ -92,7 +108,6 @@ protected DataPointSelectionEvent getDataPointSelectionEvent(MousePointSelection return de; } - @Override public void mousePointSelected(MousePointSelectionEvent e) { fireDataPointSelectionListenerDataPointSelected(getDataPointSelectionEvent(e)); } @@ -100,14 +115,17 @@ public void mousePointSelected(MousePointSelectionEvent e) { /** Registers DataPointSelectionListener to receive events. * @param listener The listener to register. */ - public void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + public synchronized void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); } /** Removes DataPointSelectionListener from the list of listeners. * @param listener The listener to remove. */ - public void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + public synchronized void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { listenerList.remove(org.das2.event.DataPointSelectionListener.class, listener); } @@ -116,31 +134,13 @@ public void removeDataPointSelectionListener(org.das2.event.DataPointSelectionLi * @param event The event to be fired */ protected void fireDataPointSelectionListenerDataPointSelected(DataPointSelectionEvent event) { - Object[] listeners; - listeners = listenerList.getListenerList(); + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==org.das2.event.DataPointSelectionListener.class) { ((org.das2.event.DataPointSelectionListener)listeners[i+1]).dataPointSelected(event); } } } - - @Override - public void keyTyped(KeyEvent keyEvent) { - if ( ( Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() & keyEvent.getModifiers() ) !=0 ) { - if ( keyEvent.getKeyChar()==KeyEvent.VK_C || keyEvent.getKeyChar()==3 ) { // 3 was observed on Linux/Centos6/Java - CrossHairRenderer r= (CrossHairRenderer) super.dragRenderer; - StringSelection stringSelection = new StringSelection( r.label.replaceAll("!c"," ") ); - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboard.setContents( stringSelection, new ClipboardOwner() { - @Override - public void lostOwnership(Clipboard clipboard, Transferable contents) { - } - }); - - } - } - } - } diff --git a/dasCore/src/org/das2/event/CrossHairRenderer.java b/dasCore/src/org/das2/event/CrossHairRenderer.java index e256b2238..421a15c6a 100755 --- a/dasCore/src/org/das2/event/CrossHairRenderer.java +++ b/dasCore/src/org/das2/event/CrossHairRenderer.java @@ -25,7 +25,11 @@ import org.das2.components.propertyeditor.Editable; import org.das2.dataset.DataSetConsumer; import org.das2.dataset.TableDataSetConsumer; -import org.virbo.dataset.DataSetUtil; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.VectorDataSet; import org.das2.datum.format.DefaultDatumFormatterFactory; import org.das2.datum.format.DatumFormatter; import org.das2.graph.DasAxis; @@ -37,13 +41,8 @@ import java.awt.*; import java.awt.geom.Point2D; import java.text.*; -import org.das2.datum.InconvertibleUnitsException; -import org.das2.datum.Units; -import org.das2.datum.UnitsUtil; -import org.das2.datum.format.EnumerationDatumFormatter; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dataset.examples.Schemes; +import java.util.ArrayList; +import java.util.List; /** * @@ -56,14 +55,21 @@ public class CrossHairRenderer extends LabelDragRenderer implements DragRenderer protected DasAxis XAxis; protected DasAxis YAxis; protected DasPlot parent; + private int ix = 0; // store the current position within the dataset object + private int iy = 0; + private int context; private DatumFormatter nfx; private DatumFormatter nfy; private DatumFormatter nfz; private FontMetrics fm; + private int dxMax = -999999; private Rectangle hDirtyBounds; private Rectangle vDirtyBounds; private Point crossHairLocation = null; private DataSetConsumer dataSetConsumer; + + private List infoItems = new ArrayList(); + /** * Holds value of property allPlanesReport. */ @@ -88,6 +94,14 @@ public CrossHairRenderer(DasPlot parent, DataSetConsumer dataSetConsumer, DasAxi vDirtyBounds = new Rectangle(); } + void addInfoItem(CrossHairMouseModule.InfoItem item) { + infoItems.add(item); + } + + void removeInfoItem(CrossHairMouseModule.InfoItem item) { + infoItems.remove(item); + } + private DatumFormatter addResolutionToFormat(DatumFormatter nfz) throws ParseException { String formatString = nfz.toString(); String result; @@ -101,51 +115,13 @@ private DatumFormatter addResolutionToFormat(DatumFormatter nfz) throws ParseExc result = ss[0] + "00" + "E0"; } } - try { - return DefaultDatumFormatterFactory.getInstance().newFormatter(result); - } catch ( IllegalArgumentException ex ) { - return nfz; //TODO: track this down. It has something to do with logLinDomainDivider for colorbar. - } + return DefaultDatumFormatterFactory.getInstance().newFormatter(result); } - private String getZComponentsString( QDataSet tds, Datum x, Datum y ) { - assert tds.rank()==3; - QDataSet xds= SemanticOps.xtagsDataSet(tds); - int i = DataSetUtil.closestIndex(xds, x); - QDataSet tds1= tds.slice(i); - QDataSet yds= SemanticOps.xtagsDataSet(tds1); - int j; - try { - j= DataSetUtil.closestIndex(yds, y); - } catch ( IllegalArgumentException ex ) { - return ex.getMessage(); - } - QDataSet rgb= tds1.slice(j); - return DataSetUtil.toString(rgb); - } - - private String getZString( QDataSet tds, Datum x, Datum y, int[] ij) { - QDataSet xds= SemanticOps.xtagsDataSet(tds); - int i; - try { - i= DataSetUtil.closestIndex(xds, x); - } catch ( InconvertibleUnitsException ex ) { - Units u= SemanticOps.getUnits(xds); - i= DataSetUtil.closestIndex( xds, x.value(), u ); - } - QDataSet tds1= tds.slice(i); - QDataSet yds= SemanticOps.xtagsDataSet(tds1); - int j; - try { - j= DataSetUtil.closestIndex(yds, y); - } catch ( InconvertibleUnitsException ex ) { - Units u= SemanticOps.getUnits(yds); - j= DataSetUtil.closestIndex( yds, y.value(), u ); - } catch ( IllegalArgumentException ex ) { - return ex.getMessage(); - } - double d= tds1.value(j); - Datum zValue = SemanticOps.getDatum( tds1, d ); + private String getZString(TableDataSet tds, Datum x, Datum y, int[] ij) { + int i = DataSetUtil.closestColumn(tds, x); + int j = TableUtil.closestRow(tds, tds.tableOfIndex(i), y); + Datum zValue = tds.getDatum(i, j); if (ij != null) { ij[0] = i; @@ -154,12 +130,8 @@ private String getZString( QDataSet tds, Datum x, Datum y, int[] ij) { try { if (dataSetConsumer instanceof TableDataSetConsumer) { - if ( !UnitsUtil.isIntervalOrRatioMeasurement( zValue.getUnits() ) ) { - nfz= new EnumerationDatumFormatter(); - } else { - nfz = ((TableDataSetConsumer) dataSetConsumer).getZAxis().getDatumFormatter(); - nfz = addResolutionToFormat(nfz); - } + nfz = ((TableDataSetConsumer) dataSetConsumer).getZAxis().getDatumFormatter(); + nfz = addResolutionToFormat(nfz); } else { nfz = DefaultDatumFormatterFactory.getInstance().newFormatter("0.000"); } @@ -169,38 +141,36 @@ private String getZString( QDataSet tds, Datum x, Datum y, int[] ij) { axis.getUnits().getDatumFormatterFactory().defaultFormatter(); } - StringBuilder result; + String result; if (zValue.isFill()) { - result = new StringBuilder( "fill" ); + result = "fill"; } else { - result = new StringBuilder( nfz.grannyFormat(zValue) ); + result = nfz.grannyFormat(zValue); } if (allPlanesReport) { if (debugging) { - result.append( "!c" ).append( tds.toString() ); + result += "!c" + tds.toString(); } - for (int iplane = 0; iplane < QDataSet.MAX_PLANE_COUNT; iplane++) { - QDataSet plane= (QDataSet) tds.property( "PLANE_"+iplane ); - if ( plane==null ) break; - result.append("!c"); - result.append( plane.property(QDataSet.NAME) ).append( ":" ).append( nfz.grannyFormat( SemanticOps.getDatum( plane, plane.value(i,j) ) ) ); - if (debugging) { - result.append(" ").append( plane.toString() ); + String[] planeIds = tds.getPlaneIds(); + for (int iplane = 0; iplane < planeIds.length; iplane++) { + if (!planeIds[iplane].equals("")) { + result = result + "!c"; + result += planeIds[iplane] + ":" + nfz.grannyFormat(((TableDataSet) tds.getPlanarView(planeIds[iplane])).getDatum(i, j)); + if (debugging) { + result += " " + ((TableDataSet) tds.getPlanarView(planeIds[iplane])).toString(); + } } } if (debugging) { - result.append( "!ci:" ).append( i ).append( " j:" ).append( j ); + result += "!ci:" + i + " j:" + j; } } - return result.toString(); + return result; } - private int closestPointVector(QDataSet ds, Datum x, Datum y) { - - QDataSet xds= SemanticOps.xtagsDataSet(ds); - Units xunits= SemanticOps.getUnits(xds); + private int closestPointVector(VectorDataSet ds, Datum x, Datum y) { - boolean xmono = SemanticOps.isMonotonic(xds); + Boolean xmono = (Boolean) ds.getProperty(DataSet.PROPERTY_X_MONOTONIC); DasAxis xa, ya; xa = (this.XAxis == null) ? parent.getXAxis() : XAxis; @@ -208,36 +178,34 @@ private int closestPointVector(QDataSet ds, Datum x, Datum y) { int start, end; Point2D.Double me = new Point2D.Double(xa.transform(x), ya.transform(y)); - if ( xmono ) { - start = DataSetUtil.getPreviousIndex(xds, xa.getDataMinimum()); - end = DataSetUtil.getNextIndex(xds, xa.getDataMaximum()); + if (xmono != null && xmono.equals(Boolean.TRUE)) { + start = DataSetUtil.getPreviousColumn(ds, xa.getDataMinimum()); + end = DataSetUtil.getNextColumn(ds, xa.getDataMaximum()); } else { start = 0; - end = xds.length(); + end = ds.getXLength(); } int bestIndex = -1; double bestXDist = Double.POSITIVE_INFINITY; double bestDist = Double.POSITIVE_INFINITY; - //int comparisons = 0; + int comparisons = 0; // prime the best dist comparison by scanning decimated dataset for (int i = start; i < end; i += 100) { - double x1 = xa.transform( xds.value(i), xunits ); + double x1 = xa.transform(ds.getXTagDatum(i)); double dist = Math.abs(x1 - me.getX()); if (dist < bestXDist) { bestXDist = dist; } } - Units units= SemanticOps.getUnits(ds); - for (int i = start; i < end; i++) { - double x1 = xa.transform( xds.value(i), xunits ); + double x1 = xa.transform(ds.getXTagDatum(i)); if (Math.abs(x1 - me.getX()) <= bestXDist) { - Point2D them = new Point2D.Double(x1, ya.transform( ds.value(i), units ) ); + Point2D them = new Point2D.Double(x1, ya.transform(ds.getDatum(i))); double dist = me.distance(them); - //comparisons++; + comparisons++; if (dist < bestDist) { bestIndex = i; bestDist = dist; @@ -248,6 +216,7 @@ private int closestPointVector(QDataSet ds, Datum x, Datum y) { return bestIndex; + } @Override @@ -255,8 +224,8 @@ public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { Graphics2D g = (Graphics2D) g1; g.setRenderingHints((RenderingHints) org.das2.DasProperties.getRenderingHints()); - QDataSet ds; - + DataSet ds; + if ( dataSetConsumer!=null ) { ds = dataSetConsumer.getConsumedDataSet(); } else { @@ -268,12 +237,7 @@ public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { } } - if ( dataSetConsumer instanceof Renderer ) { - Renderer r= (Renderer)dataSetConsumer; - if ( r.isActive()==false || r.getParent()==null ) { - ds= null; - } - } + Rectangle[] superDirty = null; Datum x = null; Datum y = null; @@ -302,66 +266,51 @@ public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { String nl = multiLine ? "!c" : " "; report = "x:" + xAsString + nl + "y:" + yAsString; - + if (ds != null) { - QDataSet xds= SemanticOps.xtagsDataSet(ds); - - if ( SemanticOps.isTableDataSet(ds) ) { - QDataSet tds; - QDataSet yds; - if ( SemanticOps.isSimpleTableDataSet(ds) ) { - tds= (QDataSet) ds; - } else if ( Schemes.isCompositeImage(ds) ) { - tds= (QDataSet) ds; - } else { - tds= SemanticOps.getSimpleTableContaining( ds, x, y ); - if ( tds==null ) tds= ds.slice(0); - } - yds= SemanticOps.ytagsDataSet(tds); - + if (ds instanceof TableDataSet) { + TableDataSet tds = (TableDataSet) ds; String zAsString; if (tds != null && snapping) { int[] ij = new int[2]; zAsString = getZString(tds, x, y, ij); - x = SemanticOps.getDatum( xds, xds.value(ij[0]) ); + x = tds.getXTagDatum(ij[0]); xAsString = nfx.format(x); - y = SemanticOps.getDatum( yds, yds.value(ij[1]) ); + y = tds.getYTagDatum(tds.tableOfIndex(ij[0]), ij[1]); yAsString = nfy.format(y); } else { - if ( tds==null ) { - zAsString= "N/A"; - } else if ( Schemes.isCompositeImage(ds) ) { - zAsString = "!c" + getZComponentsString( tds, x, y ); - } else { - zAsString = getZString(tds, x, y, null); - } + zAsString = getZString(tds, x, y, null); } report = "x:" + xAsString + nl + "y:" + yAsString + nl + "z:" + zAsString; } else { - if ( snapping) { - QDataSet vds = (QDataSet) ds; - if (vds.length() == 0) { + if (ds == null && dataSetConsumer instanceof DasPlot) { + if (((DasPlot) dataSetConsumer).getRenderers().length > 0) { + ds = ((DasPlot) dataSetConsumer).getRenderer(0).getDataSet(); + } + } + if (ds != null && snapping) { + VectorDataSet vds = (VectorDataSet) ds; + if (vds.getXLength() == 0) { yAsString = "(empty dataset)"; } else { int i = closestPointVector(vds, x, y); - x = SemanticOps.getDatum( xds, xds.value(i) ); - y = SemanticOps.getDatum( vds, vds.value(i) ); + x = vds.getXTagDatum(i); + y = vds.getDatum(i); xAsString = nfx.format(x); yAsString = nfy.format(y); if (allPlanesReport) { - StringBuilder result = new StringBuilder( yAsString ); - - for (int iplane = 0; iplane < QDataSet.MAX_PLANE_COUNT; iplane++) { - QDataSet plane= (QDataSet) vds.property( "PLANE_"+iplane ); - if ( plane==null ) break; - result.append( "!c" ); - result.append( plane.property(QDataSet.NAME) ).append( ":" ) .append( nfz.grannyFormat( SemanticOps.getDatum( plane, plane.value(i) ) ) ); - if (debugging) { - result.append( " " ).append( plane.toString() ); + String result = yAsString; + String[] planeIds = vds.getPlaneIds(); + for (int iplane = 0; iplane < planeIds.length; iplane++) { + if (!planeIds[iplane].equals("")) { + result = result + "!c"; + result += planeIds[iplane] + ":" + nfy.grannyFormat(((VectorDataSet) vds.getPlanarView(planeIds[iplane])).getDatum(i)); + if (debugging) { + result += " " + ((VectorDataSet) vds.getPlanarView(planeIds[iplane])).toString(); + } } } - - yAsString = result.toString(); + yAsString = result; } } } @@ -369,6 +318,13 @@ public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { } } + StringBuilder builder = new StringBuilder(report); + for (CrossHairMouseModule.InfoItem item : infoItems) { + builder.append("!C"); + item.label(x, y, builder); + } + report = builder.toString(); + setLabel(report); super.renderDrag(g, p1, p2); } @@ -422,19 +378,16 @@ private void drawCrossHair(Graphics g0, Point p) { } - @Override public void clear(Graphics g) { super.clear(g); parent.paintImmediately(hDirtyBounds); parent.paintImmediately(vDirtyBounds); } - @Override public boolean isPointSelection() { return true; } - @Override public boolean isUpdatingDragSelection() { return false; } @@ -471,7 +424,6 @@ public void setDebugging(boolean debugging) { this.debugging = debugging; } - @Override public Rectangle[] getDirtyBounds() { return new Rectangle[]{super.dirtyBounds, this.hDirtyBounds, this.vDirtyBounds}; } @@ -493,6 +445,7 @@ public void setSnapping(boolean b) { * @return Value of property multiLine. */ public boolean isMultiLine() { + return this.multiLine; } @@ -501,6 +454,7 @@ public boolean isMultiLine() { * @param multiLine New value of property multiLine. */ public void setMultiLine(boolean multiLine) { + this.multiLine = multiLine; } } diff --git a/dasCore/src/org/das2/event/CutoffMouseModule.java b/dasCore/src/org/das2/event/CutoffMouseModule.java index 57e9810fe..214364252 100644 --- a/dasCore/src/org/das2/event/CutoffMouseModule.java +++ b/dasCore/src/org/das2/event/CutoffMouseModule.java @@ -8,15 +8,23 @@ package org.das2.event; +import java.text.ParseException; import org.das2.DasApplication; import org.das2.DasException; import org.das2.dataset.AverageTableRebinner; import org.das2.dataset.ClippedTableDataSet; +import org.das2.dataset.DataSet; import org.das2.dataset.DataSetConsumer; import org.das2.dataset.DataSetRebinner; import org.das2.dataset.DataSetUpdateEvent; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.DefaultVectorDataSet; import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.SingleVectorDataSet; +import org.das2.dataset.TableDataSet; import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.VectorDataSetBuilder; import org.das2.datum.Datum; import org.das2.datum.DatumRange; import org.das2.datum.Units; @@ -29,14 +37,10 @@ import org.das2.util.monitor.ProgressMonitor; import java.awt.Color; import java.beans.PropertyChangeEvent; -import java.util.Collections; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.swing.JFrame; -import org.virbo.dataset.DataSetOps; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dsops.Ops; -import org.virbo.dsutil.DataSetBuilder; /** * @@ -44,6 +48,8 @@ */ public class CutoffMouseModule extends BoxSelectorMouseModule { + DasAxis xaxis, yaxis; + DataSetConsumer dataSetConsumer; DatumRange xrange; DatumRange yrange; String lastComment; @@ -55,8 +61,42 @@ public CutoffMouseModule( DasPlot parent, DataSetConsumer consumer ) { application= parent.getCanvas().getApplication(); this.dataSetConsumer= consumer; } + + public static final String CONFIG_VOYAGER_HR_LOWER= "Ondrej: min=-4. slopeMin=0.26 nave=3 cutoff=lower xres=1s"; + public static final String CONFIG_GALILEO_LOWER= "Ondrej: min=1.78 slopeMin=0.072 nave=3 cutoff=lower xres=120s"; + public static final String CONFIG_GALILEO_LOWER_60= "Ondrej: min=1.78 slopeMin=0.072 nave=3 cutoff=lower xres=60s"; + public static final String CONFIG_GALILEO_LOWER_30= "Ondrej: min=1.78 slopeMin=0.072 nave=3 cutoff=lower xres=30s"; + + /** + * see CONFIG_VOYAGER_HR_LOWER, etc. + * @param config + */ + public void setConfig( String config ) throws ParseException { + if ( !config.startsWith("Ondrej:") ) { + throw new IllegalArgumentException("config must start with Ondrej"); + } + + Pattern p= Pattern.compile("(\\S+)=(\\S+)"); + + Matcher m= p.matcher(config.substring(7) ); + + while ( m.find() ) { + String name= m.group(1); + String sval= m.group(2); + if ( name.equals("min") ) { + setLevelMin(Units.dimensionless.parse(sval)); + } else if ( name.equals("slopeMin") ) { + setSlopeMin(Units.dimensionless.parse(sval)); + } else if ( name.equals("nave") ) { + setNave( Integer.parseInt(sval) ); + } else if (name.equals("cutoff") ) { + setLowCutoff( "lower".equals(sval) ); + } else if (name.equals("xres") ) { + setXResolution( Units.seconds.parse(sval) ); + } + } + } - @Override protected void fireBoxSelectionListenerBoxSelected(BoxSelectionEvent event) { DatumRange xrange0= xrange; @@ -64,12 +104,10 @@ protected void fireBoxSelectionListenerBoxSelected(BoxSelectionEvent event) { xrange= event.getXRange(); yrange= event.getYRange(); - synchronized (this) { - if ( event.getPlane("keyChar")!=null ) { - lastComment= (String)event.getPlane("keyChar"); - } else { - lastComment= null; - } + if ( event.getPlane("keyChar")!=null ) { + lastComment= (String)event.getPlane("keyChar"); + } else { + lastComment= null; } try { @@ -108,55 +146,43 @@ public void run() { } private synchronized void recalculate( ProgressMonitor mon) { - QDataSet tds= (QDataSet)dataSetConsumer.getConsumedDataSet(); + TableDataSet tds= (TableDataSet)dataSetConsumer.getConsumedDataSet(); if ( tds==null ) return; if ( xrange==null ) return; tds= new ClippedTableDataSet( tds, xrange, yrange ); - QDataSet yds= SemanticOps.ytagsDataSet(tds); - QDataSet xds= SemanticOps.xtagsDataSet(tds); - + // average the data down to xResolution DataSetRebinner rebinner= new AverageTableRebinner(); - DatumRange range= DataSetUtil.asDatumRange( SemanticOps.bounds(tds).slice(0), true ); - + DatumRange range= DataSetUtil.xRange( tds ); RebinDescriptor ddx= getRebinDescriptor( range ); try { //TODO: why does rebin throw DasException? - tds= (QDataSet)rebinner.rebin( tds, ddx, null ); + tds= (TableDataSet)rebinner.rebin( tds, ddx, null, null ); } catch ( DasException e ) { throw new RuntimeException(e); } - - double fill= SemanticOps.getUnits(yds).getFillDouble(); - - DataSetBuilder builder= new DataSetBuilder( 1, 100 ); - builder.putProperty( QDataSet.UNITS, SemanticOps.getUnits(yds) ); - builder.putProperty( QDataSet.FILL_VALUE, fill ); - DataSetBuilder xbuilder= new DataSetBuilder( 1, 100 ); - xbuilder.putProperty( QDataSet.UNITS, SemanticOps.getUnits(xds) ); - - mon.setTaskSize( tds.length() ); + + VectorDataSetBuilder builder= new VectorDataSetBuilder( tds.getXUnits(), tds.getYUnits() ); + + mon.setTaskSize( tds.getXLength() ); mon.started(); - - for ( int i=0; i-1 ) { - xbuilder.putValue( -1, xds.value(i) ); - builder.putValue( -1, yds.value(icutoff) ); + builder.insertY( tds.getXTagDatum(i), tds.getYTagDatum( tds.tableOfIndex(i), icutoff ) ); } else { - xbuilder.putValue( -1, xds.value(i) ); - builder.putValue( -1, fill ); + Units yunits=tds.getYUnits(); + builder.insertY( tds.getXTagDatum(i), yunits.createDatum(yunits.getFillDouble()) ); } - builder.nextRecord(); - xbuilder.nextRecord(); } - + mon.finished(); if ( mon.isCancelled() ) return; @@ -165,14 +191,11 @@ private synchronized void recalculate( ProgressMonitor mon) { if ( lastComment!=null ) { comment= lastComment + " "+comment; } - - builder.putProperty( QDataSet.USER_PROPERTIES, Collections.singletonMap("comment",comment) ); - xbuilder.putProperty( QDataSet.CADENCE, DataSetUtil.asDataSet(this.xResolution ) ); - - builder.putProperty( QDataSet.DEPEND_0, xbuilder.getDataSet() ); - QDataSet vds= builder.getDataSet(); + builder.setProperty("comment",comment); + builder.setProperty( DataSet.PROPERTY_X_TAG_WIDTH, this.xResolution ); + VectorDataSet vds= builder.toVectorDataSet(); - fireDataSetUpdateListenerDataSetUpdated( new DataSetUpdateEvent( vds ) ); + fireDataSetUpdateListenerDataSetUpdated( new DataSetUpdateEvent( this,vds ) ); } @@ -181,74 +204,51 @@ private synchronized void recalculate( ProgressMonitor mon) { * levelMin in the y units of ds. * mult=-1 high cutoff, =1 low cutoff */ - public int cutoff( QDataSet ds, Datum slopeMin, int nave, int mult, Datum levelMin ) { - int nfr= ds.length(); + public int cutoff( VectorDataSet ds, Datum slopeMin, int nave, int mult, Datum levelMin ) { + int nfr= ds.getXLength(); if ( nfr < (nave+1) ) { throw new IllegalArgumentException("DataSet doesn't contain enough elements"); } double[] cumul= new double[nfr]; - Units units= SemanticOps.getUnits(ds); - + Units units= ds.getYUnits(); double level= levelMin.doubleValue( units ); double slope= slopeMin.doubleValue( units ); - cumul[0]= ds.value(0); + cumul[0]= ds.getDouble(0, units); for ( int i=1; i0 ? ave[j+k] : ave[j]; if ( uave <= level ) icof[j]=false; - icofBuilder.putValue( -1, icof[j] ? 1: 0 ); - icofBuilder.nextRecord(); - xicofBuilder.putValue( -1, xds.value(j) ); - xicofBuilder.nextRecord(); - + icofBuilder.insertY( ds.getXTagDatum(j), icof[j] ? units.dimensionless.createDatum(1) : units.dimensionless.createDatum(0) ); } } if ( cutoffSlicer!=null ) { - slopeBuilder.putProperty( QDataSet.DEPEND_0, xslopeBuilder.getDataSet() ); - cutoffSlicer.slopeRenderer.setDataSet( slopeBuilder.getDataSet() ); - levelBuilder.putProperty( QDataSet.DEPEND_0, xlevelBuilder.getDataSet() ); - cutoffSlicer.levelRenderer.setDataSet( levelBuilder.getDataSet() ); - icofBuilder.putProperty( QDataSet.DEPEND_0, xicofBuilder.getDataSet() ); - cutoffSlicer.icofRenderer.setDataSet( icofBuilder.getDataSet() ); + cutoffSlicer.slopeRenderer.setDataSet( slopeBuilder.toVectorDataSet() ); + cutoffSlicer.levelRenderer.setDataSet( levelBuilder.toVectorDataSet() ); + cutoffSlicer.icofRenderer.setDataSet( icofBuilder.toVectorDataSet() ); } int icutOff=-1; @@ -295,7 +295,6 @@ private class CutoffSlicer implements DataPointSelectionListener { DasRow row3= new DasRow( canvas, null, 2/3., 3/3., 1, -2, 0, 0 ); DasPlot plot= new DasPlot( xaxis, new DasAxis( new DatumRange( -18,-10,Units.dimensionless ), DasAxis.VERTICAL ) ) { - @Override protected void drawContent(java.awt.Graphics2D g) { super.drawContent(g); @@ -335,17 +334,16 @@ protected void drawContent(java.awt.Graphics2D g) { tweakSlicer.setDragEvents(true); // only key events fire tweakSlicer.addDataPointSelectionListener( new DataPointSelectionListener() { public void dataPointSelected( DataPointSelectionEvent e ) { - throw new IllegalArgumentException("Not implemented, since DataSetUpdateEvents take QDataSets."); -// Datum x= e.getX(); -// HashMap properties= new HashMap(); -// if ( e.getPlane("keyChar")!=null ) { -// properties.put("comment",e.getPlane("keyChar")); -// } else { -// properties.put("comment","tweak"); -// } -// fireDataSetUpdateListenerDataSetUpdated( -// new DataSetUpdateEvent(this, -// new SingleVectorDataSet( xValue, e.getX(), properties ) ) ); + Datum x= e.getX(); + HashMap properties= new HashMap(); + if ( e.getPlane("keyChar")!=null ) { + properties.put("comment",e.getPlane("keyChar")); + } else { + properties.put("comment","tweak"); + } + fireDataSetUpdateListenerDataSetUpdated( + new DataSetUpdateEvent(this, + new SingleVectorDataSet( xValue, e.getX(), properties ) ) ); } } ); topPlot.addMouseModule( tweakSlicer ); @@ -368,7 +366,6 @@ public void dataPointSelected( DataPointSelectionEvent e ) { canvas.add( plot, row1, col ); plot= new DasPlot( xaxis.createAttachedAxis(), new DasAxis( new DatumRange( -0.3,.3,Units.dimensionless ), DasAxis.VERTICAL ) ) { - @Override protected void drawContent(java.awt.Graphics2D g) { super.drawContent(g); @@ -441,7 +438,7 @@ private void recalculate( ) { public void dataPointSelected(org.das2.event.DataPointSelectionEvent event) { this.lastSelectedPoint= event; - QDataSet tds= (QDataSet)dataSetConsumer.getConsumedDataSet(); + TableDataSet tds= (TableDataSet)dataSetConsumer.getConsumedDataSet(); this.xValue= event.getX(); this.yValue= event.getY(); @@ -451,40 +448,35 @@ public void dataPointSelected(org.das2.event.DataPointSelectionEvent event) { // average the data down to xResolution DataSetRebinner rebinner= new AverageTableRebinner(); - DatumRange range= DataSetUtil.asDatumRange( DataSetOps.dependBounds( tds ).slice(0), true ); + DatumRange range= DataSetUtil.xRange( tds ); RebinDescriptor ddx= getRebinDescriptor( range ); try { - tds= (QDataSet)rebinner.rebin( tds, ddx, null ); + tds= (TableDataSet)rebinner.rebin( tds, ddx, null, null ); } catch ( DasException e ) { throw new RuntimeException(e); } - - QDataSet xds= SemanticOps.xtagsDataSet(tds); - - int i= DataSetUtil.closestIndex( xds, event.getX() ); - QDataSet contextDs= tds.slice(i); - contextLevelRenderer.setDataSet( Ops.log10( contextDs ) ); + int i= DataSetUtil.closestColumn( tds, event.getX() ); + + VectorDataSet contextDs= tds.getXSlice(i); + contextLevelRenderer.setDataSet( DataSetUtil.log10( contextDs ) ); //VectorDataSet slopeDs= VectorUtil.finiteDerivative( contextDs, nave ); //contextSlopeRenderer.setDataSet( slopeDs ); - - DatumRange xrange= DataSetUtil.asDatumRange( DataSetOps.dependBounds( tds ).slice(0), true ); - tds= new ClippedTableDataSet( tds, xrange, yrange ); - this.xValue= SemanticOps.getDatum( xds, xds.value(i) ); - + tds= new ClippedTableDataSet( tds, DataSetUtil.xRange(tds), yrange ); + + this.xValue= tds.getXTagDatum(i); topPlot.setTitle( "" + xValue + " " + yValue); - QDataSet spec= Ops.log10( tds.slice(i) ); - QDataSet xspec= SemanticOps.xtagsDataSet(spec); - + VectorDataSet spec= DataSetUtil.log10( tds.getXSlice(i) ); + int icutoff= cutoff( spec, slopeMin, nave, isLowCutoff() ? 1 : -1, levelMin ); if ( icutoff==-1 ) { - cutoff= SemanticOps.getUnits(xspec).getFillDatum(); + cutoff= spec.getXUnits().getFillDatum(); } else { - cutoff= SemanticOps.getDatum(xspec,xspec.value(icutoff)); + cutoff= spec.getXTagDatum(icutoff); } showPopup(); @@ -497,45 +489,44 @@ private void showPopup() { } public DataPointSelectionListener getSlicer( DasPlot plot, TableDataSetConsumer consumer ) { - DasAxis sourceYAxis = plot.getYAxis(); + DasAxis sourceYAxis = plot.getYAxis(); + DasAxis sourceZAxis = consumer.getZAxis(); + + DatumRange range= sourceYAxis.getDatumRange(); DasAxis xAxis = sourceYAxis.createAttachedAxis( DasAxis.HORIZONTAL ); cutoffSlicer= new CutoffSlicer( plot, xAxis ); return cutoffSlicer; } - -// private void testCutoff() { -// // see /home/jbf/voyager/cutoff/input.txt -// double[] spec= new double[] { -// -12.7093, -12.8479, -13.0042, -13.1509, -13.3007, -13.4671, -// -13.5536, -13.6603, -13.8000, -13.8873, -13.9908, -14.1162, -// -14.2016, -14.2694, -14.2844, -14.3126, -14.3507, -14.3841, -// -14.4252, -14.4779, -14.4972, -14.5226, -14.6059, -14.6517, -// -14.6545, -14.2863, -13.9616, -13.6898, -13.7407, -13.8821, -// -14.1541, -14.4287, -14.6663, -14.8647, -15.0540, -15.0863, -// -15.1190, -15.1464, -15.1479, -15.1399, -15.1284, -15.2001, -// -15.2780, -15.3611, -15.3976, -15.4230, -15.4467, -15.4879, -// -15.5437, -15.6058, -15.6501, -15.6606, -15.6737, -15.6867, -// -15.6955, -15.7425, -15.8222, -15.9376, -16.0174, -16.0091, -// }; -// double[] tags= new double[ spec.length ]; -// for ( int i=0; i< tags.length; i++ ) { tags[i]= i+1; } -// double slope= 0.266692; -// int nave=3; -// boolean isLowCutoff= true; -// int mult= isLowCutoff ? 1 : -1; -// double level= -14; -// -// DDataSet test= DDataSet.wrap(spec); -// test.putProperty( QDataSet.UNITS, Units.v2pm2Hz ); -// test.putProperty( QDataSet.DEPEND_0, DDataSet.wrap(tags) ); -// -// int icut= cutoff( test, Units.v2pm2Hz.createDatum(slope), nave, mult, Units.v2pm2Hz.createDatum(level) ); -// System.out.println("icut="+icut+" should be 25"); -// } - - - private transient java.util.ArrayList dataSetUpdateListenerList; //TODO: can we not use javax.swing.event.EventListenerList? + + private void testCutoff() { + // see /home/jbf/voyager/cutoff/input.txt + double[] spec= new double[] { + -12.7093, -12.8479, -13.0042, -13.1509, -13.3007, -13.4671, + -13.5536, -13.6603, -13.8000, -13.8873, -13.9908, -14.1162, + -14.2016, -14.2694, -14.2844, -14.3126, -14.3507, -14.3841, + -14.4252, -14.4779, -14.4972, -14.5226, -14.6059, -14.6517, + -14.6545, -14.2863, -13.9616, -13.6898, -13.7407, -13.8821, + -14.1541, -14.4287, -14.6663, -14.8647, -15.0540, -15.0863, + -15.1190, -15.1464, -15.1479, -15.1399, -15.1284, -15.2001, + -15.2780, -15.3611, -15.3976, -15.4230, -15.4467, -15.4879, + -15.5437, -15.6058, -15.6501, -15.6606, -15.6737, -15.6867, + -15.6955, -15.7425, -15.8222, -15.9376, -16.0174, -16.0091, + }; + double[] tags= new double[ spec.length ]; + for ( int i=0; i< tags.length; i++ ) { tags[i]= i+1; } + double slope= 0.266692; + int nave=3; + int mult= isLowCutoff() ? 1 : -1; + double level= -14; + int icut= cutoff( + new DefaultVectorDataSet( spec, Units.hertz, spec, Units.v2pm2Hz, new HashMap() ), + Units.v2pm2Hz.createDatum(slope), nave, mult, Units.v2pm2Hz.createDatum(level) ); + System.out.println("icut="+icut+" should be 25"); + } + + + private transient java.util.ArrayList dataSetUpdateListenerList; public synchronized void addDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { if (dataSetUpdateListenerList == null ) { @@ -640,7 +631,7 @@ public void setNave(int nave) { int oldVal= this.nave; if ( this.nave!=nave ) { this.nave = nave; - PropertyChangeEvent e= new PropertyChangeEvent( this, "nave", Integer.valueOf(oldVal), Integer.valueOf(nave) ); + PropertyChangeEvent e= new PropertyChangeEvent( this, "nave", new Integer(oldVal), new Integer(nave) ); firePropertyChangeListenerPropertyChange( e ); recalculateSoon(); } @@ -682,7 +673,7 @@ public boolean isLowCutoff() { * @param lowCutoff New value of property lowCutoff. */ public void setLowCutoff(boolean lowCutoff) { - Boolean oldVal= this.lowCutoff; + Boolean oldVal= Boolean.valueOf( this.lowCutoff ); if ( this.lowCutoff!=lowCutoff ) { this.lowCutoff = lowCutoff; PropertyChangeEvent e= new PropertyChangeEvent( this, "lowCutoff", oldVal, Boolean.valueOf(lowCutoff) ); @@ -695,13 +686,16 @@ public void setLowCutoff(boolean lowCutoff) { /** * Utility field used by event firing mechanism. */ - private javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList(); + private javax.swing.event.EventListenerList listenerList = null; /** * Registers PropertyChangeListener to receive events. * @param listener The listener to register. */ - public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { + public synchronized void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } listenerList.add(java.beans.PropertyChangeListener.class, listener); } @@ -709,7 +703,7 @@ public void addPropertyChangeListener(java.beans.PropertyChangeListener listener * Removes PropertyChangeListener from the list of listeners. * @param listener The listener to remove. */ - public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { + public synchronized void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { listenerList.remove(java.beans.PropertyChangeListener.class, listener); } @@ -719,8 +713,8 @@ public void removePropertyChangeListener(java.beans.PropertyChangeListener liste * @param event The event to be fired */ private void firePropertyChangeListenerPropertyChange(java.beans.PropertyChangeEvent event) { - Object[] listeners; - listeners = listenerList.getListenerList(); + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i]==java.beans.PropertyChangeListener.class) { ((java.beans.PropertyChangeListener)listeners[i+1]).propertyChange(event); diff --git a/dasCore/src/org/das2/event/DasEvent.java b/dasCore/src/org/das2/event/DasEvent.java index 0d8ebde9b..3fa0a79bd 100644 --- a/dasCore/src/org/das2/event/DasEvent.java +++ b/dasCore/src/org/das2/event/DasEvent.java @@ -29,8 +29,6 @@ */ public class DasEvent extends java.util.EventObject { - private static final long serialVersionUID = 1L; - /** Creates a new instance of DasEvent */ public DasEvent(Object source) { super(source); diff --git a/dasCore/src/org/das2/event/DasMouseEvent.java b/dasCore/src/org/das2/event/DasMouseEvent.java index 68e654a61..2781530b1 100644 --- a/dasCore/src/org/das2/event/DasMouseEvent.java +++ b/dasCore/src/org/das2/event/DasMouseEvent.java @@ -29,8 +29,6 @@ */ public class DasMouseEvent extends DasEvent { - private static final long serialVersionUID = 1L; - /** Creates a new instance of DasMouseEvent */ public DasMouseEvent(Object o) { super(o); diff --git a/dasCore/src/org/das2/event/DasMouseInputAdapter.java b/dasCore/src/org/das2/event/DasMouseInputAdapter.java index 5db6c13d7..99121d0e3 100755 --- a/dasCore/src/org/das2/event/DasMouseInputAdapter.java +++ b/dasCore/src/org/das2/event/DasMouseInputAdapter.java @@ -22,7 +22,6 @@ */ package org.das2.event; -import java.util.logging.Level; import org.das2.graph.DasColumn; import org.das2.graph.DasRow; import org.das2.system.DasLogger; @@ -39,11 +38,9 @@ import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; -import java.lang.reflect.Method; import java.util.*; import java.util.logging.Logger; import org.das2.components.propertyeditor.Editable; -import org.das2.util.LoggerManager; /** * DasMouseInputAdapter delegates mouse and key events to mouse modules, which @@ -69,22 +66,22 @@ public class DasMouseInputAdapter extends MouseInputAdapter implements Editable, * that a few modules could be used together simultaneously, but this implementation * only allows for one to be active at a time. */ - private ArrayList active = null; + private Vector active = null; private boolean pinned = false; - private final ArrayList modules; - private final HashMap primaryActionButtonMap; - private final HashMap secondaryActionButtonMap; + private Vector modules; + private HashMap primaryActionButtonMap; + private HashMap secondaryActionButtonMap; protected JPopupMenu primaryPopup; protected JPopupMenu secondaryPopup; + private Point primaryPopupLocation; + private Point secondaryPopupLocation; private JPanel pngFileNamePanel; private JTextField pngFileTextField; private JFileChooser pngFileChooser; JCheckBoxMenuItem primarySelectedItem; JCheckBoxMenuItem secondarySelectedItem; // must be non-null, but may contain null elements Rectangle[] dirtyBoundsList; - - private static final Logger logger = LoggerManager.getLogger( "das2.gui.dmia" ); - + Logger log = DasLogger.getLogger(DasLogger.GUI_LOG); /** * number of additional inserted popup menu items to the primary menu. */ @@ -98,9 +95,11 @@ public class DasMouseInputAdapter extends MouseInputAdapter implements Editable, protected ActionListener popupListener; protected DasCanvasComponent parent = null; + //private Point selectionStart; // in component frame + //private Point selectionEnd; // in component frame private Point dSelectionStart; // in DasCanvas device frame private Point dSelectionEnd; // in DasCanvas device frame - private final MousePointSelectionEvent mousePointSelection; + private MousePointSelectionEvent mousePointSelection; private int xOffset; // parent to canvas offset private int yOffset; // parent to canvas offset private int button = 0; // current depressed button @@ -108,29 +107,16 @@ public class DasMouseInputAdapter extends MouseInputAdapter implements Editable, private boolean drawControlPoints = false; private DragRenderer resizeRenderer = null; private Point resizeStart = null; - + /* + *this will be removed, and the component can add its own popup buttons. + */ + Vector hotSpots = null; Rectangle dirtyBounds = null; private boolean hasFocus = false; private Point pressPosition; // in the component frame - private final boolean headless; + private boolean headless; - public void setMenuLabel(String id) { - primaryPopup.setLabel(id); - secondaryPopup.setLabel(id); - } - - /** - * set the name of the menus to help with debugging - * @param name - */ - public void resetName(String name) { - if ( !this.headless ) { - primaryPopup.setName("dmia_pop1_"+name); - secondaryPopup.setName("dmia_pop2_"+name); - } - } - - private static final class MouseMode { + private static class MouseMode { String s; boolean resizeTop = false; @@ -138,35 +124,26 @@ private static final class MouseMode { boolean resizeRight = false; boolean resizeLeft = false; Point moveStart = null; // in the DasCanvas frame - static final MouseMode idle = new MouseMode("idle"); - static final MouseMode resize = new MouseMode("resize"); - static final MouseMode move = new MouseMode("move"); - static final MouseMode moduleDrag = new MouseMode("moduleDrag"); + static MouseMode idle = new MouseMode("idle"); + static MouseMode resize = new MouseMode("resize"); + static MouseMode move = new MouseMode("move"); + static MouseMode moduleDrag = new MouseMode("moduleDrag"); MouseMode(String s) { this.s = s; } - @Override + public String toString() { return s; } } - private Feedback feedback; - - public static interface Feedback { - public void setMessage( String message ); - } - - /** - * Create a DasMouseInputAdapter to handle mouse events for the component. - * @param parent the component. - */ + /** Creates a new instance of dasMouseInputAdapter */ public DasMouseInputAdapter(DasCanvasComponent parent) { this.parent = parent; - modules = new ArrayList(); + modules = new Vector(); primaryActionButtonMap = new HashMap(); secondaryActionButtonMap = new HashMap(); @@ -174,69 +151,36 @@ public DasMouseInputAdapter(DasCanvasComponent parent) { this.headless = DasApplication.getDefaultApplication().isHeadless(); if (!headless) { primaryPopup= new JPopupMenu(); - primaryPopup.setName("dmia_pop1_"+parent.getDasName()); numInserted = createPopup(primaryPopup); secondaryPopup= new JPopupMenu(); - secondaryPopup.setName("dmia_pop2_"+parent.getDasName()); numInsertedSecondary = createPopup(secondaryPopup); - } active = null; mousePointSelection = new MousePointSelectionEvent(this, 0, 0); - resizeRenderer = new BoxRenderer(parent,false); + resizeRenderer = new BoxRenderer(parent); + dirtyBoundsList = new Rectangle[0]; - this.feedback= new Feedback() { - @Override - public void setMessage(String message) { - // do nothing by default. - } - }; } - public void setFeedback( Feedback f ) { - this.feedback= f; - } - public void replaceMouseModule(MouseModule oldModule, MouseModule newModule) { JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(oldModule); primaryActionButtonMap.put(newModule, j); primaryActionButtonMap.remove(oldModule); secondaryActionButtonMap.put(newModule, secondaryActionButtonMap.get(oldModule)); secondaryActionButtonMap.remove(oldModule); - modules.remove(oldModule); - modules.add(newModule); + modules.removeElement(oldModule); + modules.addElement(newModule); } - - public synchronized void removeMouseModule(MouseModule module) { - JCheckBoxMenuItem j; - j= (JCheckBoxMenuItem) primaryActionButtonMap.remove(module); - if ( j!=null && !headless ) { - if ( primaryPopup.isAncestorOf(j) ) { - primaryPopup.remove(j); - numInserted--; - } - } - j= (JCheckBoxMenuItem) secondaryActionButtonMap.remove(module); - if ( j!=null && !headless ) { - if ( secondaryPopup.isAncestorOf(j) ) { - secondaryPopup.remove(j); - numInsertedSecondary--; - } - } - modules.remove(module); - } - /** * add a mouse module to the list of available modules. If a module with the same * label exists already, it will be replaced. - * @param module the module */ - public synchronized void addMouseModule(MouseModule module) { + public void addMouseModule(MouseModule module) { if (headless) { DasLogger.getLogger(DasLogger.GUI_LOG).fine("not adding module since headless is true"); @@ -244,7 +188,7 @@ public synchronized void addMouseModule(MouseModule module) { } else { MouseModule preExisting = getModuleByLabel(module.getLabel()); if (preExisting != null) { - DasLogger.getLogger(DasLogger.GUI_LOG).log(Level.FINE, "Replacing mouse module {0}.", module.getLabel()); + DasLogger.getLogger(DasLogger.GUI_LOG).fine("Replacing mouse module " + module.getLabel() + "."); replaceMouseModule(preExisting, module); } else { @@ -266,68 +210,25 @@ public synchronized void addMouseModule(MouseModule module) { try { // insert the check box after the separator, and at the end of the actions list. - int i= numInserted + 1 + primaryActionButtonMap.size() - 1; - if ( i>primaryPopup.getComponentCount() ) { - i= primaryPopup.getComponentCount(); - } - if ( i<0 ) { - logger.finer("here is that bug where numInserted is negative..."); - i= 0; - } - primaryPopup.add(primaryNewItem, i ); - i= numInsertedSecondary + 1 + secondaryActionButtonMap.size() - 1; - if ( i>secondaryPopup.getComponentCount() ) { - i= secondaryPopup.getComponentCount(); - } - if ( i<0 ) { - logger.finer("here is that bug where numInsertedSecondary is negative..."); - i= 0; - } - secondaryPopup.add(secondaryNewItem, i ); + primaryPopup.add(primaryNewItem, numInserted + 1 + primaryActionButtonMap.size() - 1); + secondaryPopup.add(secondaryNewItem, numInsertedSecondary + 1 + secondaryActionButtonMap.size() - 1); } catch ( IllegalArgumentException ex ) { - logger.log( Level.SEVERE, ex.getMessage(), ex ); + ex.printStackTrace(); } } } } - /** - * added so ColumnColumnConnector could delegate to DasPlot's adapter. - * @return - */ - public JPopupMenu getPrimaryPopupMenu() { - return this.primaryPopup; - } - - /** - * allow clients to cancel, to take the same action as if cancel - * were pressed. - */ - public void cancel() { - Runnable run= new Runnable() { - @Override - public void run() { - active = null; - getGlassPane().setDragRenderer(null, null, null); - parent.getCanvas().paintImmediately(0, 0, parent.getCanvas().getWidth(), parent.getCanvas().getHeight()); - feedback.setMessage(""); - refresh(); - } - }; - SwingUtilities.invokeLater(run); - } - public KeyAdapter getKeyAdapter() { return new KeyAdapter() { - @Override + public void keyPressed(KeyEvent ev) { - logger.finest("keyPressed "); - if (ev.getKeyCode() == KeyEvent.VK_ESCAPE && active != null) { + log.finest("keyPressed "); + if (ev.getKeyCode() == KeyEvent.VK_ESCAPE & active != null) { active = null; getGlassPane().setDragRenderer(null, null, null); parent.getCanvas().paintImmediately(0, 0, parent.getCanvas().getWidth(), parent.getCanvas().getHeight()); - feedback.setMessage(""); refresh(); ev.consume(); } else if (ev.getKeyCode() == KeyEvent.VK_SHIFT) { @@ -336,17 +237,16 @@ public void keyPressed(KeyEvent ev) { } else if (ev.getKeyChar() == 'p') { pinned = true; ev.consume(); - feedback.setMessage("pinned, will stay active until escape is pressed"); } else { if (active == null) { return; } - for (Object active1 : active) { - ((MouseModule) active1).keyPressed(ev); + for (int i = 0; i < active.size(); i++) { + ((MouseModule) active.get(i)).keyPressed(ev); } } } - @Override + public void keyReleased(KeyEvent ev) { if (ev.getKeyCode() == KeyEvent.VK_SHIFT) { drawControlPoints = false; @@ -355,17 +255,17 @@ public void keyReleased(KeyEvent ev) { if (active == null) { return; } - for (Object active1 : active) { - ((MouseModule) active1).keyReleased(ev); + for (int i = 0; i < active.size(); i++) { + ((MouseModule) active.get(i)).keyReleased(ev); } } - @Override + public void keyTyped(KeyEvent ev) { if (active == null) { return; } - for (Object active1 : active) { - ((MouseModule) active1).keyTyped(ev); + for (int i = 0; i < active.size(); i++) { + ((MouseModule) active.get(i)).keyTyped(ev); } } }; @@ -373,10 +273,10 @@ public void keyTyped(KeyEvent ev) { public MouseModule getPrimaryModule() { ArrayList activ = new ArrayList(); - for (Object module : modules) { - JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(module); + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(modules.get(i)); if (j.isSelected()) { - activ.add(module); + activ.add(modules.get(i)); } } return (MouseModule) activ.get(0); // at one time we allowed multiple modules at once. @@ -384,10 +284,10 @@ public MouseModule getPrimaryModule() { public MouseModule getSecondaryModule() { ArrayList activ = new ArrayList(); - for (Object module : modules) { - JCheckBoxMenuItem j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(module); + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(modules.get(i)); if (j.isSelected()) { - activ.add(module); + activ.add(modules.get(i)); } } return (MouseModule) activ.get(0); // at one time we allowed multiple modules at once. @@ -397,7 +297,6 @@ public MouseModule getSecondaryModule() { * set the primary module, the module receiving left-button events, to the * module provided. If the module is not already loaded, implicitly addMouseModule * is called. - * @param module the module */ public void setPrimaryModule(MouseModule module) { if (headless) { @@ -411,9 +310,9 @@ public void setPrimaryModule(MouseModule module) { try { Object ii = ((Map.Entry) i.next()).getValue(); ((JCheckBoxMenuItem) ii).setSelected(false); - } catch (RuntimeException ex) { - logger.log( Level.SEVERE, ex.getMessage(), ex ); - throw ex; + } catch (RuntimeException e) { + e.printStackTrace(); + throw e; } } @@ -430,8 +329,6 @@ public void setPrimaryModule(MouseModule module) { * set the secondary module, the module receiving middle-button events, to the * module provided. If the module is not already loaded, implicitly addMouseModule * is called. - * - * @param module */ public void setSecondaryModule(MouseModule module) { if (headless) { @@ -445,9 +342,9 @@ public void setSecondaryModule(MouseModule module) { try { Object ii = ((Map.Entry) i.next()).getValue(); ((JCheckBoxMenuItem) ii).setSelected(false); - } catch (RuntimeException ex) { - logger.log( Level.SEVERE, ex.getMessage(), ex ); - throw ex; + } catch (RuntimeException e) { + e.printStackTrace(); + throw e; } } @@ -470,14 +367,12 @@ public void setSecondaryModule(MouseModule module) { */ private int createPopup(JPopupMenu popup) { - synchronized (this) { - popupListener = createPopupMenuListener(); - } + popupListener = createPopupMenuListener(); Action[] componentActions = parent.getActions(); - for (Action componentAction : componentActions) { + for (int iaction = 0; iaction < componentActions.length; iaction++) { JMenuItem item = new JMenuItem(); - item.setAction(componentAction); + item.setAction(componentActions[iaction]); popup.add(item); } int numInsert = componentActions.length; @@ -485,11 +380,11 @@ private int createPopup(JPopupMenu popup) { popup.addSeparator(); // mouse modules go here popup.addSeparator(); - + Action[] canvasActions = DasCanvas.getActions(); - for (Action canvasAction : canvasActions) { + for (int iaction = 0; iaction < canvasActions.length; iaction++) { JMenuItem item = new JMenuItem(); - item.setAction(canvasAction); + item.setAction(canvasActions[iaction]); popup.add(item); } @@ -498,9 +393,9 @@ private int createPopup(JPopupMenu popup) { private ActionListener createPopupMenuListener() { return new ActionListener() { - @Override + public void actionPerformed(ActionEvent e) { - //DasMouseInputAdapter outer = DasMouseInputAdapter.this; // useful for debugging + DasMouseInputAdapter outer = DasMouseInputAdapter.this; // useful for debugging String command = e.getActionCommand(); if (command.equals("properties")) { parent.showProperties(); @@ -559,27 +454,30 @@ public void actionPerformed(ActionEvent e) { if (primarySelectedItem != null) { primarySelectedItem.setSelected(false); } - for (Object module : modules) { - JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(module); + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(modules.get(i)); if (j.isSelected()) { primarySelectedItem = j; break; } } primarySelectedItem.setSelected(true); // for case when selection wasn't changed. + //primaryPopup.show( parent, l.x, l.y ); } else if (command.equals("secondary")) { if (secondarySelectedItem != null) { secondarySelectedItem.setSelected(false); } - for (Object module : modules) { - JCheckBoxMenuItem j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(module); + Point l = secondaryPopupLocation; + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(modules.get(i)); if (j.isSelected()) { secondarySelectedItem = j; break; } } + //secondaryPopup.show( parent, l.x, l.y ); } else { - logger.log(Level.FINE, "{0}", command); + org.das2.util.DasDie.println("" + command); } } }; @@ -588,12 +486,22 @@ public void actionPerformed(ActionEvent e) { /** * call the renderDrag method of the active module's dragRenderer. This method * returns an array of Rectangles, or null, indicating the affected regions. - * It's also permissable for a array element to be null. + * It's also permisable for a array element to be null. */ - private void renderSelection() { + private void renderSelection(Graphics2D g2d) { try { - for (Object active1 : active) { - DragRenderer dr = ((MouseModule) active1).getDragRenderer(); + //DasCanvas canvas = parent.getCanvas(); + //selectionStart = SwingUtilities.convertPoint(canvas, dSelectionStart, parent); + //selectionEnd = SwingUtilities.convertPoint(canvas, dSelectionEnd, parent); + + for (int i = 0; i < active.size(); i++) { + DragRenderer dr = ((MouseModule) active.get(i)).getDragRenderer(); + + //Rectangle[] dd = dr.renderDrag( getGlassPane().getGraphics(), dSelectionStart, dSelectionEnd); + //dirtyBoundsList = new Rectangle[dd.length]; + //for (i = 0; i < dd.length; i++) { + // dirtyBoundsList[i] = new Rectangle(dd[i]); + //} getGlassPane().setDragRenderer(dr, dSelectionStart, dSelectionEnd); } } catch (RuntimeException e) { @@ -601,8 +509,7 @@ private void renderSelection() { } } - /** - * This attempts to redraw just the affected portions of parent. Presently it + /* This attempts to redraw just the affected portions of parent. Presently it * needs to call the parent's paintImmediately twice, because we don't know what * the dragRenderer's dirty bounds will be. */ @@ -614,22 +521,19 @@ private synchronized void refresh() { dd[i] = new Rectangle(dirtyBoundsList[i]); } } - for (Rectangle dd1 : dd) { - if (dd1 != null) { - //parent.getCanvas().paintImmediately(dd[i]); - parent.getCanvas().repaint(dd1); + for (int i = 0; i < dd.length; i++) { + if (dd[i] != null) { + parent.getCanvas().paintImmediately(dd[i]); } } - for (Rectangle dirtyBoundsList1 : dirtyBoundsList) { - if (dirtyBoundsList1 != null) { - //parent.getCanvas().paintImmediately(dirtyBoundsList[i]); - parent.getCanvas().repaint(dirtyBoundsList1); + for (int i = 0; i < dirtyBoundsList.length; i++) { + if (dirtyBoundsList[i] != null) { + parent.getCanvas().paintImmediately(dirtyBoundsList[i]); } } } else { if (active != null) { - //parent.getCanvas().paintImmediately(0, 0, parent.getCanvas().getWidth(), parent.getCanvas().getHeight()); - parent.getCanvas().repaint(); + parent.getCanvas().paintImmediately(0, 0, parent.getCanvas().getWidth(), parent.getCanvas().getHeight()); } } if (active == null) { @@ -643,44 +547,34 @@ private synchronized void refresh() { */ public void paint(Graphics g1) { Graphics2D g = (Graphics2D) g1.create(); + //g= (Graphics2D)getGlassPane().getGraphics(); + //g.translate(parent.getX(),parent.getY()); - DasCanvasComponent lparent= this.parent; - if ( lparent==null ) return; - g.translate(-lparent.getX(), -lparent.getY()); + g.translate(-parent.getX(), -parent.getY()); if (active != null) { - renderSelection(); + renderSelection(g); } if (hasFocus && hoverHighlite) { g.setColor(new Color(255, 0, 0, 10)); g.setStroke(new BasicStroke(10)); - g.draw(lparent.getBounds()); - g.dispose(); + g.draw(parent.getBounds()); return; } if (hasFocus && drawControlPoints) { - drawControlPoints(g,lparent); + drawControlPoints(g); } - g.dispose(); } - private void drawControlPoints(Graphics2D g, DasCanvasComponent parent ) { + private void drawControlPoints(Graphics2D g) { if (parent.getRow() != DasRow.NULL && parent.getColumn() != DasColumn.NULL) { int xLeft = parent.getColumn().getDMinimum(); int xRight = parent.getColumn().getDMaximum(); - int xMid; int yTop = parent.getRow().getDMinimum(); int yBottom = parent.getRow().getDMaximum(); - int yMid; - Rectangle r= parent.getBounds(); - xMid= r.x + r.width/2; - yMid= r.y + r.height/2; - Graphics2D gg = (Graphics2D) g.create(); - gg.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); - //gg.translate(-parent.getX(),-parent.getY()); gg.setColor(new Color(0, 0, 0, 255)); @@ -690,29 +584,23 @@ private void drawControlPoints(Graphics2D g, DasCanvasComponent parent ) { gg.fillRect(xLeft + 1, yBottom - ss + 1, ss - 2, ss - 2); gg.fillRect(xRight - ss + 1, yBottom - ss + 1, ss - 2, ss - 2); - gg.fillRect(xMid + 1 - ss/2, yTop + 1, ss - 2, ss - 2); - gg.fillRect(xRight - ss + 1, yMid + 1 - ss/2, ss - 2, ss - 2); - gg.fillRect(xMid + 1 - ss/2, yBottom - ss + 1, ss - 2, ss - 2); - gg.fillRect(xLeft + 1, yMid - ss/2 + 1, ss - 2, ss - 2); - gg.setColor(new Color(255, 255, 255, 100)); gg.drawRect(xLeft, yTop, ss, ss); gg.drawRect(xRight - ss, yTop, ss, ss); gg.drawRect(xLeft, yBottom - ss, ss, ss); gg.drawRect(xRight - ss, yBottom - ss, ss, ss); - gg.drawRect(xMid- ss/2, yTop + 1, ss, ss ); - gg.drawRect(xRight - ss, yMid - ss/2, ss, ss ); - gg.drawRect(xMid - ss/2, yBottom - ss, ss, ss ); - gg.drawRect(xLeft , yMid - ss/2, ss, ss ); + + int xmid = (xLeft + xRight) / 2; + int ymid = (yTop + yBottom) / 2; int rr = 4; g.setColor(new Color(255, 255, 255, 100)); - gg.fillOval(xMid - rr - 1, yMid - rr - 1, rr * 2 + 3, rr * 2 + 3); + gg.fillOval(xmid - rr - 1, ymid - rr - 1, rr * 2 + 3, rr * 2 + 3); gg.setColor(new Color(0, 0, 0, 255)); - gg.drawOval(xMid - rr, yMid - rr, rr * 2, rr * 2); - gg.fillOval(xMid - 1, yMid - 1, 3, 3); + gg.drawOval(xmid - rr, ymid - rr, rr * 2, rr * 2); + gg.fillOval(xmid - 1, ymid - 1, 3, 3); gg.dispose(); } @@ -759,9 +647,6 @@ private MouseMode activateMouseMode(MouseEvent e) { } else if (yBottomSide) { result = MouseMode.resize; cursor = new Cursor(Cursor.SW_RESIZE_CURSOR); - } else if (yMiddle) { - result = MouseMode.resize; - cursor = new Cursor(Cursor.W_RESIZE_CURSOR); } } else if (xRightSide) { if (yTopSide) { @@ -770,19 +655,10 @@ private MouseMode activateMouseMode(MouseEvent e) { } else if (yBottomSide) { result = MouseMode.resize; cursor = new Cursor(Cursor.SE_RESIZE_CURSOR); - } else if (yMiddle) { - result = MouseMode.resize; - cursor = new Cursor(Cursor.E_RESIZE_CURSOR); } } else if (xMiddle && yMiddle) { result = MouseMode.move; cursor = new Cursor(Cursor.MOVE_CURSOR); - } else if (xMiddle && yTopSide ) { - result = MouseMode.resize; - cursor = new Cursor(Cursor.N_RESIZE_CURSOR); - } else if ( xMiddle && yBottomSide ) { - result = MouseMode.resize; - cursor = new Cursor(Cursor.S_RESIZE_CURSOR); } } @@ -795,7 +671,7 @@ private MouseMode activateMouseMode(MouseEvent e) { result.resizeLeft = xLeftSide; } else if (result == MouseMode.move) { result.moveStart = e.getPoint(); - result.moveStart.translate( parent.getX(), parent.getY()); + result.moveStart.translate(-parent.getX(), -parent.getY()); } if (result != mouseMode) { @@ -806,14 +682,17 @@ private MouseMode activateMouseMode(MouseEvent e) { @Override public void mouseMoved(MouseEvent e) { - logger.finest("mouseMoved"); + log.finest("mouseMoved"); Point l = parent.getLocation(); xOffset = l.x; yOffset = l.y; boolean drawControlPoints0 = this.drawControlPoints; - - drawControlPoints = (e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) == MouseEvent.SHIFT_DOWN_MASK; + if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) == MouseEvent.SHIFT_DOWN_MASK) { + drawControlPoints = true; + } else { + drawControlPoints = false; + } if (drawControlPoints0 != drawControlPoints) { parent.repaint(); @@ -828,11 +707,16 @@ public void mouseMoved(MouseEvent e) { } private void showPopup(JPopupMenu menu, MouseEvent ev) { - logger.finest("showPopup"); - if ( menu != primaryPopup && menu != secondaryPopup) { + log.finest("showPopup"); + HashMap map = null; + if (menu == primaryPopup) { + map = primaryActionButtonMap; + } else if (menu == secondaryPopup) { + map = secondaryActionButtonMap; + } else { throw new IllegalArgumentException("menu must be primary or secondary popup menu"); } - for (Iterator i = modules.iterator(); i.hasNext();) { //TODO: it looks like this strange bit of code just sets the label. + for (Iterator i = modules.iterator(); i.hasNext();) { MouseModule mm = (MouseModule) i.next(); JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(mm); j.setText(mm.getLabel()); @@ -850,7 +734,7 @@ public boolean getPinned() { @Override public void mousePressed(MouseEvent e) { - logger.log(Level.FINE, "mousePressed {0} on {1}", new Object[] { mouseMode, parent } ); + log.finer("mousePressed " + mouseMode); if (pinned) { active = null; refresh(); @@ -873,15 +757,11 @@ public void mousePressed(MouseEvent e) { resizeStart.x = 0 + xOffset; } else if (mouseMode.resizeLeft) { resizeStart.x = parent.getWidth() + xOffset; - } else { - resizeStart.x = 0 + xOffset; } if (mouseMode.resizeTop) { resizeStart.y = parent.getHeight() + yOffset; } else if (mouseMode.resizeBottom) { resizeStart.y = 0 + yOffset; - } else { - resizeStart.y = 0 + yOffset; } } else if (mouseMode == MouseMode.move) { @@ -891,8 +771,11 @@ public void mousePressed(MouseEvent e) { } else { if (active == null) { button = e.getButton(); + //selectionStart = e.getPoint(); dSelectionStart = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), parent.getCanvas()); + //selectionEnd = e.getPoint(); dSelectionEnd = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), parent.getCanvas()); + //graphics = (Graphics2D) parent.getGraphics(); if (e.isControlDown() || button == MouseEvent.BUTTON3) { if (button == MouseEvent.BUTTON1 || button == MouseEvent.BUTTON3) { @@ -902,62 +785,29 @@ public void mousePressed(MouseEvent e) { } } else { - active = new ArrayList(); + active = new Vector(); if (button == MouseEvent.BUTTON1 || button == MouseEvent.BUTTON3) { - for (Object module : modules) { - JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(module); + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) primaryActionButtonMap.get(modules.get(i)); if (j.isSelected()) { - active.add(module); + active.add(modules.get(i)); } } } else { - for (Object module : modules) { - JCheckBoxMenuItem j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(module); + for (int i = 0; i < modules.size(); i++) { + JCheckBoxMenuItem j = (JCheckBoxMenuItem) secondaryActionButtonMap.get(modules.get(i)); if (j.isSelected()) { - active.add(module); + active.add(modules.get(i)); } } } - Runnable run= new Runnable() { - @Override - public void run() { - ArrayList lactive= active; - if ( lactive==null || lactive.isEmpty() ) return; - MouseModule theone= ((MouseModule)lactive.get(0)); - if ( theone==null ) return; - try { - // set the message based on whether the module overrides mouseRangeSelected - Method m= theone.getClass().getMethod("mouseRangeSelected",MouseDragEvent.class); - if ( m.equals(MouseModule.class.getMethod("mouseRangeSelected",MouseDragEvent.class)) ) { - //feedback.setMessage("" + theone.getListLabel() ); - } else { - // it's going to do something when we release. - String s= theone.getDirections(); - if ( s==null ) { - s= theone.getLabel(); - } - if ( !( s.startsWith(theone.getLabel()) ) ) { - s= theone.getLabel()+": "+s; - } - feedback.setMessage( s + ", press escape to cancel" ); - } - } catch (NoSuchMethodException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } catch (SecurityException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - - } - }; - SwingUtilities.invokeLater(run); - mouseMode = MouseMode.moduleDrag; mousePointSelection.set(e.getX() + xOffset, e.getY() + yOffset); - for (Object active1 : active) { - MouseModule j = (MouseModule) active1; + for (int i = 0; i < active.size(); i++) { + MouseModule j = (MouseModule) active.get(i); j.mousePressed(e); if (j.dragRenderer.isPointSelection()) { mouseDragged(e); @@ -970,16 +820,10 @@ public void run() { @Override public void mouseDragged(MouseEvent e) { - logger.log(Level.FINE, "mouseDragged {0} on {1}", new Object[] { mouseMode, parent } ); + log.finest("mouseDragged in " + mouseMode); if (mouseMode == MouseMode.resize) { Point p = e.getPoint(); p.translate(parent.getX(), parent.getY()); - if ( !( mouseMode.resizeBottom || mouseMode.resizeTop ) ) { - p.y= parent.getRow().getDMaximum(); - } - if ( !( mouseMode.resizeRight || mouseMode.resizeLeft ) ) { - p.x= parent.getColumn().getDMaximum(); - } getGlassPane().setDragRenderer(resizeRenderer, resizeStart, p); getGlassPane().repaint(); @@ -999,22 +843,26 @@ public void mouseDragged(MouseEvent e) { getGlassPane().setDragRenderer(resizeRenderer, p1, p2); getGlassPane().repaint(); + //resizeRenderer.clear(graphics); + //resizeRenderer.renderDrag(graphics, p1, p2); } else { if (active != null) { + //clearSelection(graphics); + //selectionEnd = e.getPoint(); dSelectionEnd = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), parent.getCanvas()); mousePointSelection.set((int) dSelectionEnd.getX(), (int) dSelectionEnd.getY()); - for (Object active1 : active) { + for (int i = 0; i < active.size(); i++) { try { - MouseModule j = (MouseModule) active1; + MouseModule j = (MouseModule) active.get(i); if (j.dragRenderer.isPointSelection()) { - logger.finest("mousePointSelected"); + log.finest("mousePointSelected"); j.mousePointSelected(mousePointSelection); } if (j.dragRenderer.isUpdatingDragSelection()) { // Really it should be the DMM that indicates it wants updates...whoops... MouseDragEvent de = j.dragRenderer.getMouseDragEvent(parent, dSelectionStart, dSelectionEnd, e.isShiftDown()); - logger.finest("mouseRangeSelected"); + log.finest("mouseRangeSelected"); j.mouseRangeSelected(de); } j.mouseDragged(e); @@ -1057,9 +905,8 @@ private void performResize(MouseEvent e) { getGlassPane().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } - @Override public void mouseReleased(MouseEvent e) { - logger.log(Level.FINE, "mouseReleased {0} on {1}", new Object[] { mouseMode, parent } ); + log.finest("mouseReleased"); if (mouseMode == MouseMode.resize) { performResize(e); getGlassPane().setDragRenderer(null, null, null); @@ -1074,15 +921,15 @@ public void mouseReleased(MouseEvent e) { } else { if (e.getButton() == button) { if (active != null) { - for (Object active1 : active) { - MouseModule j = (MouseModule) active1; + //clearSelection(graphics); + int x = e.getX(); + int y = e.getY(); + for (int i = 0; i < active.size(); i++) { + MouseModule j = (MouseModule) active.get(i); try { MouseDragEvent de = j.dragRenderer.getMouseDragEvent(parent, dSelectionStart, dSelectionEnd, e.isShiftDown()); - if ( de!=null ) { - j.mouseRangeSelected(de); - } - feedback.setMessage("" ); + j.mouseRangeSelected(de); } catch (RuntimeException ex) { DasExceptionHandler.handle(ex); } finally { @@ -1106,19 +953,36 @@ public void mouseReleased(MouseEvent e) { } + public void removeMouseModule(MouseModule module) { + // not implemented yet + } + + /** + * Getter for property mouseModules. + * @return Value of property mouseModules. + */ public MouseModule getMouseModule(int i) { return (MouseModule) modules.get(i); } public MouseModule[] getMouseModules() { MouseModule[] result = new MouseModule[modules.size()]; - result= (MouseModule[]) modules.toArray(result); + modules.copyInto(result); return result; } + /** + * @deprecated use getPrimaryModuleByLabel + * @return + */ + public String getPrimaryModuleLabel() { + MouseModule primary = getPrimaryModule(); + return primary == null ? "" : primary.getLabel(); + } + public String getPrimaryModuleByLabel() { - MouseModule primary1 = getPrimaryModule(); - return primary1 == null ? "" : primary1.getLabel(); + MouseModule primary = getPrimaryModule(); + return primary == null ? "" : primary.getLabel(); } public void setPrimaryModuleByLabel(String label) { @@ -1128,9 +992,18 @@ public void setPrimaryModuleByLabel(String label) { } } + /** + * @deprecated use getSecondaryModuleByLabel + * @return + */ + public String getSecondaryModuleLabel() { + MouseModule secondary = getPrimaryModule(); + return secondary == null ? "" : secondary.getLabel(); + } + public String getSecondaryModuleByLabel() { - MouseModule secondary1 = getPrimaryModule(); - return secondary1 == null ? "" : secondary1.getLabel(); + MouseModule secondary = getPrimaryModule(); + return secondary == null ? "" : secondary.getLabel(); } public void setSecondaryModuleByLabel(String label) { @@ -1143,15 +1016,12 @@ public void setSecondaryModuleByLabel(String label) { /** * //TODO: check this * Setter for property mouseModules. - * - * @param i the index * @param mouseModule the new mouseModule to use. */ public void setMouseModule(int i, MouseModule mouseModule) { this.modules.set(i, mouseModule); } - @Override public void mouseEntered(MouseEvent e) { hasFocus = true; if (e.isShiftDown()) { @@ -1162,7 +1032,6 @@ public void mouseEntered(MouseEvent e) { } } - @Override public void mouseExited(MouseEvent e) { hasFocus = false; if (e.isShiftDown()) { @@ -1192,7 +1061,6 @@ public synchronized void removeMenuItem(String label) { if (index != -1) { primaryPopup.remove(index); numInserted--; - logger.log(Level.FINER, "numInserted: {0}", numInserted); } } @@ -1203,24 +1071,9 @@ public synchronized void addMenuItem(final Component b) { } if (numInserted == 0) { primaryPopup.insert(new JPopupMenu.Separator(), 0); - numInserted++; - } - if ( b instanceof JPopupMenu ) { - if ( numInserted>1 ) { - primaryPopup.insert(new JPopupMenu.Separator(), 0); - numInserted++; - } - JPopupMenu c= (JPopupMenu)b; - - for ( MenuElement me : c.getSubElements() ) { - if ( me.getComponent() instanceof JCheckBoxMenuItem ) continue; //TODO: kludge - primaryPopup.insert( me.getComponent(), numInserted ); - numInserted++; - } - } else { - primaryPopup.insert(b, numInserted); - numInserted++; } + primaryPopup.insert(b, numInserted); + numInserted++; } @@ -1231,17 +1084,10 @@ public synchronized void addMenuItem(final Component b) { */ public JMenu addMenu(String label) { JMenu result = new JMenu(label); + //result.setFont(primaryPopup.getFont()); addMenuItem(result); return result; } - - /** - * return number of elements for diagnostic purposes. - * @return - */ - public int getNumInserted() { - return numInserted; - } private DasCanvas.GlassPane getGlassPane() { DasCanvas.GlassPane r = (DasCanvas.GlassPane) ((DasCanvas) parent.getParent()).getGlassPane(); @@ -1251,16 +1097,11 @@ private DasCanvas.GlassPane getGlassPane() { return r; } - /** - * remove the mouse module with the label. - * @param label the label (case-sensitive) - * @return null if not found, or the module. - */ public MouseModule getModuleByLabel(java.lang.String label) { MouseModule result = null; - for (Object module : modules) { - if (label.equals(((MouseModule) module).getLabel())) { - result = (MouseModule) module; + for (int i = 0; i < modules.size(); i++) { + if (label.equals(((MouseModule) modules.get(i)).getLabel())) { + result = (MouseModule) modules.get(i); } } return result; @@ -1275,10 +1116,6 @@ public boolean isHoverHighlite() { return this.hoverHighlite; } - /** - * glow the outline of the mouse area, for development. - * @param value - */ public void setHoverHighlite(boolean value) { this.hoverHighlite = value; } @@ -1287,24 +1124,10 @@ public void setHoverHighlite(boolean value) { * returns the position of the last mouse press. This is a hack so that * the mouse position can be obtained to get the context of the press. * The result point is in the parent's coordinate system. - * @return the position of the mouse press. - * @see #getMousePressPositionOnCanvas() */ public Point getMousePressPosition() { return this.pressPosition; } - - /** - * return the position of the last mouse press, in the canvas coordinate - * frame. - * @return the position of the mouse press in the canvas coordinate frame. - * @see #getMousePressPosition() - */ - public Point getMousePressPositionOnCanvas() { - Point r= this.pressPosition.getLocation(); // get a copy - r.translate( this.parent.getX(), this.parent.getY() ); - return r; - } private void performMove(MouseEvent e) { Point moveEnd = e.getPoint(); @@ -1319,36 +1142,16 @@ private void performMove(MouseEvent e) { int max = parent.getColumn().getDMaximum(); parent.getColumn().setDPosition(min + dx, max + dx); - min = parent.getRow().getDMinimum(); - max = parent.getRow().getDMaximum(); + min = + parent.getRow().getDMinimum(); + max = + parent.getRow().getDMaximum(); parent.getRow().setDPosition(min + dy, max + dy); } - /** - * the mouse wheel was turned so many units. - * Delegate this to the primary module, so that if it is set to "ZoomX" - * then the mousewheel will be in just the X direction. - * @param e the mouse wheel event - */ - @Override public void mouseWheelMoved(MouseWheelEvent e) { - if (primary != null) { // this seems more clear. - primary.mouseWheelMoved(e); + if (secondary != null) { + secondary.mouseWheelMoved(e); } } - - /** - * remove all references to mouse modules - */ - public void releaseAll() { - active= null; - modules.clear(); - primary= null; - secondary= null; - primaryActionButtonMap.clear(); - secondaryActionButtonMap.clear(); - setFeedback(null); - //parent=null; - } - } diff --git a/dasCore/src/org/das2/event/DasSelectionEvent.java b/dasCore/src/org/das2/event/DasSelectionEvent.java index ea8115121..038279f91 100644 --- a/dasCore/src/org/das2/event/DasSelectionEvent.java +++ b/dasCore/src/org/das2/event/DasSelectionEvent.java @@ -32,10 +32,8 @@ */ public class DasSelectionEvent extends EventObject { - - private static final long serialVersionUID = 1L; - - /** Type-safe enumeration class for selection type constants. */ + + /** Type-safe enumeration class for selection type contants. */ public static class Type { public static final Type AREA_SELECTION = @@ -67,8 +65,14 @@ private Type(String type, boolean single) protected Point mark; protected boolean isShiftDown; protected boolean clearSelection; - protected transient DasSelectionEvent.Type selectionType; + protected DasSelectionEvent.Type selectionType; + private Point selectionEnd; + + private DasSelectionEvent.Type selectionMode = DasSelectionEvent.Type.POINT_SELECTION; + + private Point selectionStart; + /** Creates a new instance of DasSelectionEvent * * @param source The source of the event. diff --git a/dasCore/src/org/das2/event/DataPointSelectionEvent.java b/dasCore/src/org/das2/event/DataPointSelectionEvent.java index a53c52a47..795f436c8 100644 --- a/dasCore/src/org/das2/event/DataPointSelectionEvent.java +++ b/dasCore/src/org/das2/event/DataPointSelectionEvent.java @@ -22,13 +22,13 @@ */ package org.das2.event; +import org.das2.dataset.DataSet; import org.das2.datum.Datum; import java.util.Map; -import org.virbo.dataset.QDataSet; /** * This is the general-purpose "a data point was selected" event. Note that - * auxiliary data is supported, such as a keystroke that triggered the event. + * auxillary data is supported, such as a keystroke that triggered the event. * * The X and Y Datums may be null, so that code may be reused. * @@ -42,7 +42,9 @@ public class DataPointSelectionEvent extends DasEvent { public long birthMilli; - private QDataSet ds=null; + private DataSet ds=null; + + private Object source; /** Creates a new instance of DataPointSelectionEvent */ public DataPointSelectionEvent(Object source, @@ -88,19 +90,14 @@ public void set( Datum x, Datum y) { this.y= y; } - public void setDataSet( QDataSet ds) { + public void setDataSet(org.das2.dataset.DataSet ds) { this.ds= ds; } - /** - * return the context dataset, from which the selection is made. - * @return - */ - public QDataSet getDataSet() { + public org.das2.dataset.DataSet getDataSet() { return this.ds; } - @Override public String toString() { return "[DataPointSelectionEvent x:"+x+" y:"+y+"]"; } diff --git a/dasCore/src/org/das2/event/DataPointSelectorMouseModule.java b/dasCore/src/org/das2/event/DataPointSelectorMouseModule.java index 70122306d..dbd489661 100644 --- a/dasCore/src/org/das2/event/DataPointSelectorMouseModule.java +++ b/dasCore/src/org/das2/event/DataPointSelectorMouseModule.java @@ -12,8 +12,8 @@ import org.das2.graph.DasAxis; import org.das2.graph.DasPlot; import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; import java.util.HashMap; -import java.util.logging.Level; /** * General purpose mouse module for getting data point selections. The client @@ -30,9 +30,8 @@ */ public class DataPointSelectorMouseModule extends MouseModule { DasAxis xaxis, yaxis; - - javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList(); - + DataSetConsumer dataSetConsumer; + javax.swing.event.EventListenerList listenerList = null; MousePointSelectionEvent lastMousePoint; public DataPointSelectorMouseModule( DasPlot parent, @@ -41,6 +40,7 @@ public DataPointSelectorMouseModule( DasPlot parent, super( parent, dragRenderer, label ); this.xaxis= parent.getXAxis(); this.yaxis= parent.getYAxis(); + this.dataSetConsumer= consumer; } private DataPointSelectionEvent getDataPointSelectionEvent(MousePointSelectionEvent e) { @@ -50,14 +50,12 @@ private DataPointSelectionEvent getDataPointSelectionEvent(MousePointSelectionEv return de; } - @Override public void mousePointSelected(MousePointSelectionEvent e) { lastMousePoint= e; if ( keyEvents ) parent.requestFocus(); if ( dragEvents ) fireDataPointSelectionListenerDataPointSelected(getDataPointSelectionEvent(e)); } - @Override public void keyPressed(KeyEvent e) { int keyCode= e.getKeyCode(); @@ -84,7 +82,7 @@ public void keyPressed(KeyEvent e) { break; } } catch ( java.awt.AWTException e1 ) { - logger.log(Level.SEVERE,null,e1); + org.das2.util.DasDie.println(e1.getMessage()); } } else { @@ -101,14 +99,17 @@ public void keyPressed(KeyEvent e) { /** Registers DataPointSelectionListener to receive events. * @param listener The listener to register. */ - public void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + public synchronized void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); } /** Removes DataPointSelectionListener from the list of listeners. * @param listener The listener to remove. */ - public void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + public synchronized void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { listenerList.remove(org.das2.event.DataPointSelectionListener.class, listener); } @@ -117,8 +118,8 @@ public void removeDataPointSelectionListener(org.das2.event.DataPointSelectionLi * @param event The event to be fired */ protected void fireDataPointSelectionListenerDataPointSelected(DataPointSelectionEvent event) { - Object[] listeners; - listeners = listenerList.getListenerList(); + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==org.das2.event.DataPointSelectionListener.class) { ((org.das2.event.DataPointSelectionListener)listeners[i+1]).dataPointSelected(event); @@ -170,7 +171,6 @@ public void setKeyEvents(boolean keyEvents) { this.keyEvents = keyEvents; } - @Override public void mouseReleased(java.awt.event.MouseEvent e) { super.mouseReleased(e); if ( releaseEvents ) { diff --git a/dasCore/src/org/das2/event/DataRangeSelectionEvent.java b/dasCore/src/org/das2/event/DataRangeSelectionEvent.java index ce19987b6..ee6292e20 100755 --- a/dasCore/src/org/das2/event/DataRangeSelectionEvent.java +++ b/dasCore/src/org/das2/event/DataRangeSelectionEvent.java @@ -23,9 +23,9 @@ package org.das2.event; +import org.das2.dataset.DataSet; import org.das2.datum.Datum; import org.das2.datum.DatumRange; -import org.virbo.dataset.QDataSet; /** * @@ -33,7 +33,7 @@ */ public class DataRangeSelectionEvent extends DasEvent { - private QDataSet ds=null; + private DataSet ds=null; Datum min; Datum max; @@ -64,11 +64,11 @@ public DatumRange getDatumRange() { return new DatumRange( min, max ); } - public void setDataSet(QDataSet ds) { + public void setDataSet(DataSet ds) { this.ds= ds; } - public QDataSet getDataSet() { + public DataSet getDataSet() { return this.ds; } diff --git a/dasCore/src/org/das2/event/DisplayDataMouseModule.java b/dasCore/src/org/das2/event/DisplayDataMouseModule.java index e6d98bdf3..f8cbd81fc 100644 --- a/dasCore/src/org/das2/event/DisplayDataMouseModule.java +++ b/dasCore/src/org/das2/event/DisplayDataMouseModule.java @@ -8,37 +8,31 @@ */ package org.das2.event; -import java.awt.event.ItemEvent; +import org.das2.dataset.ClippedTableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.VectorDataSetBuilder; import org.das2.datum.DatumRange; import org.das2.datum.Units; +import org.das2.datum.format.DatumFormatter; import org.das2.graph.DasPlot; import org.das2.graph.Renderer; +import org.das2.util.DasExceptionHandler; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; -import java.awt.Point; -import java.awt.Window; -import java.awt.event.ItemListener; -import java.util.logging.Level; -import javax.swing.DefaultComboBoxModel; import javax.swing.Icon; import javax.swing.ImageIcon; -import javax.swing.JComboBox; -import javax.swing.JComponent; +import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.SwingUtilities; -import javax.swing.table.DefaultTableColumnModel; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; -import javax.swing.table.TableModel; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dsutil.QDataSetTableModel; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import org.das2.datum.UnitsUtil; /** * @@ -46,50 +40,32 @@ */ public class DisplayDataMouseModule extends MouseModule { - private final static String LABEL = "Display Data"; - private final DasPlot plot; - private JFrame myFrame; - private JPanel myPanel; - private JTable myEdit; - private JComboBox comboBox; - private Renderer[] rends; - private DatumRange xrange; - private DatumRange yrange; + final static String LABEL = "Display Data"; + DasPlot plot; + static JFrame myFrame; + static JPanel myPanel; + static JEditorPane myEdit; /** Creates a new instance of DisplayDataMouseModule */ public DisplayDataMouseModule(DasPlot parent) { - super(parent, new BoxRenderer(parent), LABEL); + super(parent, new BoxGesturesRenderer(parent), LABEL); this.plot = parent; } - private void maybeCreateFrame(Object source) { + private void maybeCreateFrame() { if (myFrame == null) { myFrame = new JFrame(LABEL); - if ( source!=null && source instanceof JComponent ) { - Window w= SwingUtilities.getWindowAncestor((JComponent)source); - if ( w instanceof JFrame ) { - myFrame.setIconImage( ((JFrame)w).getIconImage() ); - } - } myPanel = new JPanel(); myPanel.setPreferredSize(new Dimension(300, 300)); myPanel.setLayout(new BorderLayout()); - myEdit = new JTable(); + myEdit = new JEditorPane(); myEdit.setFont(Font.decode("fixed-10")); - myEdit.setAutoResizeMode( JTable.AUTO_RESIZE_OFF ); - myEdit.getTableHeader().setReorderingAllowed(false); - - JScrollPane scrollPane = new JScrollPane( myEdit, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ); + JScrollPane scrollPane = new JScrollPane(myEdit, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); myPanel.add(scrollPane, BorderLayout.CENTER); - - comboBox= new JComboBox(); - comboBox.addItemListener( itemListener ); - - myPanel.add( comboBox, BorderLayout.NORTH ); - myFrame.getContentPane().add(myPanel); myFrame.pack(); } + return; } private String unitsStr(Units u) { @@ -99,144 +75,143 @@ private String unitsStr(Units u) { @Override public void mouseRangeSelected(MouseDragEvent e0) { - if ( !( e0 instanceof MouseBoxEvent ) ) { - throw new IllegalArgumentException("Event should be MouseBoxEvent"); // findbugs - } - MouseBoxEvent e = (MouseBoxEvent) e0; - if ( Point.distance( e.getXMaximum(), e.getYMinimum(), e.getXMaximum(), e.getYMaximum() ) < 5 ) { - return; - } - - maybeCreateFrame(e0.getSource()); + if ( e0.isGesture() ) return; + + maybeCreateFrame(); + myFrame.setVisible(true); - final DatumRange xrng; - final DatumRange yrng; + MouseBoxEvent e = (MouseBoxEvent) e0; + + DatumRange xrange; + DatumRange yrange; if (plot.getXAxis().isFlipped()) { - xrng = new DatumRange(plot.getXAxis().invTransform(e.getXMaximum()), plot.getXAxis().invTransform(e.getXMinimum())); + xrange = new DatumRange(plot.getXAxis().invTransform(e.getXMaximum()), plot.getXAxis().invTransform(e.getXMinimum())); } else { - xrng = new DatumRange(plot.getXAxis().invTransform(e.getXMinimum()), plot.getXAxis().invTransform(e.getXMaximum())); + xrange = new DatumRange(plot.getXAxis().invTransform(e.getXMinimum()), plot.getXAxis().invTransform(e.getXMaximum())); } - if ( yclip ) { - if (plot.getYAxis().isFlipped()) { - yrng = new DatumRange(plot.getYAxis().invTransform(e.getYMinimum()), plot.getYAxis().invTransform(e.getYMaximum())); - } else { - yrng = new DatumRange(plot.getYAxis().invTransform(e.getYMaximum()), plot.getYAxis().invTransform(e.getYMinimum())); - } + if (plot.getYAxis().isFlipped()) { + yrange = new DatumRange(plot.getYAxis().invTransform(e.getYMinimum()), plot.getYAxis().invTransform(e.getYMaximum())); } else { - yrng= null; + yrange = new DatumRange(plot.getYAxis().invTransform(e.getYMaximum()), plot.getYAxis().invTransform(e.getYMinimum())); } - final Renderer[] rends1 = plot.getRenderers(); + Renderer[] rends = plot.getRenderers(); - if ( rends1.length==0 ) return; - myFrame.setVisible(true); + Document doc = myEdit.getDocument(); - String[] rlabels= new String[ rends1.length ]; - int firstActive= -1; - for ( int i=0; i 1) { + buf.append("X" + unitsStr(xunits) + "\t"); + buf.append("Y" + unitsStr(units) + "\t"); + for (int j = 0; j < vdss.length; j++) { + if (!planes[j].equals("")) { + buf.append("" + planes[j] + "" + unitsStr(vdss[j].getYUnits()) + "\t"); + } + } + buf.append("\n"); + } + + VectorDataSetBuilder builder = new VectorDataSetBuilder(vds.getXUnits(), vds.getYUnits()); + for (int i = 0; i < vds.getXLength(); i++) { + if (xrange.contains(vds.getXTagDatum(i)) && (!yclip || yrange.contains(vds.getDatum(i)))) { + buf.append(xdf.format(vds.getXTagDatum(i), xunits) + "\t" + df.format(vds.getDatum(i), units)); + for (int j = 0; j < planes.length; j++) { + if (!planes[j].equals("")) { + Units u= vdss[j].getYUnits(); + if ( UnitsUtil.isIntervalMeasurement(u) || UnitsUtil.isRatioMeasurement(u) ) { + buf.append("\t" + df.format(vdss[j].getDatum(i), vdss[j].getYUnits())); + } else { + buf.append("\t" + vdss[j].getDatum(i) ); + } + } + } + + buf.append("\n"); + } + } + doc.insertString(doc.getLength(), buf.toString(), attrSet); - private void setDataSet( QDataSet ds, DatumRange xrange, DatumRange yrange ) { - TableModel tm; + } - if ( ds==null ) { - showMessageInTable( myEdit, "no dataset" ); - return; - } - if ( ds.rank()>2 ) { - QDataSet ds2= SemanticOps.getSimpleTableContaining( ds, xrange.min(), yrange.min() ); - if ( ds2==null ) { - showMessageInTable( myEdit,"data cannot be displayed" ); - return; - } else { - ds= ds2; } + } catch (BadLocationException ex) { + DasExceptionHandler.handle(ex); } - - TableColumnModel tcm; - try { - QDataSet tds; - boolean isQube= DataSetUtil.isQube(ds); - QDataSet dep1= (QDataSet) ds.property(QDataSet.DEPEND_1); // kludge code because isQube returns true. It probably should return false. - if ( dep1!=null && dep1.rank()==2 ) isQube= false; - if ( isQube) { - tds=SemanticOps.trim( ds, xrange, yrange ); - } else { - tds=SemanticOps.trim( ds, xrange, null ); // this may cause problems else where... - } - tm= new QDataSetTableModel(tds); - tcm= ((QDataSetTableModel)tm).getTableColumnModel(); - if ( dep1!=null && dep1.rank()==2 ) { - myEdit.getTableHeader().setToolTipText("Column labels reported are from the first record"); - } - } catch ( RuntimeException ex ) { - logger.log( Level.SEVERE, ex.getMessage(), ex ); - tm= new QDataSetTableModel(ds); - tcm= ((QDataSetTableModel)tm).getTableColumnModel(); - } - myEdit.setModel(tm); - myEdit.setColumnModel(tcm); - - //myEdit.setColumnModel(new DefaultTableColumnModel() ); - //myEdit.setColumnModel(tcm); // error with rank 1. - } - @Override public String getListLabel() { return getLabel(); } @@ -255,12 +230,11 @@ public String getLabel() { /** * Holds value of property yclip. */ - private boolean yclip = true; - + private boolean yclip = false; /** * Utility field used by bound properties. */ - private final java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); + private java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); /** * Adds a PropertyChangeListener to the listener list. @@ -293,6 +267,6 @@ public boolean isYclip() { public void setYclip(boolean yclip) { boolean oldYclip = this.yclip; this.yclip = yclip; - propertyChangeSupport.firePropertyChange("yclip", oldYclip, yclip); + propertyChangeSupport.firePropertyChange("yclip", new Boolean(oldYclip), new Boolean(yclip)); } } diff --git a/dasCore/src/org/das2/event/DragRenderer.java b/dasCore/src/org/das2/event/DragRenderer.java index e232edbc8..288605144 100755 --- a/dasCore/src/org/das2/event/DragRenderer.java +++ b/dasCore/src/org/das2/event/DragRenderer.java @@ -35,37 +35,28 @@ */ public interface DragRenderer { - /** - * use this color when drawing ghostly backgrounds for contrast. - */ + /* use this color when drawing ghostly backgrounds for contrast */ public Color ghostColor= new Color(255,255,255,100); - /** - * draws the drag for mousing from p1 to p2, and returns an array of + /* draws the drag for mousing from p1 to p2, and returns an array of * Rectangles covering the rendering. If nothing is drawn, then an * array of length zero should be returned, and nulls are allowed in the * array. p1 and p2, and g are in the canvas frame of reference. */ public abstract Rectangle[] renderDrag(Graphics g, Point p1, Point p2); - /** clears whatever renderDrag rendered. This is not used by the DasMouseInputAdapter, + /* clears whatever renderDrag rendered. This is not used by the DasMouseInputAdapter, * but must still be supported for now. */ public abstract void clear(Graphics g); - /** - * promotes the drag begin and end into a mouseDragEvent. - */ + /* promotes the drag begin and end into a mouseDragEvent */ public abstract MouseDragEvent getMouseDragEvent( Object source, Point p1, Point p2, boolean isModified ); - /** - * indicates that MM.mousePointSelected() should called as new mouse events come in. - */ + /* indicates that MM.mousePointSelected() should called as new mouse events come in */ public boolean isPointSelection(); - /** - * range selection events should be fired during drag. - */ + /* range selection events should be fired during drag */ public boolean isUpdatingDragSelection(); } diff --git a/dasCore/src/org/das2/event/DumpToFileMouseModule.java b/dasCore/src/org/das2/event/DumpToFileMouseModule.java new file mode 100644 index 000000000..1c41d3d5f --- /dev/null +++ b/dasCore/src/org/das2/event/DumpToFileMouseModule.java @@ -0,0 +1,115 @@ +/* + * DumpToFileMouseModule.java + * + * Created on December 1, 2004, 10:31 PM + */ + +package org.das2.event; + +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasAxis; +import org.das2.graph.DasPlot; +import org.das2.dataset.DataSetConsumer; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.dataset.VectorUtil; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.ClippedTableDataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.DatumRange; +import org.das2.util.DasExceptionHandler; +import org.das2.system.UserMessageCenter; +import java.io.*; +import java.nio.channels.*; +import javax.swing.*; + +/** + * + * @author Jeremy + */ +public class DumpToFileMouseModule extends MouseModule { + DasAxis xAxis; + DasAxis yAxis; + DataSetConsumer dsConsumer; + + public DumpToFileMouseModule(DasCanvasComponent parent, DataSetConsumer dsConsumer, DasAxis xAxis, DasAxis yAxis) { + super( parent, new BoxRenderer(parent), "Dump to File" ); + if (!xAxis.isHorizontal()) { + throw new IllegalArgumentException("X Axis orientation is not horizontal"); + } + if (yAxis.isHorizontal()) { + throw new IllegalArgumentException("Y Axis orientation is not vertical"); + } + this.xAxis= xAxis; + this.yAxis= yAxis; + this.dsConsumer= dsConsumer; + } + + public static DumpToFileMouseModule create( DasPlot parent, DataSetConsumer dsConsumer ) { + DumpToFileMouseModule result= + new DumpToFileMouseModule(parent, dsConsumer, parent.getXAxis(),parent.getYAxis()); + return result; + } + + public void mouseRangeSelected(MouseDragEvent e0) { + MouseBoxEvent e= (MouseBoxEvent)e0; + + DatumRange xrange; + DatumRange yrange; + + xrange= new DatumRange( xAxis.invTransform(e.getXMinimum()), xAxis.invTransform(e.getXMaximum()) ); + yrange= new DatumRange( yAxis.invTransform(e.getYMaximum()), yAxis.invTransform(e.getYMinimum()) ); + + DataSet ds= dsConsumer.getConsumedDataSet(); + + if ( ds==null ) { + UserMessageCenter.getDefault().notifyUser( this, "This renderer doesn't have a dataset loaded" ); + return; + } + + DataSet outds; + if ( ds instanceof TableDataSet ) { + TableDataSet tds= (TableDataSet)ds; + outds= new ClippedTableDataSet( tds, xrange, yrange ); + + } else { + VectorDataSet vds= (VectorDataSet)ds; + VectorDataSetBuilder builder= new VectorDataSetBuilder(vds.getXUnits(),vds.getYUnits()); + for ( int i=0; i4) { diff --git a/dasCore/src/org/das2/event/HorizontalDragRangeSelectorMouseModule.java b/dasCore/src/org/das2/event/HorizontalDragRangeSelectorMouseModule.java index 8b3e80e96..1e10d1a2d 100755 --- a/dasCore/src/org/das2/event/HorizontalDragRangeSelectorMouseModule.java +++ b/dasCore/src/org/das2/event/HorizontalDragRangeSelectorMouseModule.java @@ -23,21 +23,24 @@ package org.das2.event; +import org.das2.dataset.DataSetConsumer; import org.das2.dataset.TableDataSetConsumer; +import org.das2.datum.Datum; import org.das2.graph.DasAxis; import org.das2.graph.DasPlot; /** - * With the HorizontalDragRangeRenderer and VerticalSpectrogramAverager, - * this shows the average over an interval. + * * @author jbf */ public class HorizontalDragRangeSelectorMouseModule extends MouseModule { DasAxis axis; + int start; + /** Utility field used by event firing mechanism. */ - private javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList(); + private javax.swing.event.EventListenerList listenerList = null; private org.das2.dataset.DataSetConsumer dataSetConsumer; @@ -52,16 +55,13 @@ public HorizontalDragRangeSelectorMouseModule(DasPlot parent, org.das2.dataset.D } public static HorizontalDragRangeSelectorMouseModule create(DasPlot parent) { + DasAxis axis= parent.getXAxis(); HorizontalDragRangeSelectorMouseModule result= - new HorizontalDragRangeSelectorMouseModule(parent,null,parent.getXAxis()); + new HorizontalDragRangeSelectorMouseModule(parent,parent,parent.getXAxis()); return result; } - @Override public void mouseRangeSelected(MouseDragEvent e0) { - if ( !( e0 instanceof MouseRangeSelectionEvent ) ) { - throw new IllegalArgumentException("Event should be MouseRangeSelectionEvent"); // findbugs - } MouseRangeSelectionEvent e= (MouseRangeSelectionEvent)e0; org.das2.datum.Datum min; org.das2.datum.Datum max; @@ -76,14 +76,17 @@ public void mouseRangeSelected(MouseDragEvent e0) { /** Registers DataRangeSelectionListener to receive events. * @param listener The listener to register. */ - public void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); } /** Removes DataRangeSelectionListener from the list of listeners. * @param listener The listener to remove. */ - public void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); } @@ -96,8 +99,8 @@ private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionE if ( dataSetConsumer instanceof TableDataSetConsumer ) { event.setDataSet(dataSetConsumer.getConsumedDataSet()); } - Object[] listeners; - listeners= listenerList.getListenerList(); + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); diff --git a/dasCore/src/org/das2/event/HorizontalFrequencyDragRenderer.java b/dasCore/src/org/das2/event/HorizontalFrequencyDragRenderer.java index ad331145c..27299fc64 100755 --- a/dasCore/src/org/das2/event/HorizontalFrequencyDragRenderer.java +++ b/dasCore/src/org/das2/event/HorizontalFrequencyDragRenderer.java @@ -77,14 +77,24 @@ public Rectangle[] renderDrag(java.awt.Graphics g1, java.awt.Point p1, java.awt. int x2 = p2.x; int x1= p1.x; if (x2 6 ) + g.drawLine(x1+3, y, x2-3, y); + g.drawLine(x1, y+2, x1, y-2 ); //serifs + g.drawLine(x2, y+2, x2, y-2 ); + */ + g.setStroke(new BasicStroke()); g.setColor(color0); @@ -140,6 +150,8 @@ public MouseDragEvent getMouseDragEvent(Object source, java.awt.Point p1, java.a } public void keyPressed(KeyEvent e) { + int keyCode= e.getKeyCode(); + System.out.println(e); if ( e.getKeyChar()=='1' ) { ncycles= 1; } else if ( e.getKeyChar()=='2' ) { diff --git a/dasCore/src/org/das2/event/HorizontalRangeGesturesRenderer.java b/dasCore/src/org/das2/event/HorizontalRangeGesturesRenderer.java index fa2fc5ed8..a9cd4e82b 100644 --- a/dasCore/src/org/das2/event/HorizontalRangeGesturesRenderer.java +++ b/dasCore/src/org/das2/event/HorizontalRangeGesturesRenderer.java @@ -26,11 +26,6 @@ import org.das2.graph.DasCanvasComponent; import java.awt.*; -import org.das2.datum.DatumRange; -import org.das2.datum.DatumRangeUtil; -import org.das2.graph.DasAxis; -import org.das2.graph.GraphUtil; -import org.das2.util.GrannyTextRenderer; /** * @@ -94,31 +89,9 @@ public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { g.drawLine(x1+3, y, x2-3, y); g.drawLine(x1, y+2, x1, y-2 ); //serifs g.drawLine(x2, y+2, x2, y-2 ); - + dirtyBounds.setLocation(x1-2,y+3); dirtyBounds.add(x2+2,y-3); - - if ( parent instanceof DasAxis && ( x1parent.getColumn().getDMaximum() ) ) { - DasAxis p= (DasAxis)parent; - DatumRange dr= DatumRangeUtil.union( p.invTransform( x1 ), p.invTransform(x2 ) ); - dr= p.getTickV().enclosingRange( dr, true ); - g.setColor( new Color(255,255,255,200) ); - GrannyTextRenderer gtr= new GrannyTextRenderer(); - gtr.setString( g1, ""+dr ); - Rectangle r= gtr.getBounds(); - int x; - if ( x2>parent.getColumn().getDMaximum() ) { - x= x1+3; - } else { - x= x2-3-(int)gtr.getWidth(); - } - r.translate(x,y+g.getFontMetrics().getHeight() ); - g.fill( r ); - g.setColor(color0); - gtr.draw( g, x, y+g.getFontMetrics().getHeight() ); - dirtyBounds.add(x,y+g.getFontMetrics().getHeight() ); - } - } return new Rectangle[] { dirtyBounds }; } diff --git a/dasCore/src/org/das2/event/HorizontalRangeSelectorMouseModule.java b/dasCore/src/org/das2/event/HorizontalRangeSelectorMouseModule.java index 518efea68..843be3c38 100755 --- a/dasCore/src/org/das2/event/HorizontalRangeSelectorMouseModule.java +++ b/dasCore/src/org/das2/event/HorizontalRangeSelectorMouseModule.java @@ -23,17 +23,12 @@ package org.das2.event; -import java.awt.Point; -import java.awt.event.MouseWheelEvent; -import java.util.logging.Level; -import javax.swing.SwingUtilities; +import org.das2.datum.Datum; import org.das2.datum.DatumRange; import org.das2.graph.DasAxis; import org.das2.graph.DasCanvasComponent; import org.das2.graph.DasPlot; import javax.swing.event.EventListenerList; -import org.das2.datum.DatumRangeUtil; -import static org.das2.event.MouseModule.logger; /** @@ -43,10 +38,9 @@ public class HorizontalRangeSelectorMouseModule extends MouseModule { DasAxis axis; - long t0, tbirth; /** Utility field used by event firing mechanism. */ - private EventListenerList listenerList = new EventListenerList(); + private EventListenerList listenerList = null; public HorizontalRangeSelectorMouseModule(DasCanvasComponent parent, DasAxis axis) { super(parent,new HorizontalRangeGesturesRenderer(parent),"Zoom X"); @@ -57,19 +51,20 @@ public HorizontalRangeSelectorMouseModule(DasCanvasComponent parent, DasAxis axi } public static HorizontalRangeSelectorMouseModule create(DasPlot parent) { + DasAxis axis= parent.getXAxis(); HorizontalRangeSelectorMouseModule result= new HorizontalRangeSelectorMouseModule(parent,parent.getXAxis()); return result; } - @Override public void mouseRangeSelected(MouseDragEvent e0) { if (!e0.isGesture()) { - if ( !( e0 instanceof MouseRangeSelectionEvent ) ) { - throw new IllegalArgumentException("Event should be MouseRangeSelectionEvent"); // findbugs - } + Datum min; + Datum max; MouseRangeSelectionEvent e= (MouseRangeSelectionEvent)e0; - DatumRange dr= axis.invTransform(e.getMinimum(),e.getMaximum()); + min= axis.invTransform(e.getMinimum()); + max= axis.invTransform(e.getMaximum()); + DatumRange dr= new DatumRange( min, max ); DatumRange nndr= axis.getTickV().enclosingRange(dr, true); DataRangeSelectionEvent te= new DataRangeSelectionEvent(e0.getSource(),nndr.min(),nndr.max()); @@ -86,112 +81,21 @@ public void mouseRangeSelected(MouseDragEvent e0) { axis.scanNext(); } } - - - /** - * mouse wheel events zoom or pan rapidly. With a physical wheel, I (jbf) found - * that I get 17ms per click, and this is manageable. With a touchpad on a mac, - * these events come much faster, like 10ms per click, which can disorient the - * operator. So we limit the speed to 20ms per click, for now by dropping - * rapid clicks. - * - * @param e - */ - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - double nmin, nmax; - - double shift = 0.; - - if ( (e.isControlDown() )) { - if (e.getWheelRotation() < 0) { - nmin = -0.20; // pan left on xaxis - nmax = +0.80; - } else { - nmin = +0.20; // pan right on xaxis - nmax = +1.20; - } - } else if ( e.isShiftDown() ) { - if (e.getWheelRotation() < 0) { - nmin = -0.005; // pan left on xaxis - nmax = +0.995; - } else { - nmin = +0.005; // pan right on xaxis - nmax = +1.005; - } - } else { - //mac trackpads coast a while after release, so let's govern the speed a little more - if (e.getWheelRotation() < 0) { - nmin = 0.10; // zoom in - nmax = 0.90; - } else { - nmin = -0.125; // zoom out - nmax = 1.125; - } - } - - Point ep= SwingUtilities.convertPoint( e.getComponent(), e.getPoint(), parent.getCanvas() ); - - Pos xpos = axis == null ? Pos._null : position( axis.getColumn(), ep.x, 20 ); - switch (xpos) { - case min: - shift = -nmin; // this will cancel out nmin - break; - case max: - shift = nmin; - break; - } - - int clickMag = 1; - final long t1 = System.nanoTime(); - long limitNanos = (long) 40e6; - if ((t1 - t0) / clickMag < limitNanos) { - clickMag = (int) Math.floor( (double)(t1 - t0) / limitNanos ); - } - - if (clickMag == 0) return; - t0 = System.nanoTime(); - - // these will be non-null if they should be used. - DatumRange xdrnew; - - logger.log(Level.FINEST, ":ns: {0} {1}", new Object[]{System.nanoTime() - tbirth, clickMag}); - if ( true ) { - DatumRange dr = axis.getDatumRange(); - for (int i = 0; i < clickMag; i++) { - if (axis.isLog()) { - dr = DatumRangeUtil.rescaleLog(dr, nmin+shift, nmax+shift); - } else { - dr = DatumRangeUtil.rescale(dr, nmin+shift, nmax+shift); - } - } - dr= maybeRound( axis, dr ); - - if ( ! DatumRangeUtil.isAcceptable( dr, axis.isLog() ) ) { - dr= null; - } - xdrnew= dr; - } - - if ( axisIsAdjustable(axis) && xdrnew==null ) return; - - if ( axisIsAdjustable(axis) ) axis.setDatumRange(xdrnew); - - super.mouseWheelMoved(e); - } - /** Registers DataRangeSelectionListener to receive events. * @param listener The listener to register. */ - public void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new EventListenerList(); + } listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); } /** Removes DataRangeSelectionListener from the list of listeners. * @param listener The listener to remove. */ - public void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); } @@ -201,8 +105,8 @@ public void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionLi * @param event The event to be fired */ private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { - Object[] listeners; - listeners = listenerList.getListenerList(); + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); diff --git a/dasCore/src/org/das2/event/HorizontalRangeTorsionMouseModule.java b/dasCore/src/org/das2/event/HorizontalRangeTorsionMouseModule.java new file mode 100644 index 000000000..bcc177e21 --- /dev/null +++ b/dasCore/src/org/das2/event/HorizontalRangeTorsionMouseModule.java @@ -0,0 +1,109 @@ +/* + * HorizontalRangeTorsionMouseModule.java + * + * Created on February 12, 2004, 6:00 PM + */ + +package org.das2.event; + +import org.das2.graph.DasAxis; +import org.das2.datum.Units; +import java.awt.event.*; +import javax.swing.*; +/** + * + * @author Jeremy + */ +public class HorizontalRangeTorsionMouseModule extends MouseModule { + + + // this dumb idea was abandoned. + DasAxis axis; + double min; + double max; + Units units; + int x0; + int y0; + long tt; + + double inOutVelocity; + double inOutPosition; + double nextPrevVelocity; + double nextPrevPosition; + + static final double inOutFactor= 1e-6; + static final double nextPrevFactor= 100000; + + boolean mouseButtonPressed; + + /** Creates a new instance of HorizontalRangeTorsionMouseModule */ + public HorizontalRangeTorsionMouseModule( DasAxis axis ) { + //super( axis, new PointSlopeDragRenderer(axis), "AxisDriver" ); + this.axis= axis; + } + + public void mousePressed(java.awt.event.MouseEvent e) { + super.mousePressed(e); + units= axis.getUnits(); + min= axis.getDataMinimum(units); + max= axis.getDataMaximum(units); + x0= e.getX(); + y0= e.getY(); + tt= System.currentTimeMillis(); + inOutPosition=0.; + nextPrevPosition=0.; + mouseButtonPressed= true; + } + + void timerTask() { + if ( mouseButtonPressed ) { + long dt= System.currentTimeMillis() - tt; + inOutPosition+= inOutVelocity * dt; + nextPrevPosition+= nextPrevVelocity * dt; + tt+= dt; + reportPosition(); + startTimer(); + + } + } + + public void startTimer() { + int delay = 100; //milliseconds + ActionListener taskPerformer = new ActionListener() { + public void actionPerformed(ActionEvent evt) { + timerTask(); + } + }; + new Timer(delay, taskPerformer).start(); + } + + public void mouseDragged(java.awt.event.MouseEvent e) { + super.mouseDragged(e); + int dx= e.getX()-x0; + int dy= e.getY()-y0; + long dt= System.currentTimeMillis() - tt; + inOutVelocity= dy * inOutFactor; + nextPrevVelocity= dx * nextPrevFactor; + timerTask(); + } + + private void reportPosition() { + double dd= max - min; + + double offset= nextPrevPosition; + double scale= 1 + inOutPosition; + + double newMin= min + offset ; + double newMax= min + offset + dd * scale; + + //System.out.println( "min: " + newMin + "offset: " + offset + " scale: " + scale ); + //System.out.println( "" + units.createDatum(newMin) + " " + units.createDatum(newMax) ); + axis.setDataRange( units.createDatum(newMin), units.createDatum(newMax) ); + } + + public void mouseReleased(java.awt.event.MouseEvent e) { + super.mouseReleased(e); + mouseButtonPressed= false; + } + +} diff --git a/dasCore/src/org/das2/event/HorizontalSlicerMouseModule.java b/dasCore/src/org/das2/event/HorizontalSlicerMouseModule.java index b9515acdb..75bb74d2a 100755 --- a/dasCore/src/org/das2/event/HorizontalSlicerMouseModule.java +++ b/dasCore/src/org/das2/event/HorizontalSlicerMouseModule.java @@ -23,15 +23,14 @@ package org.das2.event; -import org.das2.components.HorizontalSpectrogramSlicer; import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.DataSet; import org.das2.dataset.DataSetConsumer; import org.das2.graph.DasAxis; import org.das2.graph.DasPlot; import org.das2.graph.Renderer; -import org.virbo.dataset.QDataSet; /** - * Slices spectrogram horizontally, e.g. showing one channel vs time. + * * @author jbf */ public class HorizontalSlicerMouseModule extends MouseModule { @@ -46,7 +45,7 @@ public class HorizontalSlicerMouseModule extends MouseModule { private DataPointSelectionEvent de; /** Utility field used by event firing mechanism. */ - private javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList(); + private javax.swing.event.EventListenerList listenerList = null; public HorizontalSlicerMouseModule(DasPlot parent, TableDataSetConsumer dataSetConsumer, DasAxis xaxis, DasAxis yaxis) { this( parent, (DataSetConsumer)dataSetConsumer, xaxis, yaxis ); @@ -67,7 +66,7 @@ protected HorizontalSlicerMouseModule(DasPlot parent, DataSetConsumer dataSetCon public static HorizontalSlicerMouseModule create(DasPlot parent) { DasAxis xaxis= parent.getXAxis(); DasAxis yaxis= parent.getYAxis(); - return new HorizontalSlicerMouseModule(parent,null,xaxis,yaxis); + return new HorizontalSlicerMouseModule(parent,parent,xaxis,yaxis); } public static HorizontalSlicerMouseModule create(Renderer renderer) @@ -78,9 +77,8 @@ public static HorizontalSlicerMouseModule create(Renderer renderer) return new HorizontalSlicerMouseModule(parent,renderer,xaxis,yaxis); } - @Override public void mousePointSelected(MousePointSelectionEvent e) { - QDataSet ds= dataSetConsumer.getConsumedDataSet(); + org.das2.dataset.DataSet ds= dataSetConsumer.getConsumedDataSet(); de.setDataSet(ds); de.set(xaxis.invTransform(e.getX()),yaxis.invTransform(e.getY())); @@ -90,14 +88,17 @@ public void mousePointSelected(MousePointSelectionEvent e) { /** Registers DataPointSelectionListener to receive events. * @param listener The listener to register. */ - public void addDataPointSelectionListener( DataPointSelectionListener listener) { + public synchronized void addDataPointSelectionListener( DataPointSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); } /** Removes DataPointSelectionListener from the list of listeners. * @param listener The listener to remove. */ - public void removeDataPointSelectionListener( DataPointSelectionListener listener) { + public synchronized void removeDataPointSelectionListener( DataPointSelectionListener listener) { listenerList.remove( DataPointSelectionListener.class, listener); } @@ -106,8 +107,8 @@ public void removeDataPointSelectionListener( DataPointSelectionListener listene * @param event The event to be fired */ private void fireDataPointSelectionListenerDataPointSelected(DataPointSelectionEvent event) { - Object[] listeners; - listeners = listenerList.getListenerList(); + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==org.das2.event.DataPointSelectionListener.class) { ((org.das2.event.DataPointSelectionListener)listeners[i+1]).dataPointSelected(event); @@ -115,22 +116,4 @@ private void fireDataPointSelectionListenerDataPointSelected(DataPointSelectionE } } - /** - * return the slicer listening to the slices. This returns the - * first one found. - * @return the slicer - * @throws IllegalArgumentException if no slicer is found. - */ - public HorizontalSpectrogramSlicer getSlicer() { - Object[] listeners; - synchronized (this) { - listeners = listenerList.getListenerList(); - } - for ( int i=0; i1 ) { - div= div.finerDivider(false); - DatumRange minDr= div.rangeContaining(dr.min()); - px= (int)Math.ceil( Math.abs( xAxis.transform(minDr.max()) - xAxis.transform(minDr.min()) ) ); - } - DatumRange minDr= div.rangeContaining(dr.min()); - DatumRange maxDr= div.rangeContaining(dr.max()); - Datum min= DatumRangeUtil.normalize( minDr, dr.min() ) < 0.5 ? minDr.min() : minDr.max(); - Datum max= DatumRangeUtil.normalize( maxDr, dr.max() ) < 0.5 ? maxDr.min() : maxDr.max(); - DatumRange drRound= new DatumRange( min, max ); - - dr= drRound; - } catch ( InconvertibleUnitsException ex ) { - // it's okay to do nothing, this is a transient state - } - } - return dr; - } - - /** - * return true if the axis is not an axis with enumeration units. - * @param axis the axis, which might have enumeration units. - * @return true if the axis is not an axis with enumeration units. - */ - protected static boolean axisIsAdjustable(DasAxis axis) { - return axis != null && (UnitsUtil.isIntervalMeasurement(axis.getUnits()) || UnitsUtil.isRatioMeasurement(axis.getUnits())); - } - - /** - * allow one-line directions to be added to the mouse module. - * This is used in Autoplot for the status bar. - * @return the directions, or null. - */ - public String getDirections() { - return this.directions; - } - - /** - * set the human-readable directions string, so clients like Autoplot can display - * them. - * @param directions human-readable directions. - */ - public void setDirections( String directions ) { - this.directions= directions; - } - - /** - * return the list icon. This will be be overridden by MouseModules to - * give a visual reference. - * @return the list icon. - */ - @Override public javax.swing.Icon getListIcon() { return null; } - - @Override - public void drawListIcon(Graphics2D g, int x, int y) { - // do nothing - } - - @Override + public String getListLabel() { return getLabel(); } - @Override public void keyPressed(KeyEvent keyEvent) { } - @Override public void keyReleased(KeyEvent keyEvent) { } - @Override public void keyTyped(KeyEvent keyEvent) { } - @Override public void mouseReleased(MouseEvent e) { } - @Override public void mousePressed(MouseEvent e) { } - @Override public void mouseDragged(MouseEvent e) { } - @Override public void mouseClicked(MouseEvent e) { } - @Override public void mouseEntered(MouseEvent e) { } - @Override public void mouseExited(MouseEvent e) { } - @Override public void mouseMoved(MouseEvent e) { } - @Override public void mouseWheelMoved(MouseWheelEvent e) { } - - /** - * used by subclasses to describe positions. - */ - protected enum Pos { - _null, beyondMin, min, middle, max, beyondMax - }; - + /** - * indicate if the position (pixels) is near the ends of the DasRow or - * DasColumn. Note that the max of a row is its bottom. - * @param ddp the row or column - * @param pos the position to describe. - * @param threshold pixel distance to the boundary, 20 is often used. - * @return enumeration of the position, for example Pos.beyondMin. + * this should only be called from the mouse module constructor. (Until + * it is verified that it is okay to call it elsewhere.) */ - public Pos position( DasDevicePosition ddp, int pos, int threshold ) { - int max = ddp.getDMaximum(); - int min = ddp.getDMinimum(); - if (((max - min) / threshold) < 3) threshold = (max - min) / 3; - if (pos < min) { - return Pos.beyondMin; - } else if (pos < min + threshold ) { - return Pos.min; - } else if (pos <= max - threshold) { - return Pos.middle; - } else if (pos <= max) { - return Pos.max; - } else { - return Pos.beyondMax; - } - } + protected void setDragRenderer(DragRenderer dragRenderer) { + this.dragRenderer = dragRenderer; + } } diff --git a/dasCore/src/org/das2/event/MouseRangeGestureSelectionEvent.java b/dasCore/src/org/das2/event/MouseRangeGestureSelectionEvent.java index d7eb5fc22..a20abccbf 100644 --- a/dasCore/src/org/das2/event/MouseRangeGestureSelectionEvent.java +++ b/dasCore/src/org/das2/event/MouseRangeGestureSelectionEvent.java @@ -29,7 +29,7 @@ */ public class MouseRangeGestureSelectionEvent extends MouseRangeSelectionEvent { - private transient Gesture gesture; + private Gesture gesture; /** Creates a new instance of MouseRangeGestureSelectionEvent */ public MouseRangeGestureSelectionEvent(Object source, int min, int max, Gesture g) { diff --git a/dasCore/src/org/das2/event/MoveComponentMouseModule.java b/dasCore/src/org/das2/event/MoveComponentMouseModule.java index 40a32c522..0e1bcd445 100644 --- a/dasCore/src/org/das2/event/MoveComponentMouseModule.java +++ b/dasCore/src/org/das2/event/MoveComponentMouseModule.java @@ -11,6 +11,7 @@ import org.das2.graph.DasCanvasComponent; import org.das2.graph.DasColumn; import org.das2.graph.DasRow; +import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; @@ -87,9 +88,15 @@ public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { bounds.translate(p2.x - p1.x, p2.y - p1.y); Graphics2D g = (Graphics2D) g1; + //g.drawImage( i, p2.x - p1.x, p2.y-p1.y, c ); + g.setClip(null); - g.setColor(c.getForeground()); + g.setColor(Color.BLACK); g.draw(bounds); + Point p = center(bounds); + //g.drawOval(p.x - 2, p.y - 2, 5, 5); + //g.draw(enlarge(bounds.getBounds(), 1.2)); + //System.err.println("draw " + bounds.getBounds()); return new Rectangle[] { enlarge(bounds.getBounds(), 1.2 ).getBounds()}; } diff --git a/dasCore/src/org/das2/event/PeakDetectorMouseModule.java b/dasCore/src/org/das2/event/PeakDetectorMouseModule.java new file mode 100644 index 000000000..24caf880c --- /dev/null +++ b/dasCore/src/org/das2/event/PeakDetectorMouseModule.java @@ -0,0 +1,599 @@ +/* + * Cutoff2MouseModule.java + * + * Created on November 10, 2005, 1:41 PM + * + * + */ + +package org.das2.event; + +import org.das2.DasException; +import org.das2.dataset.AverageTableRebinner; +import org.das2.dataset.ClippedTableDataSet; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetConsumer; +import org.das2.dataset.DataSetRebinner; +import org.das2.dataset.DataSetUpdateEvent; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.NoDataInIntervalException; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.SingleVectorDataSet; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.TableDataSetConsumer; +import org.das2.dataset.VectorDataSet; +import org.das2.dataset.VectorDataSetBuilder; +import org.das2.dataset.test.PolynomialDataSetDescriptor; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvas; +import org.das2.graph.DasColumn; +import org.das2.graph.DasPlot; +import org.das2.graph.DasRow; +import org.das2.graph.Psym; +import org.das2.graph.PsymConnector; +import org.das2.graph.SymColor; +import org.das2.graph.SymbolLineRenderer; +import org.das2.math.QuadFitUtil; +import org.das2.system.DasLogger; +import org.das2.util.DasMath; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.util.Arrays; +import java.util.HashMap; +import java.util.logging.Logger; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; + +/** + * + * @author Jeremy + */ +public class PeakDetectorMouseModule extends BoxSelectorMouseModule { + + DasAxis xaxis, yaxis; + DataSetConsumer dataSetConsumer; + DatumRange xrange; + DatumRange yrange; + String lastComment; + PeakSlicer peakSlicer; + + + static Logger logger= DasLogger.getLogger( DasLogger.GUI_LOG ); + + public PeakDetectorMouseModule( DasPlot parent, DataSetConsumer consumer ) { + super( parent, parent.getXAxis(), parent.getYAxis(), consumer, new BoxRenderer(parent,true), "Peak Detector" ); + this.dataSetConsumer= consumer; + } + + @Override + protected void fireBoxSelectionListenerBoxSelected(BoxSelectionEvent event) { + + final DatumRange xrange0= xrange; + final DatumRange yrange0= yrange; + + xrange= event.getXRange(); + yrange= event.getYRange(); + if ( event.getPlane("keyChar")!=null ) { + lastComment= (String)event.getPlane("keyChar"); + } else { + lastComment= null; + } + + Runnable run= new Runnable() { + public void run() { + try { + recalculate(); + } catch ( RuntimeException ex ) { + xrange= xrange0; + yrange= yrange0; + throw ex; + } + } + }; + new Thread(run).start(); + } + + /** + * return RebinDescriptor that is on descrete, repeatable boundaries. + * get us2000, divide by resolution, truncate, multiply by resolution. + */ + private RebinDescriptor getRebinDescriptor( DatumRange range ) { + double res= xResolution.doubleValue(Units.microseconds); + double min= range.min().doubleValue(Units.us2000); + min= Math.floor( min / res ); + double max= range.max().doubleValue(Units.us2000); + max= Math.ceil( max / res ); + int nbin= (int)(max-min); + + RebinDescriptor ddx= new RebinDescriptor( min*res, max*res, Units.us2000, nbin, false ); + return ddx; + } + + private VectorDataSet toDb( VectorDataSet ds, Datum reference ) { + Units refUnits= reference.getUnits(); + double refValue= reference.doubleValue(refUnits); + Units yunits= Units.dB; + Units xunits= ds.getXUnits(); + VectorDataSetBuilder builder= new VectorDataSetBuilder( xunits, yunits ); + + for ( int i=0; i=xtag>max, or -1 if no peak is found. + */ + private static int peakIndex(VectorDataSet ds, Datum min, Datum max) { + Datum yMax = ds.getYUnits().createDatum(Double.NEGATIVE_INFINITY); + int iMax = -1; + int i0 = -1; //The first x index in range + int i1 = -1; //The last x index in range + for (int i = 0; i < ds.getXLength(); i++) { + Datum y = ds.getDatum(i); + Datum x = ds.getXTagDatum(i); + if ( x.ge(min) && x.lt(max)) { + if (i0 == -1) i0 = i; + i1 = i; + if (y.gt(yMax)) { + yMax = y; + iMax = i; + } + } + } + + // We don't want to record this value if it is the first or + // last within range, since this inidicates that the value + // probably isn't really a maximum. + if (iMax == i0 || iMax == i1) { + iMax = -1; + } + return iMax; + } + + private static int[] findFive(VectorDataSet ds, int peakIndex) { + int[] indices, result; + int nIndex; + double yLowPrev, yHighPrev; + Units yUnits = ds.getYUnits(); + + indices = new int[7]; + indices[0] = peakIndex; + nIndex = 1; + + yHighPrev = yLowPrev = ds.getDouble(peakIndex, yUnits); + + for (int i = 1; i <= 3 && nIndex < 5; i++) { + double yLow = (peakIndex - i) >= 0 ? ds.getDouble(peakIndex - i, yUnits) : Double.POSITIVE_INFINITY; + double yHigh = (peakIndex + i) < ds.getXLength() ? ds.getDouble(peakIndex + i, yUnits) : Double.POSITIVE_INFINITY; + if (yLow < yLowPrev) { + indices[nIndex++] = peakIndex - i; + yLowPrev = yLow; + } else { + yLowPrev = Double.NEGATIVE_INFINITY; + } + if (yHigh < yHighPrev) { + indices[nIndex++] = peakIndex + i; + yHighPrev = yHigh; + } else { + yHighPrev = Double.NEGATIVE_INFINITY; + } + } + + Arrays.sort(indices, 0, nIndex); + result = new int[nIndex]; + System.arraycopy(indices, 0, result, 0, nIndex); + + return result; + } + + /** + * returns a double[2] if a fit is possible, null otherwise. + * @param slice a VectorDataSet with yUnits convertable to Units.dB. + */ + private double[] getFit( VectorDataSet slice, int jMax) { + double[] px, py, w; + double[] c; + int [] indices; + Datum y = slice.getXTagDatum(jMax); + Datum z = slice.getDatum(jMax); + Units xUnits = slice.getXUnits(); + Units yUnits = Units.dB; + + indices = findFive(slice, jMax); + int peakOfFive= Arrays.binarySearch(indices, jMax); + px = new double[indices.length]; + py = new double[indices.length]; + w = new double[indices.length]; + for (int iIndex = 0; iIndex < indices.length; iIndex++) { + px[iIndex] = slice.getXTagDouble(indices[iIndex], xUnits); + py[iIndex] = slice.getDouble(indices[iIndex], yUnits); + w[iIndex] = Units.dB.convertDoubleTo(Units.dimensionless, py[iIndex]); + } + + double threeDown = py[peakOfFive] - levelMin.doubleValue(Units.dB); + if (threeDown < py[0] && threeDown < py[py.length - 1]) { + return null; + } + + //Arrays.fill(w, 1.0); + c = QuadFitUtil.quadfit(px, py, w); + + if (c[2] >= -0.0) { + return null; + } + + double peak = QuadFitUtil.quadPeak(c); + if (peak < slice.getXTagDouble(0, xUnits) || peak > slice.getXTagDouble(slice.getXLength() - 1, xUnits)) { + return null; + } + + return c; + } + + + private class PeakSlicer implements DataPointSelectionListener { + + DataPointSelectionEvent lastSelectedPoint; + FitDescriptor fit; + Datum yValue; + Datum xValue; + int selectedRecord; // + + SymbolLineRenderer levelRenderer; + SymbolLineRenderer fitRenderer; + SymbolLineRenderer fitPointRenderer; + + DasPlot topPlot; + JFrame frame; + + Action prevAction= new AbstractAction("<< Prev") { + @Override + public void actionPerformed( ActionEvent e ) { + Datum xnew= xValue.subtract( xResolution ); + DataPointSelectionEvent evNew= new DataPointSelectionEvent( this, xnew, yValue ); + PeakSlicer.this.dataPointSelected(evNew); + } + } ; + + Action nextAction= new AbstractAction("Next >>") { + @Override + public void actionPerformed( ActionEvent e ) { + Datum xnew= xValue.add( xResolution ); + DataPointSelectionEvent evNew= new DataPointSelectionEvent( this, xnew, yValue ); + PeakSlicer.this.dataPointSelected(evNew); + } + } ; + + + PeakSlicer( DasPlot parent, DasAxis xaxis ) { + frame= new JFrame("Peak Slice"); + JPanel contentPanel= new JPanel(); + contentPanel.setLayout( new BorderLayout() ); + DasCanvas canvas= new DasCanvas( 300, 300 ); + contentPanel.add( canvas, BorderLayout.CENTER ); + Box npBox= Box.createHorizontalBox(); + npBox.add( new JButton( prevAction ) ); + npBox.add( new JButton( nextAction ) ); + contentPanel.add( npBox, BorderLayout.NORTH ); + + frame.getContentPane().add( contentPanel ); + frame.pack(); + frame.setVisible(false); + frame.setDefaultCloseOperation( JFrame.HIDE_ON_CLOSE ); + + DasColumn col= DasColumn.create( canvas ); + DasRow row1= DasRow.create( canvas, 0, 1 ); + + DasAxis yaxis= new DasAxis( new DatumRange( 0.001, 1,Units.dimensionless ), DasAxis.VERTICAL ) ; + yaxis.setLog(true); + DasPlot plot= new PeakDasPlot( xaxis, yaxis ); + plot.getYAxis().setLabel("level"); + plot.getXAxis().setTickLabelsVisible(false); + levelRenderer= new SymbolLineRenderer(); + + fitRenderer= new SymbolLineRenderer(); + fitRenderer = new SymbolLineRenderer(); + fitRenderer.setColor(SymColor.blue); + + fitPointRenderer = new SymbolLineRenderer(); + fitPointRenderer.setColor(SymColor.red); + fitPointRenderer.setPsymConnector(PsymConnector.NONE); + fitPointRenderer.setPsym(Psym.TRIANGLES); + fitPointRenderer.setSymSize(3.0); + + plot.addRenderer(fitRenderer); + plot.addRenderer(levelRenderer); + + topPlot= plot; + + DataPointSelectorMouseModule tweakSlicer= + new DataPointSelectorMouseModule( topPlot, levelRenderer, + new VerticalSliceSelectionRenderer(topPlot), "tweak cutoff" ) { + @Override + public void keyPressed( KeyEvent event ) { + System.err.print(event); + if ( event.getKeyCode()==KeyEvent.VK_DOWN ) { + } else if ( event.getKeyCode()==KeyEvent.VK_UP ) { + Datum xnew= xValue.subtract( xResolution ); + DataPointSelectionEvent evNew= new DataPointSelectionEvent( this, xnew, yValue ); + PeakSlicer.this.dataPointSelected(evNew); + } + } + }; + tweakSlicer.setDragEvents(true); // only key events fire + tweakSlicer.addDataPointSelectionListener( new DataPointSelectionListener() { + @Override + public void dataPointSelected( DataPointSelectionEvent e ) { + Datum x= e.getX(); + HashMap properties= new HashMap(); + if ( e.getPlane("keyChar")!=null ) { + properties.put("comment",e.getPlane("keyChar")); + } else { + properties.put("comment","tweak"); + } + fireDataSetUpdateListenerDataSetUpdated( + new DataSetUpdateEvent(this, + new SingleVectorDataSet( xValue, e.getX(), properties ) ) ); + } + } ); + topPlot.addMouseModule( tweakSlicer ); + topPlot.getDasMouseInputAdapter().setPrimaryModule(tweakSlicer); + + DataPointSelectorMouseModule levelSlicer= + new DataPointSelectorMouseModule( topPlot, levelRenderer, + new HorizontalSliceSelectionRenderer(topPlot), "peak S/N level" ); + levelSlicer.addDataPointSelectionListener( new DataPointSelectionListener() { + @Override + public void dataPointSelected( DataPointSelectionEvent e ) { + Datum y= e.getY(); + PeakDetectorMouseModule.this.setLevelMin( y ); + } + } ); + levelSlicer.setDragEvents(false); + levelSlicer.setKeyEvents(false); + levelSlicer.setReleaseEvents(true); + topPlot.addMouseModule( levelSlicer ); + + canvas.add( plot, row1, col ); + + } + + + @Override + public void dataPointSelected(org.das2.event.DataPointSelectionEvent event) { + logger.fine("got DataPointSelectionEvent: "+event.getX() ); + this.lastSelectedPoint= event; + + TableDataSet tds= (TableDataSet)dataSetConsumer.getConsumedDataSet(); + + this.xValue= event.getX(); + this.yValue= event.getY(); + + if ( xrange==null ) return; + + DatumRange range= new DatumRange( event.getX(), event.getX() ); + try { + tds= conditionData( tds, range ); + } catch (NoDataInIntervalException ex) { + return; + } + + logger.fine("find closest column " ); + int i= DataSetUtil.closestColumn( tds, event.getX() ); + + + this.xValue= tds.getXTagDatum(i); + topPlot.setTitle( "" + xValue ); + + logger.fine("doDigitize"); + fit= doDigitize( tds, i ); + if ( fit!=null ) { + levelRenderer.setDataSet( fit.digitizedDataSet ); + Datum resLimit= topPlot.getXAxis().invTransform(1) .subtract( topPlot.getXAxis().invTransform(0) ); + PolynomialDataSetDescriptor dsd= new PolynomialDataSetDescriptor( fit.fitCoef, fit.peakX.getUnits(), + fit.digitizedDataSet.getYUnits(), resLimit ) ; + dsd.setYMin(fit.digitizedDataSet.getYUnits().createDatum( -5 )); + fitRenderer.setDataSetDescriptor( dsd ); + } + + showPopup(); + } + + private void showPopup() { + if ( !frame.isVisible() ) frame.setVisible(true); + } + + class PeakDasPlot extends DasPlot { + PeakDasPlot( DasAxis x, DasAxis y ) { + super(x,y); + } + @Override + protected void drawContent(java.awt.Graphics2D g) { + super.drawContent(g); + + if ( fit!=null ) { + g.setColor( Color.GRAY ); + int ix= (int)this.getXAxis().transform( fit.peakX ); + g.drawLine( ix, 0, ix, getHeight() ); + int iy= (int)this.getYAxis().transform( Units.dB.createDatum(-3) ); + g.drawLine( 0, iy, getWidth(), iy ); + + g.setColor( Color.pink ); + ix= (int)getXAxis().transform( yValue ); + g.drawLine( ix, 0, ix, getHeight() ); + } + + } + } + } + + public DataPointSelectionListener getSlicer( DasPlot plot, TableDataSetConsumer consumer ) { + DasAxis sourceYAxis = plot.getYAxis(); + DasAxis sourceZAxis = consumer.getZAxis(); + + DatumRange range= sourceYAxis.getDatumRange(); + DasAxis xAxis = sourceYAxis.createAttachedAxis( DasAxis.HORIZONTAL ); + peakSlicer= new PeakSlicer( plot, xAxis ); + return peakSlicer; + + } + + + private transient java.util.ArrayList dataSetUpdateListenerList; + + public synchronized void addDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + if (dataSetUpdateListenerList == null ) { + dataSetUpdateListenerList = new java.util.ArrayList(); + } + dataSetUpdateListenerList.add(listener); + } + + public synchronized void removeDataSetUpdateListener(org.das2.dataset.DataSetUpdateListener listener) { + if (dataSetUpdateListenerList != null ) { + dataSetUpdateListenerList.remove(listener); + } + } + + private void fireDataSetUpdateListenerDataSetUpdated(org.das2.dataset.DataSetUpdateEvent event) { + java.util.ArrayList list; + synchronized (this) { + if (dataSetUpdateListenerList == null) return; + list = (java.util.ArrayList)dataSetUpdateListenerList.clone(); + } + for (int i = 0; i < list.size(); i++) { + ((org.das2.dataset.DataSetUpdateListener)list.get(i)).dataSetUpdated(event); + } + } + + /** + * Holds value of property levelMin. + */ + private Datum levelMin= Units.dB.createDatum(-3.); + + /** + * Getter for property levelMin. + * @return Value of property levelMin. + */ + public Datum getLevelMin() { + return this.levelMin; + } + + /** + * Setter for property levelMin. + * @param levelMin New value of property levelMin. + */ + public void setLevelMin(Datum levelMin) { + this.levelMin = levelMin; + recalculate(); + } + + private Datum xResolution= Units.milliseconds.createDatum(500); + + public Datum getXResolution() { + return this.xResolution; + } + + public void setXResolution(Datum xResolution) { + this.xResolution = xResolution; + } + +} diff --git a/dasCore/src/org/das2/event/PointSlopeDragRenderer.java b/dasCore/src/org/das2/event/PointSlopeDragRenderer.java index 7c7886943..a1cda6f5e 100644 --- a/dasCore/src/org/das2/event/PointSlopeDragRenderer.java +++ b/dasCore/src/org/das2/event/PointSlopeDragRenderer.java @@ -11,17 +11,10 @@ import org.das2.datum.Datum; import org.das2.util.GrannyTextRenderer; import java.awt.*; -import java.awt.geom.Ellipse2D; -import java.awt.geom.Line2D; import java.text.*; -import org.das2.datum.DatumRange; -import org.das2.datum.DatumUtil; -import org.das2.datum.InconvertibleUnitsException; -import org.das2.datum.Units; -import org.das2.datum.UnitsConverter; /** - * Shows the slope from the click point to the drag point. + * * @author Owner */ public class PointSlopeDragRenderer extends LabelDragRenderer { @@ -41,24 +34,8 @@ public PointSlopeDragRenderer(DasCanvasComponent parent, DasAxis xaxis, DasAxis nf= new DecimalFormat( "0.00E0" ); } - @Override public Rectangle[] renderDrag(java.awt.Graphics g1, java.awt.Point p1, java.awt.Point p2) { Graphics2D g= ( Graphics2D ) g1; - - g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); - double atan= Math.atan2( p2.y-p1.y, p2.x-p1.x ); - - Line2D line2= new Line2D.Double( p1.x + (int)(6.0 * Math.cos(atan)), (int)(p1.y + 6.0*Math.sin(atan)), p2.x, p2.y ); - - Color color0= g.getColor(); - g.setColor(new Color(255,255,255,100)); - g.setStroke(new BasicStroke( 3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND )); - g.draw( line2 ); - g.draw( new Ellipse2D.Double( p1.x-4, p1.y-4, 8, 8 ) ); - - g.setStroke(new BasicStroke()); - g.setColor(color0); - g1.drawLine( p1.x, p1.y, p2.x, p2.y ); g1.drawOval(p1.x-1, p1.y-1, 3, 3 ) ; @@ -70,31 +47,9 @@ public Rectangle[] renderDrag(java.awt.Graphics g1, java.awt.Point p1, java.awt. Datum run= xaxis.invTransform(p2.x).subtract(xaxis.invTransform(p1.x)); Datum rise= yaxis.invTransform(p2.y).subtract(yaxis.invTransform(p1.y)); - Datum xdr= xaxis.getDatumRange().width(); - Datum ydr= yaxis.getDatumRange().width(); - - Units xunits= DatumUtil.asOrderOneUnits(xdr).getUnits(); - Units yunits= DatumUtil.asOrderOneUnits(ydr).getUnits(); - run= run.convertTo(xunits); - rise= rise.convertTo(yunits); - if ( !p1.equals(p2) ) { - try { - Datum slope= rise.divide(run); - setLabel( "m="+slope ); - } catch ( InconvertibleUnitsException ex ) { - double drise= rise.doubleValue(rise.getUnits()); - double drun= run.doubleValue(run.getUnits()); - double mag= drise/drun; - String units= "" + rise.getUnits() + " / " + run.getUnits(); - setLabel( "m=" + nf.format(mag) + " " + units ); - } catch ( IllegalArgumentException ex ) { // 1/deg - double drise= rise.doubleValue(rise.getUnits()); - double drun= run.doubleValue(run.getUnits()); - double mag= drise/drun; - String units= "" + rise.getUnits() + " / " + run.getUnits(); - setLabel( "m=" + nf.format(mag) + " " + units ); - } + Datum slope= rise.divide(run); + setLabel( "m="+slope ); } else { setLabel( "" ); } diff --git a/dasCore/src/org/das2/event/TimeRangeSelectorMouseModule.java b/dasCore/src/org/das2/event/TimeRangeSelectorMouseModule.java new file mode 100644 index 000000000..1f17d0055 --- /dev/null +++ b/dasCore/src/org/das2/event/TimeRangeSelectorMouseModule.java @@ -0,0 +1,137 @@ +/* File: TimeRangeSelectorMouseModule.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.event; + +import org.das2.datum.DatumRange; +import org.das2.DasApplication; +import org.das2.datum.Datum; +import org.das2.graph.DasAxis; +import org.das2.graph.DasCanvasComponent; +import org.das2.graph.DasPlot; +import org.das2.system.DasLogger; +import javax.swing.event.EventListenerList; + + +/** + * + * @author jbf + * @deprecated Use HorizontalRangeSelectorMouseModule. + */ +public class TimeRangeSelectorMouseModule extends MouseModule { + + DasAxis timeAxis; + + /** Utility field used by event firing mechanism. */ + private EventListenerList listenerList = null; + + public String getLabel() { return "X Time Zoom"; } + + public TimeRangeSelectorMouseModule(DasCanvasComponent parent, DasAxis timeAxis) { + this.parent= parent; + this.dragRenderer= new HorizontalRangeGesturesRenderer(parent); + this.timeAxis= timeAxis; + } + + public static TimeRangeSelectorMouseModule create(DasPlot parent) { + DasAxis axis= parent.getXAxis(); + TimeRangeSelectorMouseModule result=null; + result= new TimeRangeSelectorMouseModule(parent, parent.getXAxis()); + return result; + } + + public void mouseRangeSelected(MouseRangeSelectionEvent e0) { + Datum tmin; + Datum tmax; + + if (!e0.isGesture()) { + MouseRangeSelectionEvent e= (MouseRangeSelectionEvent)e0; + Datum min= timeAxis.invTransform(e.getMinimum()); + Datum max= timeAxis.invTransform(e.getMaximum()); + + Datum nnMin= timeAxis.findTick(min,0,true); // nearest neighbor + Datum nnMax= timeAxis.findTick(max,0,true); + if (nnMin.equals(nnMax)) { + min= timeAxis.findTick(min,-1,true); + max= timeAxis.findTick(max,1,true); + } else { + min= nnMin; + max= nnMax; + } + TimeRangeSelectionEvent te= new TimeRangeSelectionEvent(parent,new DatumRange( min,max ) ); + fireTimeRangeSelectionListenerTimeRangeSelected(te); + } else if (e0.getGesture()==Gesture.BACK) { + timeAxis.setDataRangePrev(); + } else if (e0.getGesture()==Gesture.ZOOMOUT) { + timeAxis.setDataRangeZoomOut(); + } else if (e0.getGesture()==Gesture.FORWARD) { + timeAxis.setDataRangeForward(); + } else if (e0.getGesture()==Gesture.SCANPREV) { + DatumRange range0= timeAxis.getDatumRange(); + TimeRangeSelectionEvent te= new TimeRangeSelectionEvent(parent, range0.previous() ); + fireTimeRangeSelectionListenerTimeRangeSelected(te); + } else if (e0.getGesture()==Gesture.SCANNEXT) { + DatumRange range0= timeAxis.getDatumRange(); + TimeRangeSelectionEvent te= new TimeRangeSelectionEvent(parent, range0.next() ); + fireTimeRangeSelectionListenerTimeRangeSelected(te); + } else { + throw new RuntimeException("unrecognized gesture: "+e0.getGesture()); + } + + } + + /** Registers TimeRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addTimeRangeSelectionListener(org.das2.event.TimeRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new EventListenerList(); + } + listenerList.add(org.das2.event.TimeRangeSelectionListener.class, listener); + } + + /** Removes TimeRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeTimeRangeSelectionListener(org.das2.event.TimeRangeSelectionListener listener) { + listenerList.remove(org.das2.event.TimeRangeSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + + private void fireTimeRangeSelectionListenerTimeRangeSelected(TimeRangeSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.TimeRangeSelectionListener.class) { + String logmsg= "fire event: "+this.getClass().getName()+"-->"+listeners[i+1].getClass().getName()+" "+event; + DasLogger.getLogger( DasLogger.GUI_LOG ).fine(logmsg); + ((org.das2.event.TimeRangeSelectionListener)listeners[i+1]).timeRangeSelected(event); + } + } + } + +} diff --git a/dasCore/src/org/das2/event/VerticalRangeGesturesRenderer.java b/dasCore/src/org/das2/event/VerticalRangeGesturesRenderer.java index b400369d6..8fce3fe2e 100644 --- a/dasCore/src/org/das2/event/VerticalRangeGesturesRenderer.java +++ b/dasCore/src/org/das2/event/VerticalRangeGesturesRenderer.java @@ -49,7 +49,11 @@ public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { Graphics2D g= (Graphics2D) g1; - if ( gr.isGesture(p1,p2) ) { + double dx= p2.x-p1.x; + double dy= -1* ( p2.y-p1.y ); + double angle= Math.atan2(dy, dx) * 180 / Math.PI; + double radius= Math.sqrt(dy*dy+dx*dx); + if ( radius<20 ) { gr.renderDrag( g, p1, p2 ); dirtyBounds.setBounds(gr.getDirtyBounds()); } else { @@ -85,8 +89,9 @@ public Rectangle[] renderDrag(Graphics g1, Point p1, Point p2) { public MouseDragEvent getMouseDragEvent(Object source, Point p1, Point p2, boolean isModified) { - //double dx= p2.x-p1.x; - //double dy= -1* ( p2.y-p1.y ); + double dx= p2.x-p1.x; + double dy= -1* ( p2.y-p1.y ); + double radius= Math.sqrt(dy*dy+dx*dx); if ( gr.isGesture(p1,p2) ) { return gr.getMouseDragEvent(source,p1,p2,isModified); } else { diff --git a/dasCore/src/org/das2/event/VerticalRangeSelectorMouseModule.java b/dasCore/src/org/das2/event/VerticalRangeSelectorMouseModule.java index e611ee637..9601fe39f 100644 --- a/dasCore/src/org/das2/event/VerticalRangeSelectorMouseModule.java +++ b/dasCore/src/org/das2/event/VerticalRangeSelectorMouseModule.java @@ -23,18 +23,12 @@ package org.das2.event; -import java.awt.Point; -import java.awt.event.MouseWheelEvent; -import java.util.logging.Level; -import javax.swing.SwingUtilities; import org.das2.datum.Datum; import org.das2.datum.DatumRange; import org.das2.graph.DasAxis; import org.das2.graph.DasCanvasComponent; import org.das2.graph.DasPlot; import javax.swing.event.EventListenerList; -import org.das2.datum.DatumRangeUtil; -import static org.das2.event.MouseModule.logger; /** * @@ -43,12 +37,10 @@ public class VerticalRangeSelectorMouseModule extends MouseModule { DasAxis axis; - long t0, tbirth; /** Utility field used by event firing mechanism. */ - private EventListenerList listenerList = new EventListenerList(); - - @Override + private EventListenerList listenerList = null; + public String getLabel() { return "Zoom Y"; }; public VerticalRangeSelectorMouseModule(DasCanvasComponent parent, DasAxis axis) { @@ -56,32 +48,27 @@ public VerticalRangeSelectorMouseModule(DasCanvasComponent parent, DasAxis axis) throw new IllegalArgumentException("Axis orientation is not vertical"); } this.parent= parent; + // this.dragRenderer= (DragRenderer)HorizontalRangeRenderer.renderer; this.dragRenderer= new VerticalRangeGesturesRenderer(parent); this.axis= axis; } public static VerticalRangeSelectorMouseModule create(DasPlot parent) { + DasAxis axis= parent.getYAxis(); VerticalRangeSelectorMouseModule result= new VerticalRangeSelectorMouseModule(parent,parent.getYAxis()); return result; } - @Override public void mouseRangeSelected(MouseDragEvent e0) { if (!e0.isGesture()) { Datum min; Datum max; - if ( !( e0 instanceof MouseRangeSelectionEvent ) ) { - throw new IllegalArgumentException("Event should be MouseRangeSelectionEvent"); // findbugs - } + Datum nnMin; + Datum nnMax; MouseRangeSelectionEvent e= (MouseRangeSelectionEvent)e0; min= axis.invTransform(e.getMaximum()); max= axis.invTransform(e.getMinimum()); - if ( min.gt(max) ) { - Datum t= min; - min= max; - max= t; - } DatumRange dr= new DatumRange( min, max ); DatumRange nndr= axis.getTickV().enclosingRange(dr, true); DataRangeSelectionEvent te= @@ -97,122 +84,30 @@ public void mouseRangeSelected(MouseDragEvent e0) { } } - - /** - * mouse wheel events zoom or pan rapidly. With a physical wheel, I (jbf) found - * that I get 17ms per click, and this is manageable. With a touchpad on a mac, - * these events come much faster, like 10ms per click, which can disorient the - * operator. So we limit the speed to 20ms per click, for now by dropping - * rapid clicks. - * - * @param e - */ - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - double nmin, nmax; - - double shift = 0.; - - if ( (e.isControlDown() )) { - if (e.getWheelRotation() < 0) { - nmin = -0.20; // pan left on xaxis - nmax = +0.80; - } else { - nmin = +0.20; // pan right on xaxis - nmax = +1.20; - } - } else if ( e.isShiftDown() ) { - if (e.getWheelRotation() < 0) { - nmin = -0.005; // pan left on xaxis - nmax = +0.995; - } else { - nmin = +0.005; // pan right on xaxis - nmax = +1.005; - } - } else { - //mac trackpads coast a while after release, so let's govern the speed a little more - if (e.getWheelRotation() < 0) { - nmin = 0.10; // zoom in - nmax = 0.90; - } else { - nmin = -0.125; // zoom out - nmax = 1.125; - } - } - - Point ep= SwingUtilities.convertPoint( e.getComponent(), e.getPoint(), parent.getCanvas() ); - - Pos ypos = axis == null ? Pos._null : position( axis.getRow(), ep.y, 20 ); - switch (ypos) { - case min: - shift = nmin; // this will cancel out nmin - break; - case max: - shift = -nmin; - break; - } - - int clickMag = 1; - final long t1 = System.nanoTime(); - long limitNanos = (long) 40e6; - if ((t1 - t0) / clickMag < limitNanos) { - clickMag = (int) Math.floor( (double)(t1 - t0) / limitNanos ); - } - - if (clickMag == 0) return; - t0 = System.nanoTime(); - - // these will be non-null if they should be used. - DatumRange xdrnew; - - logger.log(Level.FINEST, ":ns: {0} {1}", new Object[]{System.nanoTime() - tbirth, clickMag}); - if ( true ) { - DatumRange dr = axis.getDatumRange(); - for (int i = 0; i < clickMag; i++) { - if (axis.isLog()) { - dr = DatumRangeUtil.rescaleLog(dr, nmin+shift, nmax+shift); - } else { - dr = DatumRangeUtil.rescale(dr, nmin+shift, nmax+shift); - } - } - dr= maybeRound( axis, dr ); - - if ( ! DatumRangeUtil.isAcceptable( dr, axis.isLog() ) ) { - dr= null; - } - xdrnew= dr; - } - - if ( axisIsAdjustable(axis) && xdrnew==null ) return; - - if ( axisIsAdjustable(axis) ) axis.setDatumRange(xdrnew); - - super.mouseWheelMoved(e); - } - - /** Registers DataRangeSelectionListener to receive events. * @param listener The listener to register. */ - public void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new EventListenerList(); + } listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); } /** Removes DataRangeSelectionListener from the list of listeners. * @param listener The listener to remove. */ - public void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); } - /** Notifies all registered listeners about the event. * * @param event The event to be fired */ private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { - Object[] listeners; - listeners = listenerList.getListenerList(); + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); diff --git a/dasCore/src/org/das2/event/VerticalSlicerMouseModule.java b/dasCore/src/org/das2/event/VerticalSlicerMouseModule.java index b8318be97..0347f90e3 100755 --- a/dasCore/src/org/das2/event/VerticalSlicerMouseModule.java +++ b/dasCore/src/org/das2/event/VerticalSlicerMouseModule.java @@ -27,14 +27,14 @@ import org.das2.graph.DasCanvasComponent; import org.das2.graph.DasPlot; import org.das2.graph.Renderer; -import org.virbo.dataset.QDataSet; /** * * @author jbf */ public class VerticalSlicerMouseModule extends MouseModule { - private QDataSet ds; + private org.das2.dataset.DataSet ds; + double offset; private DasAxis xaxis; private DasAxis yaxis; @@ -44,7 +44,7 @@ public class VerticalSlicerMouseModule extends MouseModule { private DataPointSelectionEvent de; /** Utility field used by event firing mechanism. */ - private javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList();; + private javax.swing.event.EventListenerList listenerList = null; public VerticalSlicerMouseModule( DasCanvasComponent parent, DataSetConsumer dataSetConsumer, DasAxis xaxis, DasAxis yaxis ) { @@ -59,7 +59,7 @@ public VerticalSlicerMouseModule( DasCanvasComponent parent, public static VerticalSlicerMouseModule create(DasPlot parent) { DasAxis xaxis= parent.getXAxis(); DasAxis yaxis= parent.getYAxis(); - return new VerticalSlicerMouseModule(parent,null,xaxis,yaxis); + return new VerticalSlicerMouseModule(parent,parent,xaxis,yaxis); } public static VerticalSlicerMouseModule create( Renderer renderer ) { @@ -67,7 +67,6 @@ public static VerticalSlicerMouseModule create( Renderer renderer ) { return new VerticalSlicerMouseModule(parent,renderer,parent.getXAxis(),parent.getYAxis()); } - @Override public void mousePointSelected(MousePointSelectionEvent e) { de.birthMilli= System.currentTimeMillis(); ds= dataSetConsumer.getConsumedDataSet(); @@ -81,14 +80,17 @@ public void mousePointSelected(MousePointSelectionEvent e) { /** Registers DataPointSelectionListener to receive events. * @param listener The listener to register. */ - public void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + public synchronized void addDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + if (listenerList == null ) { + listenerList = new javax.swing.event.EventListenerList(); + } listenerList.add(org.das2.event.DataPointSelectionListener.class, listener); } /** Removes DataPointSelectionListener from the list of listeners. * @param listener The listener to remove. */ - public void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { + public synchronized void removeDataPointSelectionListener(org.das2.event.DataPointSelectionListener listener) { listenerList.remove(org.das2.event.DataPointSelectionListener.class, listener); } @@ -97,9 +99,8 @@ public void removeDataPointSelectionListener(org.das2.event.DataPointSelectionLi * @param event The event to be fired */ private void fireDataPointSelectionListenerDataPointSelected(DataPointSelectionEvent event) { - - Object[] listeners; - listeners = listenerList.getListenerList(); + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==org.das2.event.DataPointSelectionListener.class) { ((org.das2.event.DataPointSelectionListener)listeners[i+1]).dataPointSelected(event); diff --git a/dasCore/src/org/das2/event/ZoomOutMouseModule.java b/dasCore/src/org/das2/event/ZoomOutMouseModule.java index cff7c5c8b..e55c62833 100644 --- a/dasCore/src/org/das2/event/ZoomOutMouseModule.java +++ b/dasCore/src/org/das2/event/ZoomOutMouseModule.java @@ -17,11 +17,11 @@ * * @author jbf */ -public final class ZoomOutMouseModule extends BoxSelectorMouseModule { +public class ZoomOutMouseModule extends BoxSelectorMouseModule { DasAxis parent; - private BoxSelectionListener createBoxSelectionListener() { + BoxSelectionListener createBoxSelectionListener() { return new BoxSelectionListener() { public void BoxSelected( BoxSelectionEvent event ) { DatumRange outerRange= parent.getDatumRange(); @@ -47,9 +47,7 @@ public void BoxSelected( BoxSelectionEvent event ) { }; } - /** Creates a new instance of ZoomOutMouseModule - * @param axis - */ + /** Creates a new instance of ZoomOutMouseModule */ public ZoomOutMouseModule( DasAxis axis ) { super( axis, axis.isHorizontal() ? axis : null, diff --git a/dasCore/src/org/das2/event/ZoomPanMouseModule.java b/dasCore/src/org/das2/event/ZoomPanMouseModule.java index 26493ae51..23db8ca0b 100644 --- a/dasCore/src/org/das2/event/ZoomPanMouseModule.java +++ b/dasCore/src/org/das2/event/ZoomPanMouseModule.java @@ -8,33 +8,33 @@ */ package org.das2.event; -import java.util.logging.Level; +import java.awt.Component; import org.das2.datum.Datum; import org.das2.datum.DatumRange; import org.das2.datum.DatumRangeUtil; +import org.das2.datum.TimeLocationUnits; +import org.das2.datum.UnitsUtil; import org.das2.graph.DasAxis; import org.das2.graph.DasCanvasComponent; +import org.das2.graph.TickVDescriptor; import java.awt.Cursor; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; +import javax.swing.JComponent; import javax.swing.SwingUtilities; +import org.das2.graph.DasDevicePosition; /** - * Provide navigation similar to Google Maps, where drag events result a pan on the axes, and mouse wheel events - * are zoom in and zoom out. This is typically attached to the middle mouse button. - * - * @see BoxZoomMouseModule - * + * * @author jbf */ public class ZoomPanMouseModule extends MouseModule { DasAxis xAxis; - DasAxis.Lock xAxisLock; DasAxis yAxis; + DasAxis.Lock xAxisLock; DasAxis.Lock yAxisLock; - Point p0; DatumRange xAxisRange0; DatumRange yAxisRange0; @@ -50,6 +50,34 @@ public ZoomPanMouseModule(DasCanvasComponent parent, DasAxis horizontalAxis, Das tbirth = System.nanoTime(); } + private boolean axisIsAdjustable(DasAxis axis) { + return axis != null && (UnitsUtil.isIntervalMeasurement(axis.getUnits()) || UnitsUtil.isRatioMeasurement(axis.getUnits())); + } + + private enum Pos { + _null, beyondMin, min, middle, max, beyondMax + + + + }; + + private Pos position(DasDevicePosition ddp, int pos, int threshold) { + int max = ddp.getDMaximum(); + int min = ddp.getDMinimum(); + if (((max - min) / threshold) < 3) threshold = (max - min) / 3; + if (pos < min) { + return Pos.beyondMin; + } else if (pos < min + threshold ) { + return Pos.min; + } else if (pos <= max - threshold) { + return Pos.middle; + } else if (pos <= max) { + return Pos.max; + } else { + return Pos.beyondMax; + } + } + /** * mouse wheel events zoom or pan rapidly. With a physical wheel, I (jbf) found * that I get 17ms per click, and this is managable. With a touchpad on a mac, @@ -59,7 +87,6 @@ public ZoomPanMouseModule(DasCanvasComponent parent, DasAxis horizontalAxis, Das * * @param e */ - @Override public void mouseWheelMoved(MouseWheelEvent e) { double nmin, nmax; @@ -81,13 +108,12 @@ public void mouseWheelMoved(MouseWheelEvent e) { Pos xpos = xAxis == null ? Pos._null : position(xAxis.getColumn(), ep.x, 20); Pos ypos = yAxis == null ? Pos._null : position(yAxis.getRow(), ep.y, 20); - //mac trackpads coast a while after release, so let's govern the speed a little more if (e.getWheelRotation() < 0) { - nmin = 0.10; // zoom in - nmax = 0.90; + nmin = 0.20; // zoom in + nmax = 0.80; } else { - nmin = -0.125; // zoom out - nmax = 1.125; + nmin = -0.25; // zoom out + nmax = 1.25; } switch (xpos) { case min: @@ -110,19 +136,15 @@ public void mouseWheelMoved(MouseWheelEvent e) { //int clickMag= Math.abs(e.getWheelRotation()); int clickMag = 1; final long t1 = System.nanoTime(); - long limitNanos = (long) 40e6; + long limitNanos = (long) 20e6; if ((t1 - t0) / clickMag < limitNanos) { - clickMag = (int) Math.floor( (double)(t1 - t0) / limitNanos ); + clickMag = (int) Math.floor((t1 - t0) / limitNanos); } if (clickMag == 0) return; t0 = System.nanoTime(); - // these will be non-null if they should be used. - DatumRange xdrnew=null; - DatumRange ydrnew=null; - - logger.log(Level.FINEST, ":ns: {0} {1}", new Object[]{System.nanoTime() - tbirth, clickMag}); + //System.err.println(":ns: "+(System.nanoTime()-tbirth)+" "+clickMag); if (axisIsAdjustable(xAxis)) { DatumRange dr = xAxis.getDatumRange(); for (int i = 0; i < clickMag; i++) { @@ -132,41 +154,24 @@ public void mouseWheelMoved(MouseWheelEvent e) { dr = DatumRangeUtil.rescale(dr, nmin+xshift, nmax+xshift); } } - dr= maybeRound( xAxis, dr ); - - if ( ! DatumRangeUtil.isAcceptable( dr, xAxis.isLog() ) ) { - dr= null; - } - xdrnew= dr; + xAxis.setDatumRange(dr); } - if (axisIsAdjustable(yAxis)) { DatumRange dr = yAxis.getDatumRange(); for (int i = 0; i < clickMag; i++) { + if (yAxis.isLog()) { dr = DatumRangeUtil.rescaleLog(dr, nmin+yshift, nmax+yshift); } else { dr = DatumRangeUtil.rescale(dr, nmin+yshift, nmax+yshift); } } - dr= maybeRound( yAxis, dr ); - // check bounds are still finite - if ( ! DatumRangeUtil.isAcceptable(dr, yAxis.isLog() ) ) { - dr= null; - } - ydrnew= dr; + yAxis.setDatumRange(dr); } - if ( axisIsAdjustable(xAxis) && xdrnew==null ) return; - if ( axisIsAdjustable(yAxis) && ydrnew==null ) return; - - if ( axisIsAdjustable(xAxis) ) xAxis.setDatumRange(xdrnew); - if ( axisIsAdjustable(yAxis) ) yAxis.setDatumRange(ydrnew); - super.mouseWheelMoved(e); } - @Override public void mouseReleased(MouseEvent e) { super.mouseReleased(e); if (xAxis != null) { @@ -177,11 +182,26 @@ public void mouseReleased(MouseEvent e) { yAxisLock.unlock(); yAxisLock = null; } - doPan(e); + doPan(e, false); parent.getCanvas().getGlassPane().setCursor(null); } - private void doPan(final MouseEvent e) { + /** + * round to a nice boundaries. + */ + private static DatumRange doRound(DatumRange dr, DasAxis axis) { + TickVDescriptor ticks; + if (dr.getUnits() instanceof TimeLocationUnits) { + ticks = TickVDescriptor.bestTickVTime(dr.min(), dr.max(), axis.getDLength() / 2, axis.getDLength(), true); + } else if (axis.isLog()) { + ticks = TickVDescriptor.bestTickVLogNew(dr.min(), dr.max(), axis.getDLength() / 2, axis.getDLength(), true); + } else { + ticks = TickVDescriptor.bestTickVLinear(dr.min(), dr.max(), axis.getDLength() / 2, axis.getDLength(), true); + } + return ticks.enclosingRange(dr, true); + } + + private void doPan(final MouseEvent e, boolean round) { Point p2 = e.getPoint(); if (axisIsAdjustable(xAxis)) { DatumRange dr; @@ -192,7 +212,9 @@ private void doPan(final MouseEvent e) { Datum delta = xAxis.invTransform(p0.getX()).subtract(xAxis.invTransform(p2.getX())); dr = new DatumRange(xAxisRange0.min().add(delta), xAxisRange0.max().add(delta)); } - dr= maybeRound( xAxis, dr ); + if (round) { + dr = doRound(dr, xAxis); + } xAxis.setDatumRange(dr); } if (axisIsAdjustable(yAxis)) { @@ -204,18 +226,18 @@ private void doPan(final MouseEvent e) { Datum ydelta = yAxis.invTransform(p0.getY()).subtract(yAxis.invTransform(p2.getY())); dr = new DatumRange(yAxisRange0.min().add(ydelta), yAxisRange0.max().add(ydelta)); } - dr= maybeRound( yAxis, dr ); + if (round) { + dr = doRound(dr, yAxis); + } yAxis.setDatumRange(dr); } } - @Override public void mouseDragged(MouseEvent e) { super.mouseDragged(e); - doPan(e); + doPan(e, false); } - @Override public void mousePressed(MouseEvent e) { super.mousePressed(e); p0 = e.getPoint(); @@ -229,6 +251,6 @@ public void mousePressed(MouseEvent e) { yAxisLock = yAxis.mutatorLock(); yAxisLock.lock(); } - parent.getCanvas().getGlassPane().setCursor(new Cursor(Cursor.MOVE_CURSOR)); + parent.getCanvas().getGlassPane().setCursor(new Cursor(Cursor.HAND_CURSOR)); } } diff --git a/dasCore/src/org/das2/fsm/FileStorageModel.java b/dasCore/src/org/das2/fsm/FileStorageModel.java index 796477d5b..704f5a812 100644 --- a/dasCore/src/org/das2/fsm/FileStorageModel.java +++ b/dasCore/src/org/das2/fsm/FileStorageModel.java @@ -6,34 +6,23 @@ package org.das2.fsm; -import java.awt.EventQueue; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.URI; -import java.net.UnknownHostException; -import java.text.ParseException; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.*; -import org.das2.datum.CacheTag; +import org.das2.datum.CalendarTime; +import org.das2.util.filesystem.FileObject; +import org.das2.util.filesystem.FileSystem; import org.das2.datum.Datum; import org.das2.datum.DatumRange; -import org.das2.datum.DatumRangeUtil; -import org.das2.datum.TimeParser; -import org.das2.datum.TimeUtil.TimeStruct; +import org.das2.datum.TimeUtil; import org.das2.datum.Units; -import org.das2.util.LoggerManager; -import org.das2.util.filesystem.FileObject; -import org.das2.util.filesystem.FileSystem; -import org.das2.util.filesystem.FileSystemUtil; -import org.das2.util.filesystem.LocalFileSystem; -import org.das2.util.filesystem.WebFileSystem; -import org.das2.util.monitor.NullProgressMonitor; +import org.das2.util.DasExceptionHandler; import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.monitor.NullProgressMonitor; import org.das2.util.monitor.SubTaskMonitor; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.text.*; +import java.util.*; +import java.util.regex.*; /** * Represents a method for storing data sets in a set of files by time. The @@ -41,252 +30,299 @@ * interpreted as a time digit. The model can then be used to provide the set * of files that cover a time range, etc. * - * This new implementation uses a TimeParser object to more quickly process - * file names. - * * @author Jeremy */ public class FileStorageModel { - + private Pattern pattern; + private Pattern absPattern; private String regex; - private Pattern gzpattern; - + + private FieldHandler[] fieldHandlers; + + private CalendarTime.Step timeWidth; /* in TimeUtil enum */ + private int timeWidthMultiplier; /* 7 days */ + private Datum timePhase= null; /* a file boundary */ + + private boolean[] copyToEndTime; /* indexed by TimeUtil enum */ + private boolean useEndTime; FileStorageModel parent; FileSystem root; - - TimeParser timeParser; - - String template; - - static final Logger logger= LoggerManager.getLogger("das2.system.fsm"); + + public static final int StartYear4=100; + public static final int StartYear2=101; + public static final int StartMonth=102; + public static final int StartMonthName=108; + public static final int StartDay=103; + public static final int StartDoy=104; + public static final int StartHour=105; + public static final int StartMinute=106; + public static final int StartSecond=107; + + public static final int EndYear4=200; + public static final int EndYear2=201; + public static final int EndMonth=202; + public static final int EndMonthName=208; + public static final int EndDay=203; + public static final int EndDoy=204; + public static final int EndHour=205; + public static final int EndMinute=206; + public static final int EndSecond=207; + + public static final int Ignore=300; HashMap fileNameMap=null; - private boolean allowGz= true; // if true, the getFile can use a .gz version to retrieve a file. - - List oldVersions= new ArrayList(); - - /** - * Versioning types - */ - static enum VersioningType { - none(null), - /** - * simple floating point numeric comparisons. - */ - numeric( new Comparator() { // 4.10 > 4.01 - @Override - public int compare(Object o1, Object o2) { - Double d1= Double.parseDouble((String)o1); - Double d2= Double.parseDouble((String)o2); - return d1.compareTo(d2); - } - } ), - /** - * comparison by lexical sort v2013a>v2012b. - */ - alphanumeric(new Comparator() { // a001 - @Override - public int compare(Object o1, Object o2) { - return ((String)o1).compareTo((String)o2); - } - } ), - /** - * comparison of numbers split by decimal points and dashes, so 1.20 > 1.3. - */ - numericSplit( new Comparator() { // 4.3.23 // 1.1.3-01 for RBSP (rbspice lev-2 isrhelt) - @Override - public int compare(Object o1, Object o2) { - String[] ss1= o1.toString().split("[\\.-]",-2); - String[] ss2= o2.toString().split("[\\.-]",-2); - int n= Math.min( ss1.length, ss2.length ); - for ( int i=0; i 3.2) - } - }); - - Comparator comp; - VersioningType( Comparator comp ) { - this.comp= comp; - } - }; - VersioningType versioningType; - String versionGe= null; // the version must be greater than or equal to this if non-null. - String versionLt= null; // the version must be less than this if non-null. - - /** - * return the filesystem used to implement this. - * @return filesystem - */ - public FileSystem getFileSystem() { - return this.root; + /* need to map back to TimeUtil's enums, note that we have an extra for the 2 digit year */ + private CalendarTime.Step toTimeUtilEnum( int i ) { + if( i<100 || i > 300 ) + throw new IllegalArgumentException( "enumeration is not of the correct type"); + + switch(i % 100){ + case 0: + case 1: return CalendarTime.Step.YEAR; + case 2: return CalendarTime.Step.MONTH; + // Day handled below + case 5: return CalendarTime.Step.HOUR; + case 6: return CalendarTime.Step.MINUTE; + case 7: + case 8: return CalendarTime.Step.SECOND; + } + return CalendarTime.Step.DAY; } - - /** - * return a random file from the FSM, which can be used to represent a typical file. For - * example, we need to look at metadata to see what is available. - * @param monitor progress monitor in case a file must be downloaded. - * @return a reference to the file within the FileSystem, or null if one is not found. - * @throws IOException - */ - public String getRepresentativeFile( ProgressMonitor monitor ) throws IOException { - return getRepresentativeFile( monitor, null ); + + //TODO: add + // public string format( DatumRange dr ); + // + public interface FieldHandler { + public void handle( String s, CalendarTime ts1, CalendarTime ts2 ); + public String format( CalendarTime ts1, CalendarTime ts2 ); } - /** - * return a random file from the FSM, which can be used to represent a typical file. For - * example, we need to look at metadata to see what is available. - * @param monitor progress monitor in case a file must be downloaded. - * @param childRegex the parent must contain a file/folder matching childRegex - * @return a reference to the file within the FileSystem, or null if one is not found. - * @throws IOException - */ - public String getRepresentativeFile( ProgressMonitor monitor, String childRegex ) throws IOException { - return getRepresentativeFile( monitor, childRegex, null ); + public static abstract class IntegerFieldHandler implements FieldHandler { + public void handle( String s, CalendarTime ts1, CalendarTime ts2 ) { + handleInt( Integer.parseInt(s), ts1, ts2 ); + } + abstract void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ); + public abstract String format( CalendarTime ts1, CalendarTime ts2 ); } - /** - * Return a random file from the FSM, which can be used to represent a typical file. For - * example, we need to look at metadata to see what is available. This is introduced - * to support discovery, where we just need one file to - * get started. Before, there was code that would list all files, then use - * just the first one. This may return a skeleton file, but getFileFor() must - * return a result. - * This implementation does the same as getNames(), but stops after finding a file. - * @param monitor progress monitor in case a file must be downloaded. - * @param childRegex the parent must contain a file/folder matching childRegex - * @param range hint at the range where we are looking. - * @return null if no file is found - * @throws java.io.IOException if the file cannot be downloaded. - */ - public String getRepresentativeFile( ProgressMonitor monitor, String childRegex, DatumRange range ) throws IOException { - String listRegex; - - FileSystem[] fileSystems; - String[] names; - String parentRegex=null; - DatumRange range1; // the range from the parent we are looking for. This is to limit searches... + + static final NumberFormat nf4= new DecimalFormat("0000"); + static final NumberFormat nf3= new DecimalFormat("000"); + static final NumberFormat nf2= new DecimalFormat("00"); + + private final static String[] mons= new String [] { + "", "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec" } ; + private static final FieldHandler StartMonthNameHandler= new FieldHandler() { + public void handle( String s, CalendarTime ts1, CalendarTime ts2 ) { + try { + ts1.setMonth(TimeUtil.monthNumber( s )); + } catch ( ParseException e ) { + DasExceptionHandler.handle(e); + } + } + public String format(CalendarTime ts1, CalendarTime ts2) { + return mons[ ts1.month() ]; + } + }; + + private static final FieldHandler EndMonthNameHandler= new FieldHandler() { + public void handle( String s, CalendarTime ts1, CalendarTime ts2 ) { + try { + ts2.setMonth( TimeUtil.monthNumber( s ) ); + } catch ( ParseException e ) { + DasExceptionHandler.handle(e); + } + } + public String format(CalendarTime ts1, CalendarTime ts2) { + return mons[ ts2.month() ]; + } - if ( EventQueue.isDispatchThread() ) { - logger.info("FileSystem use on the GUI event thread will often cause problems."); - new Exception("FileSystem uses event thread stack trace").printStackTrace(); + }; + + + private static final FieldHandler StartYear4Handler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setYear(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf4.format(ts1.year()); } + }; + + private static final FieldHandler StartYear2Handler= new IntegerFieldHandler() { + @Override + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setYear( i<58 ? i+2000 : i+1900); } + @Override + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.year() % 100 ); } + }; + + private static final FieldHandler StartMonthHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setMonth(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.month() ); } + }; + + private static final FieldHandler StartDayHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setDay(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.day() ); } + }; + private static final FieldHandler StartDoyHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setDayOfYear(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf3.format( ts1.dayOfYear() ); } + }; + private static final FieldHandler StartHourHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setHour(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.hour() ); } + }; + private static final FieldHandler StartMinuteHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setMinute(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.minute() ); } + }; + private static final FieldHandler StartSecondHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts1.setSecond(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts1.second() ); } + }; + + private static final FieldHandler EndYear4Handler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setYear(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf4.format( ts2.year() ); } + }; + private static final FieldHandler EndYear2Handler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setYear( i<58 ? i+2000 : i+1900); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.year() ); } + }; + + private static final FieldHandler EndMonthHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setMonth(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.month() ); } + }; + private static final FieldHandler EndDayHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setDay(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.day() ); } + }; + private static final FieldHandler EndDoyHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setDayOfYear(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf3.format( ts2.dayOfYear() ); } + }; + private static final FieldHandler EndHourHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setHour(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.hour() ); } + }; + private static final FieldHandler EndMinuteHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setMinute(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.minute() ); } + }; + private static final FieldHandler EndSecondHandler= new IntegerFieldHandler() { + public void handleInt( int i, CalendarTime ts1, CalendarTime ts2 ) { ts2.setSecond(i); } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return nf2.format( ts2.second() ); } + }; + + private static final FieldHandler IgnoreHandler= new FieldHandler() { + public void handle( String s, CalendarTime ts1, CalendarTime ts2 ) { } + public String format( CalendarTime ts1, CalendarTime ts2 ) { return "*"; } + }; + + + private void checkArgs( String regex, int[] digitList ) { + int startLsd=0, endLsd=0; + int[] startDigits= new int[7]; + int[] endDigits= new int[7]; + copyToEndTime= new boolean[8]; /* indexed by TimeUtil enum */ + int startBase=100; + int endBase=200; + int ignoreBase=300; + for ( int i=0; istartLsd ) startLsd= 103; + } else if ( digitList[i]==EndDoy ) { + endDigits[1]=1; endDigits[2]=1; + if ( 203>endLsd ) endLsd= 203; + } else if ( digitList[i]>=startBase && digitList[i]startLsd ) startLsd= digitList[i]; + } else if ( digitList[i]>=endBase && digitList[i]endLsd ) endLsd= digitList[i]; } - listRegex= regex.substring( parentRegex.length()+1 ); - } else { - fileSystems= new FileSystem[] { root }; - names= new String[] {""}; - listRegex= regex; } + if ( startDigits[StartYear2-startBase]==1 ) startDigits[StartYear4-startBase]=1; + if ( startDigits[StartYear4-startBase]==1 ) startDigits[StartYear2-startBase]=1; - String result= null; - - while ( result==null ) { - for ( int i=fileSystems.length-1; result==null && i>=0; i-- ) { - String[] files1= fileSystems[i].listDirectory( "/", listRegex, monitor.getSubtaskMonitor("create") ); - int j= files1.length-1; - while ( j>=0 && result==null ) { - String ff= names[i].equals("") ? files1[ j ] : names[i]+"/"+files1[ j ]; - if ( ff.endsWith("/") ) ff=ff.substring(0,ff.length()-1); - //try { - HashMap extra= new HashMap(); - DatumRange tr= getDatumRangeFor( ff, extra ); - boolean versionOk= true; - if ( versionGe!=null && versioningType.comp.compare( extra.get("v"), versionGe )<0 ) versionOk=false; - if ( versionLt!=null && versioningType.comp.compare( extra.get("v"), versionLt )>=0 ) versionOk=false; - if ( versionOk && timeParser.getValidRange().contains( tr ) && ( range==null || range.intersects(tr) ) ) { - if ( childRegex!=null ) { - String[] kids= fileSystems[i].listDirectory( files1[ j ],childRegex, monitor.getSubtaskMonitor("list directory") ); - if ( kids.length>0 ) { - result= ff; - } - } else { - result= ff; - } - } - //} catch ( ParseException ex ) { - // - //} - if ( result==null ) j--; - } - } - - if ( allowGz ) { - if ( result==null) { - for ( int i=fileSystems.length-1; result==null && i>=0; i-- ) { - String[] files1= fileSystems[i].listDirectory( "/", listRegex + ".gz" ); - if ( files1.length>0 ) { - int last= files1.length-1; - String ff= names[i].equals("") ? files1[ last ] : names[i]+"/"+files1[ last ]; - if ( ff.endsWith("/") ) ff=ff.substring(0,ff.length()-1); - result= ff.substring( 0,ff.length()-3 ); - } - } - } + if ( startDigits[StartDoy-startBase]==1 ) { + startDigits[StartMonth-startBase]=1; + startDigits[StartDay-startBase]=1; + } + if ( endDigits[EndYear2-endBase]==1 ) endDigits[EndYear4-endBase]=1; + if ( startDigits[EndYear4-endBase]==1 ) startDigits[EndYear2-endBase]=1; + if ( endDigits[EndDoy-endBase]==1 ) { + endDigits[EndMonth-endBase]=1; + endDigits[EndDay-endBase]=1; + } + for ( int i=0; i0 && startDigits[i]==1 && startDigits[i-1]!=1 ) { + throw new IllegalArgumentException( "more significant digits missing in startTime"); } - - if ( result==null ) { - if ( parent==null ) { - return null; - } else { // fall back to old code that would list everything. - logger.fine("fall back to old code that would list everything"); - range1= parent.getRangeFor(names[0]); - range1= range1.previous(); - if ( range!=null && !range.intersects(range1) ) { - return null; - } - String one= parent.getRepresentativeFile( monitor.getSubtaskMonitor("getRepresentativeFile"),regex.substring(parentRegex.length()+1), range1 ); - if ( one==null ) return null; - names= new String[] { one }; //parent.getNamesFor(null); - fileSystems= new FileSystem[names.length]; - for ( int i=0; i0 && startDigits[i]==0 && startDigits[i-1]==1 ) { + timeWidth= toTimeUtilEnum( startLsd ); + timeWidthMultiplier= 1; } } + + boolean canUse= true; + for ( int i=startLsd-startBase; i>=0; i-- ) { + if ( endDigits[i]==0 ) canUse=false; + if ( !canUse ) endDigits[i]= 0; + } + + for ( int i=0; i=100 && digitList[i]<200 ) { + fieldHandlerList.add(i,startHandlers[digitList[i]-100]); + } else if (digitList[i]>=200 && digitList[i]<300 ) { + fieldHandlerList.add(i,endHandlers[digitList[i]-200]); + } else if ( digitList[i]==300 ) { + fieldHandlerList.add(i,IgnoreHandler); + } else { + throw new IllegalArgumentException("unknown field handler: "+digitList[i]); + } + } + return (FieldHandler[])fieldHandlerList.toArray( new FieldHandler[fieldHandlerList.size()] ); } - /** + + /* * extract time range for file or directory from its name. * The least significant time digit is considered to be the implicitTimeWidth, * and if the width is not stated explicitly, it will be used. When @@ -294,458 +330,241 @@ public void setContext( DatumRange trdr ) { * is considered be the end time. * * .../FULL1/T8709_12/T871118.DAT - *'.../FULL1/T'YYMM_MM/TYYMMDD'.DAT' TODO: verify this. - * .../FULL1/T$y$m_$m/T$y$m$d.dat - * - * @param filename the filename, within the filesystem. - * @param extra an empty map where extra fields (such as version) are put. - * @throws IllegalArgumentException when a filename does not match the model specification. - */ - private synchronized DatumRange getDatumRangeFor( String filename, Map extra ) { - try { - extra.clear(); - if ( pattern.matcher(filename).matches() ) { - timeParser.parse( filename, extra ); - return timeParser.getTimeRange(); - } else { - if ( gzpattern!=null && gzpattern.matcher(filename).matches() ) { - timeParser.parse( filename.substring(0,filename.length()-3), extra ); - return timeParser.getTimeRange(); - } else { - throw new IllegalArgumentException( "file name ("+filename+") doesn't match model specification ("+regex+")"); - } - } - } catch ( ParseException e ) { - IllegalArgumentException e2=new IllegalArgumentException( "file name ("+filename+") doesn't match model specification ("+regex+"), parse error in field",e); - throw e2; - } catch ( NumberFormatException e ) { - IllegalArgumentException e2=new IllegalArgumentException( "file name ("+filename+") doesn't match model specification ("+regex+"), parse error in field",e); - throw e2; - } - } - - /** - * return a filename that would intersect the range. Note this file - * may not actually exist. This may be used to quantize the range. - * The template may not contain versions. - * @param start - * @param end - * @return - */ - public String getFilenameFor( Datum start, Datum end ) { - return timeParser.format( start, end ); - } - - /** - * generate the names of the files that would cover this range. This was - * taken from Autoplot's org.virbo.jythonsupport.Util. - * TODO: versioning, etc. - * @param range the time range to cover. - * @return the string names, each in the context of the filesystem. + *'.../FULL1/T'YYMM_MM/TYYMMDD'.DAT' */ - public String[] generateNamesFor( DatumRange range ) { - String sstart; - TimeParser tp= timeParser; - try { - sstart= tp.format( range.min(), null ); - } catch ( Exception ex ) { // orbit files have limited range - DatumRange dr= tp.getValidRange(); - DatumRange dd= DatumRangeUtil.sloppyIntersection(range, dr); - if ( dd.width().value()==0 ) { - return new String[0]; // no intersection - } - sstart= tp.format( dd.min(), null ); - } - + private DatumRange getDatumRangeFor( String filename ) { - try { - tp.parse(sstart); - } catch ( ParseException ex ) { - throw new RuntimeException(ex); - } - DatumRange curr= tp.getTimeRange(); - - int countLimit= 1000000; - int approxCount= (int)( 1.01 * range.width().divide(curr.width()).value() ); // extra 1% for good measure. - - if ( approxCount>countLimit*1.03 ) { - throw new IllegalArgumentException("too many intervals would be created, this is limited to about 1000000 intervals."); + if ( fieldHandlers.length==0 ) { + // e.g. FULL1 doesn't constrain time + return DatumRange.newDatumRange( -1e30, 1e30, Units.mj1958 ); } - List result= new ArrayList( approxCount ); + CalendarTime ts1= new CalendarTime(new int[]{1,1,1,0,0,0,0}); - if ( !range.intersects(curr) ) { // Sebastian has a strange case that failed, see - curr= curr.next(); - } + CalendarTime ts2= new CalendarTime(); + if ( File.separatorChar=='\\' ) filename= filename.replaceAll("\\\\", "/"); - while ( range.intersects(curr) ) { - String scurr= tp.format( curr.min(), curr.max() ); - result.add( scurr ); - DatumRange oldCurr= curr; - curr= curr.next(); - if ( oldCurr.equals(curr) ) { // orbits return next() that is this at the ends. - break; + Matcher m= pattern.matcher(filename); + if ( m.matches() ) { + for ( int i=0; i>{0}<<\n>>{1}<<", new Object[]{tf1, tf2,this.timeParser}); - TimeParser tp1= timeParser.parse(tf1); - logger.log( Level.WARNING, "tp1 start {0}", tp1.getTime(Units.us2000) ); - logger.log( Level.WARNING, "tp1 end {0}", tp1.getEndTime(Units.us2000) ); - logger.log( Level.WARNING, "tp1 tr {0}", tp1.getTimeRange() ); - TimeParser tp2= timeParser.parse(tf2); - logger.log( Level.WARNING, "tp2 start {0}", tp2.getTime(Units.us2000) ); - logger.log( Level.WARNING, "tp2 end {0}", tp2.getEndTime(Units.us2000) ); - logger.log( Level.WARNING, "tp2 tr {0}", tp2.getTimeRange() ); - throw ex; - } + if ( copyToEndTime[1] ) ts2.setYear(ts1.year()); + if ( copyToEndTime[2] ) ts2.setMonth(ts1.month()); + if ( copyToEndTime[3] ) ts2.setDay(ts1.day()); + if ( copyToEndTime[4] ) ts2.setDayOfYear( ts1.dayOfYear()); + if ( copyToEndTime[5] ) ts2.setHour(ts1.hour()); + if ( copyToEndTime[6] ) ts2.setMinute(ts1.minute()); + if ( copyToEndTime[7] ) ts2.setSecond(ts1.second()); - if ( dr2.min().equals(timeRange.max() ) ) { - return DatumRangeUtil.union( dr1, dr2.min() ); - } else { - return DatumRangeUtil.union( dr1, dr2 ); + Datum s1= ts1.toDatum(); + Datum s2= TimeUtil.next( timeWidth, ts2.toDatum() ); + for ( int ii=1; ii list= new ArrayList(); - List versionList= new ArrayList(); - List rangeList= new ArrayList(); - - monitor.setTaskSize( fileSystems.length*10 ); - monitor.started(); - Map extra= new HashMap(); - + List list= new ArrayList(); + + //TODO: support monitor by doing progress based on this for loop. for ( int i=0; i{1}", new Object[]{theListRegex, files1.length}); - + String[] files1= fileSystems[i].listDirectory( "/", listRegex ); for ( int j=0; j deleteRemote= new ArrayList(); - List remoteFiles= Arrays.asList( files1 ); - for ( String s: files2 ) { - if ( remoteFiles.indexOf(s)==-1 ) { - deleteRemote.add(s); - } - } - logger.log(Level.FINE, "local files that do not exist on remote: {0}", deleteRemote); - oldVersions.addAll(deleteRemote); - } - } - } - - if ( versioning && versioningType!=VersioningType.none) { - - Comparator comp= versioningType.comp; - - Map bestVersions= new HashMap(); - Map bestFiles= new HashMap(); - - for ( int j=0; j=0 ) - && ( versionLt==null || comp.compare( thss, versionLt )<0 ) ){ - bestVersions.put( key, thss ); - bestFiles.put( key,ff ); - } - } catch ( Exception ex ) { - logger.log( Level.WARNING, ex.getMessage(), ex ); - // doesn't match if comparator (e.g. version isn't a decimal number) - } - } else { - try { - if ( ( ( versionGe==null || comp.compare( thss, versionGe )>=0 ) ) - && ( versionLt==null || comp.compare( thss, versionLt )<0 ) - && comp.compare( thss, best ) > 0 ) { - bestVersions.put( key,thss ); - String rm= bestFiles.put( key,ff ); - if ( !oldVersions.contains(rm) ) { - oldVersions.add(rm); - } - } - } catch ( Exception ex ) { - logger.log( Level.WARNING, ex.getMessage(), ex ); - // doesn't match - } + DatumRange dr= getRangeFor(ff); + if ( targetRange==null || dr.intersects(targetRange) ) list.add(ff); + } catch ( IllegalArgumentException ex ) { + // not really part of model. } + } - - list= Arrays.asList( bestFiles.values().toArray( new String[ bestFiles.size()] ) ); } - + Collections.sort( list, new Comparator() { - @Override public int compare( Object o1, Object o2 ) { DatumRange dr1= getRangeFor( (String)o1 ); DatumRange dr2= getRangeFor( (String)o2 ); return dr1.compareTo( dr2 ); } } ); - - logger.log( Level.FINE, "getNamesFor {0} -> {1}", new Object[] { this.root, list.size() } ); - monitor.finished(); return (String[])list.toArray(new String[list.size()]); } - - public static CacheTag getCacheTagFor( FileStorageModel fsm, DatumRange range, String[] names ) { - Datum min= range.min(); - Datum max= range.max(); - for (String name : names) { - DatumRange r = fsm.getRangeFor(name); - min= min.gt(range.min()) ? r.min() : min; - max= max.lt(range.max()) ? r.max() : max; - } - return new CacheTag( min, max, null ); - } - public static CacheTag getCacheTagFor( FileStorageModel fsm, DatumRange range, File[] files ) { - String[] names= new String[files.length]; - for ( int i=0; i0 ) { + String ff= names[i].equals("") ? files1[0] : names[i]+"/"+files1[0]; + if ( ff.endsWith("/") ) ff=ff.substring(0,ff.length()-1); + result= ff; + } } - return getCacheTagFor( fsm, range, names ); + + return result; } public File[] getFilesFor( final DatumRange targetRange ) throws IOException { return getFilesFor( targetRange, new NullProgressMonitor() ); } - - /** - * return the best files found for the range, without progress feedback. - * @param targetRange - * @return - * @throws IOException - */ - public File[] getBestFilesFor( final DatumRange targetRange ) throws IOException { - return getBestFilesFor( targetRange, new NullProgressMonitor() ); - } - /** - * remove files that have been identified as old versions. - */ - public void cacheCleanup() { - if ( getFileSystem() instanceof LocalFileSystem ) { - logger.fine("local filesystems do not cache"); - } else { - for ( String s: oldVersions ) { - if ( getFileSystem().getFileObject(s).removeLocalFile()==false ) { - logger.log(Level.FINER, "removeLocalFile returned false: {0}", s); - } - } - } - } - - /** - * return the time range represented by this name. - * @param name like 2013-10-31 - * @return the timerange representing the day 2013-10-31 - * @throws IllegalArgumentException if the name is not part of the FileStorageModel. - */ public DatumRange getRangeFor( String name ) { - return getDatumRangeFor( name, new HashMap() ); - } - - /** - * return the field value for the given name. For example, if the spec - * is $Y/$m/$d/$Y$m$d_v$(v,sep).dat and the name matched is 2014/04/04/20140404_v2.3.dat - * then calling this for the field "v" would result in "2.3" This should not - * be used to retrieve fields that are components of the time range, such as - * $Y or $m. - * @param field field, for example "v" - * @param name name, for example 2014/04/04/20140404_v2.3.dat - * @return the field value, for example, "2.3" when the spec is $Y/$m/$d/$Y$m$d_v$v.dat - */ - public String getField( String field, String name ) { - HashMap hh= new HashMap(); - getDatumRangeFor( name, hh ); - if ( hh.containsKey(field) ) { - return hh.get(field); - } else { - throw new IllegalArgumentException("field is not in template: "+field); - } + return getDatumRangeFor( name ); } /** - * return true if the file came (or could come) from this FileStorageModel. - * @param file the file - * @return true if the file came (or could come) from this FileStorageModel. + * returns true if the file came (or could come) from this FileStorageModel. */ public boolean containsFile( File file ) { + maybeCreateFileNameMap(); if ( !fileNameMap.containsKey(file) ) { return false; } else { + String result= (String)fileNameMap.get(file); String name= getNameFor( file ); Matcher m= pattern.matcher( name ); return m.matches(); } } - + /** - * Provides a way to recover the model name of a file. The returned File from getFilesFor can be anywhere, - * so it would be good to provide a way to get it back into a FSM name. For example, a filesystem - * might download the remote file to a cache directory, which is the File that is provided to the - * client, sometimes the client will need to recover the name of the corresponding FileObject, so - * this maps the File back to the name. - * - * @param file the file - * @return the canonical name of the file. + * Need a way to recover the model name of a file. The returned File from getFilesFor can be anywhere, + * so it would be good to provide a way to get it back into a FSM name. */ public String getNameFor( File file ) { String result= (String)fileNameMap.get(file); @@ -755,483 +574,201 @@ public String getNameFor( File file ) { return result; } } - - /** - * check to see if "NAME.gz" exists - * @param name name of uncompressed file - * @param mon progress monitor - * @return null or the uncompressed file. - * @throws IOException - */ - private File maybeGetGzFile( String name, ProgressMonitor mon) throws IOException { - File f0 = null; - FileObject oz = root.getFileObject(name + ".gz"); - if (oz.exists()) { - File fz = oz.getFile(mon); - String sfz = fz.getPath().substring(0, fz.getPath().length() - 3); - f0 = new File(sfz); - FileSystemUtil.unzip(fz, f0); - if ( !f0.setLastModified(fz.lastModified()) ) { - throw new IllegalArgumentException("failed to set last modified"); - } - } - return f0; - } - - /** - * download the file for the given name within the filesystem. - * @param name the name within the filesystem. - * @return null or a local file which can be opened. - * @throws IOException - */ - public File getFileFor( String name ) throws IOException { - File[] ff= getFilesFor( new String[] { name }, new NullProgressMonitor() ); - if ( ff.length>0 ) { - return ff[0]; - } else { - return null; - } - } - /** - * download the file for the given name within the filesystem. - * @param name the name within the filesystem. - * @param monitor monitor for the download. - * @return null or a local file which can be opened. - * @throws IOException - */ - public File getFileFor( String name, ProgressMonitor monitor ) throws IOException { - File[] ff= getFilesFor( new String[] { name }, monitor ); - if ( ff.length>0 ) { - return ff[0]; - } else { - return null; - } + private synchronized void maybeCreateFileNameMap() { + if ( fileNameMap==null ) fileNameMap= new HashMap(); } /** - * download the files for each of the given names within the filesystem. - * @param names array of names within the filesystem - * @param monitor monitor for the downloads. - * @return local files that can be opened. - * @throws java.io.IOException during the transfer + * retrieve the file for the name. + * @throws IOException if the file cannot be transferred. */ - public File[] getFilesFor( String [] names, ProgressMonitor monitor ) throws IOException { - File[] files= new File[names.length]; - - if ( fileNameMap==null ) fileNameMap= new HashMap(); - - int numwarn= 0; + public File getFileFor( String name, ProgressMonitor monitor ) throws FileNotFoundException, IOException { + FileObject o= root.getFileObject( name ); + File file= o.getFile( monitor ); - if ( names.length>0 ) monitor.setTaskSize( names.length * 10 ); - monitor.started(); - for ( int i=0; i result= new ArrayList(files.length); - int i=0; - for (File file : files) { - if (file != null) { - result.add(file); - i++; - } - } - return result.toArray( new File[i] ); + fileNameMap.put( file, name ); + return file; } /** - * Download the files within the range. - * This might catch a bad link where getNamesFor does not. - * @param targetRange range limit, or null if no constraint used here. - * @param monitor the monitor - * @return a list of files that can be opened. - * @throws java.io.IOException + * returns a list of files that can be used */ public File[] getFilesFor( final DatumRange targetRange, ProgressMonitor monitor ) throws IOException { String[] names= getNamesFor( targetRange ); - File[] ff= getFilesFor( names, monitor ); - return ff; - } - - /** - * Get the files for the range, using versioning info ($v,etc). - * @param targetRange range limit, or null if no constraint used here. - * @param monitor the monitor - * @return a list of files that can be opened. - * @throws IOException - */ - public File[] getBestFilesFor( final DatumRange targetRange, ProgressMonitor monitor ) throws IOException { + File[] files= new File[names.length]; - if ( monitor==null ) monitor= new NullProgressMonitor(); + maybeCreateFileNameMap(); - String[] names= getNamesFor( targetRange, true, monitor.getSubtaskMonitor("get names") ); - File[] files= new File[names.length]; - - if ( fileNameMap==null ) fileNameMap= new HashMap(); - if ( names.length>0 ) monitor.setTaskSize( names.length * 10 ); - monitor.started(); for ( int i=0; i1 ) { - dirRegex= new StringBuilder( s[0] ); - for ( int i=1; i-1 && result.indexOf("%")>i ) { - System.err.println("each folder of template must have fields marked by $ or %: "+ result.substring(0,i) ); - } - if ( result.startsWith("/") ) { - result= result.substring(1); - } - return result; - } - - /** - * hide the contents of parameters as in - * product_%(o,id=ftp://stevens.lanl.gov/pub/projects/rbsp/autoplot/orbits/rbspa_pp).png --> - * product_%(______________________________________________________________________).png - * @param template - * @return - */ - private static String hideParams( String template ) { - StringBuilder result= new StringBuilder(); - boolean withinArg= false; - for ( int i=0; i - *
    http://autoplot.org/data/
    -     *C1_CP_EDI_EGD__$Y$m$d_V$v.cef
    and - * http://emfisis.physics.uiowa.edu/Flight/RBSP-A/Quick-Look/$Y/$m/$d/rbsp-a_magnetometer_4sec-gsm_emfisis-Quick-Look_$Y$m$d_v$(v,sep).cdf:
    - *
    http://emfisis.physics.uiowa.edu/Flight/RBSP-A/Quick-Look/
    -     *$Y/$m/$d/rbsp-a_magnetometer_4sec-gsm_emfisis-Quick-Look_$Y$m$d_v$(v,sep).cdf
    - *
    - * - *

    - * This new version uses regexs and is more complete than versions found in Autoplot, and they should - * eventually use this instead. Note the Autoplot one returns the index of the last /, whereas this - * returns that index plus one. - *

    - * - *

    Taken from Autoplot's AggregatingDataSourceFactory, where Autoplot just has a URI and needs to get a file list. - * See also org/autoplot/pngwalk/WalkUtil.java splitIndex, which also allows wildcards like *.

    - * @param surl a string like http://autoplot.org/data/C1_CP_EDI_EGD__$Y$m$d_V$v.cef - * @return an integer indicating the split index, so that surl.substring(0,i) includes the slash. - */ - public static int splitIndex(String surl) { - String regex= "([\\$\\%][yYxv\\(\\{])"; - Matcher m= Pattern.compile(regex).matcher(surl); - if ( m.find() ) { - int i= m.start(); - i = surl.lastIndexOf('/', i); - return i+1; + parentModel= create( root, parentRegex, parentDigitList ); } else { - return -1; + parentModel= null; } + return FileStorageModel.create( parentModel, root, regex, digitList ); } - /** - * creates a FileStorageModel for the given template, which uses: - *
    %Y-%m-%dT%H:%M:%S.%{milli}Z";
    +    /**     
          *    %Y  4-digit year
    +     *    %y  2-digit year
          *    %m  2-digit month
          *    %d  2-digit day of month
          *    %j  3-digit day of year
    -     *    %H  2-digit Hour
    -     *    %M  2-digit Minute
    -     *    %S  2-digit second
    -     *    %v  best version by number  Also %(v,sep) for 4.3.2  or %(v,alpha)
    -     *    %{milli}  3-digit milliseconds 
    - * product_$(o,id=ftp://stevens.lanl.gov/pub/projects/rbsp/autoplot/orbits/rbspa_pp).png - * @param root FileSystem source of the files. - * @param template describes how filenames are constructed. This is converted to a regular expression and may contain regex elements without groups. The - * string may contain $ instead of percents as long as there are no percents in the string. - * - * @return a newly-created FileStorageModel. + * %H 2-digit hour + * %M 2-digit minute + * %b month name */ public static FileStorageModel create( FileSystem root, String template ) { - template= makeCanonical(template); - String templatebr= hideParams( template ); - int i= templatebr.lastIndexOf("/"); - - if ( template.contains("%") && !template.contains("$") ) { //TODO: makeCanonical should do this. - template= template.replaceAll("\\%", "\\$"); - templatebr= templatebr.replaceAll("\\%", "\\$"); - } + if ( template.startsWith("/") ) template= template.substring(1); + String[] s= template.split("%"); + char[] valid_formatCodes= new char[] { 'Y', 'y', 'j', 'm', 'd', 'H', 'M', 'S', 'v', 'V', 'x', 'b' }; + String[] formatName= new String[] { "Year", "2-digit-year", "day-of-year", "month", "day", "Hour", "Minute", "Second", "version", "Version", "date", "month-name" }; + int[] formatCode_lengths= new int[] { 4, 2, 3, 2, 2, 2, 2, 2, -1, -1, -1, -1 }; + int[] formatDigit= new int[] { StartYear4, StartYear2, StartDoy, StartMonth, StartDay, StartHour, StartMinute, StartSecond, Ignore, Ignore, Ignore, StartMonthName }; - int i2= templatebr.lastIndexOf("$",i); - if ( i2 != -1 ) { - String parentTemplate= template.substring(0,i); - FileStorageModel parentFSM= FileStorageModel.create( root, parentTemplate ); - return new FileStorageModel( parentFSM, root, template ); - } else { - return new FileStorageModel( null, root, template ); - } - } - - - /** - * creates a FileStorageModel for the given template, but with a custom FieldHandler and - * field. - * - * @param root FileSystem source of the files. - * @param template describes how filenames are constructed. This is converted to a regular expression and may contain regex elements without groups. The - * string may contain $ instead of percents as long as there are no percents in the string. - * @param fieldName custom field name - * @param fieldHandler TimeParser.FieldHandler to call with the field contents. - * @return a newly-created FileStorageModel. - */ - public static FileStorageModel create( FileSystem root, String template, String fieldName, TimeParser.FieldHandler fieldHandler ) { - template= makeCanonical(template); - String templatebr= hideParams( template ); - int i= templatebr.lastIndexOf("/"); - - if ( template.contains("%") && !template.contains("$") ) { - template= template.replaceAll("\\%", "\\$"); - templatebr= templatebr.replaceAll("\\%", "\\$"); - } + int n= s.length; - int i2= templatebr.lastIndexOf("$",i); - if ( i2 != -1 ) { - String parentTemplate= template.substring(0,i); - FileStorageModel parentFSM= FileStorageModel.create( root, parentTemplate, fieldName, fieldHandler ); - return new FileStorageModel( parentFSM, root, template, fieldName, fieldHandler ); - } else { - return new FileStorageModel( null, root, template, fieldName, fieldHandler ); - } - } - - private FileStorageModel( FileStorageModel parent, FileSystem root, String template, String fieldName, TimeParser.FieldHandler fieldHandler, Object ... moreHandler ) { - this.root= root; - this.parent= parent; - this.template= template.replaceAll("\\+", "\\\\+"); + StringBuffer regex= new StringBuffer(100); + regex.append( s[0] ); - if ( template.startsWith("/") ) { // clean up double slashes immediately. (/home/jbf//data/) - template= template.substring(1); - } - - String f="v"; - versioningType= VersioningType.none; + int[] positions= new int[20]; + positions[0]= 0; + int[] dateFormat= new int[n-1]; - TimeParser.FieldHandler vh= new TimeParser.FieldHandler() { - @Override - public String configure( Map args ) { - String sep= args.get( "sep" ); - if ( sep==null && args.containsKey("dotnotation")) { - sep= "T"; - } - String alpha= args.get( "alpha" ); - if ( alpha==null && args.containsKey("alphanumeric") ) { - alpha="T"; - } - String type= args.get("type"); - if ( type!=null ) { - if ( type.equals("sep") || type.equals("dotnotation") ) { - sep= "T"; - } else if (type.equals("alpha") || type.equals("alphanumeric") ) { - alpha="T"; - } - } - if ( args.get("gt")!=null ) { - throw new IllegalArgumentException("gt specified but not supported: must be ge or lt"); - } - if ( args.get("le")!=null ) { - throw new IllegalArgumentException("le specified but not supported: must be ge or lt"); - } - String ge= args.get("ge"); - if ( ge!=null ) { - versionGe= ge; - } - String lt= args.get("lt"); - if ( lt!=null ) { - versionLt= lt; - } - if ( alpha!=null ) { - if ( sep!=null ) { - return "alpha with split not supported"; - } else { - versioningType= VersioningType.alphanumeric; - } - } else { - if ( sep!=null ) { - versioningType= VersioningType.numericSplit; - } else { - versioningType= VersioningType.numeric; - } - } - return null; - } - - @Override - public void parse( String fieldContent, TimeStruct startTime, TimeStruct timeWidth, Map extra ) { - String v= extra.get("v"); - if ( v!=null ) { - versioningType= VersioningType.numericSplit; - fieldContent= v+"."+fieldContent; // Support $v.$v.$v - } - extra.put( "v", fieldContent ); - } - - @Override - public String getRegex() { - return ".*"; - } - - @Override - public String format( TimeStruct startTime, TimeStruct timeWidth, int length, Map extra ) { - return extra.get("v"); //TODO: length - } - }; - - - if ( fieldName==null ) { - this.timeParser= TimeParser.create( template, f, vh ); - } else { - if ( moreHandler==null || moreHandler.length==0 ) { //TODO: check if it can ever be null. - this.timeParser= TimeParser.create( template, fieldName, fieldHandler, f, vh ); + int [] p= new int[20]; // position of each % specifier + p[0]= 0; + p[1]= s[0].length(); + + boolean versioning=false; + + for ( int i= 1; i args ) { return null; } - - public void parse(String fieldContent, TimeUtil.TimeStruct startTime, TimeUtil.TimeStruct timeWidth, Map extra ) { + public void handleValue(String fieldContent, CalendarTime startTime, int[] timeWidth) { int i= Integer.decode("0x"+fieldContent).intValue(); - double seconds= 86400 * i / 256; - startTime.seconds= seconds; - timeWidth.seconds= 86400 / 256.; - } - - public String getRegex() { - return ".."; - } - - public String format(TimeStruct startTime, TimeStruct timeWidth, int length, Map extra) throws IllegalArgumentException { - if ( length!=2 ) { - throw new IllegalArgumentException("length must be 2"); - } - double seconds= startTime.seconds; - if ( seconds<0 || seconds>=86400 ) throw new IllegalArgumentException("seconds of startTime must be between 0 and 86400"); - int i= (int)( seconds * 256 / 86400 ); - String r= String.format( "%2X", i ); - return r; + double seconds = (86400 * i) / 256; + startTime.setNanoSecond( Math.round(seconds * 1000000000.0) ); + startTime = startTime.step(CalendarTime.Step.MILLISEC, (int)(86400000 / 256.)); } - }; int cl= 4; - FileStorageModel fsm= FileStorageModel.create( fs, "%y%2m/%y%2m%2d%2{hex}\\..C"+cl, "hex", hexHandler ); + FileStorageModelNew fsm= FileStorageModelNew.create(fs, "%y%2m/%y%2m%2d%2{hex}\\..C"+cl, + "hex", hexHandler ); } } diff --git a/dasCore/src/org/das2/fsm/FileStorageModelNew.java b/dasCore/src/org/das2/fsm/FileStorageModelNew.java index 1762b2726..b44d372c0 100644 --- a/dasCore/src/org/das2/fsm/FileStorageModelNew.java +++ b/dasCore/src/org/das2/fsm/FileStorageModelNew.java @@ -1,45 +1,336 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * FileStorageModel.java + * + * Created on March 31, 2004, 9:52 AM */ package org.das2.fsm; -import java.io.File; -import org.das2.datum.CacheTag; import org.das2.datum.DatumRange; -import org.das2.datum.TimeParser; +import org.das2.datum.Datum; +import org.das2.util.filesystem.FileObject; import org.das2.util.filesystem.FileSystem; +import org.das2.dataset.CacheTag; +import org.das2.system.DasLogger; +import org.das2.util.monitor.ProgressMonitor; +import org.das2.util.monitor.NullProgressMonitor; +import org.das2.util.monitor.SubTaskMonitor; +import org.das2.util.TimeParser; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.text.ParseException; +import java.util.*; +import java.util.logging.Logger; +import java.util.regex.*; /** - * Preserve the old name for Jython applications. - * @author jbf - * @deprecated use FileStorageModelNew + * Represents a method for storing data sets in a set of files by time. The + * client provides a regex for the files and how each group of the regex is + * interpreted as a time digit. The model can then be used to provide the set + * of files that cover a time range, etc. + * + * This new implementation uses a TimeParser object to more quickly process + * file names. + * + * @author Jeremy */ -public final class FileStorageModelNew { +public class FileStorageModelNew { + + private Pattern pattern; + private Pattern absPattern; + private String regex; + + FileStorageModelNew parent; + FileSystem root; + + TimeParser timeParser; + + String template; + + static Logger logger= DasLogger.getLogger( DasLogger.SYSTEM_LOG ); - private FileStorageModelNew() { - throw new UnsupportedOperationException("use static create methods"); - } + HashMap fileNameMap=null; + - public static FileStorageModel create( FileSystem root, String template, String fieldName, TimeParser.FieldHandler fieldHandler ) { - return FileStorageModel.create( root, template, fieldName, fieldHandler ); + /* need to map back to TimeUtil's enums, note that we have an extra for the 2 digit year */ + private int toTimeUtilEnum( int i ) { + if ( i<100 || i > 300 ) { + throw new IllegalArgumentException( "enumeration is not of the correct type"); + } + i= i % 100; + if ( i==0 ) i=1; + return i; + } + + + /** + * extract time range for file or directory from its name. + * The least significant time digit is considered to be the implicitTimeWidth, + * and if the width is not stated explicitly, it will be used. When + * a set timeDigits are encountered twice, then the second occurrence + * is considered be the end time. + * + * .../FULL1/T8709_12/T871118.DAT + *'.../FULL1/T'YYMM_MM/TYYMMDD'.DAT' + */ + private DatumRange getDatumRangeFor( String filename ) { + try { + if ( pattern.matcher(filename).matches() ) { + timeParser.parse( filename ); + return timeParser.getTimeRange(); + } else { + throw new IllegalArgumentException( "file name ("+filename+") doesn't match model specification ("+regex+")"); + } + } catch ( ParseException e ) { + IllegalArgumentException e2=new IllegalArgumentException( "file name ("+filename+") doesn't match model specification ("+regex+"), parse error in field",e); + throw e2; + } catch ( NumberFormatException e ) { + IllegalArgumentException e2=new IllegalArgumentException( "file name ("+filename+") doesn't match model specification ("+regex+"), parse error in field",e); + throw e2; + } } - public static FileStorageModel create( FileSystem root, String template ) { - return FileStorageModel.create( root, template ); + + public String getFilenameFor( Datum start, Datum end ) { + return timeParser.format( start, end ); + } + + public String[] getNamesFor( final DatumRange targetRange ) throws IOException { + return getNamesFor( targetRange, new NullProgressMonitor() ); + } + + public String[] getNamesFor( final DatumRange targetRange, ProgressMonitor monitor ) throws IOException { + + String listRegex; + + FileSystem[] fileSystems; + String[] names; + + if ( parent!=null ) { + names= parent.getNamesFor(targetRange); + fileSystems= new FileSystem[names.length]; + for ( int i=0; i0 ) monitor.setTaskSize( names.length * 10 ); + monitor.started(); + for ( int i=0; i1 ) { + dirRegex= s[0]; + for ( int i=1; i 300 ) { - throw new IllegalArgumentException( "enumeration is not of the correct type"); - } - i= i % 100; - if ( i==0 ) i=1; - return i; - } - - //TODO: add - // public string format( DatumRange dr ); - // - public interface FieldHandler { - public void handle( String s, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ); - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ); - } - - public static abstract class IntegerFieldHandler implements FieldHandler { - public void handle( String s, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { - handleInt( Integer.parseInt(s), ts1, ts2 ); - } - abstract void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ); - public abstract String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ); - } - - static final NumberFormat nf4= new DecimalFormat("0000"); - static final NumberFormat nf3= new DecimalFormat("000"); - static final NumberFormat nf2= new DecimalFormat("00"); - - private final static String[] mons= new String [] { - "", "jan", "feb", "mar", "apr", "may", "jun", - "jul", "aug", "sep", "oct", "nov", "dec" } ; - private static final FieldHandler StartMonthNameHandler= new FieldHandler() { - public void handle( String s, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { - try { - ts1.month= TimeUtil.monthNumber( s ); - } catch ( ParseException e ) { - DasExceptionHandler.handle(e); - } - } - public String format(TimeStruct ts1, TimeStruct ts2) { - return mons[ ts1.month ]; - } - }; - - private static final FieldHandler EndMonthNameHandler= new FieldHandler() { - public void handle( String s, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { - try { - ts2.month= TimeUtil.monthNumber( s ); - } catch ( ParseException e ) { - DasExceptionHandler.handle(e); - } - } - public String format(TimeStruct ts1, TimeStruct ts2) { - return mons[ ts2.month ]; - } - - }; - - - private static final FieldHandler StartYear4Handler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts1.year= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf4.format(ts1.year); } - }; - - private static final FieldHandler StartYear2Handler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts1.year= i<58 ? i+2000 : i+1900; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts1.year % 100 ); } - }; - - private static final FieldHandler StartMonthHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts1.month= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts1.month ); } - }; - - private static final FieldHandler StartDayHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts1.day= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts1.day ); } - }; - private static final FieldHandler StartDoyHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts1.doy= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf3.format( ts1.doy ); } - }; - private static final FieldHandler StartHourHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts1.hour= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts1.hour ); } - }; - private static final FieldHandler StartMinuteHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts1.minute= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts1.minute ); } - }; - private static final FieldHandler StartSecondHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts1.seconds= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts1.seconds ); } - }; - - private static final FieldHandler EndYear4Handler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts2.year= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf4.format( ts2.year ); } - }; - private static final FieldHandler EndYear2Handler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts2.year= i<58 ? i+2000 : i+1900; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts2.year ); } - }; - - private static final FieldHandler EndMonthHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts2.month= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts2.month ); } - }; - private static final FieldHandler EndDayHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts2.day= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts2.day ); } - }; - private static final FieldHandler EndDoyHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts2.doy= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf3.format( ts2.doy ); } - }; - private static final FieldHandler EndHourHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts2.hour= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts2.hour ); } - }; - private static final FieldHandler EndMinuteHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts2.minute= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts2.minute ); } - }; - private static final FieldHandler EndSecondHandler= new IntegerFieldHandler() { - public void handleInt( int i, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { ts2.seconds= i; } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return nf2.format( ts2.seconds ); } - }; - - private static final FieldHandler IgnoreHandler= new FieldHandler() { - public void handle( String s, TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { } - public String format( TimeUtil.TimeStruct ts1, TimeUtil.TimeStruct ts2 ) { return "*"; } - }; - - - private void checkArgs( String regex, int[] digitList ) { - int startLsd=0, endLsd=0; - int[] startDigits= new int[7]; - int[] endDigits= new int[7]; - copyToEndTime= new boolean[8]; /* indexed by TimeUtil enum */ - int startBase=100; - int endBase=200; - int ignoreBase=300; - for ( int i=0; istartLsd ) startLsd= 103; - } else if ( digitList[i]==EndDoy ) { - endDigits[1]=1; endDigits[2]=1; - if ( 203>endLsd ) endLsd= 203; - } else if ( digitList[i]>=startBase && digitList[i]startLsd ) startLsd= digitList[i]; - } else if ( digitList[i]>=endBase && digitList[i]endLsd ) endLsd= digitList[i]; - } - } - if ( startDigits[StartYear2-startBase]==1 ) startDigits[StartYear4-startBase]=1; - if ( startDigits[StartYear4-startBase]==1 ) startDigits[StartYear2-startBase]=1; - - if ( startDigits[StartDoy-startBase]==1 ) { - startDigits[StartMonth-startBase]=1; - startDigits[StartDay-startBase]=1; - } - if ( endDigits[EndYear2-endBase]==1 ) endDigits[EndYear4-endBase]=1; - if ( startDigits[EndYear4-endBase]==1 ) startDigits[EndYear2-endBase]=1; - if ( endDigits[EndDoy-endBase]==1 ) { - endDigits[EndMonth-endBase]=1; - endDigits[EndDay-endBase]=1; - } - for ( int i=0; i0 && startDigits[i]==1 && startDigits[i-1]!=1 ) { - throw new IllegalArgumentException( "more significant digits missing in startTime"); - } - if ( i>0 && startDigits[i]==0 && startDigits[i-1]==1 ) { - timeWidth= toTimeUtilEnum( startLsd ); - timeWidthMultiplier= 1; - } - } - - boolean canUse= true; - for ( int i=startLsd-startBase; i>=0; i-- ) { - if ( endDigits[i]==0 ) canUse=false; - if ( !canUse ) endDigits[i]= 0; - } - - for ( int i=0; i=100 && digitList[i]<200 ) { - fieldHandlerList.add(i,startHandlers[digitList[i]-100]); - } else if (digitList[i]>=200 && digitList[i]<300 ) { - fieldHandlerList.add(i,endHandlers[digitList[i]-200]); - } else if ( digitList[i]==300 ) { - fieldHandlerList.add(i,IgnoreHandler); - } else { - throw new IllegalArgumentException("unknown field handler: "+digitList[i]); - } - } - return (FieldHandler[])fieldHandlerList.toArray( new FieldHandler[fieldHandlerList.size()] ); - } - - - /* - * extract time range for file or directory from its name. - * The least significant time digit is considered to be the implicitTimeWidth, - * and if the width is not stated explicitly, it will be used. When - * a set timeDigits are encountered twice, then the second occurrence - * is considered be the end time. - * - * .../FULL1/T8709_12/T871118.DAT - *'.../FULL1/T'YYMM_MM/TYYMMDD'.DAT' - */ - private DatumRange getDatumRangeFor( String filename ) { - - if ( fieldHandlers.length==0 ) { - // e.g. FULL1 doesn't constrain time - return DatumRange.newDatumRange( -1e30, 1e30, Units.mj1958 ); - } - - TimeUtil.TimeStruct ts1= new TimeUtil.TimeStruct(); - ts1.year=0; - ts1.day=0; - ts1.month=1; - ts1.doy=0; - ts1.hour=0; - ts1.minute=0; - ts1.seconds=0; - - TimeUtil.TimeStruct ts2= new TimeUtil.TimeStruct(); - if ( File.separatorChar=='\\' ) filename= filename.replaceAll("\\\\", "/"); - - Matcher m= pattern.matcher(filename); - if ( m.matches() ) { - for ( int i=0; i0 ) { - String ff= names[i].equals("") ? files1[0] : names[i]+"/"+files1[0]; - if ( ff.endsWith("/") ) ff=ff.substring(0,ff.length()-1); - result= ff; - } - } - - return result; - } - - public File[] getFilesFor( final DatumRange targetRange ) throws IOException { - return getFilesFor( targetRange, new NullProgressMonitor() ); - } - - public DatumRange getRangeFor( String name ) { - return getDatumRangeFor( name ); - } - - /** - * returns true if the file came (or could come) from this FileStorageModel. - */ - public boolean containsFile( File file ) { - maybeCreateFileNameMap(); - if ( !fileNameMap.containsKey(file) ) { - return false; - } else { - String result= (String)fileNameMap.get(file); - String name= getNameFor( file ); - Matcher m= pattern.matcher( name ); - return m.matches(); - } - } - - /** - * Need a way to recover the model name of a file. The returned File from getFilesFor can be anywhere, - * so it would be good to provide a way to get it back into a FSM name. - */ - public String getNameFor( File file ) { - String result= (String)fileNameMap.get(file); - if ( result==null ) { - throw new IllegalArgumentException( "File didn't come from this FileStorageModel" ); - } else { - return result; - } - } - - private synchronized void maybeCreateFileNameMap() { - if ( fileNameMap==null ) fileNameMap= new HashMap(); - } - - /** - * retrieve the file for the name. - * @throws IOException if the file cannot be transferred. - */ - public File getFileFor( String name, ProgressMonitor monitor ) throws FileNotFoundException, IOException { - FileObject o= root.getFileObject( name ); - File file= o.getFile( monitor ); - - maybeCreateFileNameMap(); - - fileNameMap.put( file, name ); - return file; - - } - - /** - * returns a list of files that can be used - */ - public File[] getFilesFor( final DatumRange targetRange, ProgressMonitor monitor ) throws IOException { - String[] names= getNamesFor( targetRange ); - File[] files= new File[names.length]; - - maybeCreateFileNameMap(); - - if ( names.length>0 ) monitor.setTaskSize( names.length * 10 ); - for ( int i=0; i1 ) { - dirRegex= s[0]; - for ( int i=1; i RIGHT(4) - * BOTTOM(2) -> LEFT(3) - * LEFT(3) -> TOP(1) - * RIGHT(4) -> BOTTOM(2) - */ - private static int[] rot90map = {0, 4, 3, 1, 2, }; - private static int[] rot90unMap = {0, 3, 4, 2, 1, }; + private boolean flipLabel = false; + /* DEBUGGING INSTANCE MEMBERS */ private static final boolean DEBUG_GRAPHICS = false; private static final Color[] DEBUG_COLORS; @@ -111,6 +101,7 @@ public AttachedLabel( String label, int orientation, double emOffset) { * @param orientation should be one of AttachedLabel.TOP, AttachedLabel.BOTTOM, AttachedLabel.LEFT, AttachedLabel.RIGHT */ public void setOrientation(int orientation) { + boolean oldIsHorizontal = isHorizontal(); setOrientationInternal(orientation); } @@ -233,6 +224,11 @@ protected void paintHorizontalLabel(Graphics2D g) { int DMax= getColumn().getDMaximum(); int DMin= getColumn().getDMinimum(); + Font labelFont = getLabelFont(); + + int tickLengthMajor = labelFont.getSize() * 2 / 3; + int tickLengthMinor = tickLengthMajor / 2; + if (!axisLabel.equals("")) { Graphics2D g2 = (Graphics2D)g.create(); int titlePositionOffset = getTitlePositionOffset(); @@ -245,10 +241,6 @@ protected void paintHorizontalLabel(Graphics2D g) { if (bottomLabel) { leftEdge = DMin + (DMax-DMin - titleWidth)/2; baseline = bottomPosition + titlePositionOffset; - if (rot90) { - g2.rotate(Math.PI, leftEdge, baseline); - g2.translate(-gtr.getWidth(), gtr.getAscent()); - } gtr.draw(g2, (float)leftEdge, (float)baseline); } if (topLabel) { @@ -275,6 +267,11 @@ protected void paintVerticalLabel(Graphics2D g) { int DMax= getRow().getDMaximum(); int DMin= getRow().getDMinimum(); + Font labelFont = getLabelFont(); + + int tickLengthMajor= labelFont.getSize()*2/3; + int tickLengthMinor = tickLengthMajor / 2; + if (!axisLabel.equals("")) { Graphics2D g2 = (Graphics2D)g.create(); int titlePositionOffset = getTitlePositionOffset(); @@ -289,16 +286,28 @@ protected void paintVerticalLabel(Graphics2D g) { leftEdge = -DMax + (DMax-DMin - titleWidth)/2; baseline = leftPosition - titlePositionOffset; gtr.draw(g2, (float)leftEdge, (float)baseline); + //leftEdge = DMin + (DMax-DMin - titleWidth)/2; + //baseline = topPosition - titlePositionOffset; + //gtr.draw(g2, (float)leftEdge, (float)baseline); } if (rightLabel) { - g2.rotate(Math.PI/2.0); - leftEdge = DMin + (DMax-DMin - titleWidth)/2; - baseline = - rightPosition - titlePositionOffset; - if (rot90) { - g2.rotate(Math.PI, leftEdge, baseline); - g2.translate(-gtr.getWidth(), gtr.getAscent()); - } - gtr.draw(g2, (float)leftEdge, (float)baseline); + if (flipLabel) { + g2.rotate(-Math.PI/2.0); + leftEdge = -DMax + (DMax-DMin - titleWidth)/2; + baseline = rightPosition + titlePositionOffset + + (int)Math.ceil(gtr.getHeight()) + - 2*(int)Math.ceil(gtr.getDescent()); + gtr.draw(g2, (float)leftEdge, (float)baseline); + //leftEdge = DMin + (DMax-DMin - titleWidth)/2; + //baseline = bottomPosition + titlePositionOffset; + //gtr.draw(g2, (float)leftEdge, (float)baseline); + } + else { + g2.rotate(Math.PI/2.0); + leftEdge = DMin + (DMax-DMin - titleWidth)/2; + baseline = - rightPosition - titlePositionOffset; + gtr.draw(g2, (float)leftEdge, (float)baseline); + } } g2.dispose(); } @@ -315,15 +324,22 @@ protected int getTitlePositionOffset() { int offset = (int)Math.ceil(emOffset * labelFont.getSize()); - orientation = rot90map[orientation]; if (orientation == BOTTOM) { offset += gtr.getAscent(); } - orientation = rot90unMap[orientation]; return offset; } + + public boolean isLabelFlipped() { + return flipLabel; + } + + public void setLabelFlipped(boolean flipLabel) { + this.flipLabel = flipLabel; + update(); + } - /** get the current font of the compoennt. + /** get the current font of the component. * @return Font of the component. */ public Font getLabelFont() { @@ -371,6 +387,11 @@ protected Rectangle getLabelBounds() { } private Rectangle getHorizontalLabelBounds() { + int topPosition = getRow().getDMinimum() - 1; + int bottomPosition = getRow().getDMaximum(); + DasDevicePosition range = getColumn(); + int DMax = range.getDMaximum(); + int DMin = range.getDMinimum(); boolean bottomLabel = (orientation == BOTTOM && !axisLabel.equals("")); boolean topLabel = (orientation == TOP && !axisLabel.equals("")); @@ -405,7 +426,12 @@ private Rectangle getHorizontalLabelBounds() { private Rectangle getVerticalLabelBounds() { boolean leftLabel = (orientation == LEFT && !axisLabel.equals("")); boolean rightLabel = (orientation == RIGHT && !axisLabel.equals("")); - + + int leftPosition = getColumn().getDMinimum() - 1; + int rightPosition = getColumn().getDMaximum(); + int DMax= getRow().getDMaximum(); + int DMin= getRow().getDMinimum(); + Rectangle bounds; int offset = getTitlePositionOffset(); diff --git a/dasCore/src/org/das2/graph/Auralizor.java b/dasCore/src/org/das2/graph/Auralizor.java index 71ea64035..b14705593 100644 --- a/dasCore/src/org/das2/graph/Auralizor.java +++ b/dasCore/src/org/das2/graph/Auralizor.java @@ -6,66 +6,35 @@ package org.das2.graph; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.channels.WritableByteChannel; -import java.util.logging.Level; -import java.util.logging.Logger; +import org.das2.dataset.VectorDataSet; import org.das2.datum.Units; import org.das2.system.DasLogger; import javax.sound.sampled.*; -import org.das2.datum.UnitsConverter; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dsops.Ops; /** - * Stream QDataSet to the sound system, using DEPEND_0 to control - * sampling rate. Note this does not support the new waveform packet - * scheme introduced a few years ago, and can easily be made to support - * it. + * * @author Owner */ public class Auralizor { - private static final int EXTERNAL_BUFFER_SIZE = 100000; - ByteBuffer buffer; - byte[] buf; + private static final int EXTERNAL_BUFFER_SIZE = 1000; + byte[] buffer; SourceDataLine line = null; int bufferInputIndex; double min; double max; Units yUnits; - QDataSet ds; + VectorDataSet ds; - /** - * set the dataset to stream. The dataset should be - * rank1 and have DEPEND_0 which is convertible - * to seconds. - * @param ds the rank 1 dataset with DEPEND_0 convertible to seconds. - */ - void setDataSet( QDataSet ds ) { + void setDataSet( VectorDataSet ds ) { this.ds= ds; } - /** - * begin streaming the sound. This will block the current - * thread until complete. - */ public void playSound() { - QDataSet dep0= (QDataSet) ds.property(QDataSet.DEPEND_0); - UnitsConverter uc= UnitsConverter.getConverter( SemanticOps.getUnits(dep0).getOffsetUnits(), Units.seconds ); - float sampleRate= (float) ( 1. / uc.convert( dep0.value(1)-dep0.value(0) ) ) ; + float sampleRate= (float) ( 1. / ds.getXTagDatum(1).subtract(ds.getXTagDatum(0)).doubleValue(Units.seconds) ) ; DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("sampleRate= "+sampleRate); - AudioFormat audioFormat= new AudioFormat( sampleRate, 16, 1, true, true ); - - buf= new byte[EXTERNAL_BUFFER_SIZE]; - buffer= ByteBuffer.wrap(buf); - - buffer.order( ByteOrder.BIG_ENDIAN ); + AudioFormat audioFormat= new AudioFormat( sampleRate, 8, 1, true, false ); DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); try { @@ -76,43 +45,22 @@ public void playSound() { catch (Exception e) { throw new RuntimeException(e); } - + buffer = new byte[EXTERNAL_BUFFER_SIZE]; bufferInputIndex= 0; line.start(); int i=0; int ibuf=0; - - while ( i namedColors; - private static final Map revNamedColors; - - static { - namedColors= new HashMap(); - namedColors.put(Color.BLACK, "black" ); - namedColors.put(Color.BLUE, "blue" ); // dark blue - namedColors.put(Color.RED, "red" ); - namedColors.put(Color.GREEN.darker(), "dark green" ); - namedColors.put(Color.DARK_GRAY, "dark grey"); // grey scale - namedColors.put(Color.GRAY, "grey" ); - namedColors.put(Color.LIGHT_GRAY, "light grey" ); - namedColors.put(Color.WHITE, "white" ); - namedColors.put( new Color(128, 128, 255), "light blue" ); // light blue - namedColors.put(Color.PINK, "pink" ); - namedColors.put(Color.GREEN, "green" ); - namedColors.put(Color.CYAN, "cyan" ); - namedColors.put(Color.YELLOW, "yellow"); - namedColors.put(Color.MAGENTA, "magenta" ); // others - namedColors.put(Color.ORANGE, "orange" ); - revNamedColors= new HashMap(); - revNamedColors.put("black",Color.BLACK ); - revNamedColors.put("blue", Color.BLUE ); // dark blue - revNamedColors.put("red", Color.RED ); - revNamedColors.put("dark green",Color.GREEN.darker() ); - revNamedColors.put( "dark grey",Color.DARK_GRAY); // grey scale - revNamedColors.put( "grey",Color.GRAY ); - revNamedColors.put( "light grey", Color.LIGHT_GRAY ); - revNamedColors.put("white",Color.WHITE ); - revNamedColors.put( "light blue", new Color(128, 128, 255) ); // light blue - revNamedColors.put("pink",Color.PINK ); - revNamedColors.put("green",Color.GREEN ); - revNamedColors.put("cyan",Color.CYAN ); - revNamedColors.put("yellow",Color.YELLOW ); - revNamedColors.put("magenta",Color.MAGENTA ); // others - revNamedColors.put("orange",Color.ORANGE ); - } - - /** - * return either a named color or - * "#" + Integer.toHexString( color.getRGB() & 0xFFFFFF) - * @param color - * @return named color or hex string like "#FF0000" for Red. - */ - public static String encodeColor( Color color ) { - String s= namedColors.get(color); - if ( s!=null ) { - return s; - } else { - return "#" + Integer.toHexString(color.getRGB() & 0xFFFFFF); - } - } - - /** - * decode the color, throwing a RuntimeException when the color - * is not parsable. Valid entries include: - * "red" "RED" "0xFF0000" "0xff0000" - * @param s - * @return - */ - public static Color decodeColor( String s ) throws NullPointerException { - s= s.toLowerCase(); - Color r= revNamedColors.get(s); - if ( r!=null ) { - return r; - } else { - return Color.decode(s); - } - } - - /** - * return standard color for slightly masking background. - * @return - */ - public static Color getRicePaperColor() { - return GraphUtil.getRicePaperColor(); - } -} diff --git a/dasCore/src/org/das2/graph/ColorWedgeColorSource.java b/dasCore/src/org/das2/graph/ColorWedgeColorSource.java deleted file mode 100644 index 24abfa49a..000000000 --- a/dasCore/src/org/das2/graph/ColorWedgeColorSource.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.das2.graph; - -/** - * a package private class representing a color wedge with a - * specific color (white or black, depending on the flag sent - * to the constructor) for the zero level - * - * @author LarryBrown - */ -class ColorWedgeColorSource { - - private final int[] INDEX = new int[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}; - private final int[] RED = new int[]{0,0,1,2,3,5,6,6,7,8,9,10,12,12,14,14,15,16,16,16,16,17,17,18,18,18,18,18,18,18,18,17,16,16,16,15,14,13,12,11,10,9,9,8,7,7,6,5,4,3,2,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,3,4,4,5,6,7,8,10,11,12,12,14,14,15,17,20,24,26,29,31,33,36,40,45,46,50,54,58,60,63,68,73,79,86,91,94,101,109,117,124,128,132,136,140,144,151,154,158,162,166,171,173,176,179,183,186,189,191,193,195,197,199,202,206,210,212,214,216,218,219,220,220,222,222,223,225,226,227,228,228,229,230,231,232,233,234,234,235,236,236,237,238,239,241,242,243,243,244,245,245,247,247,248,248,248,248,249,249,249,250,250,250,250,249,249,249,249,249,249,249,248,248,248,247,247,246,245,245,244,243,243,242,241,241,240,239,238,237,237,236,235,234,233,233,232,231,230,229,228,227,225,224,223,222,220,218,217,216,215,214,214,213,211,211,211,210,209,208,207,206,205,204,204,202,200}; - private final int[] GREEN = new int[]{0,0,0,2,3,4,5,6,7,8,8,9,11,11,12,14,16,17,19,21,23,25,27,28,29,33,36,39,41,43,46,48,50,53,56,59,61,65,70,72,75,78,82,87,91,94,97,100,104,108,111,114,116,119,121,124,126,129,131,133,134,137,140,142,144,147,149,150,152,155,157,158,160,162,163,164,165,167,168,170,171,172,173,175,176,178,179,180,182,184,186,187,189,190,192,193,195,198,200,201,202,203,204,206,208,208,209,210,212,213,215,216,217,218,220,222,224,226,227,228,230,231,232,234,236,238,239,239,240,241,241,242,242,242,242,242,242,241,241,239,238,237,236,234,233,232,230,229,227,225,223,221,221,219,216,213,209,206,203,200,196,191,187,185,183,180,179,176,174,170,168,165,161,157,154,153,152,150,148,147,144,143,142,140,138,136,133,131,129,126,124,122,120,118,116,115,113,110,109,108,106,102,98,96,94,90,87,86,83,81,79,77,75,74,73,71,69,67,66,64,63,61,60,58,56,53,54,62,65,69,74,78,81,86,86,91,97,98,95,94,90,90,85,85,85,81,80,77,75,73,70,67,60,62,70,70}; - private final int[] BLUE = new int[]{100,106,111,117,122,128,138,149,154,160,165,171,176,176,181,187,192,198,203,208,212,217,221,226,231,235,240,244,249,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,252,249,246,244,241,238,235,232,230,227,224,221,219,217,216,213,211,208,205,202,200,197,194,191,188,186,183,180,177,175,172,169,166,164,161,158,155,153,150,147,144,141,139,136,133,130,128,125,122,119,117,114,111,108,106,103,100,97,94,94,92,89,86,84,81,78,75,73,70,67,64,62,59,56,53,50,48,45,42,39,37,34,31,28,26,23,20,17,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,9,14,17,21,26,29,33,37,41,45,49,53,57,62,65,69,74,78,81,86,86,91,97,102,105,109,114,117,121,126,126,131,136,140,143,147,152,155,159,164,167,171}; - - public ColorWedgeColorSource(boolean useWhiteAtZero) { - - // By default, the color at the lowest index (0) is black (0). - - if (useWhiteAtZero) { - // set the color at the lowest index (0) to white (255) - RED[0] = 255; - GREEN[0] = 255; - BLUE[0] = 255; - } - } - - public int[] getIndex() { - return INDEX; - } - - public int[] getRed() { - return RED; - } - - public int[] getGreen() { - return GREEN; - } - - public int[] getBlue() { - return BLUE; - } -} diff --git a/dasCore/src/org/das2/graph/ColumnColumnConnector.java b/dasCore/src/org/das2/graph/ColumnColumnConnector.java index 31976f431..f2f9c85e5 100644 --- a/dasCore/src/org/das2/graph/ColumnColumnConnector.java +++ b/dasCore/src/org/das2/graph/ColumnColumnConnector.java @@ -4,140 +4,47 @@ import org.das2.datum.DatumRange; import org.das2.datum.DatumRangeUtil; import java.awt.*; -import java.awt.event.MouseEvent; import java.awt.geom.GeneralPath; -import java.beans.PropertyChangeListener; -import javax.swing.JCheckBoxMenuItem; import javax.swing.JLayeredPane; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; -import javax.swing.MenuElement; -import javax.swing.event.MouseInputAdapter; -import org.das2.DasApplication; -import org.das2.datum.Units; +import org.das2.event.DasMouseInputAdapter; /** * draws lines connecting two DasPlots, one on top of the other, typically used * to show a zoom in above of a context below. */ -public class ColumnColumnConnector extends DasCanvasComponent { +public class ColumnColumnConnector extends DasCanvasComponent implements java.beans.PropertyChangeListener { + + private DasCanvas parent; - public static final String PROP_FILL_COLOR = "fillColor"; - public static final String PROP_FILL = "fill"; - public static final String PROP_BOTTOM_CURTAIN = "bottomCurtain"; - public static final String PROP_CURTAIN_OPACITY_PERCENT = "curtainOpacityPercent"; - private DasRow topRow; + private DasRow bottomRow; private DasPlot topPlot; - private final DasPlot bottomPlot; - - /** - * true if the bottom curtain should be drawn - */ - private boolean bottomCurtainDrawn= true; - + private DasPlot bottomPlot; + public ColumnColumnConnector( DasCanvas parent, DasPlot topPlot, DasRow topRow, DasPlot bottomPlot ) { super( ); putClientProperty( JLayeredPane.LAYER_PROPERTY, DasCanvas.AXIS_LAYER ); - this.topPlot= topPlot; - this.topRow= topRow; - this.bottomPlot= bottomPlot; setForeground( Color.LIGHT_GRAY ); - if ( topRow==null ) topRow= topPlot.getRow(); setRow( topRow ); setColumn( topPlot.getColumn() ); - - PropertyChangeListener pcl= createPropertyChangeListener(); - - topPlot.addPropertyChangeListener(pcl); - topPlot.getXAxis().addPropertyChangeListener(pcl); - topPlot.getYAxis().addPropertyChangeListener(pcl); - bottomPlot.addPropertyChangeListener(pcl); - bottomPlot.getXAxis().addPropertyChangeListener(pcl); - - if ( !DasApplication.getDefaultApplication().isHeadless() ) { - JPopupMenu mi= this.bottomPlot.getDasMouseInputAdapter().getPrimaryPopupMenu(); - mi.setLabel("Plot Menu"); - - JPopupMenu delegateMenu= makeDelegateMenu( mi ); - - getDasMouseInputAdapter().addMenuItem( delegateMenu ); - MenuElement me= getDasMouseInputAdapter().getPrimaryPopupMenu().getSubElements()[0]; - ((JMenuItem)me.getComponent()).setText("Connector Properties"); - - addMouseListener(new MouseInputAdapter() { - - @Override - public void mousePressed(MouseEvent e) { - //if (e.getButton() == MouseEvent.BUTTON3) { - - DasPlot bot= ColumnColumnConnector.this.bottomPlot; - int ir = bot.findRendererAt(getX() + e.getX(), getY() + e.getY()); - Renderer r = null; - if ( ir>-1 ) { - r= (Renderer) bot.getRenderer(ir); - } - bot.setFocusRenderer(r); - //} - } - }); - - } - } - - - private JMenu makeDelegateMenu( JMenu mi ) { - JMenu result= new JMenu(mi.getText()); - for ( Component c: mi.getMenuComponents() ) { - if ( c instanceof JMenuItem ) { - JMenuItem tmi= (JMenuItem)c; - JMenuItem cmi= new JMenuItem(tmi.getAction()); - cmi.setText(tmi.getText()); - result.add(cmi); - } else if ( c instanceof JSeparator ) { - result.add( new JSeparator() ); - } else if ( c instanceof JMenu ) { - result.add( makeDelegateMenu( ((JMenu)c) ) ); - } - } - return result; - } - - /** - * create a menu that delegates to the menu underneath - * @param mi - * @return - */ - private JPopupMenu makeDelegateMenu( JPopupMenu mi ) { + this.topPlot= topPlot; + this.topRow= topRow; + if ( topRow==null ) topRow= topPlot.getRow(); + this.bottomPlot= bottomPlot; - JPopupMenu result= new JPopupMenu(); - int i=0; - for ( Component c: mi.getComponents() ) { - i=i+1; - if ( c instanceof javax.swing.JMenu ) { - result.add( makeDelegateMenu( ((JMenu)c) ) ); - } else if ( c instanceof JCheckBoxMenuItem ) { - //drop it--it's a mouse module... - } else if ( c instanceof JMenuItem ) { - JMenuItem tmi= (JMenuItem)c; - JMenuItem cmi= new JMenuItem(tmi.getAction()); - cmi.setText(tmi.getText()); - result.add(cmi); - } else if ( c instanceof JSeparator ) { - result.add( new JSeparator() ); - } - } - return result; + this.parent= parent; + topPlot.addPropertyChangeListener(this); + topPlot.getXAxis().addPropertyChangeListener(this); + topPlot.getYAxis().addPropertyChangeListener(this); + bottomPlot.addPropertyChangeListener(this); + bottomPlot.getXAxis().addPropertyChangeListener(this); } - private Rectangle getMyBounds() { int ytop= topRow.getDMaximum(); - int ybottom= this.bottomCurtainDrawn ? bottomPlot.getRow().getDMaximum() : bottomPlot.getRow().getDMinimum() ; + int ybottom= this.bottomCurtain ? bottomPlot.getRow().getDMaximum() : bottomPlot.getRow().getDMinimum() ; int xhigh= Math.max( topPlot.getColumn().getDMaximum(), bottomPlot.getColumn().getDMaximum() ); int xlow= Math.min( topPlot.getColumn().getDMinimum(), bottomPlot.getColumn().getDMinimum() ); @@ -145,12 +52,10 @@ private Rectangle getMyBounds() { return result; } - @Override public Shape getActiveRegion() { return getMyBounds(); } - @Override public void resize() { setBounds(getMyBounds()); } @@ -162,104 +67,13 @@ private Datum min( Datum d1, Datum d2 ) { private Datum max( Datum d1, Datum d2 ) { return d1.gt(d2) ? d1 : d2; } - - private void paintBottomContext( Graphics2D g, DatumRange context ) { - g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); - - g.translate(-getX(), -getY()); - - int xhigh1= (int)topPlot.getXAxis().transform(context.min()); - int xhigh2= (int)bottomPlot.getXAxis().getColumn().getDMiddle(); - int xlow1= (int)topPlot.getXAxis().transform(context.min()); - int xlow2= (int)bottomPlot.getXAxis().getColumn().getDMiddle(); - - //if ( xhigh1 > xhigh2 ) return; - paintIt( g, xhigh1, xhigh2, xlow1, xlow2 ); - } - - private void paintTopContext( Graphics2D g, DatumRange context ) { - g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); - - g.translate(-getX(), -getY()); - - int xhigh1= (int)topPlot.getXAxis().getColumn().getDMiddle(); - int xhigh2= (int)bottomPlot.getXAxis().transform(context.min()); - int xlow1= (int)topPlot.getXAxis().getColumn().getDMiddle(); - int xlow2= (int)bottomPlot.getXAxis().transform(context.min()); - - //if ( xhigh1 > xhigh2 ) return; - paintIt( g, xhigh1, xhigh2, xlow1, xlow2 ); - } - - private void paintIt( Graphics2D g, int xhigh1, int xhigh2, int xlow1, int xlow2 ) { - int hlen=3; - - int y1= topRow.getDMaximum()+hlen; - int y2= bottomPlot.getRow().getDMinimum()-1-hlen; - int y3= bottomPlot.getRow().getDMinimum()-1; - - GeneralPath gp= new GeneralPath(); - GeneralPath fillPath= new GeneralPath(); - - gp.moveTo( xlow1,y1-hlen ); fillPath.moveTo( xlow1,y1-hlen ); - gp.lineTo( xlow1,y1 ); fillPath.lineTo( xlow1,y1 ); - gp.lineTo( xlow2,y2 ); fillPath.lineTo( xlow2,y2 ); - gp.lineTo( xlow2,y3 ); fillPath.lineTo( xlow2,y3 ); - gp.moveTo( xhigh2, y3 ); - fillPath.lineTo( xhigh2,y3 ); - gp.lineTo( xhigh2,y2 ); fillPath.lineTo( xhigh2,y2 ); - gp.lineTo( xhigh1,y1 ); fillPath.lineTo( xhigh1,y1 ); - gp.lineTo( xhigh1,y1-hlen ); fillPath.lineTo( xhigh1,y1-hlen ); - - if ( fill ) { - g.setColor( fillColor ); - g.fill(fillPath); - } - - g.setColor( getForeground() ); - - g.draw( gp ); - - } - - @Override + protected void paintComponent(Graphics g1) { - - if ( ! topPlot.getXAxis().getUnits().isConvertibleTo( bottomPlot.getXAxis().getUnits() ) ) { - //context plots - // check to see if bottom panel is slice of top - DatumRange bottomContext= bottomPlot.getDisplayContext(); //TODO: this is not a closed-loop system. We should indicate timerange found in dataset. - DatumRange topContext= topPlot.getDisplayContext(); - - boolean topIsInterestingContext= topContext!=null && topContext.getUnits()!=Units.dimensionless; - - boolean useTop= bottomContext==null || ( bottomContext.getUnits()==Units.dimensionless && topIsInterestingContext ); - - //problem: Autoplot uses "0 to 100" as the default context. - //problem: the context property is the setting for the axis, not the feedback. - - if ( !useTop ) { - boolean isContext= bottomContext!=null && topPlot.getXAxis().getUnits().isConvertibleTo( bottomContext.getUnits() ); - if ( isContext ) { - Graphics2D g2=(Graphics2D)g1.create(); - paintBottomContext( g2, bottomContext ); - g2.dispose(); - return; - } - } else { - boolean isContext= topContext!=null && bottomPlot.getXAxis().getUnits().isConvertibleTo( topContext.getUnits() ); - bottomContext= topPlot.getDisplayContext(); - if ( isContext ) { - Graphics2D g2=(Graphics2D)g1.create(); - paintTopContext( g2, bottomContext ); - g2.dispose(); - return; - } else { - return; - } - } - } - + + if ( ! topPlot.getXAxis().getUnits().isConvertableTo( bottomPlot.getXAxis().getUnits() ) ) return; + + bottomPlot.addPropertyChangeListener(this); + Graphics2D g= (Graphics2D)g1.create(); g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); @@ -271,11 +85,7 @@ protected void paintComponent(Graphics g1) { int y2= bottomPlot.getRow().getDMinimum()-1-hlen; int y3= bottomPlot.getRow().getDMinimum()-1; int y4= bottomPlot.getRow().getDMaximum(); - - if ( ! topPlot.getXAxis().getUnits().isConvertibleTo( bottomPlot.getXAxis().getUnits() ) ) { - // transition state (?) that caused failure of autoplot-test500 005. - return; - } + Datum dlow= max( topPlot.getXAxis().getDataMinimum(), bottomPlot.getXAxis().getDataMinimum() ); Datum dhigh= min( topPlot.getXAxis().getDataMaximum(), bottomPlot.getXAxis().getDataMaximum() ); int xhigh1= (int)topPlot.getXAxis().transform(dhigh); @@ -307,7 +117,7 @@ protected void paintComponent(Graphics g1) { g.draw( gp ); - if ( bottomCurtain && topPlot.getYAxis().getUnits().isConvertibleTo( bottomPlot.getYAxis().getUnits() ) ) { + if ( bottomCurtain && topPlot.getYAxis().getUnits().isConvertableTo( bottomPlot.getYAxis().getUnits() ) ) { DatumRange drtop= topPlot.getYAxis().getDatumRange(); DatumRange yaxisRange= bottomPlot.getYAxis().getDatumRange(); @@ -316,15 +126,12 @@ protected void paintComponent(Graphics g1) { int y5,y6; - if ( showYPosition ) { - y5= (int)( bottomPlot.getYAxis().transform( drtop.max() )+0.00001 ); - y6= (int)( bottomPlot.getYAxis().transform( drtop.min() )+0.00001 ); - } else { - y5= bottomPlot.getYAxis().getRow().getDMinimum(); - y6= bottomPlot.getYAxis().getRow().getDMaximum(); - } + y5= (int)bottomPlot.getYAxis().transform( drtop.max() ); + y6= (int)bottomPlot.getYAxis().transform( drtop.min() ); if ( curtainOpacityPercent > 0 ) { + int xLeft= (int)topPlot.getXAxis().getColumn().getDMinimum(); + int xRight= (int)bottomPlot.getXAxis().getColumn().getDMaximum(); Color canvasColor= getCanvas().getBackground(); Color curtainColor= new Color( canvasColor.getRed(), canvasColor.getGreen(), canvasColor.getBlue(), curtainOpacityPercent * 255 / 100 ); @@ -353,14 +160,9 @@ protected void paintComponent(Graphics g1) { getDasMouseInputAdapter().paint(g1); } - private PropertyChangeListener createPropertyChangeListener() { - return new PropertyChangeListener() { - public void propertyChange(java.beans.PropertyChangeEvent propertyChangeEvent) { - bottomCurtainDrawn= topPlot.getXAxis().getUnits().isConvertibleTo( bottomPlot.getXAxis().getUnits() ); - markDirty(); - update(); - } - }; + public void propertyChange(java.beans.PropertyChangeEvent propertyChangeEvent) { + markDirty(); + update(); } /** @@ -377,7 +179,6 @@ public void propertyChange(java.beans.PropertyChangeEvent propertyChangeEvent) { * Adds a PropertyChangeListener to the listener list. * @param l The listener to add. */ - @Override public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { propertyChangeSupport.addPropertyChangeListener(l); } @@ -386,7 +187,6 @@ public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { * Removes a PropertyChangeListener from the listener list. * @param l The listener to remove. */ - @Override public void removePropertyChangeListener(java.beans.PropertyChangeListener l) { propertyChangeSupport.removePropertyChangeListener(l); } @@ -407,7 +207,7 @@ public void setFillColor(Color fillColor) { Color oldFillColor = this.fillColor; this.fillColor = fillColor; repaint(); - propertyChangeSupport.firePropertyChange(PROP_FILL_COLOR, oldFillColor, fillColor); + propertyChangeSupport.firePropertyChange("fillColor", oldFillColor, fillColor); } /** @@ -431,7 +231,7 @@ public void setFill(boolean fill) { boolean oldFill = this.fill; this.fill = fill; repaint(); - propertyChangeSupport.firePropertyChange(PROP_FILL, Boolean.valueOf(oldFill), Boolean.valueOf(fill)); + propertyChangeSupport.firePropertyChange("fill", new Boolean(oldFill), new Boolean(fill)); } /** @@ -455,7 +255,7 @@ public void setBottomCurtain(boolean bottomCurtain) { boolean oldBottomCurtain = this.bottomCurtain; this.bottomCurtain = bottomCurtain; repaint(); - propertyChangeSupport.firePropertyChange(PROP_BOTTOM_CURTAIN, Boolean.valueOf(oldBottomCurtain), Boolean.valueOf(bottomCurtain)); + propertyChangeSupport.firePropertyChange("bottomCurtain", new Boolean(oldBottomCurtain), new Boolean(bottomCurtain)); } /** @@ -479,26 +279,8 @@ public void setCurtainOpacityPercent(int curtainOpacityPercent) { int oldCurtainOpacityPercent = this.curtainOpacityPercent; this.curtainOpacityPercent = Math.max( 0, Math.min( 100, curtainOpacityPercent ) ); repaint(); - propertyChangeSupport.firePropertyChange(PROP_CURTAIN_OPACITY_PERCENT, oldCurtainOpacityPercent, curtainOpacityPercent ); + propertyChangeSupport.firePropertyChange("curtainOpacityPercent", new Integer(oldCurtainOpacityPercent), new Integer(curtainOpacityPercent)); } - private boolean showYPosition = true; - public static final String PROP_SHOWYPOSITION = "showYPosition"; - - public boolean isShowYPosition() { - return showYPosition; - } - - /** - * don't indicate the y axis position, for example if relating data with the time axes but different Y units. - * @param showYPosition - */ - public void setShowYPosition(boolean showYPosition) { - boolean oldShowYPosition = this.showYPosition; - this.showYPosition = showYPosition; - repaint(); - propertyChangeSupport.firePropertyChange(PROP_SHOWYPOSITION, oldShowYPosition, showYPosition); - } - } diff --git a/dasCore/src/org/das2/graph/ContoursRenderer.java b/dasCore/src/org/das2/graph/ContoursRenderer.java index 8df81d9bf..bc3a89f49 100755 --- a/dasCore/src/org/das2/graph/ContoursRenderer.java +++ b/dasCore/src/org/das2/graph/ContoursRenderer.java @@ -9,9 +9,17 @@ package org.das2.graph; import org.das2.DasException; +import org.das2.components.propertyeditor.Displayable; +import org.das2.dataset.AverageTableRebinner; +import org.das2.dataset.ClippedTableDataSet; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.RebinDescriptor; +import org.das2.dataset.TableDataSet; +import org.das2.dataset.TableUtil; +import org.das2.dataset.VectorDataSet; import org.das2.datum.DatumVector; import org.das2.datum.Units; -import org.virbo.math.Contour; +import org.das2.math.Contour; import java.awt.BasicStroke; import org.das2.util.monitor.ProgressMonitor; import java.awt.Color; @@ -30,233 +38,58 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.Map; import javax.swing.Icon; import javax.swing.ImageIcon; -import org.virbo.dataset.DDataSet; -import org.virbo.dataset.DataSetOps; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; -import org.virbo.dataset.JoinDataSet; -import org.virbo.dsops.Ops; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** - * Renderer for making contour plots. + * Renderer for making contour plots * @author jbf */ -public class ContoursRenderer extends Renderer { +public class ContoursRenderer extends Renderer implements Displayable { + /** Creates a new instance of ContoursRenderer */ public ContoursRenderer() { } - GeneralPath[] paths; String[] pathLabels; - /** - * autorange on the data, returning a rank 2 bounds for the dataset. - * - * @param ds the dataset. - * @return a bounding box - * @see org.virbo.dataset.examples.Schemes#boundingBox() - */ - public static QDataSet doAutorange( QDataSet ds ) { - - QDataSet xds; - QDataSet yds; - - if ( ds.rank()!=2 ) { - throw new IllegalArgumentException("ds rank must be 2"); - } - - xds= SemanticOps.xtagsDataSet(ds); - yds= SemanticOps.ytagsDataSet(ds); - - QDataSet xrange= doRange( xds ); - QDataSet yrange= doRange( yds ); - - JoinDataSet bds= new JoinDataSet(2); - bds.join(xrange); - bds.join(yrange); - - return bds; + public synchronized void render(Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { - } + Graphics2D g = (Graphics2D) g1; - private static QDataSet doRange( QDataSet xds ) { - QDataSet xrange= Ops.extent(xds); - if ( xrange.value(1)==xrange.value(0) ) { - if ( !"log".equals( xrange.property(QDataSet.SCALE_TYPE)) ) { - xrange= DDataSet.wrap( new double[] { xrange.value(0)-1, xrange.value(1)+1 } ).setUnits( SemanticOps.getUnits(xrange) ); - } else { - xrange= DDataSet.wrap( new double[] { xrange.value(0)/10, xrange.value(1)*10 } ).setUnits( SemanticOps.getUnits(xrange) ); - } - } - xrange= Ops.rescaleRangeLogLin(xrange, -0.1, 1.1 ); - return xrange; - } - - /** - * return false if the inputs are okay, true if there's no data, etc. - * @param lparent the parent - * @return false if the inputs are okay, true if there's no data - */ - private boolean checkInputs( DasPlot lparent ) { - QDataSet tds = (QDataSet) getDataSet(); - - if (tds == null) { - lparent.postMessage(this, "no data set", DasPlot.INFO, null, null); - return true; - } - if (tds.rank()!=2 ) { - lparent.postMessage(this, "dataset must be rank 2", DasPlot.INFO, null, null); - return true; - } - - if ( vds==null ) { - return true; - } - - if (paths == null) { // findbugs experiment: does a single read, which should be thread-safe, trigger findbugs IS2_INCONSISTENT_SYNC? - return true; - } - - return false; - } - - @Override - public synchronized void render(Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { - - DasPlot lparent= getParent(); - - Graphics2D g = (Graphics2D) g1.create(); - if ( ds==null ) { - lparent.postMessage(this, "no data set", DasPlot.INFO, null, null); - return; - } - if ( ds.rank()!=2 ) { - lparent.postMessage(this, "dataset rank must be 2", DasPlot.INFO, null, null); - return; - } - QDataSet _xds= SemanticOps.xtagsDataSet(ds); - if ( _xds.rank()!=1 ) { - lparent.postMessage(this, "xtags must be rank 1", DasPlot.INFO, null, null); - return; - } - QDataSet _yds= SemanticOps.ytagsDataSet(ds); - if ( _yds.rank()!=1 ) { - lparent.postMessage(this, "ytags must be rank 1", DasPlot.INFO, null, null); - return; + if (parent.getCanvas().isAntiAlias()) { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } - if ( paths==null ) { + if (paths == null) { return; } - - if (lparent.getCanvas().isAntiAlias()) { - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - } - - if ( checkInputs(lparent) ) return; - g.setColor(color); g.setStroke( new BasicStroke((float)lineThick) ); if (drawLabels) { Area labelClip = paintLabels(g); - Shape rclip = g.getClip() == null ? new Rectangle(lparent.getX(), lparent.getY(), lparent.getWidth(), lparent.getHeight()) : g.getClip(); + Shape rclip = g.getClip() == null ? new Rectangle(parent.getX(), parent.getY(), parent.getWidth(), parent.getHeight()) : g.getClip(); Area clip = new Area(rclip); clip.subtract(labelClip); g.setClip(clip); + //g.draw( labelClip ); } - for (GeneralPath path : paths) { - if (path != null) { - g.draw(path); + for (int i = 0; i < paths.length; i++) { + if (paths[i] != null) { + g.draw(paths[i]); } } } - @Override - public void setControl(String s) { - super.setControl(s); - this.contours= getControl( "levels", contours ); - this.drawLabels= getBooleanControl( "labels", drawLabels ); - this.lineThick= getDoubleControl( PROP_LINETHICK, lineThick ); - this.labelCadence= getDoubleControl( "labelCadence", labelCadence ); - this.color= getColorControl( "color", color ); - updateContours(); - } - - @Override - public String getControl() { - Map controls= new LinkedHashMap(); - controls.put( "levels", contours ); - controls.put( "labels", encodeBooleanControl( drawLabels ) ); - controls.put( PROP_LINETHICK, String.valueOf(lineThick) ); - controls.put( "labelCadence", String.valueOf(labelCadence) ); - controls.put( CONTROL_KEY_COLOR, encodeColorControl( color ) ); - return Renderer.formatControl(controls); - } - - @Override - public boolean acceptsDataSet(QDataSet ds) { - if ( ds==null ) return true; - if ( ds.rank()!=2 ) return false; - QDataSet xds= SemanticOps.xtagsDataSet(ds); - if ( xds.rank()!=1 ) return false; - QDataSet yds= SemanticOps.ytagsDataSet(ds); - if ( yds.rank()!=1 ) return false; - return true; - } - - - @Override - public void setDataSet(QDataSet ds) { - super.setDataSet(ds); //To change body of generated methods, choose Tools | Templates. - if ( acceptsDataSet(ds) ) { - updateContours(); - } - } - - QDataSet vds; // the contours - - private synchronized void updateContours() { - QDataSet tds= (QDataSet) getDataSet(); - if ( tds==null ) { - vds= null; - return; - } - - if ( tds.rank()==2 && tds.length(0)==3 && tds.property(QDataSet.DEPEND_0)!=null ) { - // hey it was already done... - logger.fine("contour was already performed"); - vds= tds; - return; - } - - Units units = SemanticOps.getUnits(tds); - - String[] cons = this.contours.trim().split(","); - double[] dcons = new double[cons.length]; - for (int i = 0; i < cons.length; i++) { - if (cons[i].trim().equals("")) { - continue; - } - double c = Double.parseDouble(cons[i]); - dcons[i] = c; - } - DatumVector dv = DatumVector.newDatumVector(dcons, units ); - vds= Contour.contour(tds, DDataSet.wrap(dv.toDoubleArray(units) ) ); - } - /** * returns clip, in the canvas reference frame - * @param g the graphics context. - * @return the bounds for the area affected. */ private Area paintLabels(final Graphics2D g) { @@ -271,15 +104,15 @@ private Area paintLabels(final Graphics2D g) { g.setFont(font); - GeneralPath[] lpaths= getPaths(); + String[] cons = this.contours.trim().split(","); double minLength= 20; - for (int i = 0; i < lpaths.length; i++) { - if (lpaths[i] == null) { + for (int i = 0; i < paths.length; i++) { + if (paths[i] == null) { continue; } String label = pathLabels[i]; - GeneralPath p = lpaths[i]; + GeneralPath p = paths[i]; if (p != null) { @@ -346,39 +179,79 @@ private Area paintLabels(final Graphics2D g) { return clip; } + protected void installRenderer() { + } - @Override - public Icon getListIcon() { - return new ImageIcon(ContoursRenderer.class.getResource("/images/icons/contoursRenderer.png")); + protected void uninstallRenderer() { } - @Override - public String getListLabel() { - return "" + ( getLegendLabel().length()> 0 ? getLegendLabel() +" " : "contours" ); + protected Element getDOMElement(Document document) { + return null; + } + + public Icon getListIcon() { + return new ImageIcon(SpectrogramRenderer.class.getResource("/images/icons/contoursRenderer.png")); } - @Override public synchronized void updatePlotImage(DasAxis xAxis, DasAxis yAxis, ProgressMonitor monitor) throws DasException { - - super.incrementUpdateCount(); + super.updatePlotImage(xAxis, yAxis, monitor); - QDataSet tds= getDataSet(); - if ( tds==null ) { + TableDataSet tds = (TableDataSet) getDataSet(); + + if (tds == null) { return; } + tds = new ClippedTableDataSet(tds, xAxis.getDatumRange(), yAxis.getDatumRange()); - Units units = SemanticOps.getUnits( tds ); + Units units = tds.getZUnits(); - double d0 = units.getFillDouble(); - - if ( vds==null ) { - return; + String[] cons = this.contours.trim().split(","); + double[] dcons = new double[cons.length]; + for (int i = 0; i < cons.length; i++) { + if (cons[i].trim().equals("")) { + continue; + } + double c = Double.parseDouble(cons[i]); + dcons[i] = c; } + DatumVector dv = DatumVector.newDatumVector(dcons, tds.getZUnits()); + + final boolean rebin= false; + if (rebin) { + RebinDescriptor xRebinDescriptor; + xRebinDescriptor = new RebinDescriptor( + xAxis.getDataMinimum(), xAxis.getDataMaximum(), + xAxis.getWidth() / 2, + xAxis.isLog()); + + RebinDescriptor yRebinDescriptor = new RebinDescriptor( + yAxis.getDataMinimum(), yAxis.getDataMaximum(), + yAxis.getHeight() / 2, + yAxis.isLog()); + + if (DataSetUtil.guessXTagWidth(tds).gt(xRebinDescriptor.binWidthDatum())) { + xRebinDescriptor = null; + } + if (TableUtil.guessYTagWidth(tds).gt(yRebinDescriptor.binWidthDatum())) { + yRebinDescriptor = null; + } + AverageTableRebinner rebinner = new AverageTableRebinner(); + rebinner.setInterpolate(false); + + if (xRebinDescriptor != null || yRebinDescriptor != null) { + tds = (TableDataSet) rebinner.rebin(tds, xRebinDescriptor, yRebinDescriptor, null); + } + } + + VectorDataSet vds = Contour.contour(tds, dv); + + paths = new GeneralPath[dv.getLength()]; + + double d0 = units.getFillDouble(); + int ii = -1; - QDataSet xds = (QDataSet) DataSetOps.unbundle( vds, 0 ); - QDataSet yds = (QDataSet) DataSetOps.unbundle( vds, 1 ); - QDataSet zds= (QDataSet) DataSetOps.unbundle( vds, 2 ); - QDataSet ids= SemanticOps.xtagsDataSet(zds); + VectorDataSet xds = (VectorDataSet) vds.getPlanarView(Contour.PLANE_X); + VectorDataSet yds = (VectorDataSet) vds.getPlanarView(Contour.PLANE_Y); Units xunits = xAxis.getUnits(); Units yunits = yAxis.getUnits(); @@ -388,21 +261,27 @@ public synchronized void updatePlotImage(DasAxis xAxis, DasAxis yAxis, ProgressM GeneralPath currentPath = null; - int n0 = 0; // node counter. Breaks are indicated by increment, so keep track of the last node. + double n0 = 0; // node counter. Breaks are indicated by increment, so keep track of the last node. + + double slen = 0.; // path length + + float fx0 = 0f, fy0 = 0f; // for calculating path length NumberFormat nf = new DecimalFormat("0.00"); - for (int i = 0; i < zds.length(); i++) { - double d = zds.value(i); - int n = (int) ids.value(i); + for (int i = 0; i < vds.getXLength(); i++) { + double d = vds.getDouble(i, units); + int n = (int) vds.getXTagDouble(i, Units.dimensionless); - float fx = (float) xAxis.transform( xds.value(i), xunits ); - float fy = (float) yAxis.transform( yds.value(i), yunits ); + float fx = (float) xAxis.transform(xds.getDatum(i)); + float fy = (float) yAxis.transform(yds.getDatum(i)); if (d != d0) { + ii++; + if ( currentPath!=null && simplifyPaths ) { GeneralPath newPath= new GeneralPath(); - GraphUtil.reducePath( currentPath.getPathIterator(null), newPath ); + newPath= GraphUtil.reducePath( currentPath.getPathIterator(null), newPath ); list.set( list.indexOf(currentPath), newPath ); } @@ -412,12 +291,17 @@ public synchronized void updatePlotImage(DasAxis xAxis, DasAxis yAxis, ProgressM d0 = d; currentPath.moveTo(fx, fy); - - } else if (n != (n0 + 1)) { - if ( currentPath!=null ) currentPath.moveTo(fx, fy); - + slen = 0.; + fx0 = fx; + fy0 = fy; + } + if (n != (n0 + 1)) { + currentPath.moveTo(fx, fy); + fx0 = fx; + fy0 = fy; + slen = 0.; } else { - if ( currentPath!=null ) currentPath.lineTo(fx, fy); + currentPath.lineTo(fx, fy); } n0 = n; } @@ -425,115 +309,105 @@ public synchronized void updatePlotImage(DasAxis xAxis, DasAxis yAxis, ProgressM paths = (GeneralPath[]) list.toArray(new GeneralPath[list.size()]); pathLabels = (String[]) labels.toArray(new String[labels.size()]); } - /** - * the contour locations, a comma-separated list + * Holds value of property contours. */ private String contours = "-.7,-.6,-.5,-.4,-.3,-.2,-.1,0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9"; /** - * return the contour locations, a comma-separated list - * @return the contour locations, a comma-separated list + * Getter for property contours. + * @return Value of property contours. */ public String getContours() { return this.contours; } /** - * set the contour locations, a comma-separated list - * @param contours the contour locations, a comma-separated list + * Setter for property contours. + * @param contours New value of property contours. */ public void setContours(String contours) { String oldContours = this.contours; this.contours = contours; - updateContours(); update(); propertyChangeSupport.firePropertyChange("contours", oldContours, contours); } /** - * the inter-label distance, in ems. + * property labelCadence, the inter-label distance, in ems. */ private double labelCadence = 100; /** - * return the inter-label distance, in ems. - * @return get the inter-label distance, in ems. + * Getter for property labelCadence. + * @return Value of property labelCadence. */ public double getLabelCadence() { return this.labelCadence; } /** - * set the inter-label distance, in ems. - * @param labelCadence the inter-label distance, in ems. + * Setter for property labelCadence. + * @param labelCadence New value of property labelCadence. */ public void setLabelCadence(double labelCadence) { double oldLabelCadence = this.labelCadence; this.labelCadence = labelCadence; update(); - propertyChangeSupport.firePropertyChange("labelCadence", oldLabelCadence, labelCadence ); + propertyChangeSupport.firePropertyChange("labelCadence", new Double(oldLabelCadence), new Double(labelCadence)); } - private synchronized GeneralPath[] getPaths() { - return paths; - } - - @Override public boolean acceptContext(int x, int y) { - GeneralPath[] lpaths= getPaths(); - if (lpaths == null) { + if (paths == null) { return false; } - for (GeneralPath lpath : lpaths) { - if (lpath != null) { - if (lpath.intersects(x - 2, y - 2, 5, 5)) { + for (int i = 0; i < paths.length; i++) { + if (paths[i] != null) { + if (paths[i].intersects(x - 2, y - 2, 5, 5)) { return true; } } } return false; } - /** - * true if labels should be drawn. + * Holds value of property drawLabels. */ private boolean drawLabels; /** - * true if labels should be drawn. - * @return true if labels should be drawn. + * Getter for property drawLabels. + * @return Value of property drawLabels. */ public boolean isDrawLabels() { return this.drawLabels; } /** - * true if labels should be drawn. - * @param drawLabels true if labels should be drawn. + * Setter for property drawLabels. + * @param drawLabels New value of property drawLabels. */ public void setDrawLabels(boolean drawLabels) { boolean oldDrawLabels = this.drawLabels; this.drawLabels = drawLabels; update(); - propertyChangeSupport.firePropertyChange("drawLabels", oldDrawLabels, drawLabels ); + propertyChangeSupport.firePropertyChange("drawLabels", new Boolean(oldDrawLabels), new Boolean(drawLabels)); } - /** - * the color for contour lines + * Holds value of property color. */ private Color color = Color.BLACK; /** - * Get the color for contour lines - * @return the color for contour lines + * Getter for property color. + * @return Value of property color. */ public Color getColor() { return this.color; } /** - * Set the color for contour lines - * @param color the color for contour lines + * Setter for property color. + * @param color New value of property color. */ public void setColor(Color color) { Color oldColor = this.color; @@ -542,25 +416,19 @@ public void setColor(Color color) { propertyChangeSupport.firePropertyChange("color", oldColor, color); } - /** - * true if we should reduce paths to remove features that fall within a pixel, etc. - */ + public String getListLabel() { + return "Contours Renderer"; + } + + private boolean simplifyPaths = true; public static final String PROP_SIMPLIFYPATHS = "simplifyPaths"; - /** - * return true if we should reduce paths to remove features that fall within a pixel, etc. - * @return true if we should reduce paths - */ public boolean isSimplifyPaths() { return this.simplifyPaths; } - /** - * set to true if we should reduce paths to remove features that fall within a pixel, etc. - * @param newsimplifyPaths true if we should reduce paths - */ public void setSimplifyPaths(boolean newsimplifyPaths) { boolean oldsimplifyPaths = simplifyPaths; this.simplifyPaths = newsimplifyPaths; @@ -568,33 +436,20 @@ public void setSimplifyPaths(boolean newsimplifyPaths) { propertyChangeSupport.firePropertyChange(PROP_SIMPLIFYPATHS, oldsimplifyPaths, newsimplifyPaths); } - /** - * the line thickness in pixels. - */ + private double lineThick = 1.0; - /** - * handle for the property lineThick. - */ public static final String PROP_LINETHICK = "lineThick"; - /** - * get the line thickness in pixels. - * @return the line thickness in pixels. - */ public double getLineThick() { return this.lineThick; } - /** - * set the line thickness in pixels. - * @param newlineThick the line thickness in pixels. - */ public void setLineThick(double newlineThick) { double oldlineThick = lineThick; this.lineThick = newlineThick; - update(); propertyChangeSupport.firePropertyChange(PROP_LINETHICK, oldlineThick, newlineThick); } + } diff --git a/dasCore/src/org/das2/graph/CurveRenderer.java b/dasCore/src/org/das2/graph/CurveRenderer.java index 76c347478..ccb6bbfa2 100644 --- a/dasCore/src/org/das2/graph/CurveRenderer.java +++ b/dasCore/src/org/das2/graph/CurveRenderer.java @@ -23,23 +23,26 @@ package org.das2.graph; import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.Units; import org.das2.DasException; import org.das2.util.monitor.ProgressMonitor; import java.awt.*; import java.awt.geom.*; -import org.virbo.dataset.DataSetOps; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.SemanticOps; /** - * Old renderer that really doesn't do anything that the SeriesRenderer can do. + * * @author jbf */ public class CurveRenderer extends Renderer { private String xplane; private String yplane; - + + private Units xunits; // xUnits of the axis + private Units yunits; // yUnits of the axis + private double[][] idata; // data transformed to pixel space + private boolean antiAliased= true; private SymColor color= SymColor.black; private PsymConnector psymConnector = PsymConnector.SOLID; @@ -49,10 +52,8 @@ public class CurveRenderer extends Renderer { private GeneralPath path; - /** The dataset descriptor should return a rank 2 QDataSet with time for - * and a bundle descriptor for BUNDLE_1. DataSetOps.unbundle is used - * to extract the xplane and yplane components. - * + /** The dataset descriptor should return a Vector data set with planes identified + * by xplane and yplane. */ public CurveRenderer( DataSetDescriptor dsd, String xplane, String yplane ) { super(dsd); @@ -63,20 +64,24 @@ public CurveRenderer( DataSetDescriptor dsd, String xplane, String yplane ) { this.yplane= yplane; } + protected void uninstallRenderer() { + } + + protected void installRenderer() { + } public void render(java.awt.Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressMonitor mon) { long timer0= System.currentTimeMillis(); - QDataSet dataSet= getDataSet(); + VectorDataSet dataSet= (VectorDataSet)getDataSet(); - if (dataSet == null || dataSet.length() == 0) { + if (dataSet == null || dataSet.getXLength() == 0) { return; } - - QDataSet xds= DataSetOps.unbundle( dataSet, xplane ); - QDataSet yds= DataSetOps.unbundle( dataSet, yplane ); - QDataSet wds= SemanticOps.weightsDataSet(xds); - + + VectorDataSet xds= (VectorDataSet)dataSet.getPlanarView(xplane); + VectorDataSet yds= (VectorDataSet)dataSet.getPlanarView(yplane); + Graphics2D graphics= (Graphics2D) g1.create(); RenderingHints hints0= graphics.getRenderingHints(); @@ -114,11 +119,15 @@ public void render(java.awt.Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressM } - for (int index = 0; index < xds.length(); index++) { - if ( wds.value()>0 ) { - double i = xAxis.transform(xds.value(index),xUnits); - double j = yAxis.transform(yds.value(index),yUnits); - psym.draw( g1, i, j, (float)symSize ); + for (int index = 0; index < xds.getXLength(); index++) { + if ( ! yUnits.isFill(yds.getDouble(index,yUnits)) ) { + double i = xAxis.transform(xds.getDouble(index,xUnits),xUnits); + double j = yAxis.transform(yds.getDouble(index,yUnits),yUnits); + if ( Double.isNaN(j) ) { + //DasApplication.getDefaultApplication().getDebugLogger().warning("got NaN"); + } else { + psym.draw( g1, i, j, (float)symSize ); + } } } @@ -128,16 +137,16 @@ public void render(java.awt.Graphics g1, DasAxis xAxis, DasAxis yAxis, ProgressM public void updatePlotImage(DasAxis xAxis, DasAxis yAxis, ProgressMonitor monitor) throws DasException { super.updatePlotImage( xAxis, yAxis, monitor ); - QDataSet dataSet= getDataSet(); + VectorDataSet dataSet= (VectorDataSet)getDataSet(); - if (dataSet == null || dataSet.length() == 0) { + if (dataSet == null || dataSet.getXLength() == 0) { return; } - QDataSet xds= DataSetOps.unbundle( dataSet, xplane ); - QDataSet yds= DataSetOps.unbundle( dataSet, yplane ); + VectorDataSet xds= (VectorDataSet)dataSet.getPlanarView(xplane); + VectorDataSet yds= (VectorDataSet)dataSet.getPlanarView(yplane); - path= GraphUtil.getPath( xAxis, yAxis, xds, yds, false, false ); + path= GraphUtil.getPath( xAxis, yAxis, xds, yds, false ); } /** Getter for property lineWidth. @@ -154,7 +163,6 @@ public double getLineWidth() { */ public void setLineWidth(double lineWidth) { this.lineWidth = (float)lineWidth; - updateCacheImage(); } protected org.w3c.dom.Element getDOMElement(org.w3c.dom.Document document) { @@ -167,7 +175,7 @@ public PsymConnector getPsymConnector() { public void setPsymConnector(PsymConnector p) { psymConnector = p; - updateCacheImage(); + refreshImage(); } /** Getter for property psym. @@ -185,7 +193,7 @@ public void setPsym(Psym psym) { if (psym == null) throw new NullPointerException("psym cannot be null"); Object oldValue = this.psym; this.psym = psym; - updateCacheImage(); + refreshImage(); } } diff --git a/dasCore/src/org/das2/graph/DasAnnotation.java b/dasCore/src/org/das2/graph/DasAnnotation.java index 339a6c2c4..9a87cbf86 100644 --- a/dasCore/src/org/das2/graph/DasAnnotation.java +++ b/dasCore/src/org/das2/graph/DasAnnotation.java @@ -15,68 +15,42 @@ import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.net.URL; -import java.text.ParseException; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JMenuItem; -import javax.swing.SwingUtilities; -import org.das2.datum.DatumRange; -import org.das2.datum.InconvertibleUnitsException; -import org.das2.datum.LoggerManager; -import org.das2.datum.Units; /** - * This makes a DasCanvasComponent for GrannyTextRenderer, and - * optionally adds an arrow to point at things. - * - * TODO: See http://autoplot.org//developer.annotations - * + * This component-izes a GrannyTextRenderer, and composes with an Arrow. * @author Jeremy */ public class DasAnnotation extends DasCanvasComponent { - private static final Logger logger= LoggerManager.getLogger("das.graph.annotation"); - String templateString; GrannyTextRenderer gtr; - BufferedImage img; - /** * point at this thing */ private DasAnnotation.PointDescriptor pointAt; - //private final MouseModule arrowToMouseModule; + private MouseModule arrowToMouseModule; - /** - * Create the annotation - * @param string the message, which may contain %p which will be replaced with a label. */ + /** Creates a new instance of DasAnnotation */ public DasAnnotation(String string) { super(); - - if ( string.startsWith("http:" ) ) { - this.gtr= null; - try { - this.img= ImageIO.read( new URL(string) ); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - this.gtr = new GrannyTextRenderer(); - } - } else { - this.gtr = new GrannyTextRenderer(); - this.gtr.setString( getFont(), "" ); - } + this.gtr = new GrannyTextRenderer(); this.templateString = string; + Action removeArrowAction = new AbstractAction("remove arrow") { + + public void actionPerformed(ActionEvent e) { + pointAt = null; + repaint(); + } + }; + + this.getDasMouseInputAdapter().addMenuItem(new JMenuItem(removeArrowAction)); + Action removeMeAction = new AbstractAction("remove") { - @Override + public void actionPerformed(ActionEvent e) { DasCanvas canvas = getCanvas(); // TODO: confirm dialog @@ -87,129 +61,13 @@ public void actionPerformed(ActionEvent e) { this.getDasMouseInputAdapter().addMenuItem(new JMenuItem(removeMeAction)); - MouseModule mm = new MoveComponentMouseModule(this) { - Point p0; - - @Override - public void mousePressed(MouseEvent e) { - super.mousePressed(e); - p0= e.getPoint(); - } - - @Override - public void mouseReleased(MouseEvent e) { - if ( getAnchorType()==AnchorType.CANVAS ) { - Point p= e.getPoint(); - int dx = p.x - p0.x; - int dy = p.y - p0.y; - adjustAnchorOffset( dx, dy ); - resize(); - repaint(); - } else { - Point p= e.getPoint(); - int dx = p.x - p0.x; - int dy = p.y - p0.y; - adjustAnchorOffset( dx, dy ); - resize(); - repaint(); - } - } - }; - mm.setLabel("Move Annotation"); - + MouseModule mm = new MoveComponentMouseModule(this); this.getDasMouseInputAdapter().setPrimaryModule(mm); - - MouseModule pointAtMouseModule= new MouseModule( this, new ArrowDragRenderer(), "Point At" ) { - Point p0; - - @Override - public void mousePressed(MouseEvent e) { - super.mousePressed(e); - if ( plot!=null ) { - p0= e.getPoint(); - p0= SwingUtilities.convertPoint( DasAnnotation.this, p0, plot.getCanvas() ); - } - } - - @Override - public void mouseReleased(MouseEvent e) { - if ( plot!=null ) { - Datum x= plot.getXAxis().invTransform(e.getX()+getX()); - Datum y= plot.getYAxis().invTransform(e.getY()+getY()); - setPointAtX(x); - setPointAtY(y); - if ( getAnchorType()==AnchorType.CANVAS ) { - setXrange( new DatumRange(x,x) ); - setYrange( new DatumRange(y,y) ); - } - setShowArrow(true); - } - } - }; - this.getDasMouseInputAdapter().addMouseModule(pointAtMouseModule); - + arrowToMouseModule = createArrowToMouseModule(this); + this.getDasMouseInputAdapter().setSecondaryModule(arrowToMouseModule); } - private void adjustAnchorOffset( int dx, int dy ) { - if ( anchorPosition==AnchorPosition.NW ) { - } else if ( anchorPosition==AnchorPosition.N ) { - } else if ( anchorPosition==AnchorPosition.OutsideN ) { - dy= -dy; - } else if ( anchorPosition==AnchorPosition.NE ) { - dx= -dx; - } else if ( anchorPosition==AnchorPosition.OutsideNE ) { - dy= -dy; - } else if ( anchorPosition==AnchorPosition.SW ) { - dy= -dy; - } else if ( anchorPosition==AnchorPosition.SE ) { - dx= -dx; - dy= -dy; - } else if ( anchorPosition==AnchorPosition.OutsideNNW ) { - dy= -dy; - } else if ( anchorPosition==AnchorPosition.OutsideNNE ) { - dx= -dx; - dy= -dy; - } else if ( anchorPosition==AnchorPosition.Center ) { - dy= -dy; - } else if ( anchorPosition==AnchorPosition.W ) { - dy= -dy; - } else if ( anchorPosition==AnchorPosition.E ) { - dx= -dx; - dy= -dy; - } else if ( anchorPosition==AnchorPosition.OutsideE ) { - dy= -dy; - } else if ( anchorPosition==AnchorPosition.S ) { - dy= -dy; - } - - String offset= getAnchorOffset(); - double em = getEmSize(); - if ( offset.trim().length()==0 ) { - offset= String.format("%.2fem,%.2fem", dx/em, dy/em ); - this.setAnchorOffset(offset); - } else { - try { - String[] ss= offset.split(",",-2); - double[] dd; - dd= DasDevicePosition.parseLayoutStr(ss[0]); - dd[1]= dd[1] + dx/em; - ss[0]= DasDevicePosition.formatFormatStr(dd); - dd= DasDevicePosition.parseLayoutStr(ss[1]); - dd[1]= dd[1] + dy/em; - ss[1]= DasDevicePosition.formatFormatStr(dd); - offset= ss[0]+","+ss[1]; - this.setAnchorOffset(offset); - } catch (ParseException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - } - - /** - * create a PointDescriptor using and x and y Datum. - */ public static class DatumPairPointDescriptor implements PointDescriptor { DasPlot p; @@ -222,81 +80,82 @@ public DatumPairPointDescriptor(DasPlot p, Datum x, Datum y) { this.p = p; } - @Override public Point getPoint() { int ix = (int) (p.getXAxis().transform(x)); int iy = (int) (p.getYAxis().transform(y)); return new Point(ix, iy); } - @Override public String getLabel() { return "" + x + "," + y; } } - public static final String PROP_TEXT = "text"; - - /** - * Set the text, which can be Granny Text. - * @param string the text - * @see GrannyTextRenderer - */ + private MouseModule createArrowToMouseModule(final DasAnnotation anno) { + return new MouseModule(DasAnnotation.this, new ArrowDragRenderer(), "Point At") { + + Point head; + Point tail; + + @Override + public void mousePressed(MouseEvent e) { + super.mousePressed(e); + tail= e.getPoint(); + tail.translate(anno.getX(), anno.getY()); + Rectangle r= DasAnnotation.this.getActiveRegion().getBounds(); + if ( !r.contains(tail) ) { + tail= null; + } + } + + @Override + public void mouseReleased(MouseEvent e) { + super.mouseReleased(e); + if ( tail==null ) return; + head = e.getPoint(); + head.translate(anno.getX(), anno.getY()); + DasCanvasComponent c = parent.getCanvas().getCanvasComponentAt(head.x, head.y); + if (c instanceof DasPlot) { + final DasPlot p = (DasPlot) c; + final Datum x = p.getXAxis().invTransform(head.x); + final Datum y = p.getYAxis().invTransform(head.y); + anno.setPointAt(new DatumPairPointDescriptor(p, x, y)); + setBounds(calcBounds()); + } + + } + }; + } + public void setText(String string) { - String oldValue= this.templateString; this.templateString = string; if ( this.getGraphics()!=null ) { - if ( string.startsWith("http:") ) { - try { - img= ImageIO.read(new URL(string)); - gtr= null; - } catch ( IOException ex ) { - gtr= new GrannyTextRenderer(); - gtr.setString( this.getGraphics(), getString() ); - } - } else { - gtr.setString( this.getGraphics(), getString() ); - } - resize(); + gtr.setString( this.getGraphics(), getString() ); + calcBounds(); } - firePropertyChange( PROP_TEXT, oldValue, string ); + repaint(); } - /** - * get the text, which can be Granny Text. - * @return the text. - * @see GrannyTextRenderer - */ public String getText() { return templateString; } @Override public void resize() { - Font f= getFont(); - if ( f!=null ) { - super.resize(); - Font thefont= f; - if ( fontSize>0 && f!=null ) thefont= f.deriveFont(fontSize); - if ( this.gtr!=null ) { - this.gtr.setString( thefont, getString() ); - } - Rectangle r= calcBounds(); - r.add( r.x+r.width+1, r.y+r.height+1 ); - if ( anchorType==AnchorType.CANVAS || plot==null ) { - r= r.intersection( new Rectangle(0,0,getCanvas().getWidth(),getCanvas().getHeight()) ); // clip at canvas boundaries - } else { - r= r.intersection( DasDevicePosition.toRectangle( plot.getRow(), plot.getColumn() ) ); // clip at plot boundaries - } - setBounds(r); - } + super.resize(); + this.gtr.setString(this.getGraphics(), getString() ); + Rectangle r= calcBounds(); + setBounds(r); } @Override public Shape getActiveRegion() { - Rectangle r = getAnnotationBubbleBounds(); + Rectangle r = gtr.getBounds(); + int em = (int) getEmSize() / 2; + r = new Rectangle(r.x, r.y + (int) gtr.getAscent(), r.width + 2 * em + 3, r.height + 2 * em + 3); + r.translate(getColumn().getDMinimum(), getRow().getDMinimum()); return r; } @@ -312,136 +171,51 @@ public boolean acceptContext(int x, int y) { return false; } - /** - * This item should only accept mouse events on the bubble - * @param x - * @param y - * @return - */ - @Override - public boolean contains(int x, int y) { - return acceptContext( x+getX(), y+getY() ); - } - - /** - * calculate the bounds in the canvas coordinate system. - * @return - */ private Rectangle calcBounds() { Rectangle r = (Rectangle)getActiveRegion(); + int em = (int) getEmSize() / 2; if (pointAt != null) { Point head = pointAt.getPoint(); r.add(head); } - if ( anchorBorderType!=BorderType.NONE ) { - Rectangle anchorRect= getAnchorBounds(); - r.add(anchorRect); - } - if ( showArrow ) { - int headx= 0; - int heady= 0; - if ( plot!=null ) { - try { - headx= (int)plot.getXAxis().transform(pointAtX); - heady= (int)plot.getYAxis().transform(pointAtY); - } catch ( InconvertibleUnitsException ex ) { - - } - r.add( headx, heady ); - } - } - int s= Math.max( getFont().getSize()/5, 3 ); - return new Rectangle( r.x-s, r.y-s, r.width+s*2+1, r.height+s*2+1 ); + r.x-= em; + r.y-= em; + r.width+= em*2; + r.height+= em*2; + + return r; } @Override public void paintComponent(Graphics g1) { - - Graphics2D g = (Graphics2D) g1.create(); - - double em2 = g.getFont().getSize(); - - Stroke stroke0= g.getStroke(); - - g.setStroke(new BasicStroke((float) (em2 / 8), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ); - - g.translate( -getX(), -getY() ); - if ( anchorType!=AnchorType.CANVAS && plot!=null ) { - Rectangle r= DasDevicePosition.toRectangle( plot.getRow(), plot.getColumn() ); - g.setClip( r ); - } - - Color fore = getCanvas().getForeground(); - Color ltextColor= fore; - Color back= getCanvas().getBackground(); + // TODO: need to draw based on row, col, not on bounds which may move with arrow. + + Graphics2D g = (Graphics2D) g1.create(); //SVG bug - if ( isOverrideColors() ) { - fore= getForeground(); - ltextColor= textColor; - back= getBackground(); - } - + g.translate( getColumn().getDMinimum()-getX(), getRow().getDMinimum()-getY() ); + + Color fore = g.getColor(); + + Color canvasColor = getCanvas().getBackground(); + Color back = new Color(canvasColor.getRed(), canvasColor.getGreen(), canvasColor.getBlue(), + 80 * 255 / 100); if ( fontSize>0 ) g.setFont( getFont().deriveFont(fontSize) ); int em = (int) getEmSize() / 2; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - if ( gtr!=null ) gtr.setString( g, getString() ); - Rectangle r; + gtr.setString( g, getString() ); + Rectangle r = gtr.getBounds(); - r= getAnnotationBubbleBounds(); - - if ( anchorPosition==AnchorPosition.N - || anchorPosition==AnchorPosition.OutsideN - || anchorPosition==AnchorPosition.Center - || anchorPosition==AnchorPosition.S ) { - if ( gtr!=null ) gtr.setAlignment( GrannyTextRenderer.CENTER_ALIGNMENT ); - } - - if ( showArrow ) { - - int headx= 0; - int heady= 0; - if ( plot!=null ) { - try { - headx= (int)plot.getXAxis().transform(pointAtX); - heady= (int)plot.getYAxis().transform(pointAtY); - } catch ( InconvertibleUnitsException ex ) { - - } - } - - Point head = new Point(headx,heady); - - Graphics2D g2 = (Graphics2D) g.create(); - -// if ( anchorType==AnchorType.CANVAS ) { -// g2.setClip(null); -// } else { -// g2.setClip( g.getClip() ); -// } - Point2D tail2d= new Point2D.Double( r.x + r.width/2, r.y + r.height/2 ); - Point2D head2d= new Point2D.Double( head.x, head.y ); - Rectangle2D rect2d= new Rectangle2D.Double(r.x, r.y, r.width, r.height ); - Point2D p2d= GraphUtil.lineRectangleIntersection( tail2d, head2d, rect2d ); - Point p= p2d==null ? head : new Point( (int)p2d.getX(), (int)p2d.getY() ); - - g2.setStroke( new BasicStroke( (float) (em2/4), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ); - - Color glowColor= getCanvas().getBackground(); - g2.setColor( new Color( glowColor.getRed(), glowColor.getGreen(), glowColor.getBlue(), 128 ) ); - Arrow.paintArrow(g2, head, p, em2, this.arrowStyle ); - - g2.setStroke( stroke0 ); - g2.setColor( fore ); - Arrow.paintArrow(g2, head, p, em2, this.arrowStyle ); - - g2.dispose(); - } + r.x = em; + r.y = em; + r = new Rectangle(r.x - em + 1, r.y - em + 1, r.width + 2 * em - 1, r.height + 2 * em - 1); + + //r.translate( em, em + (int) gtr.getAscent()); g.setColor(back); if (borderType == BorderType.RECTANGLE || borderType == BorderType.NONE) { @@ -450,40 +224,48 @@ public void paintComponent(Graphics g1) { g.fillRoundRect(r.x, r.y, r.width, r.height, em * 2, em * 2); } - g.setColor(ltextColor); + g.setColor(fore); + + gtr.draw(g, em, em + (float) gtr.getAscent()); - if ( gtr!=null ) { - gtr.draw(g, r.x+em, r.y + em + (float) gtr.getAscent() ); - } else { - g.drawImage( img, r.x+em, r.y+em, this ); + if (pointAt != null) { + double em2 = getCanvas().getFont().getSize(); + g.setStroke(new BasicStroke((float) (em2 / 8))); + //g.drawLine( r.x, r.y+r.height, r.x+r.width, r.y+r.height ); + + Point head = pointAt.getPoint(); + head.translate(-getColumn().getDMinimum(), -getRow().getDMinimum()); + int tx = Math.min(head.x, r.x + r.width * 2 / 3); + tx = Math.max(tx, r.x + r.width * 1 / 3); + Point tail = new Point(tx, r.y + r.height); + Graphics2D g2 = (Graphics2D) g.create(); + g2.setClip(null); + //Arrow.paintArrow(g2, head, tail, em2); + + Point2D tail2d= new Point2D.Double( r.x + r.width/2, r.y + r.y + r.height/2 ); + Point2D head2d= new Point2D.Double( head.x, head.y ); + Rectangle2D rect2d= new Rectangle2D.Double(r.x, r.y, r.width, r.height ); + Point2D p2d= GraphUtil.lineRectangleIntersection( tail2d, head2d, rect2d ); + Point p= p2d==null ? head : new Point( (int)p2d.getX(), (int)p2d.getY() ); + Arrow.paintArrow(g2, head, p, em2, this.arrowStyle ); + } - g.setColor(fore); - if (borderType != BorderType.NONE) { if (borderType == BorderType.RECTANGLE) { g.draw(r); } else if (borderType == BorderType.ROUNDED_RECTANGLE) { g.drawRoundRect(r.x, r.y, r.width, r.height, em * 2, em * 2); } - } - if ( anchorBorderType!=BorderType.NONE ) { - Rectangle anchorRect= getAnchorBounds(); - if ( anchorBorderType== BorderType.RECTANGLE ) { - g.draw(anchorRect); - } else if ( anchorBorderType==BorderType.ROUNDED_RECTANGLE ) { - g.drawRoundRect(anchorRect.x, anchorRect.y, anchorRect.width, anchorRect.height, em * 2, em * 2); - } } - - // this was useful for debugging. -// Graphics2D g0 = (Graphics2D) g.create(); -// Rectangle clip= g0.getClipBounds(); -// if ( clip!=null ) { -// g0.setColor(Color.ORANGE ); -// g0.drawRoundRect( clip.x, clip.y, clip.width-1, clip.height-1, 5, 5 ); -// } + + /*r= DasColumn.toRectangle( getRow(), getColumn() ); + r.translate( -getX(), -getY() ); + r.width--; + r.height--; + ((Graphics2D)g1).draw( r ); + */ g.dispose(); @@ -491,167 +273,6 @@ public void paintComponent(Graphics g1) { } - /** - * return the bounds of that thing we are anchored to. Note - * AnchorType.DATA is treated the same as AnchorType.PLOT, but the thought - * is that it could look at the render's click - * @return the bounds of that thing we are anchored to. - */ - private Rectangle getAnchorBounds() { - Rectangle anchorRect= new Rectangle(); - if ( ( anchorType==AnchorType.PLOT || anchorType==AnchorType.DATA ) && plot!=null && xrange!=null && yrange!=null ) { - if ( anchorBorderType==BorderType.NONE && showArrow ) { // this is really confusing, when you can't see the anchor. - try { - anchorRect.x= (int)(plot.getXAxis().transform( pointAtX ) ); - } catch ( InconvertibleUnitsException ex ) { - anchorRect.x= getColumn().getDMiddle(); - } - try { - anchorRect.y= (int)(plot.getYAxis().transform( pointAtY ) ); - } catch ( InconvertibleUnitsException ex ) { - anchorRect.y= getRow().getDMiddle(); - } - anchorRect.width= 1; - anchorRect.height= 1; - } else { - try { - anchorRect.x= (int)(plot.getXAxis().transform(xrange.min())); - int x1= (int)(plot.getXAxis().transform(xrange.max())); - if ( x10 ) { - String[] ss= anchorOffset.split(","); - if ( ss.length==2 ) { - double[] dd; - try { - dd= DasDevicePosition.parseLayoutStr(ss[0]); - xoffset= (int)( getCanvas().getWidth() * dd[0] + em * dd[1] + dd[2] ); - dd= DasDevicePosition.parseLayoutStr(ss[1]); - yoffset= (int)( getCanvas().getHeight() * dd[0] + em * dd[1] + dd[2] ); - } catch ( NumberFormatException ex ) { - logger.log( Level.WARNING, null, ex ); - xoffset= 0; - yoffset= 0; - } catch ( ParseException ex ) { - logger.log( Level.WARNING, null, ex ); - xoffset= 0; - yoffset= 0; } - } else { - logger.warning("anchorOffset"); - } - } - - if ( anchorPosition==AnchorPosition.NW ) { - r.x = anchor.x + em + xoffset ; - r.y = anchor.y + em + yoffset ; - } else if ( anchorPosition==AnchorPosition.N ) { - r.x = anchor.x + anchor.width/2 - (int)( r.getWidth() / 2 ) + xoffset ; - r.y = anchor.y + em + yoffset ; - } else if ( anchorPosition==AnchorPosition.OutsideN ) { - r.x = anchor.x + anchor.width/2 - (int)( r.getWidth() / 2 ) + xoffset ; - r.y = anchor.y - (int)r.getHeight() - yoffset ; - } else if ( anchorPosition==AnchorPosition.NE ) { - r.x = anchor.x + anchor.width - r.width - xoffset; - r.y = anchor.y + em + yoffset ; - } else if ( anchorPosition==AnchorPosition.OutsideNE ) { - r.x = anchor.x + anchor.width + em + xoffset; - r.y = anchor.y + em - yoffset; - } else if ( anchorPosition==AnchorPosition.SW ) { - r.x = anchor.x + em + xoffset; - r.y = anchor.y + anchor.height - r.height - yoffset; - } else if ( anchorPosition==AnchorPosition.SE ) { - r.x = anchor.x + anchor.width - r.width - xoffset; - r.y = anchor.y + anchor.height - r.height - yoffset; - } else if ( anchorPosition==AnchorPosition.OutsideNNW ) { - r.x = anchor.x + em + xoffset ; - r.y = anchor.y - (int)r.getHeight() - yoffset ; - } else if ( anchorPosition==AnchorPosition.OutsideNNE ) { - r.x = anchor.x + anchor.width - r.width - xoffset; - r.y = anchor.y - (int)r.getHeight() - yoffset ; - } else if ( anchorPosition==AnchorPosition.Center ) { - r.x = anchor.x + anchor.width/2 - (int)( r.getWidth() / 2 ) + xoffset ; - r.y = anchor.y + anchor.height/2 - (int)( r.getHeight() / 2 ) - yoffset; - } else if ( anchorPosition==AnchorPosition.W ) { - r.x = anchor.x + em + xoffset; - r.y = anchor.y + anchor.height/2 - (int)( r.getHeight() / 2 ) - yoffset; - } else if ( anchorPosition==AnchorPosition.E ) { - r.x = anchor.x + anchor.width - r.width - xoffset; - r.y = anchor.y + anchor.height/2 - (int)( r.getHeight() / 2 ) - yoffset; - } else if ( anchorPosition==AnchorPosition.OutsideE ) { - r.x = anchor.x + anchor.width + em + xoffset; - r.y = anchor.y + anchor.height/2 - (int)( r.getHeight() / 2 ) - yoffset; - } else if ( anchorPosition==AnchorPosition.S ) { - r.x = anchor.x + anchor.width/2 - (int)( r.getWidth() / 2 ) + xoffset ; - r.y = anchor.y + anchor.height - r.height - yoffset; - } - - if ( gtr==null ) { - r.x-= em/2; - r.y-= em/2; - r.width+= em; - r.height+= em; - } else { - r.x-= em; - r.y-= em; - r.width+= em; - r.height+= em; - } - - return r; - } - - /** - * something to point at - */ public interface PointDescriptor { Point getPoint(); @@ -659,23 +280,11 @@ public interface PointDescriptor { String getLabel(); } - /** - * set the thing to point at. If %p will be replaced by p.getLabel() - * @param p the thing. - */ public void setPointAt(PointDescriptor p) { this.pointAt = p; repaint(); } - /** - * return the thing we are pointing at. - * @return the thing we are pointing at. - */ - public PointDescriptor getPointAt() { - return this.pointAt; - } - private String getString() { String s = templateString; if (this.templateString != null && this.templateString.contains("%") && pointAt!=null ) { @@ -684,77 +293,61 @@ private String getString() { return s; } + public PointDescriptor getPointAt() { + return this.pointAt; + } + @Override protected void installComponent() { super.installComponent(); - if ( this.gtr!=null ) { - this.gtr.setString( this.getFont(), getString() ); - } + this.gtr.setString( this.getFont(), getString() ); } - /** - * the font size in points, or zero if we should use the canvas size. - */ - float fontSize= 0.f; + float fontSize= 0; /** - * the font size in points. If zero, then use the canvas size. - * @return the font size in points. + * Getter for property fontSize. + * @return Value of property fontSize. */ public float getFontSize() { return fontSize; } /** - * the font size in pixels. - */ - public static final String PROP_FONT_SIZE= "fontSize"; - - /** - * override the canvas font size. If zero, then use the canvas size, - * otherwise, use this size. + * override the canvas font size. If zero, then use the canvas size, otherwise, + * use this size. + * * @param fontSize New value of property fontSize. */ public void setFontSize(float fontSize) { - float oldsize= this.fontSize; this.fontSize= fontSize; Font f = getFont(); if (f == null) { - if ( getCanvas()==null ) return; f = getCanvas().getBaseFont(); } - if ( fontSize>0 ) f= f.deriveFont(fontSize); Font newFont= f; + if ( fontSize>0 ) f= f.deriveFont(fontSize); Graphics g= this.getGraphics(); if ( g==null ) return; g.setFont(newFont); - if ( gtr!=null ) gtr.setString( g, getString() ); - resize(); + gtr.setString( g, getString() ); + setBounds(calcBounds()); repaint(); - firePropertyChange( PROP_FONT_SIZE, oldsize, fontSize ); } - /** - * the current border type. - */ - private BorderType borderType = BorderType.NONE; + public enum BorderType { + NONE, RECTANGLE, ROUNDED_RECTANGLE + } + private BorderType borderType = BorderType.NONE; public static final String PROP_BORDERTYPE = "borderType"; - /** - * the border type - * @return the border type - */ public BorderType getBorderType() { return this.borderType; } - /** - * set the border type to NONE, rounded rectangle, etc. - * @param newborderType the border type - */ public void setBorderType(BorderType newborderType) { BorderType oldborderType = borderType; this.borderType = newborderType; @@ -763,166 +356,16 @@ public void setBorderType(BorderType newborderType) { firePropertyChange(PROP_BORDERTYPE, oldborderType, newborderType); } - private AnchorPosition anchorPosition = AnchorPosition.NW; - - public static final String PROP_ANCHORPOSITION = "anchorPosition"; - - /** - * get the location within the box where the annotation will be drawn. - * @return anchorPosition - */ - public AnchorPosition getAnchorPosition() { - return anchorPosition; - } - - /** - * set the location within the box where the annotation will be drawn. - * @param anchorPosition - */ - public void setAnchorPosition(AnchorPosition anchorPosition) { - AnchorPosition oldAnchorPosition = this.anchorPosition; - this.anchorPosition = anchorPosition; - firePropertyChange(PROP_ANCHORPOSITION, oldAnchorPosition, anchorPosition); - } - private DasPlot plot; - - PropertyChangeListener plotListener= new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - logger.finer("plot change, resizing"); - resize(); - } - }; - - public void setPlot( DasPlot p ) { - if ( this.plot!=null ) { - this.plot.getXAxis().removePropertyChangeListener(plotListener); - this.plot.getYAxis().removePropertyChangeListener(plotListener); - } - p.getXAxis().addPropertyChangeListener(plotListener); - p.getYAxis().addPropertyChangeListener(plotListener); - this.plot= p; - } - - private DatumRange xrange= DatumRange.newDatumRange(0,10,Units.dimensionless); - - public static final String PROP_XRANGE = "xrange"; - - public DatumRange getXrange() { - return xrange; - } - - public void setXrange(DatumRange xrange) { - DatumRange oldXrange = this.xrange; - this.xrange = xrange; - resize(); - repaint(); - firePropertyChange(PROP_XRANGE, oldXrange, xrange); - } - - private DatumRange yrange= DatumRange.newDatumRange(0,10,Units.dimensionless);; - - public static final String PROP_YRANGE = "yrange"; - - public DatumRange getYrange() { - return yrange; - } - - public void setYrange(DatumRange yrange) { - DatumRange oldYrange = this.yrange; - this.yrange = yrange; - resize(); - repaint(); - firePropertyChange(PROP_YRANGE, oldYrange, yrange); - } - private Datum pointAtX = Datum.create(0); - - public static final String PROP_POINTATX = "pointAtX"; - - public Datum getPointAtX() { - return pointAtX; - } - - public void setPointAtX(Datum pointAtX) { - Datum oldPointAtX = this.pointAtX; - this.pointAtX = pointAtX; - firePropertyChange(PROP_POINTATX, oldPointAtX, pointAtX); - } - - private Datum pointAtY = Datum.create(0); - - public static final String PROP_POINTATY = "pointAtY"; - - public Datum getPointAtY() { - return pointAtY; - } - - public void setPointAtY(Datum pointAtY) { - Datum oldPointAtY = this.pointAtY; - this.pointAtY = pointAtY; - firePropertyChange(PROP_POINTATY, oldPointAtY, pointAtY); - } - - private boolean showArrow = false; - - public static final String PROP_SHOWARROW = "showArrow"; - - public boolean isShowArrow() { - return showArrow; - } - - public void setShowArrow(boolean showArrow) { - boolean oldShowArrow = this.showArrow; - this.showArrow = showArrow; - firePropertyChange(PROP_SHOWARROW, oldShowArrow, showArrow); - } - - - private BorderType anchorBorderType = BorderType.NONE; - - public static final String PROP_ANCHORBORDERTYPE = "anchorBorderType"; - - public BorderType getAnchorBorderType() { - return anchorBorderType; - } - - public void setAnchorBorderType(BorderType anchorBorderType) { - BorderType oldAnchorBorderType = this.anchorBorderType; - this.anchorBorderType = anchorBorderType; - firePropertyChange(PROP_ANCHORBORDERTYPE, oldAnchorBorderType, anchorBorderType); - } - - private AnchorType anchorType = AnchorType.CANVAS; - - public static final String PROP_ANCHORTYPE = "anchorType"; - - public AnchorType getAnchorType() { - return anchorType; - } - - public void setAnchorType(AnchorType anchorType) { - AnchorType oldAnchorType = this.anchorType; - this.anchorType = anchorType; - firePropertyChange(PROP_ANCHORTYPE, oldAnchorType, anchorType); - } private Arrow.HeadStyle arrowStyle = Arrow.HeadStyle.DRAFTING; public static final String PROP_ARROWSTYLE = "arrowStyle"; - /** - * get the arrow style - * @return the arrow style - */ public Arrow.HeadStyle getArrowStyle() { return this.arrowStyle; } - /** - * set the arrow style to BIG,SMALL,DRAFTING. - * @param newarrowStyle the arrow style - */ public void setArrowStyle( Arrow.HeadStyle newarrowStyle) { Arrow.HeadStyle oldarrowStyle = arrowStyle; this.arrowStyle = newarrowStyle; @@ -930,65 +373,5 @@ public void setArrowStyle( Arrow.HeadStyle newarrowStyle) { firePropertyChange(PROP_ARROWSTYLE, oldarrowStyle, newarrowStyle); } - private boolean overrideColors = false; - - public static final String PROP_OVERRIDECOLORS = "overrideColors"; - - public boolean isOverrideColors() { - return overrideColors; - } - - /** - * true will use the colors specified, otherwise the canvas colors are used. - * @param overrideColors - */ - public void setOverrideColors(boolean overrideColors) { - boolean oldOverrideColors = this.overrideColors; - this.overrideColors = overrideColors; - firePropertyChange(PROP_OVERRIDECOLORS, oldOverrideColors, overrideColors); - } - - private Color textColor = new Color(0, 0, 0, 0); - - public static final String PROP_TEXTCOLOR = "textColor"; - - public Color getTextColor() { - return textColor; - } - - /** - * the color of the text, or if transparent then the border - * color should be used. - * - * @param textColor - */ - public void setTextColor(Color textColor) { - Color oldTextColor = this.textColor; - this.textColor = textColor; - repaint(); - firePropertyChange(PROP_TEXTCOLOR, oldTextColor, textColor); - } - - private String anchorOffset=""; - - public static final String PROP_ANCHOROFFSET = "anchorOffset"; - - public String getAnchorOffset() { - return anchorOffset; - } - - /** - * the offset in x and y for the text bubble from the anchor. For - * example, "4em,4em" will place a OutsideNE label 4ems up and 4ems over - * from the default. - * - * @param anchorOffset - */ - public void setAnchorOffset(String anchorOffset) { - String oldAnchorOffset = this.anchorOffset; - this.anchorOffset = anchorOffset; - firePropertyChange(PROP_ANCHOROFFSET, oldAnchorOffset, anchorOffset); - } - } diff --git a/dasCore/src/org/das2/graph/DasAnnotationBeanInfo.java b/dasCore/src/org/das2/graph/DasAnnotationBeanInfo.java new file mode 100644 index 000000000..4a1200659 --- /dev/null +++ b/dasCore/src/org/das2/graph/DasAnnotationBeanInfo.java @@ -0,0 +1,82 @@ +/* File: DasColorBarBeanInfo.java + * Copyright (C) 2002-2003 The University of Iowa + * Created by: Jeremy Faden + * Jessica Swanner + * Edward E. West + * + * This file is part of the das2 library. + * + * das2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.das2.graph; + +import org.das2.beans.DasCanvasComponentBeanInfo; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; +import org.das2.components.propertyeditor.EnumerationEditor; + +/** + * BeanInfo class for DasColorBar + * + * @author Edward West + */ +public class DasAnnotationBeanInfo extends SimpleBeanInfo { + + private static PropertyDescriptor[] properties; + static { + try { + properties = new PropertyDescriptor[] { + new PropertyDescriptor("text", DasAnnotation.class), + new PropertyDescriptor("borderType", DasAnnotation.class), + new PropertyDescriptor("arrowStyle", DasAnnotation.class ), + new PropertyDescriptor("fontSize", DasAnnotation.class ), + }; + properties[1].setPropertyEditorClass( EnumerationEditor.class ); + properties[2].setPropertyEditorClass( EnumerationEditor.class ); + System.err.println("yeah!!!"); + } catch ( IntrospectionException e) { + e.printStackTrace(); + properties= null; + } + + } + + + @Override + public PropertyDescriptor[] getPropertyDescriptors() { + return properties; + } + + @Override + public BeanInfo[] getAdditionalBeanInfo() { + BeanInfo[] additional = { + new DasCanvasComponentBeanInfo(), + }; + return additional; + + /*try { + BeanInfo[] additional = { + Introspector.getBeanInfo( DasAxis.class ), + }; + return additional; + } catch ( IntrospectionException e ) { + throw new RuntimeException(e); + }*/ + } + +} diff --git a/dasCore/src/org/das2/graph/DasAxis.java b/dasCore/src/org/das2/graph/DasAxis.java index 82b69ed54..33906eb5e 100755 --- a/dasCore/src/org/das2/graph/DasAxis.java +++ b/dasCore/src/org/das2/graph/DasAxis.java @@ -22,81 +22,77 @@ */ package org.das2.graph; -import java.util.logging.Level; -import org.das2.event.MouseModule; -import org.das2.event.TimeRangeSelectionEvent; -import org.das2.event.TimeRangeSelectionListener; -import org.das2.event.HorizontalRangeSelectorMouseModule; -import org.das2.event.DataRangeSelectionEvent; -import org.das2.event.DataRangeSelectionListener; -import org.das2.event.VerticalRangeSelectorMouseModule; -import org.das2.event.ZoomPanMouseModule; -import org.das2.datum.format.DefaultDatumFormatterFactory; -import org.das2.datum.DatumRange; -import org.das2.datum.format.DatumFormatter; -import org.das2.datum.Units; -import org.das2.datum.DatumVector; -import org.das2.datum.Datum; -import org.das2.datum.DatumRangeUtil; -import org.das2.datum.InconvertibleUnitsException; -import org.das2.datum.TimeLocationUnits; -import org.das2.DasProperties; -import org.das2.util.GrannyTextRenderer; -import org.das2.util.DasExceptionHandler; -import org.das2.util.DasMath; -import org.das2.DasApplication; import java.awt.*; import java.awt.event.*; import java.awt.geom.AffineTransform; - -import javax.swing.border.*; - import java.awt.geom.GeneralPath; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.*; -import javax.swing.*; import java.util.*; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.regex.*; -import org.das2.system.DasLogger; -import java.util.logging.Logger; +import javax.swing.*; +import javax.swing.border.*; +import org.das2.DasApplication; import org.das2.DasException; -import org.das2.components.DasProgressWheel; -import org.das2.datum.DatumUtil; -import org.das2.datum.DomainDivider; -import org.das2.datum.DomainDividerUtil; -import org.das2.datum.OrbitDatumRange; -import org.das2.datum.UnitsConverter; -import org.das2.datum.UnitsUtil; -import org.das2.system.RequestProcessor; -import org.das2.util.LoggerManager; -import org.das2.util.TickleTimer; -import org.virbo.dataset.ArrayDataSet; -import org.virbo.dataset.DDataSet; -import org.virbo.dataset.JoinDataSet; -import org.virbo.dataset.QDataSet; -import org.virbo.dataset.QFunction; -import org.virbo.dataset.SemanticOps; +import org.das2.DasNameException; +import org.das2.DasProperties; +import org.das2.DasPropertyException; +import org.das2.NameContext; +import org.das2.dasml.FormBase; +import org.das2.dataset.DataSet; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.DataSetUpdateEvent; +import org.das2.dataset.DataSetUpdateListener; +import org.das2.dataset.DataSetUtil; +import org.das2.dataset.VectorDataSet; +import org.das2.datum.Datum; +import org.das2.datum.DatumRange; +import org.das2.datum.DatumRangeUtil; +import org.das2.datum.DatumVector; +import org.das2.datum.InconvertibleUnitsException; +import org.das2.datum.TimeLocationUnits; +import org.das2.datum.TimeUtil; +import org.das2.datum.Units; +import org.das2.datum.format.DatumFormatter; +import org.das2.datum.format.DatumFormatterFactory; +import org.das2.datum.format.DefaultDatumFormatterFactory; +import org.das2.datum.format.TimeDatumFormatterFactory; +import org.das2.event.DataRangeSelectionEvent; +import org.das2.event.DataRangeSelectionListener; +import org.das2.event.HorizontalRangeSelectorMouseModule; +import org.das2.event.MouseModule; +import org.das2.event.TimeRangeSelectionEvent; +import org.das2.event.TimeRangeSelectionListener; +import org.das2.event.VerticalRangeSelectorMouseModule; +import org.das2.event.ZoomPanMouseModule; +import org.das2.system.DasLogger; +import org.das2.util.DasExceptionHandler; +import org.das2.util.DasMath; +import org.das2.util.Entities; +import org.das2.util.GrannyTextRenderer; +import org.das2.util.monitor.NullProgressMonitor; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** * One dimensional axis component that transforms data to device space and back, - * and provides controls for navigating the 1-D data space. + * and provides controls for nagivating the 1-D data space. * @author eew */ - -public class DasAxis extends DasCanvasComponent implements DataRangeSelectionListener, TimeRangeSelectionListener, Cloneable { +public class DasAxis extends DasCanvasComponent + implements DataRangeSelectionListener, TimeRangeSelectionListener, Cloneable { public static final String PROP_LABEL = "label"; + public static final String PROP_Y_LABEL = "yLabel"; public static final String PROP_LOG = "log"; public static final String PROP_OPPOSITE_AXIS_VISIBLE = "oppositeAxisVisible"; public static final String PROP_BOUNDS = "bounds"; - public static final String PROP_SCAN_RANGE="scanRange"; - public static final String PROP_UNITS = "units"; - public static final String PROPERTY_TICKS = "ticks"; - private static final int MAX_TCA_LINES=10; // maximum number of TCA lines /* * PUBLIC CONSTANT DECLARATIONS */ @@ -106,97 +102,40 @@ public class DasAxis extends DasCanvasComponent implements DataRangeSelectionLis public static final int BOTTOM = 2; /** This value indicates that the axis should be located to the left of its cell */ public static final int LEFT = 3; - /** This value indicates that the axis should be located to the right of its cell */ + /** This value indicateds that the axis should be located to the right of its cell */ public static final int RIGHT = 4; /** This value indicates that the axis should be oriented horizontally */ public static final int HORIZONTAL = BOTTOM; /** This value indicates that the axis should be oriented vertically */ public static final int VERTICAL = LEFT; - /** This indicate the axis ticks should go up */ - private static final int UP = 995; - /** This indicates the axis ticks should go down */ - private static final int DOWN = 996; - + /** */ + public static final int UP = 995; + /** */ + public static final int DOWN = 996; /* Constants defining the action commands and labels for the scan buttons. */ - private static final String SCAN_PREVIOUS_LABEL = "<< step"; - private static final String SCAN_NEXT_LABEL = "step >>"; - - // these cannot be set to positive because it pushes the titles out. This needs to be done with care and it's 5:15pm on release day! DEBUG_GRAPHICS needs to be an environment variable, or just a switch. - int downPad=0; // TODO: if we had the graphics, we could get this perfectly, but it doesn't hurt to have extra pixels. - int leftPad=0; - int upPad=0; - int rightPad=0; + private static final String SCAN_PREVIOUS_LABEL = "<< scan"; + private static final String SCAN_NEXT_LABEL = "scan >>"; + + public static String PROP_UNITS= "units"; /* GENERAL AXIS INSTANCE MEMBERS */ protected DataRange dataRange; + public static String PROPERTY_TICKS = "ticks"; - /** - * get the userDatumFormatter, which converts Datums into Strings. This - * can be null if none should be used. - * @return the userDatumFormatter. - */ public DatumFormatter getUserDatumFormatter() { return userDatumFormatter; } - /** - * set the userDatumFormatter, which converts Datums into Strings. This - * can be null if none should be used. - * @param userDatumFormatter the userDatumFormatter. - */ public void setUserDatumFormatter(DatumFormatter userDatumFormatter) { - logger.log(Level.FINE, "setUserDatumFormatter({0})", userDatumFormatter); - DatumFormatter old= this.userDatumFormatter; this.userDatumFormatter = userDatumFormatter; - if ( old!=userDatumFormatter ) { - updateTickV(); - }//TODO: this results in data read on event thread - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - resize(); - repaint(); - } - }); + updateTickV(); + repaint(); } - /** * if non-null, try to use this formatter. */ private DatumFormatter userDatumFormatter = null; - /** - * set the action for the next button - * @param label the label (step or scan) - * @param abstractAction the action to invoke. - */ - public void setNextAction( String label, AbstractAction abstractAction) { - if ( !DasApplication.getDefaultApplication().isHeadless() ) { - ActionListener[] als= this.scanNext.getActionListeners(); - for ( ActionListener al : als ) { - this.scanNext.removeActionListener(al); - } - this.scanNext.setAction(abstractAction); - this.scanNext.setText(""+ label +" >>" ); - } - } - - /** - * set the action for the prev button - * @param label the label (step or scan) - * @param abstractAction the action to invoke. - */ - public void setPreviousAction( String label,AbstractAction abstractAction) { - if ( !DasApplication.getDefaultApplication().isHeadless() ) { - ActionListener[] als= this.scanPrevious.getActionListeners(); - for ( ActionListener al : als ) { - this.scanPrevious.removeActionListener(al); - } - this.scanPrevious.setAction(abstractAction); - this.scanPrevious.setText("<< "+label ); - } - } - /** * until we switch to java 1.5, use this lock object instead of * java.util.concurrent.lock @@ -207,12 +146,6 @@ public interface Lock { public void unlock(); } - - /** - * synchronized block for calculating ticks uses this. - */ - private final Object tickLock= new Object(); - /* Affine Transform, dependent on min, max and axis position * pixel= at_m * data + at_b * where data is data point in linear space (i.e. log property implemented) @@ -223,7 +156,7 @@ public interface Lock { private int tickDirection = 1; // 1=down or left, -1=up or right protected String axisLabel = ""; protected TickVDescriptor tickV; - private boolean autoTickV = true; + protected boolean autoTickV = true; private boolean ticksVisible = true; private boolean tickLabelsVisible = true; private boolean oppositeAxisVisible = false; @@ -234,94 +167,68 @@ public interface Lock { protected JPanel secondaryInputPanel; private ScanButton scanPrevious; private ScanButton scanNext; - - /** - * limits of the scan range. Scan buttons will only be shown with within this range. If not set, then there is no limit to range - * and the buttons are always available. - */ - private DatumRange scanRange; - private boolean animated = ("on".equals(DasProperties.getInstance().get("visualCues"))); /* Rectangles representing different areas of the axis */ private Rectangle blLineRect; private Rectangle trLineRect; - private Rectangle blTickRect; // bottom or left rectangle bounds for the ticks (green in DEBUG_GRAPHICS) - private Rectangle trTickRect; // top or right rectangle bounds for the ticks (green in DEBUG_GRAPHICS) - private Rectangle blLabelRect; // bottom or left rectangle bounds for the tick labels (blue) - private Rectangle trLabelRect; // top or right rectangle bounds for the tick labels (blue) - private Rectangle blTitleRect; // bottom or left rectangle bounds for the axis label (grey) - private Rectangle trTitleRect; // top or right rectangle bounds for the axis label (grey) - private Integer leftXOverride = null; // this is deprecated, use labelOffset instead. - private String labelOffset= ""; // empty string means default, or "5em" or "50px" etc. - + private Rectangle blTickRect; + private Rectangle trTickRect; + private Rectangle blLabelRect; + private Rectangle trLabelRect; + private Rectangle blTitleRect; + private Rectangle trTitleRect; + /** TODO: Currently under implemented! */ private boolean flipped; /* TIME LOCATION UNITS RELATED INSTANCE MEMBERS */ private javax.swing.event.EventListenerList timeRangeListenerList = null; private TimeRangeSelectionEvent lastProcessedEvent = null; /* TCA RELATED INSTANCE MEMBERS */ - private QFunction tcaFunction; - private QDataSet tcaData = null; - private final Object tcaDataLock= new Object(); - + private DataSetDescriptor dsd; + private VectorDataSet[] tcaData = new VectorDataSet[0]; + private DatumFormatter[] tcaFormatters = new DatumFormatter[0]; private String dataset = ""; private boolean drawTca; + private static DatumFormatterFactory formatterFactory + = DefaultDatumFormatterFactory.getInstance(); + private static DatumFormatterFactory timeFormatterFactory + = TimeDatumFormatterFactory.getInstance(); + + public static String PROPERTY_DATUMRANGE = "datumRange"; - private TickleTimer tcaTimer; - - public static final String PROPERTY_DATUMRANGE = "datumRange"; /* DEBUGGING INSTANCE MEMBERS */ - private static final boolean DEBUG_GRAPHICS = false; + private static boolean DEBUG_GRAPHICS = false; private static final Color[] DEBUG_COLORS; - - int tickLen= 0; // this is reset after sizing. - - /** - * the tick length specification, in em or pts. - */ - String tickLenStr= "0.66em"; - - final int TICK_LABEL_GAP_MIN= 4; // minimum number of pixels to label - + static { if (DEBUG_GRAPHICS) { DEBUG_COLORS = new Color[]{ Color.BLACK, Color.RED, Color.GREEN, Color.BLUE, - Color.GRAY, Color.CYAN, Color.MAGENTA, Color.YELLOW.darker(),}; + Color.GRAY, Color.CYAN, Color.MAGENTA, Color.YELLOW, + }; } else { DEBUG_COLORS = null; } } private int debugColorIndex = 0; private DasPlot dasPlot; - private JMenu bookmarksMenu; + private JMenu favoritesMenu; private JMenu backMenu; - private static final Logger logger = LoggerManager.getLogger("das2.graphics.axis"); + private static final Logger logger = DasLogger.getLogger(DasLogger.GRAPHICS_LOG); - /** - * Create an axis object, relating data and canvas pixel coordinates. - * @param min the minimum value - * @param max the maximum value - * @param orientation DasAxis.TOP, DasAxis.BOTTOM, DasAxis.LEFT, DasAxis.RIGHT. + /** TODO + * @param min + * @param max + * @param orientation DasAxis.VERTICAL, DasAxis.HORIZONTAL, DasAxis.RIGHT, etc. */ public DasAxis(Datum min, Datum max, int orientation) { this(min, max, orientation, false); } - /** - * Create an axis object, relating data and canvas pixel coordinates. - * @param range the initial range for the axis. - * @param orientation the position relative to a plot, one of DasAxis.TOP, DasAxis.BOTTOM, DasAxis.LEFT, DasAxis.RIGHT. - */ - public DasAxis(DatumRange range, int orientation) { - this(range.min(), range.max(), orientation); - } - - /** - * Create an axis object, relating data and canvas pixel coordinates. - * @param min the minimum value - * @param max the maximum value - * @param orientation the position relative to a plot, one of DasAxis.TOP, DasAxis.BOTTOM, DasAxis.LEFT, DasAxis.RIGHT. - * @param log if true then the axis is a log axis. + /** TODO Tell us if these are screen or data coordinates + * @param min + * @param max + * @param orientation + * @param log */ public DasAxis(Datum min, Datum max, int orientation, boolean log) { this(orientation); @@ -331,10 +238,9 @@ public DasAxis(Datum min, Datum max, int orientation, boolean log) { copyHistory(); } - /** - * Create an axis object, relating data and canvas pixel coordinates. - * @param range the range object allowing connections between axes. A DataRange is a mutable object. - * @param orientation the position relative to a plot, one of DasAxis.TOP, DasAxis.BOTTOM, DasAxis.LEFT, DasAxis.RIGHT + /** TODO + * @param range + * @param orientation */ protected DasAxis(DataRange range, int orientation) { this(orientation); @@ -344,6 +250,19 @@ protected DasAxis(DataRange range, int orientation) { copyHistory(); } + private void addListenersToDataRange(DataRange range, PropertyChangeListener listener) { + range.addPropertyChangeListener(PROP_LOG, listener); + range.addPropertyChangeListener("minimum", listener); + range.addPropertyChangeListener("maximum", listener); + range.addPropertyChangeListener(DataRange.PROPERTY_DATUMRANGE, listener); + range.addPropertyChangeListener("history", listener); + range.addPropertyChangeListener("favorites", listener); + } + + public DasAxis(DatumRange range, int orientation) { + this(range.min(), range.max(), orientation); + } + private DasAxis(int orientation) { super(); setOpaque(false); @@ -352,116 +271,53 @@ private DasAxis(int orientation) { if (!DasApplication.getDefaultApplication().isHeadless()) { backMenu = new JMenu("Back"); mouseAdapter.addMenuItem(backMenu); - bookmarksMenu = new JMenu("Bookmarks"); - mouseAdapter.addMenuItem(bookmarksMenu); + favoritesMenu = new JMenu("Favorites"); + mouseAdapter.addMenuItem(favoritesMenu); } dataRangePropertyListener = createDataRangePropertyListener(); setLayout(new AxisLayoutManager()); maybeInitializeInputPanels(); maybeInitializeScanButtons(); - if ( !DasApplication.getDefaultApplication().isHeadless() ) { - scanNext.setEnabled( true ); - scanPrevious.setEnabled( true ); - } add(primaryInputPanel); add(secondaryInputPanel); - try { - this.updateTickLength(); - } catch (ParseException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - // this doesn't fire - this.addPropertyChangeListener( "font", new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - try { - updateTickLength(); - } catch (ParseException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - } - }); - - TickMaster.getInstance().register(this); // weak map will unregister - } - private void addListenersToDataRange(DataRange range, PropertyChangeListener listener) { - range.addPropertyChangeListener(PROP_LOG, listener); - range.addPropertyChangeListener("minimum", listener); - range.addPropertyChangeListener("maximum", listener); - range.addPropertyChangeListener(DataRange.PROPERTY_DATUMRANGE, listener); - range.addPropertyChangeListener("history", listener); - range.addPropertyChangeListener("favorites", listener); - } - - /** - * add the range to the favorites list, which is accessible from the popup-menu. - * @param range the range to add. - */ public void addToFavorites(final DatumRange range) { dataRange.addToFavorites(range); copyFavorites(); } - /** - * remove the range from the favorites list, which is accessible from the popup-menu. - * @param range the range to add. - */ - public void removeFromFavorites(final DatumRange range) { - dataRange.removeFromFavorites(range); - copyFavorites(); - } - private void copyFavorites() { if (DasApplication.getDefaultApplication().isHeadless()) { return; } - bookmarksMenu.removeAll(); + favoritesMenu.removeAll(); List favorites = dataRange.getFavorites(); for (Iterator i = favorites.iterator(); i.hasNext();) { final DatumRange r = (DatumRange) i.next(); // copied code from addToFavorites Action action = new AbstractAction(r.toString()) { - @Override + public void actionPerformed(ActionEvent e) { - LoggerManager.logGuiEvent(e); DasAxis.this.setDatumRange(r); } }; JMenuItem menuItem = new JMenuItem(action); - bookmarksMenu.add(menuItem); + favoritesMenu.add(menuItem); } + Action action = new AbstractAction("add to favorites") { - bookmarksMenu.add( new JSeparator() ); - Action action = new AbstractAction("bookmark this range") { - @Override public void actionPerformed(ActionEvent e) { - LoggerManager.logGuiEvent(e); DasAxis.this.addToFavorites(DasAxis.this.getDatumRange()); } }; JMenuItem addItem = new JMenuItem(action); - bookmarksMenu.add(addItem); - - bookmarksMenu.add( new JSeparator() ); - Action action2 = new AbstractAction("remove bookmark for range") { - @Override - public void actionPerformed(ActionEvent e) { - LoggerManager.logGuiEvent(e); - DasAxis.this.removeFromFavorites(DasAxis.this.getDatumRange()); - } - }; - JMenuItem rmItem = new JMenuItem(action2); - bookmarksMenu.add(rmItem); + favoritesMenu.add(addItem); } private void copyHistory() { if (DasApplication.getDefaultApplication().isHeadless()) { return; } - if ( enableHistory==false ) { - return; - } backMenu.removeAll(); List history = dataRange.getHistory(); int ii = 0; @@ -469,9 +325,8 @@ private void copyHistory() { final int ipop = ii; final DatumRange r = (DatumRange) i.next(); // copied code from addToFavorites Action action = new AbstractAction(r.toString()) { - @Override + public void actionPerformed(ActionEvent e) { - LoggerManager.logGuiEvent(e); dataRange.popHistory(ipop); DasAxis.this.setDataRangePrev(); } @@ -482,41 +337,6 @@ public void actionPerformed(ActionEvent e) { } } - /** - * true if the axis built-in history is enabled. - */ - protected boolean enableHistory = true; - - /** - * true if the axis built-in history is enabled. - */ - public static final String PROP_ENABLEHISTORY = "enableHistory"; - - /** - * true if the axis built-in history is enabled. - * @return true if the axis built-in history is enabled. - */ - public boolean isEnableHistory() { - return enableHistory; - } - - /** - * true if the axis built-in history is enabled. - * @param enableHistory true if the axis built-in history is enabled. - */ - public void setEnableHistory(boolean enableHistory) { - boolean oldEnableHistory = this.enableHistory; - this.enableHistory = enableHistory; - if ( ! DasApplication.getDefaultApplication().isHeadless() ) { - if ( !enableHistory ) { - getDasMouseInputAdapter().removeMenuItem(backMenu.getText()); - } else { - getDasMouseInputAdapter().addMenuItem(backMenu); - } - } - firePropertyChange(PROP_ENABLEHISTORY, oldEnableHistory, enableHistory); - } - /* PRIVATE INITIALIZATION FUNCTIONS */ private void maybeInitializeInputPanels() { if (primaryInputPanel == null) { @@ -543,15 +363,14 @@ private void maybeInitializeScanButtons() { private ActionListener createScanActionListener() { return new ActionListener() { - @Override + public void actionPerformed(ActionEvent e) { - LoggerManager.logGuiEvent(e); String command = e.getActionCommand(); - DasLogger.getLogger(DasLogger.GUI_LOG).log(Level.FINE, "event {0}", command); + DasLogger.getLogger(DasLogger.GUI_LOG).fine("event " + command); if (command.equals(SCAN_PREVIOUS_LABEL)) { - if ( scanRange==null || scanRange.intersects(getDatumRange().previous()) ) scanPrevious(); + scanPrevious(); } else if (command.equals(SCAN_NEXT_LABEL)) { - if ( scanRange==null || scanRange.intersects(getDatumRange().next()) ) scanNext(); + scanNext(); } } }; @@ -559,7 +378,7 @@ public void actionPerformed(ActionEvent e) { private PropertyChangeListener createDataRangePropertyListener() { return new PropertyChangeListener() { - @Override + public void propertyChange(PropertyChangeEvent e) { String propertyName = e.getPropertyName(); Object oldValue = e.getOldValue(); @@ -567,33 +386,23 @@ public void propertyChange(PropertyChangeEvent e) { if (propertyName.equals(PROP_LOG)) { update(); firePropertyChange(PROP_LOG, oldValue, newValue); - markDirty("transform:"+propertyName+"="+newValue); } else if (propertyName.equals("minimum")) { update(); firePropertyChange("dataMinimum", oldValue, newValue); - markDirty("transform:"+propertyName+"=" + (DasAxis.this.getUnits().createDatum((Number)newValue))); } else if (propertyName.equals("maximum")) { update(); firePropertyChange("dataMaximum", oldValue, newValue); - markDirty("transform:"+propertyName+"=" + (DasAxis.this.getUnits().createDatum((Number)newValue)) ); } else if (propertyName.equals("favorites")) { copyFavorites(); } else if (propertyName.equals(DataRange.PROPERTY_DATUMRANGE)) { - //if ( UnitsUtil.isTimeLocation( DasAxis.this.getUnits() ) ) { // Autoplot test018_003. - // DatumRange dr= (DatumRange)newValue; - // System.err.println("new range="+dr); - // if ( dr.equals( DatumRangeUtil.parseTimeRangeValid("2000-01-03 09:36 to 2000-01-07 00:00")) ) { - // System.err.println("here min"); - // } - //} update(); firePropertyChange(PROPERTY_DATUMRANGE, oldValue, newValue); - markDirty("transform:"+propertyName+"="+newValue); } else if (propertyName.equals("history")) { if (!dataRange.valueIsAdjusting()) { copyHistory(); } } + markDirty(); } }; } @@ -627,14 +436,8 @@ private void installMouseModules() { } } - /** - * Set the axis orientation. One of: DasAxis.TOP, DasAxis.BOTTOM, - * DasAxis.LEFT, DasAxis.RIGHT - * @param orientation the orientation, one of TOP,BOTTOM,LEFT,RIGHT. - * @see #TOP - * @see #BOTTOM - * @see #LEFT - * @see #RIGHT + /** TODO + * @param orientation */ public void setOrientation(int orientation) { boolean oldIsHorizontal = isHorizontal(); @@ -644,14 +447,11 @@ public void setOrientation(int orientation) { } } - /** - * This is a private internal implementation for + /* This is a private internal implementation for * {@link #setOrientation(int)}. This method is provided * to avoid calling a non-final non-private instance method * from a constructor. Doing so can create problems if the * method is overridden in a subclass. - * - * @param orientation TOP,BOTTOM,LEFT,RIGHT */ private void setOrientationInternal(int orientation) { this.orientation = orientation; @@ -668,60 +468,32 @@ private void setOrientationInternal(int orientation) { } } - /** - * Set the range for the axis. Note this is allowed to change the units as well. - * @param dr the new range. The range must be interval or ratio measurements. - */ public void setDatumRange(DatumRange dr) { - //System.err.println("setDatumRange("+dr+")"); - if ( !UnitsUtil.isIntervalOrRatioMeasurement(dr.getUnits()) ) { - throw new IllegalArgumentException("units cannot be ordinal or nominal"); - } - if ( dr.width().value()==0 ) { - throw new IllegalArgumentException("width is zero: "+dr); - } - DatumRange oldRange= dataRange.getDatumRange(); - Units oldUnits = getUnits(); - if ( !rangeIsAcceptable(dr) ) { - logger.log(Level.INFO, "invalid range ignored{0}", dr); - return; - } - synchronized ( tickLock ) { - if (getUnits().isConvertibleTo(dr.getUnits())) { - //this.setDataRange(dr.min(), dr.max()); - this.dataRange.setRange(dr); - } else { - this.resetRange(dr); - } - } - if ( oldUnits!=dr.getUnits() ) { - firePropertyChange(PROP_UNITS, oldUnits, dr.getUnits()); + if ( getUnits().isConvertableTo(dr.getUnits()) ) { + this.setDataRange(dr.min(), dr.max()); + } else { + Units oldUnits= getUnits(); + this.resetRange(dr); + firePropertyChange( PROP_UNITS, oldUnits, dr.getUnits() ); } - firePropertyChange( PROPERTY_DATUMRANGE, oldRange, dr ); + } - /** - * return the current range - * @return the current range - */ public DatumRange getDatumRange() { return dataRange.getDatumRange(); } /* - * Return true if the range is acceptable, having some non-zero length, - * false otherwise. Note this method is overriden by DasLabelAxis. - * @return true if the range is acceptible. + * @returns true is the range is acceptible, false otherwise. This method + * is overriden by DasLabelAxis. */ protected boolean rangeIsAcceptable(DatumRange dr) { return dr.min().lt(dr.max()); } - /** - * Set the data range. minimum must be less than maximum, but for nominal - * data they can be equal. - * @param minimum the minimum value - * @param maximum the maximum value + /** TODO + * @param minimum + * @param maximum */ public void setDataRange(Datum minimum, Datum maximum) { @@ -732,10 +504,10 @@ public void setDataRange(Datum minimum, Datum maximum) { } DatumRange newRange = new DatumRange(minimum, maximum); - logger.log(Level.FINE, "enter dasAxis.setDataRange( {0} )", newRange); + logger.fine("enter dasAxis.setDataRange( " + newRange + " )"); if (!rangeIsAcceptable(newRange)) { - logger.log(Level.WARNING, "invalid range ignored: {0}", newRange); + logger.warning("invalid range ignored"); return; } @@ -745,11 +517,8 @@ public void setDataRange(Datum minimum, Datum maximum) { max0 = dataRange.getMaximum(); if (dataRange.isLog()) { - min = Math.log10(minimum.doubleValue(getUnits())); - max = Math.log10(maximum.doubleValue(getUnits())); - if ( minimum.doubleValue(getUnits())==0 ) { // avoid log zero - min= max/1000; - } + min = DasMath.log10(minimum.doubleValue(getUnits())); + max = DasMath.log10(maximum.doubleValue(getUnits())); } else { min = minimum.doubleValue(getUnits()); max = maximum.doubleValue(getUnits()); @@ -761,16 +530,11 @@ public void setDataRange(Datum minimum, Datum maximum) { DatumRange oldRange = dataRange.getDatumRange(); dataRange.setRange(newRange); - refreshScanButtons(false); - update(); createAndFireRangeSelectionEvent(); firePropertyChange(PROPERTY_DATUMRANGE, oldRange, newRange); } - /** - * clear the internal history. - */ public void clearHistory() { dataRange.clearHistory(); } @@ -783,9 +547,7 @@ private void createAndFireRangeSelectionEvent() { } } - /** - * set the range to the previous interval. - */ + /** TODO */ public void setDataRangePrev() { logger.fine("enter dasAxis.setDataRangePrev()"); DatumRange oldRange = dataRange.getDatumRange(); @@ -801,9 +563,7 @@ public void setDataRangePrev() { firePropertyChange(PROPERTY_DATUMRANGE, oldRange, newRange); } - /** - * set the range to the next interval. - */ + /** TODO */ public void setDataRangeForward() { logger.fine("enter dasAxis.setDataRangeForward()"); double min0 = dataRange.getMinimum(); @@ -819,22 +579,16 @@ public void setDataRangeForward() { firePropertyChange(PROPERTY_DATUMRANGE, oldRange, newRange); } - /** - * set the range to min-width to max+width. - */ + /** TODO */ public void setDataRangeZoomOut() { logger.fine("enter dasAxis.setDataRangeZoomOut()"); double t1 = dataRange.getMinimum(); double t2 = dataRange.getMaximum(); - double width = t2 - t1; - double min = t1 - width; - double max = t2 + width; + double delta = t2 - t1; + double min = t1 - delta; + double max = t2 + delta; animateChange(t1, t2, min, max); DatumRange oldRange = dataRange.getDatumRange(); - if ( !DatumRangeUtil.isAcceptable( DatumRange.newDatumRange( min, max, getUnits() ), isLog() ) ) { - logger.info("zoom out limit"); - return; - } dataRange.setRange(min, max); DatumRange newRange = dataRange.getDatumRange(); update(); @@ -842,37 +596,34 @@ public void setDataRangeZoomOut() { firePropertyChange(PROPERTY_DATUMRANGE, oldRange, newRange); } - /** - * return the mutable DataRange object. This is used - * to bind axes together by sharing an internal model. - * @return the DataRange. + /** TODO + * @return */ public DataRange getDataRange() { return this.dataRange; } - /** - * @deprecated this is not used. - */ + /** TODO */ protected void deviceRangeChanged() { } - /** - * return the minimum value - * @return the minimum value + /** TODO + * @return */ public Datum getDataMinimum() { return dataRange.getDatumRange().min(); } - /** - * return the maximum value - * @return the maximum value + /** TODO + * @return */ public Datum getDataMaximum() { return dataRange.getDatumRange().max(); } + /* + * + */ /** * This is the preferred method for getting the range of the axis. * @return a DatumRange indicating the range of the axis. @@ -882,45 +633,40 @@ public DatumRange getRange() { return dataRange.getDatumRange(); } - /** - * get the bigger extent of the range of this axis, in the given units. - * @param units the units which the result should be returned in. - * @return the value in the units. + /** TODO + * @param units + * @return */ public double getDataMaximum(Units units) { return getDataMaximum().doubleValue(units); } - /** - * get the smaller extent of the range of this axis, in the given units. - * @param units the units which the result should be returned in. - * @return the value in the units. + /** TODO + * @param units + * @return */ public double getDataMinimum(Units units) { return getDataMinimum().doubleValue(units); } - /** - * set the bigger extent of the range of this axis. - * @param max the new value + /** TODO + * @param max */ public void setDataMaximum(Datum max) { dataRange.setMaximum(max); update(); } - /** - * set the smaller extent of the range of this axis. - * @param min the new value + /** TODO + * @param min */ public void setDataMinimum(Datum min) { dataRange.setMinimum(min); update(); } - /** - * return true if the axis is log. - * @return true if the axis is log. + /** TODO + * @return */ public boolean isLog() { return dataRange.isLog(); @@ -929,100 +675,53 @@ public boolean isLog() { /** * Set the axis to be log or linear. If necessary, axis range will be * adjusted to make the range valid. - * @param log true if the axis should be log. + * @param log */ public void setLog(boolean log) { boolean oldLog = isLog(); - DatumRange range = getDatumRange(); + DatumRange range= getDatumRange(); dataRange.setLog(log); update(); if (log != oldLog) { firePropertyChange(PROP_LOG, oldLog, log); } // switching log can change the axis range. - if (!range.equals(getDatumRange())) { - firePropertyChange(PROPERTY_DATUMRANGE, range, getDatumRange()); + if ( ! range.equals(getDatumRange()) ) { + firePropertyChange( PROPERTY_DATUMRANGE, range, getDatumRange() ); } } - /** - * return the units of the axis. - * @return the units of the axis. + /** TODO + * @return */ public Units getUnits() { return dataRange.getUnits(); } - /** - * set the units of the axis, which must be convertible from the old units. - * @param newUnits the units of the axis. - * @see #resetRange(org.das2.datum.DatumRange) - */ public void setUnits(Units newUnits) { dataRange.setUnits(newUnits); } - - /** - * limit the scan buttons to operate within this range. - * http://sourceforge.net/p/autoplot/bugs/473/ - * @param range the range to limit the scanning - */ - public void setScanRange( DatumRange range ) { - DatumRange old= this.scanRange; - this.scanRange= range; - if ( scanNext!=null ) { // headless will have null scanNext - SwingUtilities.invokeLater( new Runnable() { - @Override - public void run() { - DatumRange range= getScanRange(); - if ( range==null ) { - scanNext.setToolTipText(null); - scanPrevious.setToolTipText(null); - } else { - scanNext.setToolTipText("scan limited to
    "+range.toString()); - scanPrevious.setToolTipText("scan limited to
    "+range.toString()); - } - } - }); - } - firePropertyChange( PROP_SCAN_RANGE, old, range ); - } /** - * get the limit the scan buttons, which may be null. - * @return the limit the scan buttons, which may be null. - */ - public DatumRange getScanRange( ) { - return this.scanRange; - } - - /** - * set the axis to the new range, allowing the units to change. - * @param range the new range + * changes the units of the axis to a new unit. */ public synchronized void resetRange(DatumRange range) { - DatumRange oldRange= this.getDatumRange(); if (range.getUnits() != this.getUnits()) { - synchronized ( tickLock ) { - if (dasPlot != null) { - dasPlot.invalidateCacheImage(); - } - logger.log(Level.FINEST, "replaceRange({0})", range); - dataRange.resetRange(range); + if (dasPlot != null) { + dasPlot.invalidateCacheImage(); } - setScanRange(null); + logger.finest("replaceRange(" + range + ")"); + dataRange.resetRange(range); } else { dataRange.setRange(range); } updateTickV(); - markDirty("range"); - firePropertyChange(PROPERTY_DATUMRANGE, oldRange, range); + markDirty(); update(); } - /** - * if true, then the axis and its ticks on the opposite side will also be drawn. - * @param visible if true, then the axis and its ticks on the opposite side will also be drawn. + /** TODO + * @param visible */ public void setOppositeAxisVisible(boolean visible) { if (visible == oppositeAxisVisible) { @@ -1035,81 +734,57 @@ public void setOppositeAxisVisible(boolean visible) { firePropertyChange(PROP_OPPOSITE_AXIS_VISIBLE, oldValue, visible); } - /** - * return true if the opposite side is also drawn. - * @return true if the opposite side is also drawn. + /** TODO + * @return */ public boolean isOppositeAxisVisible() { return oppositeAxisVisible; } - /** - * set the label for the axis. - * - * The label for this axis is displayed below the ticks for horizontal axes + /** Mutator method for the title property of this axis. + * + * The title for this axis is displayed below the ticks for horizontal axes * or to left of the ticks for vertical axes. - * @param t The new label for this axis + * @param t The new title for this axis */ public void setLabel(String t) { if (t == null) { throw new NullPointerException("axis label cannot be null"); } Object oldValue = axisLabel; - axisLabel = t; + axisLabel = t; update(); firePropertyChange(PROP_LABEL, oldValue, t); } /** - * get the label for the axis. + * Accessor method for the title property of this axis. + * * @return A String instance that contains the title displayed - * for this axis, or "" if the axis has no title. + * for this axis, or null if the axis has no title. */ public String getLabel() { return axisLabel; } - /** - * true if the axis is animated. Transitions in axis position are drawn - * rapidly to animate the transition. - * @return true if the axis is animated. + /** Getter for property animated. + * @return Value of property animated. */ public boolean isAnimated() { return this.animated; } - /** - * if true then the axis will be animated. - * @param animated if true then the axis will be animated. + /** Setter for property animated. + * @param animated new value of property animated. */ public void setAnimated(boolean animated) { this.animated = animated; } - /** - * if true then additional ephemeris (TCA) ticks are drawn. - * @return if true then additional ticks are drawn. - * @deprecated use isDrawTca() - */ public boolean getDrawTca() { - return isDrawTca(); - } - - /** - * true if additional tick labels are drawn using the TCA function. - * @return true if additional ticks will be drawn. - * @see #setTcaFunction(org.virbo.dataset.QFunction) - */ - public boolean isDrawTca() { return drawTca; } - - /** - * if true then turn on additional tick labels using the TCA function. - * @param b if true then additional ticks will be drawn. - * @see #setTcaFunction(org.virbo.dataset.QFunction) - */ public void setDrawTca(boolean b) { boolean oldValue = drawTca; if (b && getOrientation() != BOTTOM) { @@ -1119,58 +794,17 @@ public void setDrawTca(boolean b) { return; } drawTca = b; - markDirty("drawTca"); + markDirty(); update(); firePropertyChange("showTca", oldValue, b); } - private static QFunction tcaFunction( String dataset ) throws DasException { - QFunction result= null; - if ( dataset.startsWith("/") ) { - throw new IllegalArgumentException("das2 legacy TCA stuff needs to be implemented"); - } else if ( dataset.startsWith("class:") ) { - try { - try { - // class:org.autoplot.tca.AutoplotTCASource:vap+file:/tmp/foo.txt?rank2=field1-field4&depend0=field0 - int argPos= dataset.indexOf(':',6); - String className; - String arg= null; - if ( argPos==-1 ) { - className= dataset.substring(6); - result = (QFunction) Class.forName(className).newInstance(); - } else { - className= dataset.substring(6,argPos); - arg= dataset.substring(argPos+1); - try { - result = (QFunction) Class.forName(className).getConstructor(String.class).newInstance(arg); - } catch ( Exception ex ) { //TODO: more precise - throw new DasException(ex); - } - } - - } catch (InstantiationException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } catch (IllegalAccessException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - } catch (ClassNotFoundException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - } - return result; - } - - /** - * return the path for the TCA dataset. This will be a das2server data set - * address, such as http://www-pw.physics.uiowa.edu/das/das2Server?voyager/tca/earth - * @return the path of the TCA dataset, or "". - */ public String getDataPath() { return dataset; } - + /** - * @see setDrawTca which turns on additional ticks. + * * @param dataset The URL identifier string of a TCA data set, or "" for no TCAs. */ public void setDataPath(String dataset) { @@ -1182,207 +816,119 @@ public void setDataPath(String dataset) { return; } this.dataset = dataset; - if (dataset.equals("")) { - tcaFunction = null; - this.tcaData= null; + dsd = null; } else { try { - tcaFunction= tcaFunction( dataset ); - maybeStartTcaTimer(); - this.tcaData= null; - if ( tcaFunction==null ) { - throw new IllegalArgumentException("unable to implement tca QFunction: "+dataset ); - } - //tcaFunction = DataSetDescriptor.create(dataset); - } catch (org.das2.DasException de) { + dsd = DataSetDescriptor.create(dataset); + } catch ( org.das2.DasException de) { DasExceptionHandler.handle(de); } } - markDirty("tcaDataPath"); + markDirty(); update(); firePropertyChange("dataPath", oldValue, dataset); } - - private void maybeStartTcaTimer() { - final DasCanvas lcanvas= getCanvas(); - final Object tcaLock= "tcastart_"+this.getDasName(); - if ( lcanvas!=null ) { - lcanvas.registerPendingChange( this, tcaLock ); - } else { - logger.log( Level.FINER, "canvas is not yet set, returning"); - return; - } - if ( tcaTimer==null ) { - tcaTimer= new TickleTimer( 200, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - DasCanvas mycanvas= getCanvas(); - logger.log( Level.FINER, "mstca, mycanvas={0}", mycanvas); - if ( lcanvas!=null ) { - mycanvas= lcanvas; // I don't understand this. When we entered maybeStartTcaTimer, lcanvas was not null. - // Be careful that this canvas is the same one that set the lock! - } - if ( mycanvas!=null ) { - mycanvas.performingChange( DasAxis.this, tcaLock ); - } else { - maybeStartTcaTimer(); - return; - } - try { - updateTCASoon(); - } finally { - mycanvas.changePerformed( DasAxis.this, tcaLock ); - } - } - }); - tcaTimer.tickle("startTcaTimer"); - } else { - tcaTimer.tickle("startTcaTimer"); - } - } - - /** - * Add auxiliary data to an axis (usually OrbitAttitude data for a time axis). + + /** Add auxiliary data to an axis (usually OA data for a time axis). * This function does the same thing as setDataPath, but with a different interface. - * The QFunction must have one input parameter which will be positions on this axis - * (e.g. times from a time axis). - * @see setDrawTca which turns on additional ticks. - * @param f will be called upon to generate auxiliary data sets, or null to disable. - */ - public synchronized void setTcaFunction( QFunction f ) { - QFunction oldF= this.tcaFunction; - this.tcaFunction= f; - maybeStartTcaTimer(); - markDirty("tcaFunction"); + * dsdAux must return a non-empty string from getDataSetID() + * @param dsdAux will be called upon to generate auxiliary data sets. To avoid nonsensical + * graphs the X axis for this dataset must be the same as the that handed to the + * renderer. + */ + public void setDataSetDescriptor(DataSetDescriptor dsdAux) { + if (dsdAux == null) { + throw new NullPointerException("null DataSetDescriptor not allowed"); + } + DataSetDescriptor oldVal = dsd; + dsd = dsdAux; + markDirty(); update(); - firePropertyChange("dataSetDescriptor", null, null ); - firePropertyChange("dataPath", null, null ); - firePropertyChange("tcaFunction", oldF, f ); - - } + String oldDataset = dataset; + dataset = dsd.getDataSetID(); + firePropertyChange("dataSetDescriptor", oldVal, dsd); + firePropertyChange("dataPath", oldDataset, dataset); + } + + public DataSetDescriptor getDataSetDescriptor() { + return dsd; + } + + public void setTcaData(VectorDataSet ds) { + logger.fine("got TCADataSet"); + List itemList = (List) ds.getProperty("plane-list"); + VectorDataSet[] newData = new VectorDataSet[itemList.size()]; + DatumFormatter[] newFormatters = new DatumFormatter[itemList.size()]; + newData[0] = ds; + for (int i = 1; i < itemList.size(); i++) { + newData[i] = (VectorDataSet) ds.getPlanarView((String) itemList.get(i)); + } + for (int i = 0; i < newData.length; i++) { + String format = (String)newData[i].getProperty("format"); + newFormatters[i] = getFormatter(format, newData[i].getYUnits()); + } + tcaData = newData; + tcaFormatters = newFormatters; + update(); + } + + private DatumFormatter getFormatter(String format, Units u) { + if (format == null) return null; + try { + if (u.isConvertableTo(Units.t2000)) { + return timeFormatterFactory.newFormatter(format); + } + return formatterFactory.newFormatter(format); + } + catch (ParseException ex) { + return null; + } + catch (IllegalArgumentException ex) { + return null; + } + } + + private final DataSetUpdateListener tcaListener = new DataSetUpdateListener() { + + @Override + public void dataSetUpdated(DataSetUpdateEvent e) { + VectorDataSet ds = (VectorDataSet) e.getDataSet(); + if (ds == null) { + logger.warning(String.valueOf(e.getException())); + return; + } + setTcaData(ds); + } + }; - /** - * update the TCAs using the QFunction this.tcaFunction. We get an example - * input from the function, then call it for each tick. We add the property - * "CONTEXT_0" to be a BINS dataset indicating the overall range for the - * read. - */ private void updateTCADataSet() { - QFunction ltcaFunction= this.tcaFunction; - if ( ltcaFunction==null ) { - this.tcaData= null; - return; - } - logger.fine("updateTCADataSet"); - - if ( valueIsAdjusting() ) { - logger.finest("someone is adjusting this, wait until later to call."); - return; - } - - Units u= getUnits(); - DatumVector tickVDV= getTickV().tickV; - if ( !u.isConvertibleTo(tickVDV.getUnits()) ) { - return; // transitional state + double[] tickV = getTickV().tickV.toDoubleArray(getUnits()); + Datum data_minimum; + Datum data_maximum; + Datum iinterval; + if (tickV.length == 1) { + data_minimum = Datum.create(tickV[0], getTickV().units); + data_maximum = Datum.create(tickV[0], getTickV().units); + iinterval = data_maximum.subtract(data_minimum); + } else { + data_minimum = Datum.create(tickV[0], getTickV().units); + data_maximum = Datum.create(tickV[tickV.length - 1], getTickV().units); + iinterval = (data_maximum.subtract(data_minimum)).divide(tickV.length - 1); } - double[] ltickV = tickVDV.toDoubleArray(u); - - DDataSet dep0= DDataSet.createRank1(ltickV.length); - dep0.putProperty(QDataSet.UNITS,u); - - logger.log(Level.FINEST, "update for {0} to {1}", new Object[]{tickVDV.get(0), tickVDV.get(tickVDV.getLength()-1)}); - - try { - - JoinDataSet ltcaData= new JoinDataSet(2); - ArrayDataSet ex= ArrayDataSet.copy( ltcaFunction.exampleInput() ); // can be rank 0 or rank 1. - QDataSet bds= (QDataSet) ex.property(QDataSet.BUNDLE_0); - Units tcaUnits; - if ( bds==null ) { - logger.info("no bundle descriptor, dealing with it."); - tcaUnits= (Units) ex.property( QDataSet.UNITS, 0 ); - } else { - tcaUnits= (Units)bds.property( QDataSet.UNITS, 0 ); - } - if ( tcaUnits==null ) tcaUnits=Units.dimensionless; + data_maximum = data_maximum.add(iinterval); + final Datum interval = iinterval; + tcaData = null; + tcaFormatters = null; - UnitsConverter uc; - if ( !u.isConvertibleTo(tcaUnits) ) { - logger.info("tca units are not convertable"); - return; - } - uc= UnitsConverter.getConverter( u, tcaUnits ); - - DatumRange context= getDatumRange(); // this may not contain all the ticks. - context= DatumRangeUtil.union( context, u.createDatum( uc.convert(ltickV[0]) ) ); - context= DatumRangeUtil.union( context, u.createDatum( uc.convert(ltickV[ltickV.length-1]) ) ); - ex.putProperty( QDataSet.CONTEXT_0, 0, org.virbo.dataset.DataSetUtil.asDataSet( context ) ); - QDataSet dx= org.virbo.dataset.DataSetUtil.asDataSet( getDatumRange().width().divide( getColumn().getWidth() ) ); - ex.putProperty( QDataSet.DELTA_PLUS, 0, dx ); - ex.putProperty( QDataSet.DELTA_MINUS, 0, dx ); - - QDataSet outDescriptor=null; - - QDataSet ticks1= null; + this.dsd.requestDataSet(data_minimum, data_maximum.add(Datum.create(1.0, Units.seconds)), interval, new NullProgressMonitor(), getCanvas(), tcaListener); - JoinDataSet timeDs= new JoinDataSet(ex.rank()+1); - for ( int i=0; i=8; - } - - private void updateDomainDivider() { - DatumRange dr = getDatumRange(); - - majorTicksDomainDivider= DomainDividerUtil.getDomainDivider( dr.min(), dr.max(), isLog() ); - - while ( majorTicksDomainDivider.boundaryCount(dr.min(), dr.max() ) > 100 ) { - majorTicksDomainDivider= majorTicksDomainDivider.coarserDivider(false); - } - - DatumVector major = majorTicksDomainDivider.boundaries(dr.min(), dr.max()); - DatumVector major1 = majorTicksDomainDivider.finerDivider(false).boundaries(dr.min(), dr.max()); - - DatumFormatter df; - df = DomainDividerUtil.getDatumFormatter(majorTicksDomainDivider, dr); - while ( !hasLabelCollisions(major1,df)) { - majorTicksDomainDivider = majorTicksDomainDivider.finerDivider(false); - if ( majorTicksDomainDivider.boundaryCount( dr.min(), dr.max() ) <=1 ) { - continue; - } - df = DomainDividerUtil.getDatumFormatter(majorTicksDomainDivider, dr); - major= major1; - major1 = majorTicksDomainDivider.finerDivider(false).boundaries(dr.min(), dr.max()); - } - - while ( hasLabelCollisions(major,df)) { - majorTicksDomainDivider = majorTicksDomainDivider.coarserDivider(false); - df = DomainDividerUtil.getDatumFormatter(majorTicksDomainDivider, dr); - major = majorTicksDomainDivider.boundaries(dr.min(), dr.max()); - } - - while (major.getLength() < 2) { - majorTicksDomainDivider = majorTicksDomainDivider.finerDivider(false); - major = majorTicksDomainDivider.boundaries(dr.min(), dr.max()); - df = DomainDividerUtil.getDatumFormatter(majorTicksDomainDivider, dr); - } - - DomainDivider minorTickDivider= majorTicksDomainDivider; - DatumVector minor = major; - DatumVector minor1 = minorTickDivider.finerDivider(true).boundaries(dr.min(), dr.max()); - while ( ! hasTickCollisions(minor1) ) { - minorTickDivider= minorTickDivider.finerDivider(true); - minor= minor1; - minor1= minorTickDivider.finerDivider(true).boundaries(dr.min(), dr.max()); - } - //while ( ! hasTickCollisions(minor1) ) { - //if ( ! hasTickCollisions(minor1) ) { - //minorTickDivider= minorTickDivider.finerDivider(true); - //minor= minor1; - //minor1= minorTickDivider.finerDivider(true).boundaries(dr.min(), dr.max()); - //} - minorTickDivider.boundaries(dr.min(), dr.max()); - this.minorTicksDomainDivider= minorTickDivider; - - this.tickV = TickVDescriptor.newTickVDescriptor(major, minor); - dividerDatumFormatter= DomainDividerUtil.getDatumFormatter(majorTicksDomainDivider, dr); - - datumFormatter = resolveFormatter(tickV); - - } - - /** - * calculate a set of linear spaced ticks based on the DomainDivider class. - * @param dr the range. - * @return the ticks. - */ - private TickVDescriptor updateTickVDomainDivider( DatumRange dr ) { - - try { - long nminor= minorTicksDomainDivider.boundaryCount( dr.min(), dr.max() ); - while ( nminor>=DomainDivider.MAX_BOUNDARIES ) { - //TODO: what should we do here? Transitional state? - return this.tickV; - } - DatumVector major = majorTicksDomainDivider.boundaries(dr.min(), dr.max()); - DatumVector minor = minorTicksDomainDivider.boundaries(dr.min(), dr.max()); - - TickVDescriptor tickV1 = TickVDescriptor.newTickVDescriptor(major, minor); - tickV1.datumFormatter= dividerDatumFormatter; - - return tickV1; - } catch ( InconvertibleUnitsException ex ) { - // it's okay to do nothing. - return tickV; - } - } - - /** - * calculate a set of time ticks. - * @param dr the range. - * @return the ticks. - */ - private TickVDescriptor updateTickVTime( DatumRange dr ) { + private void updateTickVTime() { int nTicksMax; + DatumRange dr = getDatumRange(); Datum pixel = dr.width().divide(getDLength()); DatumFormatter tdf; - TickVDescriptor ltickV; - FontMetrics fm = getFontMetrics(getTickLabelFont()); - if (isHorizontal()) { // two passes to avoid clashes -- not guarenteed - ltickV = TickVDescriptor.bestTickVTime(dr.min().subtract(pixel), dr.max().add(pixel), 3, 8, false); + tickV = TickVDescriptor.bestTickVTime(dr.min().subtract(pixel), dr.max().add(pixel), 3, 8, false); - tdf = resolveFormatter(ltickV); - Rectangle bounds = getMaxBounds(tdf, ltickV); + tdf = resolveFormatter(tickV); + Rectangle bounds = getMaxBounds(tdf, tickV); int tickSizePixels = (int) (bounds.width + getEmSize() * 2); if (drawTca) { + FontMetrics fm = getFontMetrics(getTickLabelFont()); String item = format(99999.99, "(f8.2)"); int width = fm.stringWidth(item) + (int) (getEmSize() * 2); if (width > tickSizePixels) { @@ -1807,13 +1158,14 @@ private TickVDescriptor updateTickVTime( DatumRange dr ) { int axisSize = getColumn().getWidth(); nTicksMax = Math.max(2, axisSize / tickSizePixels); - ltickV = TickVDescriptor.bestTickVTime( dr.min(), dr.max(), 2, nTicksMax, false); - tdf = resolveFormatter(ltickV); + tickV = TickVDescriptor.bestTickVTime(getDataMinimum(), getDataMaximum(), 2, nTicksMax, false); + tdf = resolveFormatter(tickV); - bounds = getMaxBounds(tdf, ltickV); + bounds = getMaxBounds(tdf, tickV); tickSizePixels = (int) (bounds.getWidth() + getEmSize() * 2); if (drawTca) { + FontMetrics fm = getFontMetrics(getTickLabelFont()); String item = format(99999.99, "(f8.2)"); int width = fm.stringWidth(item); if (width > tickSizePixels) { @@ -1828,21 +1180,21 @@ private TickVDescriptor updateTickVTime( DatumRange dr ) { boolean overlap = true; while (overlap && nTicksMax > 2) { - ltickV = TickVDescriptor.bestTickVTime( dr.min(), dr.max(), 2, nTicksMax, false); + tickV = TickVDescriptor.bestTickVTime(getDataMinimum(), getDataMaximum(), 2, nTicksMax, false); - if (ltickV.getMajorTicks().getLength() <= 1) { + if (tickV.getMajorTicks().getLength() <= 1) { // we're about to have an assertion error, time to debug; - logger.log(Level.INFO, "about to assert error: {0}", ltickV.getMajorTicks()); + System.err.println("about to assert error: " + tickV.getMajorTicks()); } - assert (ltickV.getMajorTicks().getLength() > 1); + assert (tickV.getMajorTicks().getLength() > 1); - tdf = resolveFormatter(ltickV); + tdf = resolveFormatter(tickV); - bounds = getMaxBounds(tdf, ltickV); + bounds = getMaxBounds(tdf, tickV); tickSizePixels = (int) (bounds.getWidth() + getEmSize() * 2); - double x0 = transform(ltickV.getMajorTicks().get(0)); - double x1 = transform(ltickV.getMajorTicks().get(1)); + double x0 = transform(tickV.getMajorTicks().get(0)); + double x1 = transform(tickV.getMajorTicks().get(1)); if (x1 - x0 > tickSizePixels) { overlap = false; @@ -1850,219 +1202,83 @@ private TickVDescriptor updateTickVTime( DatumRange dr ) { nTicksMax = nTicksMax - 1; } } - ltickV = TickVDescriptor.bestTickVTime( dr.min(), dr.max(), 2, nTicksMax, true); + tickV = TickVDescriptor.bestTickVTime(getDataMinimum(), getDataMaximum(), 2, nTicksMax, true); } else { - int tickSizePixels = fm.getHeight(); + int tickSizePixels = getFontMetrics(getTickLabelFont()).getHeight(); int axisSize = getRow().getHeight(); nTicksMax = axisSize / tickSizePixels; nTicksMax = (nTicksMax > 1 ? nTicksMax : 2); nTicksMax = (nTicksMax < 10 ? nTicksMax : 10); - ltickV = TickVDescriptor.bestTickVTime( dr.min(), dr.max(), 3, nTicksMax, true); + tickV = TickVDescriptor.bestTickVTime(getDataMinimum(), getDataMaximum(), 3, nTicksMax, true); } - datumFormatter = resolveFormatter(ltickV); - - return ltickV; + datumFormatter = resolveFormatter(tickV); + if (drawTca && !dataset.equals("") && dsd != null) { + updateTCADataSet(); + } } - /** - * update the TCA (ephemeris) axis on the current thread. - * The lock will have pendingChange and changePerformed with it. - */ - private void updateTCAImmediately( ) { - logger.fine("enter updateTCAImmediately..."); - synchronized (this) { - logger.fine("...got lock."); - final DasProgressWheel tcaProgress= new DasProgressWheel(); - tcaProgress.started(); - Runnable run= new Runnable() { - @Override - public void run() { - tcaProgress.getPanel(DasAxis.this); + public synchronized void updateTickV() { + if ( !valueIsAdjusting() ) { + if (autoTickV) { + TickVDescriptor oldTicks = this.tickV; + if (getUnits() instanceof TimeLocationUnits) { + updateTickVTime(); + } else if (dataRange.isLog()) { + updateTickVLog(); + } else { + updateTickVLinear(); } - }; - SwingUtilities.invokeLater(run); - - try { - updateTCADataSet(); - } finally { - tcaProgress.finished(); - repaint(); + firePropertyChange(PROPERTY_TICKS, oldTicks, this.tickV); } } - } - /** - * update the TCA dataset. This will load the TCAs on a RequestProcessor thread sometime soon. - */ - private void updateTCASoon() { - final DasCanvas lcanvas= getCanvas(); - logger.log(Level.FINE, "updateTCASoon {0}", lcanvas); - if ( lcanvas!=null ) { - final Object tcaLock= "tcaload_"+this.getDasName(); - lcanvas.registerPendingChange( this, tcaLock ); - RequestProcessor.invokeLater( new Runnable() { - @Override - public void run() { - lcanvas.performingChange( DasAxis.this, tcaLock ); - try { - updateTCAImmediately( ); - } finally { - lcanvas.changePerformed( DasAxis.this, tcaLock ); - } - } - }, DasAxis.this ); - } } + private String errorMessage; /** - * call-back for TickMaster - * @param ticks the new ticks. + * checks the validity of the state, setting variable errorMessage to non-null if there is a problem. */ - protected void resetTickV( TickVDescriptor ticks ) { - TickVDescriptor oldTicks = this.tickV; - this.tickV= ticks; - datumFormatter = resolveFormatter(tickV); - if ( drawTca && tcaFunction != null ) { - tcaTimer.tickle("resetTickV"); - } - firePropertyChange(PROPERTY_TICKS, oldTicks, this.tickV); - repaint(); - } + private void checkState() { + double dmin = getDataMinimum(dataRange.getUnits()); + double dmax = getDataMaximum(dataRange.getUnits()); - private boolean getAutoTickV() { - return autoTickV; - } - - /** - * recalculate the tick positions. - */ - protected void updateTickV() { - boolean lautoTickV= getAutoTickV(); - DatumRange dr= getDatumRange(); - if ( !dr.min().isFinite() && !dr.max().isFinite() ) { - logger.info( "range is not finite..."); - return; + String em = ""; + + if (Double.isNaN(dmin)) { + em += "dmin is NaN, "; } - if (!valueIsAdjusting()) { - if ( getFont()==null ) return; - - //if ( getCanvas()==null || getCanvas().getHeight()==0 ) return; - //if ( ( isHorizontal() ? getColumn().getWidth() : getRow().getHeight() ) < 2 ) return; // canvas is not sized yet - if ( useDomainDivider ) { - updateDomainDivider(); //TODO: doesn't consider width of TCAs. - } else { - this.majorTicksDomainDivider= null; - } - if (lautoTickV) { - if ( getDatumRange().width().value()==0 ) { - throw new IllegalArgumentException("datum range width is zero" ); - } - if (majorTicksDomainDivider != null) { - TickVDescriptor newTicks= updateTickVDomainDivider(dr); - TickMaster.getInstance().offerTickV( this, newTicks ); - } else { - TickVDescriptor newTicks; - synchronized ( tickLock ) { - if (getUnits() instanceof TimeLocationUnits) { - newTicks= updateTickVTime(dr); - } else if (dataRange.isLog()) { - newTicks= updateTickVLog(dr); - } else { - newTicks= updateTickVLinear(dr); - } - } - //resetTickV(newTicks); - if ( this.tickV==null ) resetTickV( newTicks ); // transition cases, pngwalk. - TickMaster.getInstance().offerTickV( this, newTicks ); - } - } - } else { - if (lautoTickV) { - try { - if (majorTicksDomainDivider != null) { - TickVDescriptor newTicks= updateTickVDomainDivider(dr); - TickMaster.getInstance().offerTickV( this, newTicks ); - } else { - TickVDescriptor newTicks; - if (getUnits() instanceof TimeLocationUnits) { - newTicks= updateTickVTime(dr); - } else if (dataRange.isLog()) { - newTicks= updateTickVLog(dr); - } else { - newTicks= updateTickVLinear(dr); - } - //resetTickV(newTicks); - if ( this.tickV==null ) this.tickV= newTicks; // transition cases - TickMaster.getInstance().offerTickV( this, newTicks ); - } - } catch ( NullPointerException ex ) { - logger.log( Level.WARNING, ex.toString(), ex ); - } - } + if (Double.isNaN(dmax)) { + em += "dmax is NaN, "; + } + if (Double.isInfinite(dmin)) { + em += "dmin is infinite, "; + } + if (Double.isInfinite(dmax)) { + em += "dmax is infinite, "; + } + if (dmin >= dmax) { + em += "min => max, "; + } + if (dataRange.isLog() && dmin <= 0) { + em += "min<= 0 and log, "; } + if (em.length() == 0) { + this.errorMessage = null; + } else { + this.errorMessage = em; + } } -// private String errorMessage; -// -// /** -// * checks the validity of the state, setting variable errorMessage to non-null if there is a problem. -// */ -// private void checkState() { -// double dmin = getDataMinimum(dataRange.getUnits()); -// double dmax = getDataMaximum(dataRange.getUnits()); -// -// String em = ""; -// -// if (Double.isNaN(dmin)) { -// em += "dmin is NaN, "; -// } -// if (Double.isNaN(dmax)) { -// em += "dmax is NaN, "; -// } -// if (Double.isInfinite(dmin)) { -// em += "dmin is infinite, "; -// } -// if (Double.isInfinite(dmax)) { -// em += "dmax is infinite, "; -// } -// if (dmin >= dmax) { -// em += "min => max, "; -// } -// if (dataRange.isLog() && dmin <= 0) { -// em += "min<= 0 and log, "; -// } -// -// if (em.length() == 0) { -// this.errorMessage = null; -// } else { -// this.errorMessage = em; -// } -// } - /** - * paints the axis component. The tickV's and bounds should be calculated at this point - * @param graphics the graphics context. - */ - @Override + /** paints the axis component. The tickV's and bounds should be calculated at this point */ protected void paintComponent(Graphics graphics) { - logger.finest("enter DasAxis.paintComponent"); - - if (getCanvas().isValueAdjusting()) { - return; - } - - try { - updateTickLength(); - } catch (ParseException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - + logger.fine("enter DasAxis.paintComponent"); /* This was code was keeping axes from being printed on PC's Shape saveClip = null; if (getCanvas().isPrintingThread()) { @@ -2070,20 +1286,7 @@ protected void paintComponent(Graphics graphics) { graphics.setClip(null); } */ - logger.log(Level.FINEST, "DasAxis clip={0} @ {1},{2}", new Object[]{graphics.getClip(), getX(), getY()}); -// Here's an effective way to debug axis bounds: -// if ( "axis_0".equals( getDasName() ) ) { -// System.err.println("DasAxis clip=" + graphics.getClip() + " @ " + getX() + "," + getY()); -// Rectangle rr= graphics.getClip().getBounds(); -// if ( rr.getHeight()==376 ) { -// System.err.println(" here"); -// //return; -// } else { -// System.err.println(" here2"); -// //return; -// } -// graphics.drawRoundRect( rr.x, rr.y, rr.width, rr.height, 30, 30 ); -// } + logger.fine("DasAxis clip=" + graphics.getClip() + " @ "+getX()+","+getY() ); Graphics2D g = (Graphics2D) graphics.create(); //g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); @@ -2091,7 +1294,7 @@ protected void paintComponent(Graphics graphics) { //g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); g.translate(-getX(), -getY()); g.setColor(getForeground()); - + /* Debugging code */ /* The compiler will optimize it out if DEBUG_GRAPHICS == false */ if (DEBUG_GRAPHICS) { @@ -2137,26 +1340,14 @@ protected void paintComponent(Graphics graphics) { } /* End debugging code */ - TickVDescriptor tickV1; - tickV1= this.tickV; //findbugs IS2_INCONSISTENT_SYNC. This caused deadlock. I think accessing tickV once is a correct fix and doesn't need to be synchronized. - if (tickV1 == null || tickV1.tickV.getUnits().isConvertibleTo(getUnits())) { + if (tickV == null || tickV.tickV.getUnits().isConvertableTo(getUnits())) { if (isHorizontal()) { paintHorizontalAxis(g); } else { paintVerticalAxis(g); } } else { - if ( getCanvas().isPrintingThread() ) { - this.updateImmediately(); - g.setClip(null); - logger.info("calculated ticks on printing thread, this may cause problems"); - if (isHorizontal()) { - paintHorizontalAxis(g); - } else { - paintVerticalAxis(g); - } - - } + //System.err.println("inconvertable units"); } Rectangle clip = g.getClipBounds(); @@ -2171,11 +1362,11 @@ protected void paintComponent(Graphics graphics) { Font tickLabelFont = getTickLabelFont(); FontMetrics tickLabelFontMetrics = getFontMetrics(tickLabelFont); int tickLength = tickLabelFont.getSize() * 2 / 3; - int tick_label_gap = tickLabelFontMetrics.stringWidth(" "); + int tickLabelGap = tickLabelFontMetrics.stringWidth(" "); int lineHeight = tickLabelFont.getSize() + getLineSpacing(); - int baseLine = position + tickLength + tick_label_gap + tickLabelFont.getSize(); - int rightEdge = DMin - tickLabelFontMetrics.stringWidth("0000") - tick_label_gap; + int baseLine = position + tickLength + tickLabelGap + tickLabelFont.getSize(); + int rightEdge = DMin - tickLabelFontMetrics.stringWidth("000") - tickLabelGap; // Note assumes top label is date but lower labels are shorter. GrannyTextRenderer idlt = new GrannyTextRenderer(); /* @@ -2188,43 +1379,22 @@ protected void paintComponent(Graphics graphics) { int width, leftEdge; - if ( tcaData==null ) { + for (int i = 0; i < tcaData.length; i++) { + String label = (String) tcaData[i].getProperty(DataSet.PROPERTY_Y_LABEL); + if (label == null) { + label = (String) tcaData[i].getProperty("label"); + } + if (label == null) { + label = ""; + } baseLine += lineHeight; - idlt.setString( g, "tcaData not available" ); - idlt.draw( graphics, (float)(rightEdge-idlt.getWidth()), (float)baseLine ); - } else { - QDataSet bds= (QDataSet) tcaData.property(QDataSet.BUNDLE_1); - if ( bds==null ) { - logger.fine("expected TCA data to have BUNDLE dataset"); - } - int lines= Math.min( MAX_TCA_LINES, tcaData.length(0) ); - for (int i = 0; i < lines; i++) { - baseLine += lineHeight; - if ( bds==null ) { - idlt.setString( g, "???" ); - } else { - String label= (String) bds.property( QDataSet.LABEL, i ) ; - if ( label==null ) label= (String) bds.property( QDataSet.NAME, i ); - if ( label==null ) { - idlt.setString( g, "????" ); // This shouldn't happen, but does... We need to check earlier - } else { - idlt.setString( g, label ); - } - } - width = (int) Math.floor(idlt.getWidth() + 0.5); - leftEdge = rightEdge - width; - idlt.draw(g, (float) leftEdge, (float) baseLine); - } + idlt.setString(g, label); + width = (int) Math.floor(idlt.getWidth() + 0.5); + leftEdge = rightEdge - width; + idlt.draw(g, (float) leftEdge, (float) baseLine); } } - boolean drawBounds= false; - if ( drawBounds ) { - Rectangle b= getAxisBounds(); - g.setColor( Color.GREEN ); - g.draw( new Rectangle( b.x, b.y, b.width-1, b.height-1 ) ); - } - g.dispose(); getDasMouseInputAdapter().paint(graphics); @@ -2235,42 +1405,7 @@ protected void paintComponent(Graphics graphics) { */ } - private String resolveString( String text, String name, String value ) { - if ( text.contains("%{" ) ) { - text= text.replaceAll("%\\{"+name+"\\}", value ); - } else if ( text.contains("$(") ) { - text= text.replaceAll("\\$\\("+name+"\\)", value ); - } - return text; - } - - private String resolveAxisLabel() { - String result= resolveString( axisLabel, "UNITS", getDatumRange().getUnits().toString() ); - DatumRange dr= getDatumRange(); - String sdr; - if ( UnitsUtil.isTimeLocation( dr.getUnits() ) ) { - sdr= DatumRangeUtil.formatTimeRange(dr,true); - } else { - sdr= dr.toString(); - } - if ( dr instanceof OrbitDatumRange ) { - String abbrevName= ((OrbitDatumRange)dr).toString(); - String[] ss= abbrevName.split(":"); - if ( abbrevName.split(":").length>3 ) { - abbrevName= ss[0]+":"+ss[ss.length-1]; - } - sdr= sdr+" ("+abbrevName +")"; - } - result= resolveString( result, "RANGE", sdr ); - result= resolveString( result, "SCAN_RANGE", String.valueOf(getScanRange()) ); - - return result; - } - - /** - * Paint the axis if it is horizontal - * @param g the graphics context - */ + /** Paint the axis if it is horizontal */ protected void paintHorizontalAxis(Graphics2D g) { try { Rectangle clip = g.getClipBounds(); @@ -2281,7 +1416,7 @@ protected void paintHorizontalAxis(Graphics2D g) { boolean bottomLine = ((orientation == BOTTOM || oppositeAxisVisible) && blLineRect != null && blLineRect.intersects(clip)); boolean bottomTicks = ((orientation == BOTTOM || oppositeAxisVisible) && blTickRect != null && blTickRect.intersects(clip)); boolean bottomTickLabels = ((orientation == BOTTOM && tickLabelsVisible) && blLabelRect != null && blLabelRect.intersects(clip)); - boolean bottomLabel = ((orientation == BOTTOM && !axisLabel.equals("")) ); // && blTitleRect != null && blTitleRect.intersects(clip)); + boolean bottomLabel = ((orientation == BOTTOM && !axisLabel.equals("")) && blTitleRect != null && blTitleRect.intersects(clip)); boolean topLine = ((orientation == TOP || oppositeAxisVisible) && trLineRect != null && trLineRect.intersects(clip)); boolean topTicks = ((orientation == TOP || oppositeAxisVisible) && trTickRect != null && trTickRect.intersects(clip)); boolean topTickLabels = ((orientation == TOP && tickLabelsVisible) && trLabelRect != null && trLabelRect.intersects(clip)); @@ -2292,27 +1427,10 @@ protected void paintHorizontalAxis(Graphics2D g) { int DMax = getColumn().getDMaximum(); int DMin = getColumn().getDMinimum(); + Font labelFont = getTickLabelFont(); + TickVDescriptor ticks = getTickV(); - if ( getCanvas().isPrintingThread() ) { - // check that the ticks are up-to-date. autoplot_test033 showed this was happening. - if ( ticks!=null ) { - final DatumVector majorTicks = ticks.getMajorTicks(); - DatumRange x= DatumRangeUtil.union( majorTicks.get(0), majorTicks.get(majorTicks.getLength()-1)); - if ( ! x.intersects(this.getDatumRange() ) ) { - logger.fine("last ditch effort to get useful ticks that we didn't get before because of thread order"); - TickVDescriptor ticks2= TickMaster.getInstance().requestTickV(this); - if ( ticks2!=null ) ticks= ticks2; - } - } else { - throw new IllegalArgumentException("ticks are not calculated"); - } - } - - if ( DMax-DMin < 2 ) { - return; - } - if (bottomLine) { g.drawLine(DMin, bottomPosition, DMax, bottomPosition); } @@ -2320,7 +1438,7 @@ protected void paintHorizontalAxis(Graphics2D g) { g.drawLine(DMin, topPosition, DMax, topPosition); } - int tickLengthMajor = tickLen; //labelFont.getSize() * 2 / 3; + int tickLengthMajor = labelFont.getSize() * 2 / 3; int tickLengthMinor = tickLengthMajor / 2; int tickLength; @@ -2332,16 +1450,16 @@ protected void paintHorizontalAxis(Graphics2D g) { if (DMin <= tickPosition && tickPosition <= DMax) { tickLength = tickLengthMajor; if (bottomTicks) { - if ( tickLength!=0 ) g.drawLine(tickPosition, bottomPosition, tickPosition, bottomPosition + tickLength); + g.drawLine(tickPosition, bottomPosition, tickPosition, bottomPosition + tickLength); } if (bottomTickLabels) { - drawLabel(g, tick1, labels[i], i, tickPosition, bottomPosition + Math.max(0,tickLength) ); + drawLabel(g, tick1, labels[i], i, tickPosition, bottomPosition + tickLength); } if (topTicks) { - if ( tickLength!=0 ) g.drawLine(tickPosition, topPosition, tickPosition, topPosition - tickLength); + g.drawLine(tickPosition, topPosition, tickPosition, topPosition - tickLength); } if (topTickLabels) { - drawLabel(g, tick1, labels[i], i, tickPosition, topPosition - Math.max(0,tickLength) + 1); + drawLabel(g, tick1, labels[i], i, tickPosition, topPosition - tickLength + 1); } } } @@ -2352,51 +1470,28 @@ protected void paintHorizontalAxis(Graphics2D g) { if (DMin <= tickPosition && tickPosition <= DMax) { tickLength = tickLengthMinor; if (bottomTicks) { - if ( tickLength!=0 ) g.drawLine(tickPosition, bottomPosition, tickPosition, bottomPosition + tickLength); + g.drawLine(tickPosition, bottomPosition, tickPosition, bottomPosition + tickLength); } if (topTicks) { - if ( tickLength!=0 ) g.drawLine(tickPosition, topPosition, tickPosition, topPosition - tickLength); + g.drawLine(tickPosition, topPosition, tickPosition, topPosition - tickLength); } } } - boolean debugBoundsBox= false; - if ( debugBoundsBox ) { - Color c0= g.getColor(); - g.setColor( Color.MAGENTA ); - g.drawRoundRect( clip.x, clip.y, clip.width-1, clip.height-1, 14, 14 ); - //g.drawLine( clip.x, clip.y+clip.height-1, clip.x+clip.width-1, clip.y+clip.height-1 ); - g.setColor( c0 ); - } - if (!axisLabel.equals("")) { Graphics2D g2 = (Graphics2D) g.create(); int titlePositionOffset = getTitlePositionOffset(); GrannyTextRenderer gtr = new GrannyTextRenderer(); - g2.setFont(getLabelFont()); - if ( dasPlot!=null ) gtr.setAlignment( dasPlot.getMultiLineTextAlignment() ); - String axislabel1= resolveAxisLabel(); - gtr.setString(g2, axislabel1); + gtr.setString(g2, axisLabel); int titleWidth = (int) gtr.getWidth(); int baseline; int leftEdge; - if ( debugBoundsBox ) { - baseline = bottomPosition + titlePositionOffset; - g2.drawString( ">>"+axislabel1+"<<", clip.x, clip.y + g2.getFontMetrics().getHeight() ); - gtr.draw(g2, (float) clip.x, (float) clip.y + g2.getFontMetrics().getHeight()*2 ); - g2.drawString( ""+baseline+ " "+bottomPosition+ " " +titlePositionOffset + " "+bottomLabel, clip.x, clip.y + 3*g2.getFontMetrics().getHeight() ); - } + g2.setFont(getLabelFont()); if (bottomLabel) { leftEdge = DMin + (DMax - DMin - titleWidth) / 2; baseline = bottomPosition + titlePositionOffset; gtr.draw(g2, (float) leftEdge, (float) baseline); } - if ( debugBoundsBox ) { - leftEdge = DMin + (DMax - DMin - titleWidth) / 2; - baseline = bottomPosition + titlePositionOffset; - g2.drawLine( clip.x, clip.y, (int)leftEdge, (int)baseline ); - System.err.println("20130712_1424"); - } if (topLabel) { leftEdge = DMin + (DMax - DMin - titleWidth) / 2; baseline = topPosition - titlePositionOffset; @@ -2405,14 +1500,11 @@ protected void paintHorizontalAxis(Graphics2D g) { g2.dispose(); } } catch (InconvertibleUnitsException ex) { - // do nothing + logger.log(Level.WARNING, ex.getMessage(), ex); } } - /** - * Paint the vertical axis - * @param g the graphics context - */ + /** Paint the axis if it is vertical */ protected void paintVerticalAxis(Graphics2D g) { try { Rectangle clip = g.getClipBounds(); @@ -2434,12 +1526,11 @@ protected void paintVerticalAxis(Graphics2D g) { int DMax = getRow().getDMaximum(); int DMin = getRow().getDMinimum(); + Font labelFont = getTickLabelFont(); + + TickVDescriptor ticks = getTickV(); - if ( DMax-DMin<2 ) { - return; - } - if (leftLine) { g.drawLine(leftPosition, DMin, leftPosition, DMax); } @@ -2447,42 +1538,43 @@ protected void paintVerticalAxis(Graphics2D g) { g.drawLine(rightPosition, DMin, rightPosition, DMax); } - int tickLengthMajor = tickLen; + int tickLengthMajor = labelFont.getSize() * 2 / 3; int tickLengthMinor = tickLengthMajor / 2; int tickLength; String[] labels = tickFormatter(ticks.tickV, getDatumRange()); for (int i = 0; i < ticks.tickV.getLength(); i++) { Datum tick1 = ticks.tickV.get(i); - int tickPosition = (int) Math.floor(transform(tick1)+0.0001); + int tickPosition = (int) Math.floor(transform(tick1)); if (DMin <= tickPosition && tickPosition <= DMax) { tickLength = tickLengthMajor; if (leftTicks) { - if ( tickLength!=0 ) g.drawLine(leftPosition, tickPosition, leftPosition - tickLength, tickPosition); + g.drawLine(leftPosition, tickPosition, leftPosition - tickLength, tickPosition); } if (leftTickLabels) { - drawLabel(g, tick1, labels[i], i, leftPosition - Math.max( 0,tickLength ), tickPosition); + drawLabel(g, tick1, labels[i], i, leftPosition - tickLength, tickPosition); } if (rightTicks) { - if ( tickLength!=0 ) g.drawLine(rightPosition, tickPosition, rightPosition + tickLength, tickPosition); + g.drawLine(rightPosition, tickPosition, rightPosition + tickLength, tickPosition); } if (rightTickLabels) { - drawLabel(g, tick1, labels[i], i, rightPosition + Math.max( 0,tickLength ), tickPosition); + drawLabel(g, tick1, labels[i], i, rightPosition + tickLength, tickPosition); } } } for (int i = 0; i < ticks.minorTickV.getLength(); i++) { + tickLength = tickLengthMinor; double tick1 = ticks.minorTickV.doubleValue(i, getUnits()); - int tickPosition = (int) Math.floor(transform(tick1, ticks.units)+0.0001); + int tickPosition = (int) Math.floor(transform(tick1, ticks.units)); if (DMin <= tickPosition && tickPosition <= DMax) { tickLength = tickLengthMinor; if (leftTicks) { - if ( tickLength!=0 ) g.drawLine(leftPosition, tickPosition, leftPosition - tickLength, tickPosition); + g.drawLine(leftPosition, tickPosition, leftPosition - tickLength, tickPosition); } if (rightTicks) { - if ( tickLength!=0 ) g.drawLine(rightPosition, tickPosition, rightPosition + tickLength, tickPosition); + g.drawLine(rightPosition, tickPosition, rightPosition + tickLength, tickPosition); } } } @@ -2492,12 +1584,11 @@ protected void paintVerticalAxis(Graphics2D g) { Graphics2D g2 = (Graphics2D) g.create(); int titlePositionOffset = getTitlePositionOffset(); GrannyTextRenderer gtr = new GrannyTextRenderer(); - g2.setFont(getLabelFont()); - if ( dasPlot!=null ) gtr.setAlignment(dasPlot.getMultiLineTextAlignment()); - gtr.setString(g2, resolveAxisLabel() ); + gtr.setString(g2, axisLabel); int titleWidth = (int) gtr.getWidth(); int baseline; int leftEdge; + g2.setFont(getLabelFont()); if (leftLabel) { g2.rotate(-Math.PI / 2.0); leftEdge = -DMax + (DMax - DMin - titleWidth) / 2; @@ -2505,14 +1596,14 @@ protected void paintVerticalAxis(Graphics2D g) { gtr.draw(g2, (float) leftEdge, (float) baseline); } if (rightLabel) { - if (flipLabel) { - g2.rotate(-Math.PI / 2.0); + if ( flipLabel ) { + g2.rotate( -Math.PI / 2.0); leftEdge = DMin + (DMax - DMin + titleWidth) / 2; baseline = rightPosition + titlePositionOffset; gtr.draw(g2, (float) -leftEdge, (float) baseline); g2.getClipBounds(); } else { - g2.rotate(Math.PI / 2.0); + g2.rotate( Math.PI / 2.0 ); leftEdge = DMin + (DMax - DMin - titleWidth) / 2; baseline = -rightPosition - titlePositionOffset; gtr.draw(g2, (float) leftEdge, (float) baseline); @@ -2524,30 +1615,9 @@ protected void paintVerticalAxis(Graphics2D g) { // do nothing } } - /** - * allows multiple stacked axes to be manually lined up - * (if you somehow magically know the offset) - * @deprecated use setLabelOffset instead. - */ - public void setLeftXLabelOverride(int leftXOverride) - { - this.leftXOverride = leftXOverride; - } - - /** - * explicitly set the position of the label from the axis. - * For example, "4em" will position the y-axis label 4ems away from the - * axis. - * @param spec offset string like "5em+5px" - */ - public void setLabelOffset( String spec ) { - this.labelOffset= spec; - update(); - } - /** - * get the offset of the label from the baseline in pixels. - * @return the offset. + /** TODO + * @return */ protected int getTitlePositionOffset() { Font tickLabelFont = getTickLabelFont(); @@ -2556,51 +1626,38 @@ protected int getTitlePositionOffset() { int tickLength = tickLabelFont.getSize() * 2 / 3; GrannyTextRenderer gtr = new GrannyTextRenderer(); - if ( dasPlot!=null ) gtr.setAlignment( dasPlot.getMultiLineTextAlignment() ); gtr.setString(labelFont, axisLabel); int offset; if (orientation == BOTTOM) { offset = tickLabelFont.getSize() + tickLength + fm.stringWidth(" ") + labelFont.getSize() + labelFont.getSize() / 2; - if ( drawTca && tcaData != null ) { - offset += Math.min( MAX_TCA_LINES, tcaData.length(0) ) * (tickLabelFont.getSize() + getLineSpacing()); - } - if ( labelOffset.length()>0 && axisLabel.length()>0 ) { - offset= tickLabelFont.getSize() + (int)DasDevicePosition.parseLayoutStr( labelOffset, getEmSize(), getRow().getHeight(), 0 ); - } } else if (orientation == TOP) { offset = tickLength + fm.stringWidth(" ") + labelFont.getSize() + labelFont.getSize() / 2 + (int) gtr.getDescent(); } else if (orientation == LEFT) { //offset = tickLength + (int)this.blLabelRect.getWidth() + fm.stringWidth(" ") + labelFont.getSize() / 2 + (int) gtr.getDescent(); - offset = getColumn().getDMinimum() - blLabelRect.x + labelFont.getSize() / 2 + (int) gtr.getDescent(); + offset= getColumn().getDMinimum() - blLabelRect.x + labelFont.getSize() / 2 + (int) gtr.getDescent(); } else { - if ( trLabelRect==null ) { - offset= 20; + offset= this.trLabelRect.x + this.trLabelRect.width - getColumn().getDMaximum() + labelFont.getSize() / 2 + + (int) ( flipLabel ? gtr.getAscent() : gtr.getDescent() ); + /*if ( !flipLabel ) { + offset = tickLength + (int)this.trLabelRect.getWidth() + fm.stringWidth(" ") + labelFont.getSize() / 2 + (int) gtr.getDescent(); } else { - offset = this.trLabelRect.x + this.trLabelRect.width - getColumn().getDMaximum() + labelFont.getSize() / 2 + (int) (flipLabel ? gtr.getAscent() : gtr.getDescent()); - } + offset = tickLength + (int)this.trLabelRect.getWidth() + fm.stringWidth(" ") + labelFont.getSize() / 2 + (int) gtr.getAscent(); + offset= this.trLabelRect.x + this.trLabelRect.width - getColumn().getDMaximum() + labelFont.getSize() / 2 + }*/ + } + if (getOrientation() == BOTTOM && drawTca && tcaData != null) { + offset += tcaData.length * (tickLabelFont.getSize() + getLineSpacing()); } return offset; } - /** - * calculate the spacing (whitespace) between the TCA items. - * @return the spacing between lines. - */ public int getLineSpacing() { return getTickLabelFont().getSize() / 4; } - /** - * Draw each label of the axis. - * @param g graphics context - * @param value value to look up. - * @param label the label - * @param index the index. The default does not use this, but overriding methods may use it. - * @param x tick position relative to the canvas - * @param y tick position relative to the canvas - */ + /** TODO */ protected void drawLabel(Graphics2D g, Datum value, String label, int index, int x, int y) { if (!tickLabelsVisible) { @@ -2616,10 +1673,8 @@ protected void drawLabel(Graphics2D g, Datum value, String label, int index, int int height = (int) idlt.getHeight(); int ascent = (int) idlt.getAscent(); - int tick_label_gap = tickLen/2; //getFontMetrics(getTickLabelFont()).stringWidth(" "); + int tick_label_gap = getFontMetrics(getTickLabelFont()).stringWidth(" "); - if ( tick_label_gap= ltcaData.length() ) { + index = DataSetUtil.closestColumn(tcaData[0], value); + if (index < 0 || index > tcaData[0].getXLength()) { return; } + pixelSize = getDatumRange().width().divide(getDLength()).doubleValue( + getUnits().getOffsetUnits()); - pixelSize = getDatumRange().width().divide(getDLength()).doubleValue( getUnits().getOffsetUnits() ); - - if (ltcaData.length() == 0) { + if ( tcaData[0].getXLength()==0 ) { g.drawString("tca data is empty", leftEdge, baseLine); return; } - tcaValue = dep0.value(index); + tcaValue = tcaData[0].getXTagDouble(index, getUnits()); //Added in to say take nearest nieghbor as long as the distance to the nieghbor is //not more than the xtagwidth. - QDataSet xTagWidth = org.virbo.dataset.DataSetUtil.guessCadenceNew( dep0, null ); - - double limit; - try { - UnitsConverter uc= UnitsConverter.getConverter( SemanticOps.getUnits(dep0).getOffsetUnits(), getUnits().getOffsetUnits() ); - limit = Math.max( uc.convert( xTagWidth.value() ), pixelSize ); //DANGER: check that this is correct - } catch ( InconvertibleUnitsException ex ) { - ex.printStackTrace(); - throw new RuntimeException( ex ); - } + double xTagWidth = DataSetUtil.guessXTagWidth(tcaData[0]).doubleValue( + getUnits().getOffsetUnits()); + double limit = Math.max(xTagWidth, pixelSize); - if (Math.abs(tcaValue - value.doubleValue(getUnits())) > limit) { //TODO:suspicious + if (Math.abs(tcaValue - value.doubleValue(getUnits())) > limit) { return; } Font tickLabelFont = getTickLabelFont(); FontMetrics fm = getFontMetrics(tickLabelFont); int lineHeight = tickLabelFont.getSize() + getLineSpacing(); - - int lines= Math.min( MAX_TCA_LINES, ltcaData.length(0) ); - - for (int i = 0; i < lines; i++) { - try { - baseLine += lineHeight; - QDataSet test1= ltcaData.slice(index); - QDataSet v1= ArrayDataSet.copy( test1.slice(i) ); - String item; - item= org.virbo.dataset.DataSetUtil.getStringValue( v1, v1.value() ); - width = fm.stringWidth(item); - leftEdge = rightEdge - width; - g.drawString(item, leftEdge, baseLine); - } catch ( RuntimeException ex ) { - g.drawString("except!c"+ex.getMessage(),leftEdge, baseLine); - } - } - } - - /** - * get the font for tick labels. If the component currently has null for the - * font, then Font.decode("sans-12") is used and a warning logged. - * @return the font to use for ticks. + for (int i = 0; i < tcaData.length; i++) { + baseLine += lineHeight; + String item; + if (tcaFormatters[i] == null) { + item = format(tcaData[i].getDouble(index, tcaData[i].getYUnits()), "(f8.2)"); + } + else { + item = tcaFormatters[i].format(tcaData[i].getDatum(index)); + } + width = fm.stringWidth(item); + leftEdge = rightEdge - width; + g.drawString(item, leftEdge, baseLine); + } + } + + /** TODO + * @return */ public Font getTickLabelFont() { - Font f= this.getFont(); - if ( f==null ) { - //logger.warning("2285: font was null, using sans-12. Code should check first."); - f= Font.decode("sans-12"); - } - return f; + return this.getFont(); } - /** - * get the font for labels. If the component currently has null for the - * font, then Font.decode("sans-12") is used. - * @return the font to use for labels. + /** TODO + * @param tickLabelFont */ - public Font getLabelFont() { - Font f= this.getFont(); - if ( f==null ) { - logger.warning("2285: font was null, using sans-12"); - f= Font.decode("sans-12"); - } - - if ( fontSize.length()>0 && !fontSize.equals("1em") ) { - try { - double[] dd= DasDevicePosition.parseLayoutStr(getFontSize()); - if ( dd[1]==1 && dd[2]==0 ) { - // do nothing - } else { - double parentSize= f.getSize2D(); - double newSize= dd[1]*parentSize + dd[2]; - f= f.deriveFont((float)newSize); - return f; - } - } catch (ParseException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - return f; + public void setTickLabelFont(Font tickLabelFont) { } - - private String fontSize = "1em"; - - public static final String PROP_FONTSIZE = "fontSize"; - public String getFontSize() { - return fontSize; + /** TODO + * @return + */ + public Font getLabelFont() { + return this.getFont(); } - public void setFontSize(String fontSize) { - String oldFontSize = this.fontSize; - this.fontSize = fontSize; - firePropertyChange(PROP_FONTSIZE, oldFontSize, fontSize); + /** TODO + * @param labelFont + */ + public void setLabelFont(Font labelFont) { + // TODO: whah?--jbf } - /** - * class for storing an axis transform. This is used to keep track - * of the cache image in DasPlot. - */ public static class Memento { private DatumRange range; private int dmin, dmax; private boolean log; private boolean flipped; - private boolean horizontal; - - @Override - public int hashCode() { - int hash = 5; - hash = 29 * hash + (this.range != null ? this.range.hashCode() : 0); - hash = 29 * hash + this.dmin; - hash = 29 * hash + this.dmax; - hash = 29 * hash + ( this.log ? 1 : 0 ); - hash = 29 * hash + ( this.flipped ? 1 : 0 ); - hash = 29 * hash + ( this.horizontal ? 1 : 0 ); - return hash; - } + private DasAxis axis; - @Override public boolean equals(Object o) { - if ( o==null || !( o instanceof Memento) ) { - return false; - } else { - Memento m = (Memento) o; - return this == m || (this.range.equals(m.range) && + Memento m = (Memento) o; + return this == m || (this.range.equals(m.range) && this.dmin == m.dmin && this.dmax == m.dmax && this.log == m.log && this.flipped == m.flipped && - this.horizontal == m.horizontal ); - } + this.axis == m.axis); } - @Override public String toString() { - return (log ? "log " : "") + range.toString() + " (" + ( DatumUtil.asOrderOneUnits(range.width()).toString() ) + ") " + (dmax - dmin) + " pixels @ " + dmin; + return (log ? "log " : "") + range.toString() + " " + (dmax - dmin) + " pixels @ " + dmin; } } - /** - * get the memento object which identifies the state of the axis transformation. - * @return the momento. - */ public Memento getMemento() { Memento result = new Memento(); result.range = this.getDatumRange(); @@ -2863,16 +1824,13 @@ public Memento getMemento() { } result.log = this.isLog(); result.flipped = flipped; - result.horizontal = this.isHorizontal(); + result.axis = this; return result; } /** * return the AffineTransform, or null. The transform will be applied after the input * transform is applied. So to just get the transform, pass in identity. - * @param memento memento from another axis state. - * @param at initial transform - * @return the transform from that state to this state, or null if no transform can be created. */ public AffineTransform getAffineTransform(Memento memento, AffineTransform at) { if (at == null) { @@ -2884,7 +1842,7 @@ public AffineTransform getAffineTransform(Memento memento, AffineTransform at) { if (memento.flipped != flipped) { return null; } - if (!memento.range.getUnits().isConvertibleTo(getUnits())) { + if (!memento.range.getUnits().isConvertableTo(getUnits())) { return null; } @@ -2892,16 +1850,10 @@ public AffineTransform getAffineTransform(Memento memento, AffineTransform at) { //return getAffineTransform(memento.range, false, at); double dmin0, dmax0; + dmin0 = transform(memento.range.min()); dmax0 = transform(memento.range.max()); - double scale2 = (0. + getMemento().dmin - getMemento().dmax) / (memento.dmin - memento.dmax); - double trans2 = -1 * memento.dmin * scale2 + getMemento().dmin; - - if ( dmin0==10000 || dmin0==-10000 | dmax0==10000 | dmax0==10000 ) { - logger.info("unable to create transform"); - } - if (!(isHorizontal() ^ flipped)) { double tmp = dmin0; dmin0 = dmax0; @@ -2911,13 +1863,11 @@ public AffineTransform getAffineTransform(Memento memento, AffineTransform at) { if (!isHorizontal()) { double dmin1 = getRow().getDMinimum(); double dmax1 = getRow().getDMaximum(); - double scaley = (dmin0 - dmax0) / (dmin1 - dmax1); double transy = -1 * dmin1 * scaley + dmin0; at.translate(0., transy); at.scale(1., scaley); - at.translate(0., trans2 ); - at.scale(1., scale2 ); + } else { double dmin1 = getColumn().getDMinimum(); double dmax1 = getColumn().getDMaximum(); @@ -2926,8 +1876,6 @@ public AffineTransform getAffineTransform(Memento memento, AffineTransform at) { double transx = -1 * dmin1 * scalex + dmin0; at.translate(transx, 0); at.scale(scalex, 1.); - at.translate( trans2, 0. ); - at.scale( scale2, 1. ); } @@ -2938,10 +1886,9 @@ public AffineTransform getAffineTransform(Memento memento, AffineTransform at) { } } - /** - * @return a copy of the axis, also cloning the dataRange that backs the axis. + /** TODO + * @return */ - @Override public Object clone() { try { DasAxis result = (DasAxis) super.clone(); @@ -2970,9 +1917,9 @@ private int getMaxLabelWidth() { try { Font f = getTickLabelFont(); TickVDescriptor ticks = getTickV(); - if ( ticks==null ) return 10; DatumVector tickv = ticks.tickV; int size = Integer.MIN_VALUE; + Graphics g = this.getGraphics(); for (int i = 0; i < tickv.getLength(); i++) { String label = tickFormatter(tickv.get(i)); GrannyTextRenderer idlt = new GrannyTextRenderer(); @@ -2988,11 +1935,9 @@ private int getMaxLabelWidth() { } } - /** - * calculate the biggest label width - * @param fm the font metrics. + /** TODO + * @param fm * @deprecated use getMaxLabelWidth() - * @see #getMaxLabelWidth() * @return the width in pixels of the widest label. */ protected int getMaxLabelWidth(FontMetrics fm) { @@ -3016,94 +1961,71 @@ protected int getMaxLabelWidth(FontMetrics fm) { } } - /** - * reset bounds (and unused transform), invalidate the tickV, etc. - */ - @Override + /** TODO */ public void resize() { resetTransform(); - if ( getFont()==null ) { - return; - } Rectangle oldBounds = this.getBounds(); setBounds(getAxisBounds()); //setBounds(getAxisBoundsNew()); invalidate(); - TickVDescriptor ltickV= tickV; - if (ltickV == null || ltickV.tickV.getUnits().isConvertibleTo(getUnits())) { + if (tickV == null || tickV.tickV.getUnits().isConvertableTo(getUnits())) { validate(); } firePropertyChange(PROP_BOUNDS, oldBounds, getBounds()); } + /** * calculate the bounds of the labels. This should including regions that * the labels could occupy if the axis were panned, so that result doesn't * change during panning. - * @param bounds the bounds which will be enlarged, or null. + * * @return Rectangle in the canvas coordinate frame. */ - protected Rectangle getLabelBounds(Rectangle bounds) { - TickVDescriptor ltickV= this.getTickV(); - DatumRange dr= getDatumRange(); - - if ( ltickV==null || !ltickV.tickV.getUnits().isConvertibleTo(getUnits() ) ) { - logger.fine("tickV cannot be used because of units."); - return bounds; - } + protected synchronized Rectangle getLabelBounds( Rectangle bounds ) { + String[] labels = tickFormatter(this.getTickV().tickV, getDatumRange()); - String[] labels = tickFormatter( ltickV.tickV, dr ); + int majorTickLength = (int) getEmSize(); GrannyTextRenderer gtr = new GrannyTextRenderer(); Font labelFont = this.getLabelFont(); - Font font= this.getFont(); - double dmin, dmax; - if ( isHorizontal() ) { - dmin= getColumn().getDMinimum(); - dmax= getColumn().getDMaximum(); - } else { - dmin= getRow().getDMinimum(); - dmax= getRow().getDMaximum(); - } - - DatumVector ticks = ltickV.tickV; + int tickLen = (int) getEmSize(); + + double dmin= transform(getDataMinimum()); + double dmax= transform(getDataMaximum()); + + DatumVector ticks= this.getTickV().tickV; for (int i = 0; i < labels.length; i++) { Datum d = ticks.get(i); + DatumRange dr= getDatumRange(); if (DatumRangeUtil.sloppyContains(dr, d)) { - gtr.setString(font, labels[i]); + gtr.setString(labelFont, labels[i]); Rectangle rmin = gtr.getBounds(); - Rectangle rmax = new Rectangle(rmin); // same bound, but positioned at the axis max. + Rectangle rmax= new Rectangle(rmin); // same bound, but positioned at the axis max. double flw = gtr.getLineOneWidth(); - int tick_label_gap = tickLen/2; //getFontMetrics(getTickLabelFont()).stringWidth(" "); - if ( tick_label_gap<5 ) tick_label_gap= TICK_LABEL_GAP_MIN; - int space= tick_label_gap; - - int zeroOrPosTickLen= Math.max(0,tickLen); if (isHorizontal()) { if (getOrientation() == BOTTOM) { - rmin.translate((int) (dmin - flw / 2), getRow().bottom() + space + zeroOrPosTickLen + labelFont.getSize()); - rmax.translate((int) (dmax - flw / 2), getRow().bottom() + space + zeroOrPosTickLen + labelFont.getSize()); + rmin.translate((int) (dmin - flw / 2), getRow().bottom() + tickLen + labelFont.getSize() ); + rmax.translate((int) (dmax - flw / 2), getRow().bottom() + tickLen + labelFont.getSize() ); } else { - rmin.translate((int) (dmin - flw / 2), getRow().top() - space - zeroOrPosTickLen - (int) rmin.getHeight()); - rmax.translate((int) (dmax - flw / 2), getRow().top() - space - zeroOrPosTickLen - (int) rmax.getHeight()); + rmin.translate((int) (dmin - flw / 2), getRow().top() - tickLen - (int) rmin.getHeight()); + rmin.translate((int) (dmax - flw / 2), getRow().top() - tickLen - (int) rmax.getHeight()); } - if ( bounds==null ) bounds= rmin; bounds.add(rmin); bounds.add(rmax); } else { if (getOrientation() == LEFT) { - rmin.translate(-(int) rmin.getWidth() - space - zeroOrPosTickLen + getColumn().left(), + rmin.translate(-(int) rmin.getWidth() - tickLen + getColumn().left(), (int) (dmin + getEmSize() / 2)); - rmax.translate(-(int) rmax.getWidth() - space - zeroOrPosTickLen + getColumn().left(), - (int) (dmax + getEmSize() / 2) ); + rmax.translate(-(int) rmax.getWidth() - tickLen + getColumn().left(), + (int) (dmax + getEmSize() / 2)); } else { - rmin.translate( space + zeroOrPosTickLen + getColumn().right(), (int) (dmin + getEmSize() / 2)); - rmax.translate( space + zeroOrPosTickLen + getColumn().right(), (int) (dmax + getEmSize() / 2)); + rmin.translate(tickLen + getColumn().right(), (int) (dmin + getEmSize() / 2)); + rmax.translate(tickLen + getColumn().right(), (int) (dmax + getEmSize() / 2)); } - if ( bounds==null ) bounds= rmin; bounds.add(rmin); bounds.add(rmax); } @@ -3120,27 +2042,18 @@ protected Rectangle getLabelBounds(Rectangle bounds) { */ protected Rectangle getAxisBounds() { Rectangle bounds; - - try { - updateTickLength(); - } catch (ParseException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - if (isHorizontal()) { bounds = getHorizontalAxisBounds(); } else { bounds = getVerticalAxisBounds(); } - if (getOrientation() == BOTTOM && isVisible() && isTickLabelsVisible()) { - QDataSet ltcaData= tcaData; - if (drawTca && ltcaData != null && ltcaData.length() != 0) { + if (getOrientation() == BOTTOM && areTickLabelsVisible()) { + if (drawTca && tcaData != null && tcaData.length != 0) { int DMin = getColumn().getDMinimum(); + int DMax = getColumn().getDMaximum(); Font tickLabelFont = getTickLabelFont(); int tick_label_gap = getFontMetrics(tickLabelFont).stringWidth(" "); - int ntca= ltcaData.length(); - int lines= Math.min( MAX_TCA_LINES, Math.max( ltcaData.length(ntca-1), Math.max( ltcaData.length(ntca/2), ltcaData.length(0) ) ) ); - int tcaHeight = (tickLabelFont.getSize() + getLineSpacing()) * lines; + int tcaHeight = (tickLabelFont.getSize() + getLineSpacing()) * tcaData.length; int maxLabelWidth = getMaxLabelWidth(); bounds.height += tcaHeight; blLabelRect.height += tcaHeight; @@ -3150,29 +2063,20 @@ protected Rectangle getAxisBounds() { GrannyTextRenderer idlt = new GrannyTextRenderer(); idlt.setString(tickLabelFont, "SCET"); int tcaLabelWidth = (int) Math.floor(idlt.getWidth() + 0.5); - QDataSet bds= (QDataSet) ltcaData.property(QDataSet.BUNDLE_1); - if ( bds!=null && bds.length() 0) { - tcaLabelWidth += rightEdgeGap; int tcaLabelSpace = DMin - tcaLabelWidth - tick_label_gap; int minX = Math.min(tcaLabelSpace - maxLabelWidth / 2, bounds.x); int maxX = bounds.x + bounds.width; @@ -3182,14 +2086,16 @@ protected Rectangle getAxisBounds() { blLabelRect.width = maxX - minX; } } - } - if ( bounds.x < -999 ) { - logger.log(Level.FINE, "suspecious bounds calculated: {0}", bounds); - } else { - bounds.add( bounds.x-this.leftPad, bounds.y-this.upPad ); - bounds.add(bounds.x+bounds.width+this.rightPad, bounds.y+bounds.height+this.downPad ); - } + for (int i = 0; i < tickV.tickV.getLength(); i++) { + if (false) { // this is unnecessary? I think it's a kludge for multiline ticks... + bounds.height += getTickLabelFont().getSize() + getLineSpacing(); + if (getTickDirection() == -1) { + bounds.y -= getTickLabelFont().getSize() + getLineSpacing(); + } + } + } + } return bounds; } @@ -3199,8 +2105,8 @@ private Rectangle getHorizontalAxisBounds() { DasDevicePosition range = getColumn(); int DMax = range.getDMaximum(); int DMin = range.getDMinimum(); - int DWidth = DMax - DMin; - + int DWidth= DMax- DMin; + boolean bottomTicks = (orientation == BOTTOM || oppositeAxisVisible); boolean bottomTickLabels = (orientation == BOTTOM && tickLabelsVisible); boolean bottomLabel = (bottomTickLabels && !axisLabel.equals("")); @@ -3210,7 +2116,10 @@ private Rectangle getHorizontalAxisBounds() { Rectangle bounds; - // start with the bounds of the base line. + Font tickLabelFont = getTickLabelFont(); + + int tickSize = tickLabelFont.getSize() * 2 / 3; + if (bottomTicks) { if (blLineRect == null) { blLineRect = new Rectangle(); @@ -3227,92 +2136,80 @@ private Rectangle getHorizontalAxisBounds() { //Add room for ticks if (bottomTicks) { int x = DMin; - int y = bottomPosition + 1 - Math.max( -tickLen, 0 ); + int y = bottomPosition + 1; int width = DWidth; - int height = Math.abs( tickLen ); + int height = tickSize; //The last tick is at position (x + width), so add 1 to width - if ( isVisible() ) { - blTickRect = setRectangleBounds(blTickRect, x, y, width + 1, height ); - } else { - blTickRect = setRectangleBounds(blTickRect, x, y, width + 1, 1 ); - } + blTickRect = setRectangleBounds(blTickRect, x, y, width + 1, height); } if (topTicks) { int x = DMin; - int y = topPosition - Math.max( 0, tickLen ); + int y = topPosition - tickSize; int width = DWidth; - int height = Math.abs( tickLen ); + int height = tickSize; //The last tick is at position (x + width), so add 1 to width - if ( isVisible() ) { - trTickRect = setRectangleBounds(trTickRect, x, y, width + 1, height ); - } else { - trTickRect = setRectangleBounds(trTickRect, x, y, 1, height ); - } - + trTickRect = setRectangleBounds(trTickRect, x, y, width + 1, height); } + //int maxLabelWidth = getMaxLabelWidth(); //int tick_label_gap = getFontMetrics(tickLabelFont).stringWidth(" "); if (bottomTickLabels) { - blLabelRect = getLabelBounds(new Rectangle(DMin, blTickRect.y, DWidth, 10)); - if ( labelOffset.length()>0 && axisLabel.length()>0 ) { - blLabelRect.y= (bottomPosition) + (int)DasDevicePosition.parseLayoutStr( labelOffset, getEmSize(), 0, 0 ); - } + blLabelRect = getLabelBounds( new Rectangle( DMin, blTickRect.y, DWidth, 10 ) ); + //int x = DMin - maxLabelWidth / 2; + //int y = blTickRect.y + blTickRect.height; + //int width = DMax - DMin + maxLabelWidth; + //int height = tickLabelFont.getSize() * 3 / 2 + tick_label_gap; + //blLabelRect = setRectangleBounds(blLabelRect, x, y, width, height); } if (topTickLabels) { - trLineRect = getLabelBounds(new Rectangle(DMin, topPosition - 10, DWidth, 10)); - if ( labelOffset.length()>0 && axisLabel.length()>0 ) { - trLineRect.y= (bottomPosition) - (int)DasDevicePosition.parseLayoutStr( labelOffset, getEmSize(), 0, 0 ); - } + trLineRect = getLabelBounds( new Rectangle( DMin, topPosition-10, DWidth, 10 ) ); + //int x = DMin - maxLabelWidth / 2; + //int y = topPosition - (tickLabelFont.getSize() * 3 / 2 + tick_label_gap + 1); + //int width = DMax - DMin + maxLabelWidth; + //int height = tickLabelFont.getSize() * 3 / 2 + tick_label_gap; + //trLabelRect = setRectangleBounds(trLabelRect, x, y, width, height); } //Add room for the axis label Font labelFont = getLabelFont(); - - if ( labelFont==null ) { - return new Rectangle(); - } - GrannyTextRenderer gtr = new GrannyTextRenderer(); gtr.setString(labelFont, getLabel()); int labelSpacing = (int) gtr.getHeight() + labelFont.getSize() / 2; - - boolean v= isVisible(); - - if (bottomLabel && v ) { + if (bottomLabel) { int x = DMin; int y = blLabelRect.y + blLabelRect.height; int width = DMax - DMin; int height = labelSpacing; blTitleRect = setRectangleBounds(blTitleRect, x, y, width, height); } - if (topLabel && v ) { + if (topLabel) { int x = DMin; - int y = trLineRect.y - labelSpacing; + int y = trLabelRect.y - labelSpacing; int width = DMax - DMin; int height = labelSpacing; trTitleRect = setRectangleBounds(trTitleRect, x, y, width, height); } bounds = new Rectangle((orientation == BOTTOM) ? blLineRect : trLineRect); - if (bottomTicks && v ) { + if (bottomTicks) { bounds.add(blLineRect); bounds.add(blTickRect); } - if (bottomTickLabels && v ) { + if (bottomTickLabels) { bounds.add(blLabelRect); } - if (bottomLabel && v ) { + if (bottomLabel) { bounds.add(blTitleRect); } - if (topTicks && v ) { + if (topTicks) { bounds.add(trLineRect); bounds.add(trTickRect); } - if (topTickLabels && v ) { - bounds.add(trLineRect); + if (topTickLabels) { + bounds.add(trLabelRect); } - if (topLabel && v ) { + if (topLabel) { bounds.add(trTitleRect); } @@ -3341,10 +2238,14 @@ private Rectangle getVerticalAxisBounds() { int rightPosition = getColumn().getDMaximum(); int DMax = getRow().getDMaximum(); int DMin = getRow().getDMinimum(); - int DWidth = DMax - DMin; - + int DWidth= DMax-DMin; + Rectangle bounds; + Font tickLabelFont = getTickLabelFont(); + + int tickSize = tickLabelFont.getSize() * 2 / 3; + if (leftTicks) { if (blLineRect == null) { blLineRect = new Rectangle(); @@ -3360,17 +2261,17 @@ private Rectangle getVerticalAxisBounds() { //Add room for ticks if (leftTicks) { - int x = leftPosition - Math.min( 0,tickLen ); + int x = leftPosition - tickSize; int y = DMin; - int width = Math.abs( tickLen ); + int width = tickSize; int height = DWidth; //The last tick is at position (y + height), so add 1 to height blTickRect = setRectangleBounds(blTickRect, x, y, width, height + 1); } if (rightTicks) { - int x = rightPosition + 1 + Math.min( 0,tickLen ); + int x = rightPosition + 1; int y = DMin; - int width = Math.abs( tickLen ); + int width = tickSize; int height = DWidth; //The last tick is at position (y + height), so add 1 to height trTickRect = setRectangleBounds(trTickRect, x, y, width, height + 1); @@ -3386,36 +2287,19 @@ private Rectangle getVerticalAxisBounds() { //int width = maxLabelWidth + tick_label_gap; //int height = DMax - DMin + tickLabelFont.getSize() * 2; //blLabelRect = setRectangleBounds(blLabelRect, x, y, width, height); - blLabelRect = getLabelBounds(new Rectangle(blTickRect.x - 10, DMin, 10, DWidth)); - if ( labelOffset.length()>0 && axisLabel.length()>0 ) { - blLabelRect.x = (leftPosition+1) - (int)DasDevicePosition.parseLayoutStr( labelOffset, getEmSize(), DWidth, 0 ); - } - if ( leftXOverride != null ) blLabelRect.x = leftXOverride; // deprecated. - } else if ( leftLabel ) { - blLabelRect = getLabelBounds(new Rectangle( getColumn().getDMinimum(), DMin, 1, DWidth)); - } else { - blLabelRect = blTickRect; + blLabelRect= getLabelBounds( new Rectangle(blTickRect.x-10,DMin,10,DWidth) ); } if (rightTickLabels) { - trLabelRect = getLabelBounds(new Rectangle(trTickRect.x + trTickRect.width, DMin, 10, DWidth)); - if ( labelOffset.length()>0 && axisLabel.length()>0 ) { - trLabelRect.width = (rightPosition) + (int)DasDevicePosition.parseLayoutStr( labelOffset, getEmSize(), DWidth, 0 ) - rightPosition; - } - //int x = trTickRect.x + trTickRect.width; - //int y = DMin - tickLabelFont.getSize(); - //int width = maxLabelWidth + tick_label_gap; - //int height = DMax - DMin + tickLabelFont.getSize() * 2; - //trLabelRect = setRectangleBounds(trLabelRect, x, y, width, height); - } else { - trLabelRect = trTickRect; + trLabelRect= getLabelBounds( new Rectangle(trTickRect.x+trTickRect.width,DMin,10,DWidth) ); + //int x = trTickRect.x + trTickRect.width; + //int y = DMin - tickLabelFont.getSize(); + //int width = maxLabelWidth + tick_label_gap; + //int height = DMax - DMin + tickLabelFont.getSize() * 2; + //trLabelRect = setRectangleBounds(trLabelRect, x, y, width, height); } //Add room for the axis label Font labelFont = getLabelFont(); - if ( labelFont==null ) { - return new Rectangle(); - } - GrannyTextRenderer gtr = new GrannyTextRenderer(); gtr.setString(labelFont, getLabel()); int labelSpacing = (int) gtr.getHeight() + labelFont.getSize() / 2; @@ -3434,27 +2318,25 @@ private Rectangle getVerticalAxisBounds() { trTitleRect = setRectangleBounds(trTitleRect, x, y, width, height); } - boolean v= isVisible(); - bounds = new Rectangle((orientation == LEFT) ? blLineRect : trLineRect); - if (leftTicks && v ) { + if (leftTicks) { bounds.add(blLineRect); bounds.add(blTickRect); } - if (leftTickLabels && v ) { + if (leftTickLabels) { bounds.add(blLabelRect); } - if (leftLabel && v ) { + if (leftLabel) { bounds.add(blTitleRect); } - if (rightTicks && v ) { + if (rightTicks) { bounds.add(trLineRect); bounds.add(trTickRect); } - if (rightTickLabels && v ) { + if (rightTickLabels) { bounds.add(trLabelRect); } - if (rightLabel && v ) { + if (rightLabel) { bounds.add(trTitleRect); } @@ -3486,61 +2368,48 @@ public boolean isHorizontal() { return orientation == BOTTOM || orientation == TOP; } - /** - * return the tick direction, 1=down or left, -1=up or right - * @return 1=down or left, -1=up or right + /** TODO + * @return */ public int getTickDirection() { return tickDirection; } - /** - * return the formatter which converts Datum tick positions to a string. - * @return the formatter which converts Datum tick positions to a string. + /** TODO + * @return */ public DatumFormatter getDatumFormatter() { return datumFormatter; } - /** - * Transforms a Datum in data coordinates to a horizontal or vertical + /** Transforms a Datum in data coordinates to a horizontal or vertical * position on the parent canvas. * @param datum a data value * @return Horizontal or vertical position on the canvas. - * @throws InconvertibleUnitsException */ public double transform(Datum datum) { return transform(datum.doubleValue(getUnits()), getUnits()); } - /** - * this was never utilized, but at_m and at_b could be used to speed up - * transformation. - * @param data the data location - * @param units the units - * @return the pixel location. - */ protected double transformFast(double data, Units units) { if (dataRange.isLog()) { if (data <= 0.) { data = dataRange.getMinimum() - 3; // TODO verify that dataRange.getMinimum() is log. } else { - data = Math.log10(data); + data = DasMath.log10(data); } } double result = at_m * data + at_b; return result; } - /** - * Transforms a double in the given units in data coordinates to a horizontal or vertical + /** Transforms a double in the given units in data coordinates to a horizontal or vertical * position on the parent canvas. * @param data a data value * @param units the units of the given data value. * @return Horizontal or vertical position on the canvas. - * @throws InconvertibleUnitsException */ - public double transform(double data, Units units) { + protected double transform(double data, Units units) { DasDevicePosition range; // TODO: consider optimization here if (isHorizontal()) { @@ -3552,66 +2421,6 @@ public double transform(double data, Units units) { } } - /** - * Transforms the rank 0 dataset a horizontal or vertical - * position on the parent canvas. This was introduced to support when - * the rank 1 iterator would return QDataSets instead of doubles. - * @param data a data value - * @param units the units of the given data value. - * @return Horizontal or vertical position on the canvas. - * @throws InconvertibleUnitsException - */ - public double transform( QDataSet data, Units units) { - DasDevicePosition range; - double d; - if ( units==data.property(QDataSet.UNITS) ) { - d= data.value(); - } else if ( units==Units.dimensionless && data.property(QDataSet.UNITS)==null ) { - d= data.value(); - } else { - UnitsConverter uc= SemanticOps.getUnits(data).getConverter(units); - d= uc.convert(data.value()); - } - if (isHorizontal()) { - range = getColumn(); - return transform(d, units, range.getDMinimum(), range.getDMaximum()); - } else { - range = getRow(); - return transform(d, units, range.getDMaximum(), range.getDMinimum()); - } - } - - /** - * Transforms the rank 0 dataset a horizontal or vertical - * position on the parent canvas. - * @param data a data value - * @return Horizontal or vertical position on the canvas. - * @throws InconvertibleUnitsException - */ - public double transform( QDataSet data ) { - DasDevicePosition range; - double d=data.value(); - Units units= (Units)data.property(QDataSet.UNITS); - if ( units==null ) units= Units.dimensionless; - if (isHorizontal()) { - range = getColumn(); - return transform(d, units, range.getDMinimum(), range.getDMaximum()); - } else { - range = getRow(); - return transform(d, units, range.getDMaximum(), range.getDMinimum()); - } - } - - /** - * Transforms a double in the given units in data coordinates to a horizontal or vertical - * position on the parent canvas. - * @param data a data value - * @param units the units of the given data value. - * @param dmin the axis minimum. - * @param dmax the axis maximum. - * @return Horizontal or vertical position on the canvas. - * @throws InconvertibleUnitsException - */ protected double transform(double data, Units units, int dmin, int dmax) { if (units != dataRange.getUnits()) { data = units.convertDoubleTo(dataRange.getUnits(), data); @@ -3624,7 +2433,7 @@ protected double transform(double data, Units units, int dmin, int dmax) { if (data <= 0.) { data = -1e308; } else { - data = Math.log10(data); + data = DasMath.log10(data); } } @@ -3647,32 +2456,6 @@ protected double transform(double data, Units units, int dmin, int dmax) { return result; } - /** - * get the range covered by the two points. This allows clients to - * get a range without having to worry about the flipped property. - * - * @param idata1 pixel position on the axis, in the canvas frame. - * @param idata2 pixel position on the axis, in the canvas frame. - * @return DatumRange implied by the two pixel positions. - * @throws InconvertibleUnitsException - */ - public DatumRange invTransform( double idata1, double idata2 ) { - Datum d1= invTransform(idata1); - Datum d2= invTransform(idata2); - if ( d1.lt(d2) ) { - return new DatumRange(d1,d2); - } else { - return new DatumRange(d2,d1); - } - } - - /** - * return the data location for the given pixel position. Plot - * coordinates have 0,0 at the upper-left hand corner of the screen. - * @param idata the pixel location on the axis, in the canvas frame. - * @return the data location. - * @throws InconvertibleUnitsException - */ public Datum invTransform(double idata) { double data; DasDevicePosition range = (isHorizontal() @@ -3694,75 +2477,75 @@ public Datum invTransform(double idata) { double resolution = data_range / getDLength(); if (dataRange.isLog()) { - data = Math.pow(10,data); - resolution = data * (Math.pow(10,resolution) - 1); + data = DasMath.exp10(data); + resolution = data * (DasMath.exp10(resolution) - 1); } Datum result = Datum.create(data, dataRange.getUnits(), resolution); - + return result; } /** * return a label for this datum and visible range. This is intended - * to be overridden to change behavior. Note that both tickFormatter methods + * to be overriden to change behavior. Note that both tickFormatter methods * should be overridden. - * @param d the location + * @param tickv * @return string, possibly with Granny control characters. */ protected String tickFormatter(Datum d) { + // TODO: label the axis with the Unit! return datumFormatter.grannyFormat(d, d.getUnits()); + } /** * return the tick labels for these datums and visible range. This is intended - * to be overridden to change behavior. Note that both tickFormatter methods + * to be overriden to change behavior. Note that both tickFormatter methods * should be overridden. - * @param tickV the ticks - * @param datumRange the range + * @param tickV + * @param datumRange * @return Strings, possibly with Granny control characters. */ protected String[] tickFormatter(DatumVector tickV, DatumRange datumRange) { return datumFormatter.axisFormat(tickV, datumRange); } - /** - * target event handlers to reset the axis bounds. - * @param e the event + /** TODO + * @param e */ - @Override public void dataRangeSelected(DataRangeSelectionEvent e) { this.setDataRange(e.getMinimum(), e.getMaximum()); } - /** - * Locates the next or previous tick starting at xDatum. + /** TODO + * @param xDatum + * @param direction + * @param minor + * @return * - * @param xDatum find the tick closest to this. - * @param direction -1 previous, 1 next, 0 closest - * @param minor find closest minor tick, major if false. - * @return the closest tick. If there is no tick in the given direction, then - * the behavior is undefined. - * @deprecated Use getTickVDescriptor.findTick + * @depricated. Use getTickVDescriptor.findTick */ public Datum findTick(Datum xDatum, double direction, boolean minor) { return getTickV().findTick(xDatum, direction, minor); } - /** - * animate the change from one range to another. - * @param min0 the initial range - * @param max0 the initial range - * @param min1 the final range - * @param max1 the final range + /** TODO + * @param min0 + * @param max0 + * @param min1 + * @param max1 */ private void animateChange(double min0, double max0, double min1, double max1) { if (animated && EventQueue.isDispatchThread()) { + + + logger.fine("animate axis"); - boolean drawTca0 = isDrawTca(); + boolean drawTca0 = getDrawTca(); setDrawTca(false); long t0 = System.currentTimeMillis(); @@ -3798,30 +2581,23 @@ private void animateChange(double min0, double max0, double min1, double max1) { frames++; } - logger.log(Level.FINE, "animation frames/sec= {0}", (1000. * frames / transitionTime)); + logger.fine("animation frames/sec= " + (1000. * frames / transitionTime)); setDrawTca(drawTca0); this.dataRange = dataRange0; } } - /** - * reset the transform and update the tick locations. - */ - @Override + /** TODO */ protected void updateImmediately() { super.updateImmediately(); - logger.log(Level.FINE, "updateImmadiately{0} {1}", new Object[]{getDatumRange(), isLog()}); + logger.finer("" + getDatumRange() + " " + isLog()); resetTransform(); - try { - updateTickV(); - } catch ( InconvertibleUnitsException ex ) { - // sometimes the units are changed while the ticks are being calculated. This whole system needs review, but for now, avoid the RTE. - updateTickV(); - } + updateTickV(); } - /** + /** TODO + * @return * @deprecated use isTickLabelsVisible */ public boolean areTickLabelsVisible() { @@ -3830,15 +2606,14 @@ public boolean areTickLabelsVisible() { /** * true if the tick labels should be drawn. - * @return true if the tick labels should be drawn. + * @return */ public boolean isTickLabelsVisible() { return tickLabelsVisible; } - - /** - * set true if the tick labels should be drawn. - * @param b true if the tick labels should be drawn. + + /** TODO + * @param b */ public void setTickLabelsVisible(boolean b) { if (tickLabelsVisible == b) { @@ -3850,92 +2625,229 @@ public void setTickLabelsVisible(boolean b) { firePropertyChange("tickLabelsVisible", oldValue, b); } - /** - * perform actions necessary when starting the component, such as handling TCA. - */ - @Override + /** TODO */ protected void installComponent() { super.installComponent(); - QFunction ltcaFunction= this.tcaFunction; - if ( ltcaFunction!=null ) { - maybeStartTcaTimer(); - } } - - /** - * remove the component row and column update listener. - */ - @Override + + /** TODO */ protected void uninstallComponent() { super.uninstallComponent(); } + /** Process an <axis> element. + * + * @param element The DOM tree node that represents the element + */ + static DasAxis processAxisElement(Element element, FormBase form) throws DasPropertyException, DasNameException, ParseException { + String name = element.getAttribute("name"); + boolean log = element.getAttribute(PROP_LOG).equals("true"); + Datum dataMinimum; + Datum dataMaximum; + if ("TIME".equals(element.getAttribute("units"))) { + String min = element.getAttribute("dataMinimum"); + String max = element.getAttribute("dataMaximum"); + dataMinimum = (min == null || min.equals("") ? TimeUtil.create("1979-02-26") : TimeUtil.create(min)); + dataMaximum = (max == null || max.equals("") ? TimeUtil.create("1979-02-27") : TimeUtil.create(max)); + } else { + String min = element.getAttribute("dataMinimum"); + String max = element.getAttribute("dataMaximum"); + dataMinimum = (min == null || min.equals("") ? Datum.create(1.0) : Datum.create(Double.parseDouble(min))); + dataMaximum = (max == null || max.equals("") ? Datum.create(10.0) : Datum.create(Double.parseDouble(max))); + } + int orientation = parseOrientationString(element.getAttribute("orientation")); + DasAxis axis = new DasAxis(dataMinimum, dataMaximum, orientation, log); + String rowString = element.getAttribute("row"); + if (!rowString.equals("")) { + DasRow row = (DasRow) form.checkValue(rowString, DasRow.class, ""); + axis.setRow(row); + } + String columnString = element.getAttribute("column"); + if (!columnString.equals("")) { + DasColumn column = (DasColumn) form.checkValue(columnString, DasColumn.class, ""); + axis.setColumn(column); + } + + axis.setLabel(element.getAttribute(PROP_LABEL)); + axis.setOppositeAxisVisible(!element.getAttribute(PROP_OPPOSITE_AXIS_VISIBLE).equals("false")); + axis.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); - /** - * create another axis that follows this axis. It will have the same - * range and orientation. - * @return attached axis. + axis.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, axis); + + return axis; + } + + /** TODO + * @param i + * @return + */ + protected static String orientationToString(int i) { + switch (i) { + case TOP: + return "top"; + case BOTTOM: + return "bottom"; + case LEFT: + return "left"; + case RIGHT: + return "right"; + default: + throw new IllegalStateException("invalid orienation: " + i); + } + } + + /** TODO + * @param orientationString + * @return + */ + protected static int parseOrientationString(String orientationString) { + if (orientationString.equals("horizontal")) { + return HORIZONTAL; + } else if (orientationString.equals("vertical")) { + return VERTICAL; + } else if (orientationString.equals("left")) { + return LEFT; + } else if (orientationString.equals("right")) { + return RIGHT; + } else if (orientationString.equals("top")) { + return TOP; + } else if (orientationString.equals("bottom")) { + return BOTTOM; + } else { + throw new IllegalArgumentException("Invalid orientation: " + orientationString); + } + } + + /** TODO + * @param document + * @return */ + public Element getDOMElement(Document document) { + Element element; + if (this.isAttached()) { + element = document.createElement("attachedaxis"); + } else { + element = document.createElement("axis"); + } + if (this.isAttached()) { + element.setAttribute("ref", this.getMasterAxis().getDasName()); + } else { + String minimumStr = getDataMinimum().toString(); + element.setAttribute("dataMinimum", minimumStr); + String maximumStr = getDataMaximum().toString(); + element.setAttribute("dataMaximum", maximumStr); + } + + element.setAttribute("name", getDasName()); + element.setAttribute("row", getRow().getDasName()); + element.setAttribute("column", getColumn().getDasName()); + + element.setAttribute(PROP_LABEL, getLabel()); + element.setAttribute(PROP_LOG, Boolean.toString(isLog())); + element.setAttribute("tickLabelsVisible", Boolean.toString(areTickLabelsVisible())); + element.setAttribute(PROP_OPPOSITE_AXIS_VISIBLE, Boolean.toString(isOppositeAxisVisible())); + element.setAttribute("animated", Boolean.toString(isAnimated())); + element.setAttribute("orientation", orientationToString(getOrientation())); + + return element; + } + + /** Create a new axis that uses the same DataRange object as this axis. + * + * @return A new DasAxis with the same "backing store" (i.e. the DataRange object) + * as this axis. + */ public DasAxis createAttachedAxis() { - DasAxis result= new DasAxis(this.dataRange, this.getOrientation()); - result.setScanRange( this.getScanRange() ); - return result; + return new DasAxis(this.dataRange, this.getOrientation()); } - /** - * create another axis that follows this axis. It will have the same - * range. - * @param orientation the position relative to a plot, one of DasAxis.TOP, DasAxis.BOTTOM, DasAxis.LEFT, DasAxis.RIGHT - * @return attached axis. + /** + * Note: This is commonly used for pop-up slicer windows. + * @return */ public DasAxis createAttachedAxis(int orientation) { - DasAxis result= new DasAxis(this.dataRange, orientation); - result.setScanRange( this.getScanRange() ); - return result; + return new DasAxis(this.dataRange, orientation); } - /** - * set the plot that will get updates - * @param p plot object. + /** Process a <attachedaxis> element. + * + * @param element The DOM tree node that represents the element */ + static DasAxis processAttachedaxisElement(Element element, FormBase form) throws DasPropertyException, DasNameException { + String name = element.getAttribute("name"); + DasAxis ref = (DasAxis) form.checkValue(element.getAttribute("ref"), DasAxis.class, ""); + int orientation = (element.getAttribute("orientation").equals("horizontal") ? HORIZONTAL : DasAxis.VERTICAL); + + DasAxis axis = ref.createAttachedAxis(orientation); + + String rowString = element.getAttribute("row"); + if (!rowString.equals("")) { + DasRow row = (DasRow) form.checkValue(rowString, DasRow.class, ""); + axis.setRow(row); + } + String columnString = element.getAttribute("column"); + if (!columnString.equals("")) { + DasColumn column = (DasColumn) form.checkValue(columnString, DasColumn.class, ""); + axis.setColumn(column); + } + + axis.setDataPath(element.getAttribute("dataPath")); + axis.setDrawTca(element.getAttribute("showTca").equals("true")); + axis.setLabel(element.getAttribute(PROP_LABEL)); + axis.setOppositeAxisVisible(!element.getAttribute(PROP_OPPOSITE_AXIS_VISIBLE).equals("false")); + axis.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); + + axis.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, axis); + + return axis; + } + public void setPlot(DasPlot p) { dasPlot = p; } - - /** - * scan to the previous interval. If we were looking at a day with fuzz, then - * scan to the previous day. + /** TODO + * @param name + * @return */ - public void scanPrevious() { - DatumRange dr= getDatumRange(); - if ( dataRange.isLog() ) { - dr= DatumRangeUtil.rescaleLog( dr, -1., 0 ); - } else { - dr= dr.previous(); + public static DasAxis createNamedAxis(String name) { + DasAxis axis = new DasAxis(Datum.create(1.0, Units.dimensionless), Datum.create(10.0, Units.dimensionless), DasAxis.HORIZONTAL); + if (name == null) { + name = "axis_" + Integer.toHexString(System.identityHashCode(axis)); + } + try { + axis.setDasName(name); + } catch (DasNameException dne) { + DasExceptionHandler.handle(dne); } - setDatumRange( dr ); + return axis; } - /** - * scan to the next interval. If we were looking at a day with fuzz, then - * scan to the next day. - */ + /** TODO */ + public void scanPrevious() { + Datum delta = (getDataMaximum().subtract(getDataMinimum())).multiply(1.0); + Datum tmin = getDataMinimum().subtract(delta); + Datum tmax = getDataMaximum().subtract(delta); + setDataRange(tmin, tmax); + } + + /** TODO */ public void scanNext() { - DatumRange dr= getDatumRange(); - if ( dataRange.isLog() ) { - dr= DatumRangeUtil.rescaleLog( dr, 1., 2. ); - } else { - dr= getDatumRange().next(); - } - setDatumRange( dr ); + Datum delta = (getDataMaximum().subtract(getDataMinimum())).multiply(1.0); + Datum tmin = getDataMinimum().add(delta); + Datum tmax = getDataMaximum().add(delta); + setDataRange(tmin, tmax); } - /** - * get the region containing the axis. - * @return the region containing the axis. + /** TODO + * @return */ - @Override public Shape getActiveRegion() { Rectangle primaryBounds = primaryInputPanel.getBounds(); primaryBounds.translate(getX(), getY()); @@ -3957,80 +2869,55 @@ public Shape getActiveRegion() { * parent panel (this), must not recieve the events. (This is because * the DasPlot between them should get the events, and the DasPlot does * not have a simple rectangular boundary. - * @param l the listener */ - @Override public void addMouseWheelListener(MouseWheelListener l) { maybeInitializeInputPanels(); primaryInputPanel.addMouseWheelListener(l); secondaryInputPanel.addMouseWheelListener(l); } - /** - * remove mouse wheel listener. - * @param l the listener - * @see #maybeInitializeInputPanels() - */ - @Override public void removeMouseWheelListener(MouseWheelListener l) { maybeInitializeInputPanels(); primaryInputPanel.removeMouseWheelListener(l); secondaryInputPanel.removeMouseWheelListener(l); } - /** - * add mouse wheel listener. - * @param l the listener - * @see #maybeInitializeInputPanels() + /** TODO + * @param l */ - @Override public void addMouseListener(MouseListener l) { maybeInitializeInputPanels(); primaryInputPanel.addMouseListener(l); secondaryInputPanel.addMouseListener(l); } - /** - * remove mouse motion listener. - * @param l the listener - * @see #maybeInitializeInputPanels() + /** TODO + * @param l */ - @Override public void removeMouseListener(MouseListener l) { maybeInitializeInputPanels(); primaryInputPanel.removeMouseListener(l); secondaryInputPanel.removeMouseListener(l); } - /** - * add mouse motion listener. - * @param l the listener - * @see #maybeInitializeInputPanels() + /** TODO + * @param l */ - @Override public void addMouseMotionListener(MouseMotionListener l) { maybeInitializeInputPanels(); primaryInputPanel.addMouseMotionListener(l); secondaryInputPanel.addMouseMotionListener(l); } - /** - * remove mouse motion listener. - * @param l the listener - * @see #maybeInitializeInputPanels() + /** TODO + * @param l */ - @Override public void removeMouseMotionListener(MouseMotionListener l) { maybeInitializeInputPanels(); primaryInputPanel.removeMouseMotionListener(l); secondaryInputPanel.removeMouseMotionListener(l); } - /** - * call back for time range selection event. - * @param e the event - */ - @Override public void timeRangeSelected(TimeRangeSelectionEvent e) { if (e.getSource() != this && !e.equals(lastProcessedEvent)) { setDatumRange(e.getRange()); // setDatumRange fires the event @@ -4038,8 +2925,7 @@ public void timeRangeSelected(TimeRangeSelectionEvent e) { } } - /** - * Registers TimeRangeSelectionListener to receive events. + /** Registers TimeRangeSelectionListener to receive events. * @param listener The listener to register. */ public synchronized void addTimeRangeSelectionListener(org.das2.event.TimeRangeSelectionListener listener) { @@ -4049,19 +2935,18 @@ public synchronized void addTimeRangeSelectionListener(org.das2.event.TimeRangeS timeRangeListenerList.add(org.das2.event.TimeRangeSelectionListener.class, listener); } - /** - * Removes TimeRangeSelectionListener from the list of listeners. + /** Removes TimeRangeSelectionListener from the list of listeners. * @param listener The listener to remove. */ public synchronized void removeTimeRangeSelectionListener(org.das2.event.TimeRangeSelectionListener listener) { timeRangeListenerList.remove(org.das2.event.TimeRangeSelectionListener.class, listener); } - /** - * Notifies all registered listeners about the event. + /** Notifies all registered listeners about the event. + * * @param event The event to be fired */ - private synchronized void fireTimeRangeSelectionListenerTimeRangeSelected(TimeRangeSelectionEvent event) { + private void fireTimeRangeSelectionListenerTimeRangeSelected(TimeRangeSelectionEvent event) { if (timeRangeListenerList == null) { return; } @@ -4075,6 +2960,38 @@ private synchronized void fireTimeRangeSelectionListenerTimeRangeSelected(TimeRa } } + static DasAxis processTimeaxisElement(Element element, FormBase form) throws org.das2.DasPropertyException,org.das2.DasNameException, DasException, java.text.ParseException { + String name = element.getAttribute("name"); + Datum timeMinimum = TimeUtil.create(element.getAttribute("timeMinimum")); + Datum timeMaximum = TimeUtil.create(element.getAttribute("timeMaximum")); + int orientation = parseOrientationString(element.getAttribute("orientation")); + + DasAxis timeaxis = new DasAxis(timeMinimum, timeMaximum, orientation); + + String rowString = element.getAttribute("row"); + if (!rowString.equals("")) { + DasRow row = (DasRow) form.checkValue(rowString, DasRow.class, ""); + timeaxis.setRow(row); + } + String columnString = element.getAttribute("column"); + if (!columnString.equals("")) { + DasColumn column = (DasColumn) form.checkValue(columnString, DasColumn.class, ""); + timeaxis.setColumn(column); + } + + timeaxis.setDataPath(element.getAttribute("dataPath")); + timeaxis.setDrawTca(element.getAttribute("showTca").equals("true")); + timeaxis.setLabel(element.getAttribute(PROP_LABEL)); + timeaxis.setOppositeAxisVisible(!element.getAttribute(PROP_OPPOSITE_AXIS_VISIBLE).equals("false")); + timeaxis.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); + + timeaxis.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, timeaxis); + + return timeaxis; + } private static final java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\([eEfF]\\d+.\\d+\\)"); private static String format(double d, String f) { @@ -4135,7 +3052,6 @@ private static String format(double d, String f) { return result; } - @Override public String toString() { String retValue; retValue = super.toString() + "(" + getUnits() + ")"; @@ -4144,19 +3060,16 @@ public String toString() { protected class AxisLayoutManager implements LayoutManager { //NOOP - /** TODO * @param name * @param comp */ - @Override public void addLayoutComponent(String name, Component comp) { } /** TODO * @param parent */ - @Override public void layoutContainer(Container parent) { if (DasAxis.this != parent) { throw new IllegalArgumentException(); @@ -4168,7 +3081,7 @@ public void layoutContainer(Container parent) { } if (drawTca && getOrientation() == BOTTOM && tcaData != null) { Rectangle bounds = primaryInputPanel.getBounds(); - int tcaHeight = (getTickLabelFont().getSize() + getLineSpacing()) * Math.min( MAX_TCA_LINES, tcaData.length(0)); + int tcaHeight = (getTickLabelFont().getSize() + getLineSpacing()) * tcaData.length; bounds.height += tcaHeight; primaryInputPanel.setBounds(bounds); } @@ -4197,9 +3110,6 @@ protected void horizontalLayout() { if (topTicks) { topBounds = new Rectangle(DMin, topPosition, DMax - DMin + 1, 1); } - assert bottomBounds!=null; - assert topBounds!=null; - //Add room for ticks if (bottomTicks) { bottomBounds.height += tickSize; @@ -4265,9 +3175,6 @@ protected void verticalLayout() { if (rightTicks) { rightBounds = new Rectangle(rightPosition, DMin, 1, DMax - DMin + 1); } - assert leftBounds!=null; - assert rightBounds!=null; - //Add room for ticks if (leftTicks) { leftBounds.width += tickSize; @@ -4311,7 +3218,6 @@ protected void verticalLayout() { * @param parent * @return */ - @Override public Dimension minimumLayoutSize(Container parent) { return new Dimension(); } @@ -4320,67 +3226,22 @@ public Dimension minimumLayoutSize(Container parent) { * @param parent * @return */ - @Override public Dimension preferredLayoutSize(Container parent) { return new Dimension(); } - + //NOOP /** TODO * @param comp */ - @Override public void removeLayoutComponent(Component comp) { } } - private void refreshScanButtons(boolean reset) { - if ( scanNext==null ) return; // headless - if ( scanRange!=null ) { - if ( !scanRange.getUnits().isConvertibleTo(getDatumRange().getUnits()) ) { - scanRange=null; - } - } - if ( reset || scanPrevious.hover ) { - boolean t= ( scanRange==null || ( false ? scanRange.intersects(getDatumRange().next()) : scanRange.intersects(getDatumRange().previous()) ) ); - scanPrevious.hover= t; - } - if ( reset || scanNext.hover ) { - boolean t= ( scanRange==null || ( true ? scanRange.intersects(getDatumRange().next()) : scanRange.intersects(getDatumRange().previous()) ) ); - scanNext.hover= t; - } - } - - /** - * set the label for the popup button - * @param label concise label - * @param tooltip text for popup tooltip - */ - public void setNextActionLabel( String label, String tooltip ) { - if ( scanNext!=null ) { - scanNext.setText(label); - scanNext.setToolTipText(tooltip); - } - } - - /** - * set the label for the popup button - * @param label concise label - * @param tooltip text for popup tooltip - */ - public void setPreviousActionLabel( String label, String tooltip ) { - if ( scanPrevious!=null ) { - scanPrevious.setText(label); - scanPrevious.setToolTipText(tooltip); - } - } - - - private class ScanButton extends JButton { + private static class ScanButton extends JButton { private boolean hover; private boolean pressed; - private boolean nextButton; /** TODO * @param text */ @@ -4389,21 +3250,19 @@ public ScanButton(String text) { setContentAreaFilled(false); setText(text); setFocusable(false); - nextButton= SCAN_NEXT_LABEL.equals(text); - setBorder(new CompoundBorder( new LineBorder(Color.BLACK), new EmptyBorder(2, 2, 2, 2))); this.addMouseListener(new MouseAdapter() { - @Override + public void mousePressed(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { setForeground(Color.LIGHT_GRAY); - pressed = scanRange==null || ( nextButton ? scanRange.intersects(getDatumRange().next()) : scanRange.intersects(getDatumRange().previous()) ); + pressed = true; repaint(); } } - @Override + public void mouseReleased(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { setForeground(Color.BLACK); @@ -4411,12 +3270,12 @@ public void mouseReleased(MouseEvent e) { repaint(); } } - @Override + public void mouseEntered(MouseEvent e) { - hover = scanRange==null || scanRange.width().value()==0 || ( nextButton ? scanRange.intersects(getDatumRange().next()) : scanRange.intersects(getDatumRange().previous()) ); + hover = true; repaint(); } - @Override + public void mouseExited(MouseEvent e) { hover = false; repaint(); @@ -4427,9 +3286,7 @@ public void mouseExited(MouseEvent e) { /** TODO * @param g */ - @Override protected void paintComponent(Graphics g) { - if ( getCanvas().isPrintingThread() ) return; if (hover || pressed) { Graphics2D g2 = (Graphics2D) g; g2.setColor(Color.white); @@ -4445,7 +3302,6 @@ protected void paintComponent(Graphics g) { /** TODO * @param g */ - @Override protected void paintBorder(Graphics g) { if (hover || pressed) { super.paintBorder(g); @@ -4453,93 +3309,28 @@ protected void paintBorder(Graphics g) { } } - private void updateTickLength() throws ParseException { - double[] pos = DasDevicePosition.parseLayoutStr(this.tickLenStr); - if ( pos[0]==0 ) { - this.tickLen = (int) ( Math.round( pos[1]*getEmSize() + pos[2] ) ); // make independent from row layout for initialization. - } else { - this.tickLen = (int) (Math.round( pos[0]*getRow().getHeight() + pos[1]*getEmSize() + pos[2] )); - } - } - - /** - * get the tick length string, for example "0.66em" - * @return the tick length string - */ - public String getTickLength() { - return this.tickLenStr; - } - - /** - * set the tick length string, for example - * "0.33em" "5px" "-0.33em" - * @param tickLengthStr the tick length string. - */ - public void setTickLength( String tickLengthStr ) { - this.tickLenStr= tickLengthStr; - try { - updateTickLength(); - resize(); - repaint(); - } catch (ParseException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - } - - public static final String PROP_FLIPPED = "flipped"; - - /** - * return true if the axis is flipped. - * @return true if the axis is flipped. - */ public boolean isFlipped() { return flipped; } - /** - * flip over the axis. - * @param b - */ public void setFlipped(boolean b) { - boolean oldFlipped= this.flipped; update(); this.flipped = b; - firePropertyChange(PROP_FLIPPED,oldFlipped,flipped); } - - /** - * the formatString, or "" if default behavior. - */ protected String formatString = ""; - public static final String PROP_FORMATSTRING = "formatString"; - /** - * return the format string for each tick. - * @return the format string for each tick. - */ public String getFormat() { return formatString; } - - /** - * true means flip the right label so all vertical labels are facing the same direction. - */ + protected boolean flipLabel = false; public static final String PROP_FLIPLABEL = "flipLabel"; - /** - * true if the right vertical label should be flipped. - * @return true if the right vertical label should be flipped. - */ public boolean isFlipLabel() { return flipLabel; } - /** - * true if the right vertical label should be flipped. - * @param flipLabel true if the right vertical label should be flipped. - */ public void setFlipLabel(boolean flipLabel) { boolean oldFlipLabel = this.flipLabel; this.flipLabel = flipLabel; @@ -4547,100 +3338,11 @@ public void setFlipLabel(boolean flipLabel) { firePropertyChange(PROP_FLIPLABEL, oldFlipLabel, flipLabel); } + /** - * the formatter identified to work with the divider - */ - protected DatumFormatter dividerDatumFormatter = null; - public static final String PROP_DIVIDERDATUMFORMATTER = "dividerDatumFormatter"; - - public DatumFormatter getDividerDatumFormatter() { - return dividerDatumFormatter; - } - - public void setDividerDatumFormatter(DatumFormatter dividerDatumFormatter) { - DatumFormatter oldDividerDatumFormatter = this.dividerDatumFormatter; - this.dividerDatumFormatter = dividerDatumFormatter; - firePropertyChange(PROP_DIVIDERDATUMFORMATTER, oldDividerDatumFormatter, dividerDatumFormatter); - } - - protected DomainDivider minorTicksDomainDivider = null; - public static final String PROP_MINORTICKSDOMAINDIVIDER = "minorTicksDomainDivider"; - - /** - * return the domain divider for minor ticks, or null. - * @return the domain divider for minor ticks, or null. - */ - public DomainDivider getMinorTicksDomainDivider() { - return minorTicksDomainDivider; - } - - public void setMinorTicksDomainDivider(DomainDivider minorTicksDomainDivider) { - DomainDivider oldMinorTicksDomainDivider = this.minorTicksDomainDivider; - this.minorTicksDomainDivider = minorTicksDomainDivider; - firePropertyChange(PROP_MINORTICKSDOMAINDIVIDER, oldMinorTicksDomainDivider, minorTicksDomainDivider); - } - - protected DomainDivider majorTicksDomainDivider = null; - public static final String PROP_MAJORTICKSDOMAINDIVIDER = "majorTicksDomainDivider"; - - /** - * return the domain divider for major ticks, or null. - * @return the domain divider for major ticks, or null. - */ - public DomainDivider getMajorTicksDomainDivider() { - return majorTicksDomainDivider; - } - - public void setMajorTicksDomainDivider(DomainDivider majorTicksDomainDivider) { - DomainDivider oldMajorTicksDomainDivider = this.majorTicksDomainDivider; - this.majorTicksDomainDivider = majorTicksDomainDivider; - firePropertyChange(PROP_MAJORTICKSDOMAINDIVIDER, oldMajorTicksDomainDivider, majorTicksDomainDivider); - } - - protected boolean useDomainDivider = false; - public static final String PROP_USEDOMAINDIVIDER = "useDomainDivider"; - - /** - * true if the domain divider should be used. This is a new object that - * locates ticks. - * @return true if the domain divider should be used. - */ - public boolean isUseDomainDivider() { - return useDomainDivider; - } - - /** - * true if the domain divider should be used. This is a new object that - * locates ticks. - * @param useDomainDivider true if the domain divider should be used. - */ - public void setUseDomainDivider(boolean useDomainDivider) { - boolean oldUseDomainDivider = this.useDomainDivider; - this.useDomainDivider = useDomainDivider; - if ( oldUseDomainDivider!=useDomainDivider ) { - updateTickV(); - } - firePropertyChange(PROP_USEDOMAINDIVIDER, oldUseDomainDivider, useDomainDivider); - } - - /** - * set the component visible or invisible. The axis bounds are updated. So in addition to the JComponent visibility, this - * also makes a useful property to completely hide the axis. drawTickLabels hides the labels but still draws the ticks, this - * completely hides the axis. Note too that even though it is not visible, tick positions are still updated to support - * drawing a grid on the plot. - * @param aFlag - */ - @Override - public void setVisible(boolean aFlag) { - super.setVisible(aFlag); - update(); - } - - /** - * set a hint at the format string. Examples include:
      - *
    • 0.000 - *
    • %H:%M!c%Y-%j - *
    + * set a hint at the format string. Examples include: + * 0.000 + * %H:%M!c%Y-%j * @param formatString */ public void setFormat(String formatString) { @@ -4650,9 +3352,6 @@ public void setFormat(String formatString) { if (formatString.equals("")) { setUserDatumFormatter(null); } else { - if ( formatString.contains("$") && !formatString.contains("%") ) { - formatString= formatString.replaceAll("\\$","%"); - } setUserDatumFormatter(getUnits().getDatumFormatterFactory().newFormatter(formatString)); } updateTickV(); @@ -4667,10 +3366,8 @@ private void resetTransform() { DasDevicePosition pos; if (isHorizontal()) { pos = getColumn(); - if ( pos==DasColumn.NULL) return; } else { pos = getRow(); - if ( pos==DasRow.NULL) return; } double dmin = pos.getDMinimum(); double dmax = pos.getDMaximum(); @@ -4684,10 +3381,6 @@ private void resetTransform() { at_b = at[1]; } - /** - * get an object to lock the axis for multiple operations. - * @return - */ public Lock mutatorLock() { return dataRange.mutatorLock(); } diff --git a/dasCore/src/org/das2/graph/DasCanvas.java b/dasCore/src/org/das2/graph/DasCanvas.java index 4571470a7..5a7d8f6ec 100755 --- a/dasCore/src/org/das2/graph/DasCanvas.java +++ b/dasCore/src/org/das2/graph/DasCanvas.java @@ -22,9 +22,15 @@ */ package org.das2.graph; -import java.util.logging.Level; import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.DasNameException; import org.das2.DasProperties; +import org.das2.DasPropertyException; +import org.das2.NameContext; +import org.das2.dasml.FormBase; +import org.das2.dasml.FormComponent; +import org.das2.dasml.ParsedExpressionException; import org.das2.event.DragRenderer; import org.das2.graph.dnd.TransferableCanvasComponent; import org.das2.system.DasLogger; @@ -33,6 +39,7 @@ import org.das2.util.DasExceptionHandler; import org.das2.util.DasPNGConstants; import org.das2.util.DasPNGEncoder; +import org.das2.util.awt.EventQueueBlocker_1; import org.das2.util.awt.GraphicsOutput; import java.awt.BasicStroke; import java.awt.Color; @@ -50,7 +57,6 @@ import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Stroke; -import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; @@ -62,6 +68,7 @@ import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.print.PageFormat; import java.awt.print.Printable; @@ -76,19 +83,17 @@ import java.lang.reflect.InvocationTargetException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.locks.Lock; import java.util.logging.Logger; import java.util.prefs.Preferences; -import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JComponent; @@ -98,6 +103,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; import javax.swing.LookAndFeel; import javax.swing.Scrollable; import javax.swing.SwingConstants; @@ -107,43 +113,72 @@ import javax.swing.filechooser.FileFilter; import org.das2.components.propertyeditor.Editable; import org.das2.components.propertyeditor.PropertyEditor; -import org.das2.datum.Datum; -import org.das2.datum.TimeParser; -import org.das2.datum.TimeUtil; -import org.das2.datum.Units; -import org.das2.datum.UnitsUtil; -import org.das2.event.DasUpdateEvent; -import org.das2.system.ChangesSupport; -import org.das2.system.DefaultMonitorFactory; -import org.das2.system.DefaultMonitorFactory.MonitorEntry; -import org.das2.system.EventQueueBlocker; -import org.das2.system.MonitorFactory; -import org.das2.util.monitor.ProgressMonitor; - -/** Canvas for das2 graphics. The DasCanvas contains any number of DasCanvasComponents such as axes, plots, colorbars, etc. +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** Canvas for das2 graphics. The DasCanvas contains any number of DasCanvasComponents + * such as axes, plots, colorbars, etc. + * + * A basic hierarchy of ownership is: + *
    + * DasCanvas
    + *  |
    + *  +- {@link DasDevicePosition} (2-N objects)
    + *  |   |
    + *  |   +- {@link DasDevicePosition} (sub positions, 0-N objects)
    + *  |
    + *  +- {@link DasCanvasComponent} (0-N objects)
    + *
    + * For example: For a 2-D line plot the basic components are: + *
    + * DasCavas
    + *  |
    + *  +- {@link DasRow} (a DasDevicePosition)
    + *  |
    + *  +- {@link DasColumn} (a DasDevicePosition)
    + *  |
    + *  +- {@link DasAxis} (a DasCanvasComponent)
    + *  |
    + *  +- {@link DasAxis} (a DasCanvasComponent)
    + *  |
    + *  +- {@link DasPlot} (a DasCanvasComponent)
    + *     |
    + *     +- {@link SymbolLineRenderer} (Helper for DasPlot, implements a data painting style.)
    + *
    + * Note that {@link DasPlot}s don't know how to paint data, that job is delegated to one or + * more {@link Renderer} objects. {@link SymbolLineRenderer}s know how to draw simple line + * plots. + * + * @see DasColumn + * @see DasRow + * @see DasAxis + * @see DasPlot + * @see Renderer * @author eew */ -public class DasCanvas extends JLayeredPane implements Printable, Editable, Scrollable { +public class DasCanvas extends JLayeredPane implements Printable, Editable, FormComponent, Scrollable { /** Default drawing layer of the JLayeredPane */ public static final Integer DEFAULT_LAYER = JLayeredPane.DEFAULT_LAYER; /** Z-Layer for drawing the plot. */ - public static final Integer PLOT_LAYER = 300; + public static final Integer PLOT_LAYER = new Integer(300); /** Z-Layer for vertical axis. Presently lower than the horizontal axis, presumably to remove ambiguity */ - public static final Integer VERTICAL_AXIS_LAYER = 400; + public static final Integer VERTICAL_AXIS_LAYER = new Integer(400); /** Z-Layer */ - public static final Integer HORIZONTAL_AXIS_LAYER = 500; + public static final Integer HORIZONTAL_AXIS_LAYER = new Integer(500); /** Z-Layer */ public static final Integer AXIS_LAYER = VERTICAL_AXIS_LAYER; /** Z-Layer */ - public static final Integer ANNOTATION_LAYER = 1000; + public static final Integer ANNOTATION_LAYER = new Integer(1000); /** Z-Layer */ - public static final Integer GLASS_PANE_LAYER = 30000; + public static final Integer GLASS_PANE_LAYER = new Integer(30000); private static final Paint PAINT_ROW = new Color(0xff, 0xb2, 0xb2, 0x92); private static final Paint PAINT_COLUMN = new Color(0xb2, 0xb2, 0xff, 0x92); private static final Paint PAINT_SELECTION = Color.GRAY; private static final Stroke STROKE_DASHED; - + static { float thick = 3.0f; @@ -153,28 +188,11 @@ public class DasCanvas extends JLayeredPane implements Printable, Editable, Scro STROKE_DASHED = new BasicStroke(thick, cap, join, thick, dash, 0.0f); } - /** - * return the canvas that has the focus. - * @return - */ - public static DasCanvas getFocusCanvas() { - return currentCanvas; - } - - private final List topDecorators= Collections.synchronizedList(new LinkedList()); - private final List bottomDecorators= Collections.synchronizedList(new LinkedList()); - private final Painter[] emptyPainterArray= new Painter[0]; // so we can call atomic copy. - - /** - * Java6 has paintingForPrint, use this for now. - */ - boolean lpaintingForPrint= false; - - private static DasCanvas currentCanvas; - /* Canvas actions */ protected static abstract class CanvasAction extends AbstractAction { + protected static DasCanvas currentCanvas; + CanvasAction(String label) { super(label); } @@ -183,11 +201,12 @@ protected static abstract class CanvasAction extends AbstractAction { private static FileFilter getFileNameExtensionFilter(final String description, final String ext) { return new FileFilter() { + @Override public boolean accept(File f) { - if ( f.toString()==null ) return false; return f.isDirectory() || f.toString().endsWith(ext); } + @Override public String getDescription() { return description; } @@ -196,14 +215,14 @@ public String getDescription() { private static File currentFile; public static final Action SAVE_AS_PNG_ACTION = new CanvasAction("Save as PNG") { + @Override public void actionPerformed(ActionEvent e) { final JFileChooser fileChooser = new JFileChooser(); fileChooser.setDialogTitle("Write to PNG"); fileChooser.setFileFilter(getFileNameExtensionFilter("png files", "png")); Preferences prefs = Preferences.userNodeForPackage(DasCanvas.class); String savedir = prefs.get("savedir", null); - if (savedir != null) - fileChooser.setCurrentDirectory(new File(savedir)); + if (savedir != null) fileChooser.setCurrentDirectory(new File(savedir)); if (currentFile != null) fileChooser.setSelectedFile(currentFile); int choice = fileChooser.showSaveDialog(currentCanvas); if (choice == JFileChooser.APPROVE_OPTION) { @@ -229,6 +248,7 @@ public void run() { }; public static final Action SAVE_AS_SVG_ACTION = new CanvasAction("Save as SVG") { + @Override public void actionPerformed(ActionEvent e) { final JFileChooser fileChooser = new JFileChooser(); fileChooser.setApproveButtonText("Select File"); @@ -236,8 +256,7 @@ public void actionPerformed(ActionEvent e) { fileChooser.setFileFilter(getFileNameExtensionFilter("svg files", "svg")); Preferences prefs = Preferences.userNodeForPackage(DasCanvas.class); String savedir = prefs.get("savedir", null); - if (savedir != null) - fileChooser.setCurrentDirectory(new File(savedir)); + if (savedir != null) fileChooser.setCurrentDirectory(new File(savedir)); if (currentFile != null) fileChooser.setSelectedFile(currentFile); int choice = fileChooser.showSaveDialog(currentCanvas); if (choice == JFileChooser.APPROVE_OPTION) { @@ -263,6 +282,7 @@ public void run() { }; public static final Action SAVE_AS_PDF_ACTION = new CanvasAction("Save as PDF") { + @Override public void actionPerformed(ActionEvent e) { final JFileChooser fileChooser = new JFileChooser(); fileChooser.setApproveButtonText("Select File"); @@ -270,8 +290,7 @@ public void actionPerformed(ActionEvent e) { fileChooser.setFileFilter(getFileNameExtensionFilter("pdf files", "pdf")); Preferences prefs = Preferences.userNodeForPackage(DasCanvas.class); String savedir = prefs.get("savedir", null); - if (savedir != null) - fileChooser.setCurrentDirectory(new File(savedir)); + if (savedir != null) fileChooser.setCurrentDirectory(new File(savedir)); if (currentFile != null) fileChooser.setSelectedFile(currentFile); int choice = fileChooser.showDialog(currentCanvas, "Select File"); if (choice == JFileChooser.APPROVE_OPTION) { @@ -297,52 +316,28 @@ public void run() { }; public static final Action EDIT_DAS_PROPERTIES_ACTION = new AbstractAction("DAS Properties") { + @Override public void actionPerformed(ActionEvent e) { org.das2.DasProperties.showEditor(); } }; - - private static boolean printBusy= false; - - private static void doPrintImmediately( Component me ) { - Printable p = currentCanvas.getPrintable(); - PrinterJob pj = PrinterJob.getPrinterJob(); - pj.setPrintable(p); -// PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet(); // This doesn't work for me on Linux, and http://stackoverflow.com/questions/4554452/java-native-print-dialog-change-icon says there's a access restriction. -// if ( me!=null ) { -// Window w= SwingUtilities.getWindowAncestor(me); -// Frame owner= w instanceof Frame ? (Frame)w : null; -// if ( owner!=null ) aset.add(new sun.print.DialogOwner(owner)); -// } - if (pj.printDialog()) { - try { - pj.print(); - } catch (PrinterException pe) { - Object[] message = {"Error printing", pe.getMessage()}; - JOptionPane.showMessageDialog(null, message, "ERROR", JOptionPane.ERROR_MESSAGE); - } - } - } - public static final Action PRINT_ACTION = new CanvasAction("Print...") { - @Override + + @Override public void actionPerformed(ActionEvent e) { - if ( printBusy ) { - JOptionPane.showMessageDialog( currentCanvas, "Another task is trying to print, please wait.", "Please Wait", JOptionPane.INFORMATION_MESSAGE ); - } else { - printBusy= true; - Runnable run= new Runnable() { - @Override - public void run() { - doPrintImmediately(currentCanvas); - printBusy= false; - } - }; - new Thread(run,"printThread").start(); + Printable p = currentCanvas.getPrintable(); + PrinterJob pj = PrinterJob.getPrinterJob(); + pj.setPrintable(p); + if (pj.printDialog()) { + try { + pj.print(); + } catch (PrinterException pe) { + Object[] message = {"Error printing", pe.getMessage()}; + JOptionPane.showMessageDialog(null, message, "ERROR", JOptionPane.ERROR_MESSAGE); + } } } }; - public static final Action REFRESH_ACTION = new CanvasAction("Refresh") { public void actionPerformed(ActionEvent e) { @@ -353,25 +348,9 @@ public void actionPerformed(ActionEvent e) { } }; - /** - * returns a list of all the rows and columns on the canvas. - * @return - */ - public List devicePositionList() { - return Collections.unmodifiableList(devicePositionList); - } - - @Override - public void setBounds(int x, int y, int width, int height) { - super.setBounds(x, y, width, height); - } - - @Override - public void invalidate() { - super.invalidate(); - } public static final Action ABOUT_ACTION = new CanvasAction("About") { + @Override public void actionPerformed(ActionEvent e) { String aboutContent = AboutUtil.getAboutHtml(); @@ -386,46 +365,35 @@ public void actionPerformed(ActionEvent e) { } }; - private static boolean disableActions = false; - - public static void setDisableActions(boolean val) { - disableActions = val; - } - public static Action[] getActions() { - if ( disableActions ) { - return new Action[0]; - } else { - return new Action[]{ + return new Action[]{ ABOUT_ACTION, REFRESH_ACTION, EDIT_DAS_PROPERTIES_ACTION, PRINT_ACTION, SAVE_AS_PNG_ACTION, SAVE_AS_SVG_ACTION, - SAVE_AS_PDF_ACTION,}; - } + SAVE_AS_PDF_ACTION, + }; } - private DasApplication application; private static final Logger logger = DasLogger.getLogger(DasLogger.GRAPHICS_LOG); private final GlassPane glassPane; private String dasName; private JPopupMenu popup; private boolean editable; - - List devicePositionList = new ArrayList(); - transient org.das2.util.DnDSupport dndSupport; - ChangesSupport stateSupport; + private int printing = 0; + List devicePositionList = new ArrayList(); + org.das2.util.DnDSupport dndSupport; + DasCanvasStateSupport stateSupport; /** The set of Threads that are currently printing this canvas. * This set is used to determine of certain operations that are only * appropriate in printing situations should occur. */ private Set printingThreads; - /** - * Creates a new instance of DasCanvas. - * + /** Creates a new instance of DasCanvas + * TODO */ public DasCanvas() { LookAndFeel.installColorsAndFont(this, "Panel.background", "Panel.foreground", "Panel.font"); @@ -450,111 +418,19 @@ public DasCanvas() { dndSupport = new CanvasDnDSupport(); } } - makeCurrent(); - stateSupport = new ChangesSupport(null, this); - stateSupport.addPropertyChangeListener(new PropertyChangeListener() { - - public void propertyChange(PropertyChangeEvent e) { - if (Boolean.FALSE.equals(e.getNewValue())) { - setOpaque(true); - repaint(); - } else { - setOpaque(false); - } - } - }); - } - - /** - * add a decorator that will be painted on top of all other objects. - * Each decorator object should complete painting within 100 milliseconds, and the - * total for all decorators should not exceed 300 milliseconds. - * This should be done on the event thread. - * @param painter - */ - public void addTopDecorator(Painter painter) { - this.topDecorators.add( painter ); - repaint(); - } - - /** - * remove the decorator. This should be done on the event thread. - * @param painter - */ - public void removeTopDecorator(Painter painter) { - this.topDecorators.remove( painter ); - repaint(); - } - - /** - * remove all top decorators. This should be done on the event thread. - */ - public void removeTopDecorators() { - this.topDecorators.clear( ); - repaint(); + CanvasAction.currentCanvas = this; + stateSupport = new DasCanvasStateSupport(this); } - /** - * returns true if there are any top decorators. - * @return true if there are any decorators. - */ - public boolean hasTopDecorators() { - return ! this.topDecorators.isEmpty(); - } - - /** - * add a decorator that will be painted on below all other objects. - * Each decorator object should complete painting within 100 milliseconds, and the - * total for all decorators should not exceed 300 milliseconds. - * This should be done on the event thread. - * @param painter - */ - public void addBottomDecorator(Painter painter) { - this.bottomDecorators.add( painter ); - repaint(); - } - - /** - * remove the decorator. This should be done on the event thread. - * @param painter - */ - public void removeBottomDecorator(Painter painter) { - this.bottomDecorators.remove( painter ); - repaint(); - } - - /** - * remove all bottom decorators. This should be done on the event thread. - */ - public void removeBottomDecorators() { - this.bottomDecorators.clear( ); - repaint(); - } - - - /** - * returns true if there are any bottom decorators. - * @return true if there are any decorators. - */ - public boolean hasBottomDecorators() { - return ! this.bottomDecorators.isEmpty(); - } - private MouseInputAdapter createMouseInputAdapter() { return new MouseInputAdapter() { - @Override public void mousePressed(MouseEvent e) { - makeCurrent(); - if ( e.isPopupTrigger() && e.getX()<10 && e.getY()<10 ) popup.show(DasCanvas.this, e.getX(), e.getY()); - } - - @Override - public void mouseReleased(MouseEvent e) { - makeCurrent(); - if ( e.isPopupTrigger() && e.getX()<10 && e.getY()<10 ) popup.show(DasCanvas.this, e.getX(), e.getY()); + Point primaryPopupLocation = e.getPoint(); + CanvasAction.currentCanvas = DasCanvas.this; + if (SwingUtilities.isRightMouseButton(e)) + popup.show(DasCanvas.this, e.getX(), e.getY()); } - }; } @@ -562,20 +438,23 @@ private JPopupMenu createPopupMenu() { JPopupMenu popup = new JPopupMenu(); JMenuItem props = new JMenuItem(PROPERTIES_ACTION); - props.setText("DasCanvas Properties"); popup.add(props); popup.addSeparator(); Action[] actions = getActions(); - if ( !disableActions ) { - for (Action action : actions) { - JMenuItem item = new JMenuItem(); - item.setAction(action); - popup.add(item); - } + for (int iaction = 0; iaction < actions.length; iaction++) { + JMenuItem item = new JMenuItem(); + item.setAction(actions[iaction]); + popup.add(item); } + popup.addSeparator(); + + JMenuItem close = new JMenuItem("close"); + close.setToolTipText("close this popup"); + popup.add(close); + return popup; } @@ -586,15 +465,14 @@ public Component getGlassPane() { return glassPane; } - /** - * return a list of all the rows and columns. - * @return a list of all the rows and columns. + /** TODO + * @return */ public List getDevicePositionList() { return Collections.unmodifiableList(devicePositionList); } private int displayLockCount = 0; - private final Object displayLockObject = new String("DISPLAY_LOCK_OBJECT");//findbugs DM_STRING_CTOR okay + private Object displayLockObject = new String("DISPLAY_LOCK_OBJECT"); /** * Lock the display for this canvas. All Mouse and Key events are captured @@ -603,7 +481,7 @@ public List getDevicePositionList() { * @param o the Object requesting the lock. * @see #freeDisplay(Object); */ - void lockDisplay(Object o) { + synchronized void lockDisplay(Object o) { synchronized (displayLockObject) { displayLockCount++; //if (displayLockCount == 1) { @@ -619,7 +497,7 @@ void lockDisplay(Object o) { * @param o the object releasing its lock on the display * @see #lockDisplay(Object) */ - void freeDisplay(Object o) { + synchronized void freeDisplay(Object o) { synchronized (displayLockObject) { displayLockCount--; if (displayLockCount == 0) { @@ -629,8 +507,8 @@ void freeDisplay(Object o) { } } - /** - * Creates a new instance of DasCanvas with the specified width and height + /** Creates a new instance of DasCanvas with the specified width and height + * TODO * @param width The width of the DasCanvas * @param height The height of the DasCanvas */ @@ -647,27 +525,20 @@ public void setApplication(DasApplication application) { this.application = application; } - /** - * simply returns getPreferredSize() + /** simply returns getPreferredSize(). * @return getPreferredSize() */ - @Override + @Override public Dimension getMaximumSize() { return getPreferredSize(); } - /** - * paints the canvas itself. If printing, stamps the date on it as well. - * @param g1 the Graphics object + /** paints the canvas itself. If printing, stamps the date on it as well. + * @param gl the Graphics object */ - @Override + @Override protected void paintComponent(Graphics g1) { - logger.finest("entering DasCanvas.paintComponent"); - - if (stateSupport.isValueAdjusting()) { - logger.finest("value is adjusting, returning"); - return; - } + logger.fine("entering DasCanvas.paintComponent"); Graphics2D g = (Graphics2D) g1; @@ -685,19 +556,6 @@ protected void paintComponent(Graphics g1) { g2.fill(g2.getClipBounds()); } g.setColor(getForeground()); - - Painter[] decor; - decor= bottomDecorators.toArray(emptyPainterArray); - for ( Painter p : decor ) { - try { - Graphics2D g2= (Graphics2D) g.create(); // create a graphics object in case they reset colors, etc. - p.paint(g2); - } catch ( Exception ex ) { - g.drawString( "bottomDecorator causes exception: "+ex.toString(), 20, 20 ); - ex.printStackTrace(); - } - } - if (isPrintingThread()) { int width, height; Date now; @@ -708,16 +566,8 @@ protected void paintComponent(Graphics g1) { if (!printingTag.equals("")) { now = new Date(); - if ( printingTag.contains("$Y") || printingTag.contains("$y") ) { - TimeParser tp= TimeParser.create(printingTag); - Datum nowTZ= TimeUtil.now().add( Units.hours.createDatum( TimeZone.getDefault().getRawOffset()/3600000 ) ); - s= tp.format( nowTZ, nowTZ ); - } else if ( printingTag.contains("'yy") ) { - dateFormat = new SimpleDateFormat(printingTag); - s = dateFormat.format(now); - } else { - s = printingTag; - } + dateFormat = new SimpleDateFormat(printingTag); + s = dateFormat.format(now); oldFont = g.getFont(); font = oldFont.deriveFont((float) oldFont.getSize() / 2); @@ -726,21 +576,19 @@ protected void paintComponent(Graphics g1) { height = metrics.getHeight(); g.setFont(font); - g.drawString(s, getWidth() - width - 2 * height, getHeight() - metrics.getAscent() ); + g.drawString(s, getWidth() - width, getHeight() -height ); g.setFont(oldFont); } } - } - /** - * Prints the canvas, scaling and possibly rotating it to improve fit. + /** Prints the canvas, scaling and possibly rotating it to improve fit. * @param printGraphics the Graphics object. * @param format the PageFormat object. * @param pageIndex should be 0, since the image will be on one page. * @return Printable.PAGE_EXISTS or Printable.NO_SUCH_PAGE */ - @Override + @Override public int print(Graphics printGraphics, PageFormat format, int pageIndex) { if (pageIndex != 0) return NO_SUCH_PAGE; @@ -780,7 +628,8 @@ public int print(Graphics printGraphics, PageFormat format, int pageIndex) { * swing to handle the DasCanvasComponents. */ protected static class RowColumnLayout implements LayoutManager { - @Override + + @Override public void layoutContainer(Container target) { synchronized (target.getTreeLock()) { int count = target.getComponentCount(); @@ -795,24 +644,27 @@ public void layoutContainer(Container target) { } } } - @Override + + @Override public Dimension minimumLayoutSize(Container target) { return new Dimension(0, 0); } - @Override + + @Override public Dimension preferredLayoutSize(Container target) { return new Dimension(400, 300); } - @Override + + @Override public void addLayoutComponent(String name, Component comp) { } - @Override + + @Override public void removeLayoutComponent(Component comp) { } } /** - * return true if the current thread is registered as the one printing this component. * @return true if the current thread is registered as the one printing this component. */ protected final boolean isPrintingThread() { @@ -821,20 +673,8 @@ protected final boolean isPrintingThread() { } } - /** - * Java1.6 has this function native - * @return - */ - public boolean lisPaintingForPrint() { - return this.lpaintingForPrint; - } - - /** - * print the canvas, filling the background and the painting. - * @param g1 the graphics context. - */ - @Override - public void print(Graphics g1) { + @Override + public void print(Graphics g) { synchronized (this) { if (printingThreads == null) { printingThreads = new HashSet(); @@ -843,34 +683,20 @@ public void print(Graphics g1) { } try { setOpaque(false); - - Graphics2D g= (Graphics2D)g1; - // if svg - g.setColor( this.getBackground() ); - g.fillRect(0,0,getWidth(),getHeight()); - g.setColor( this.getForeground() ); - g.setBackground( this.getBackground() ); - - //logger.fine("*** print graphics: " + g); - //logger.fine("*** print graphics clip: " + g.getClip()); - - if ( logger.isLoggable(Level.FINER) ) { - for (int i = 0; i < getComponentCount(); i++) { - Component c = getComponent(i); - if (c instanceof DasCanvasComponent) { - DasCanvasComponent p = (DasCanvasComponent) c; - logger.log(Level.FINER, "-- {0} --", p); - logger.log(Level.FINER, " DasPlot.isDirty()={0}", p.isDirty()); - logger.log(Level.FINER, " DasPlot.getBounds()={0}", p.getBounds()); - } + logger.fine("*** print graphics: " + g); + logger.fine("*** print graphics clip: " + g.getClip()); + + for (int i = 0; i < getComponentCount(); i++) { + Component c = getComponent(i); + if (c instanceof DasPlot) { + DasPlot p = (DasPlot) c; + logger.fine(" DasPlot.isDirty()=" + p.isDirty()); + logger.fine(" DasPlot.getBounds()=" + p.getBounds()); + /* System.err.println(" DasPlot.isDirty()=" + p.isDirty()); + System.err.println(" DasPlot.getBounds()=" + p.getBounds()); */ } } - lpaintingForPrint= true; - super.print(g); - - lpaintingForPrint= false; - } finally { setOpaque(true); synchronized (this) { @@ -879,8 +705,7 @@ public void print(Graphics g1) { } } - /** - * Returns an instance of java.awt.print.Printable that can + /** Returns an instance of java.awt.print.Printable that can * be used to render this canvas to a printer. The current implementation * returns a reference to this canvas. This method is provided so that in * the future, the canvas can delegate it's printing to another object. @@ -890,258 +715,52 @@ public Printable getPrintable() { return this; } - /** - * return the colorbar or null if there is not a single, unique colorbar. - * @param plot a das plot - * @return null or the single colorbar. - */ - private DasColorBar findOneColorBar( DasPlot plot ) { - Renderer[] rr= plot.getRenderers(); - DasColorBar result= null; - int count=0; - for ( Renderer r: rr ) { - if ( r.getColorBar()!=null ) { - result= r.getColorBar(); - count++; - } - } - return count==1 ? result : null; - } - - /** - * encode the plot in a JSON fragment. See http://autoplot.org/richPng - * @param plot the plot to describe. - * @param indent indent new lines this amount - * @param isInList is in list, so append a comma after the y_axis. - * @return the JSON code - */ - private String getJSONForPlot( DasPlot plot, String indent, boolean isInList ) { - DasColorBar cb= findOneColorBar(plot); - boolean inclColorbar= ( cb!=null && cb.isVisible() ) ; - StringBuilder json= new StringBuilder(); - json.append( String.format( "%s\"title\":\"%s\", \n", indent, plot.getTitle().replaceAll("\"", "\\\"") ) ); - DasAxis axis= plot.getXAxis(); - String minstr= UnitsUtil.isTimeLocation( axis.getDataMinimum().getUnits() ) ? - String.format( "\"%s\"", axis.getDataMinimum().toString() ) : - String.valueOf( axis.getDataMinimum( axis.getUnits() ) ); - String maxstr= UnitsUtil.isTimeLocation( axis.getDataMaximum().getUnits() ) ? - String.format( "\"%s\"", axis.getDataMaximum().toString() ) : - String.valueOf( axis.getDataMaximum( axis.getUnits() ) ); - String unitsstr= UnitsUtil.isTimeLocation( axis.getDataMinimum().getUnits() ) ? "UTC" : axis.getDataMinimum().getUnits().toString(); - String dpos; - dpos= String.format( "\"left\":%d, \"right\":%d", (int)plot.getColumn().getDMinimum(), (int)plot.getColumn().getDMaximum() ); - json.append( String.format( "%s\"xaxis\": { \"label\":\"%s\", \"min\":%s, \"max\":%s, %s, \"type\":\"%s\", \"units\":\"%s\" },\n", - indent, - axis.getLabel().replaceAll("\"", "\\\"") , - minstr, maxstr, dpos, - axis.isLog() ? "log" : "lin", - unitsstr ) ); - axis= plot.getYAxis(); - minstr= UnitsUtil.isTimeLocation( axis.getDataMinimum().getUnits() ) ? - String.format( "'%s'", axis.getDataMinimum().toString() ) : - String.valueOf( axis.getDataMinimum( axis.getUnits() ) ); - maxstr= UnitsUtil.isTimeLocation( axis.getDataMaximum().getUnits() ) ? - String.format( "'%s'", axis.getDataMaximum().toString() ) : - String.valueOf( axis.getDataMaximum( axis.getUnits() ) ); - unitsstr= UnitsUtil.isTimeLocation( axis.getDataMinimum().getUnits() ) ? "UTC" : axis.getDataMinimum().getUnits().toString(); - dpos= String.format( "\"top\":%d, \"bottom\":%d", (int)plot.getRow().getDMinimum(), (int)plot.getRow().getDMaximum() ); - - if ( inclColorbar ) { - json.append( String.format( "%s\"yaxis\": { \"label\":\"%s\", \"min\":%s, \"max\":%s, %s, \"type\":\"%s\", \"units\":\"%s\" },\n", - indent, - axis.getLabel().replaceAll("\"", "\\\"") , - minstr, maxstr, dpos, - axis.isLog() ? "log" : "lin", - unitsstr ) ); - } else { - json.append( String.format( "%s\"yaxis\": { \"label\":\"%s\", \"min\":%s, \"max\":%s, %s, \"type\":\"%s\", \"units\":\"%s\" }%s\n", - indent, - axis.getLabel().replaceAll("\"", "\\\"") , - minstr, maxstr, dpos, - axis.isLog() ? "log" : "lin", - unitsstr, isInList ? "," : "" ) ); - } - // if we can identify a colorbar for the plot, include it as well, with coordinates for the min and max colors. - if ( inclColorbar ) { - assert cb!=null; - minstr= UnitsUtil.isTimeLocation( cb.getDataMinimum().getUnits() ) ? - String.format( "'%s'", cb.getDataMinimum().toString() ) : - String.valueOf( cb.getDataMinimum( cb.getUnits() ) ); - maxstr= UnitsUtil.isTimeLocation( cb.getDataMaximum().getUnits() ) ? - String.format( "'%s'", cb.getDataMaximum().toString() ) : - String.valueOf( cb.getDataMaximum( cb.getUnits() ) ); - unitsstr= UnitsUtil.isTimeLocation( cb.getDataMinimum().getUnits() ) ? "UTC" : cb.getDataMinimum().getUnits().toString(); - // locate the painted colorbar so that it could be used to lookup colors. - int[] pos= new int[4]; - if ( cb.isHorizontal() ) { - pos[1]=pos[3]=cb.getRow().getDMiddle(); - pos[0]= cb.getColumn().getDMinimum(); - pos[2]= cb.getColumn().getDMaximum(); - } else { - pos[0]=pos[2]=cb.getColumn().getDMiddle(); - pos[1]= cb.getRow().getDMaximum()-2; // flip over because 0,0 is upper-left. - pos[3]= cb.getRow().getDMinimum()+1; // tweak to get inside the axis. This is all just to get ballpark Z values anyway... - } - json.append( String.format( "%s\"zaxis\": { \"label\":\"%s\", \"min\":%s, \"max\":%s, \"minpixel\":[%d,%d], \"maxpixel\":[%d,%d], \"type\":\"%s\", \"units\":\"%s\" }%s\n", - indent, - cb.getLabel().replaceAll("\"", "\\\"") , - minstr, maxstr, pos[0], pos[1], pos[2], pos[3], - cb.isLog() ? "log" : "lin", - unitsstr, isInList ? "," : "" ) ); - } - - return json.toString(); - } - - /** - * returns JSON code that can be used to get plot positions and axes. - * @return - */ - public String getImageMetadata() { - List plots= new ArrayList(); - - for ( Component c: this.getCanvasComponents() ) { - if ( c instanceof DasPlot ) { - plots.add( (DasPlot)c ); - } - } - - StringBuilder json= new StringBuilder(); - - json.append( String.format("{ \"size\":[%d,%d],\n", this.getWidth(),this.getHeight() ) ); - json.append( String.format(" \"numberOfPlots\":%d,\n",plots.size() ) ); - - if ( plots.size()>0 ) { - json.append(" \"plots\": [\n"); - DasPlot lastPlot= plots.get( plots.size()-1 ); - for ( DasPlot p: plots ) { - json.append( " {\n"); - json.append( getJSONForPlot( p, " ", false ) ); - json.append( " }"); - if ( p!=lastPlot ) json.append(","); - json.append( "\n" ); - } - json.append(" ]"); - } - json.append("}" ); - return json.toString(); - - } - - public String broken= null; - /** * uses getImage to get an image of the canvas and encodes it * as a png. * - * Note this now puts in a JSON representation of plot locations in the "plotInfo" tag. The plotInfo - * tag will contain: - *
    {@code
    -     *   "size:[640,480]"
    -     *   "numberOfPlots:0"   
    -     *   "plots: { ... }"  where each plot contains:
    -     *   "title" "xaxis" "yaxis"
    -     *}
    - * See http://autoplot.org/richPng. - * @param out the outputStream. This is left open, so the opener code must close it! - * @param w width in pixels - * @param h height in pixels - * @throws IOException if there is an error opening the file for writing - */ - public void writeToPng( OutputStream out, int w, int h ) throws IOException { - - logger.fine("Enter writeToPng"); - - BufferedImage image = getImage(w,h); - - boolean doCheckAPBug1129= false; - if ( doCheckAPBug1129 ) { // see DemoAPBug1129.java. - List peaks= new ArrayList(); - int z0= ( image.getRGB( 150,150 ) & 0xFF ); - double dz= 0; - for ( int i=0; i<50; i++ ) { - //System.err.println( String.format( "%d %d %d %d", i, ( c & 0xFF0000 ) >> 16, ( c & 0x00FF00 ) >> 8, c & 0x0000FF ) ); - int z1= ( image.getRGB( 150,150-i ) & 0xFF ); - - double dz1= z1-z0; - if ( dz>0 && dz1< 0 ) { - peaks.add(i); - } - dz= dz1; - z0= z1; - } - System.err.println("peaks: "+peaks); - if ( peaks.size()>1 ) { - logger.warning("double peak detected"); - broken= "doublePeakDetected"; - } else { - broken= null; - } - } - - if ( getBackground().getAlpha()>0 ) { - logger.fine("Encoding image into png"); - - DasPNGEncoder encoder = new DasPNGEncoder(); - encoder.addText(DasPNGConstants.KEYWORD_CREATION_TIME, new Date().toString()); - javax.swing.JFrame f= DasApplication.getDefaultApplication().getMainFrame(); - if ( f!=null ) { - String title= f.getTitle(); - int i= title.indexOf(" - Autoplot"); - if ( i>-1 ) { - title= title.substring(i+3) + " > " + title.substring(0,i); // Autoplot > foo.vap - } - encoder.addText(DasPNGConstants.KEYWORD_SOFTWARE, title ); - } - encoder.addText(DasPNGConstants.KEYWORD_PLOT_INFO, getImageMetadata() ); - encoder.write(image, out); - } else { - logger.info("ImageIO used to create image with transparent background, no metadata will be put in image."); - ImageIO.write(image, "png", out ); - } - } - - /** - * uses getImage to get an image of the canvas and encodes it - * as a png. + * TODO: this should take an output stream, and then a helper class + * manages the file. (e.g. web servers) * - * Note this now puts in a JSON representation of plot locations in the "plotInfo" tag. - * See http://autoplot.org/richPng * @param filename the specified filename * @throws IOException if there is an error opening the file for writing */ - public void writeToPng( String filename) throws IOException { + public void writeToPng(String filename) throws IOException { + writeToPng(filename, Collections.EMPTY_MAP); + } - int w= getWidth(); - int h= getHeight(); + public void writeToPng(String filename, Map txt) throws IOException { - if (h==0 || w==0) { - Dimension p = getPreferredSize(); - w=(int) p.getWidth(); - h=(int) p.getHeight(); - } - - logger.log( Level.FINE, "Write to png {0} **************************************", filename); final FileOutputStream out = new FileOutputStream(filename); + + logger.fine("Enter writeToPng"); + + Image image = getImage(getWidth(), getHeight()); + + DasPNGEncoder encoder = new DasPNGEncoder(); + encoder.addText(DasPNGConstants.KEYWORD_CREATION_TIME, new Date().toString()); + for (String key : txt.keySet()) { + encoder.addText(key, txt.get(key)); + } try { - writeToPng( out, w, h ); + logger.fine("Encoding image into png"); + encoder.write((BufferedImage) image, out); + logger.fine("write png file " + filename); + } catch (IOException ioe) { } finally { - out.close(); + try { + out.close(); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } } - logger.log(Level.FINE, "Wrote png file {0} **************************************", filename); - } - /** - * write the canvas to a PDF file. - * @param filename the PDF file name. - * @throws IOException - */ public void writeToPDF(String filename) throws IOException { try { writeToGraphicsOutput(filename, "org.das2.util.awt.PdfGraphicsOutput"); - DasLogger.getLogger(DasLogger.GRAPHICS_LOG).log(Level.FINE, "write pdf file {0}", filename); + DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("write pdf file " + filename); } catch (NoClassDefFoundError cnfe) { DasExceptionHandler.handle(new RuntimeException("PDF output is not available", cnfe)); } catch (ClassNotFoundException cnfe) { @@ -1158,9 +777,8 @@ public void writeToPDF(String filename) throws IOException { * parameter settings. * @param out OutputStream to receive the data * @param go GraphicsOutput object. - * @throws java.io.IOException */ - public void writeToGraphicsOutput(OutputStream out, GraphicsOutput go) throws IOException { + public void writeToGraphicsOutput(OutputStream out, GraphicsOutput go) throws IOException, IllegalAccessException { go.setOutputStream(out); go.setSize(getWidth(), getHeight()); go.start(); @@ -1168,28 +786,14 @@ public void writeToGraphicsOutput(OutputStream out, GraphicsOutput go) throws IO go.finish(); } - /** - * write to future implementations of graphicsOutput. - * @param filename - * @param graphicsOutput - * @throws IOException - * @throws ClassNotFoundException - * @throws InstantiationException - * @throws IllegalAccessException - * @see org.das2.util.awt.GraphicsOutput - */ public void writeToGraphicsOutput(String filename, String graphicsOutput) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { - FileOutputStream out=null; - try { - out= new FileOutputStream(filename); - Class goClass = Class.forName(graphicsOutput); - GraphicsOutput go = (GraphicsOutput) goClass.newInstance(); - writeToGraphicsOutput(out, go); - } finally { - if ( out!=null ) out.close(); - } + FileOutputStream out = new FileOutputStream(filename); + Class goClass = Class.forName(graphicsOutput); + GraphicsOutput go = (GraphicsOutput) goClass.newInstance(); + writeToGraphicsOutput(out, go); + } /** @@ -1199,7 +803,7 @@ public void writeToGraphicsOutput(String filename, String graphicsOutput) public void writeToSVG(String filename) throws IOException { try { writeToGraphicsOutput(filename, "org.das2.util.awt.SvgGraphicsOutput"); - DasLogger.getLogger(DasLogger.GRAPHICS_LOG).log(Level.FINE, "write svg file {0}", filename); + DasLogger.getLogger(DasLogger.GRAPHICS_LOG).fine("write svg file " + filename); } catch (ClassNotFoundException cnfe) { DasExceptionHandler.handle(new RuntimeException("SVG output is not available", cnfe)); } catch (InstantiationException ie) { @@ -1210,68 +814,25 @@ public void writeToSVG(String filename) throws IOException { } /** - * returns true if work needs to be done to make the canvas clean. - * This checks each component's isDirty. - * @return true if work needs to be done to make the canvas clean + * returns true if work needs to be done to make the canvas clean. This checks each component's + * isDirty. + * + * @return */ public boolean isDirty() { - DasCanvasComponent[] cc = this.getCanvasComponents(); - boolean result = false; - for (DasCanvasComponent cc1 : cc) { - boolean dirty1 = cc1.isDirty(); - if (dirty1) { - logger.log(Level.FINE, "component is marked as dirty: {0}", cc[1]); - cc1.isDirty(); - } - result = result | dirty1 ; + DasCanvasComponent[] cc= this.getCanvasComponents(); + boolean result= false; + for ( int i=0; i result ) { - stateSupport.pendingChanges(result); - if ( Toolkit.getDefaultToolkit().getSystemEventQueue().peekEvent(DasUpdateEvent.DAS_UPDATE_EVENT_ID) != null ) { - result.put( "dasUpdate", "eventQueueContainsUpdateEvents"); - //TODO: it would be better to check for the eventQueueBlocker object as well. - } + return; } /** @@ -1396,9 +892,6 @@ public void pendingChanges( Map result ) { * validate or revalidate should probably do this. */ public void resizeAllComponents() { - for (DasDevicePosition devicePositionList1 : devicePositionList) { - devicePositionList1.revalidate(); - } for (int i = 0; i < getComponentCount(); i++) { Component c = getComponent(i); if (c instanceof DasCanvasComponent) { @@ -1407,127 +900,60 @@ public void resizeAllComponents() { } } - /** - * process all pending operations and make sure we're repainted. See PlotCommand in Autoplot. - */ - public void waitUntilValid() { - for (int i = 0; i < getComponentCount(); i++) { - Component c = getComponent(i); - if (c instanceof DasPlot) { - ((DasPlot) c).invalidateCacheImage(); - } - } - if ( ! this.isShowing() || "true".equals(DasApplication.getProperty("java.awt.headless", "false")) ) { - this.addNotify(); - logger.log(Level.FINER, "setSize({0})", getPreferredSize()); - this.setSize(getPreferredSize()); - logger.finer("validate()"); - this.validate(); - - resizeAllComponents(); - } else { - resizeAllComponents(); - } - waitUntilIdle(); - - } - /** * resets the width and height, then waits for all update * messages to be processed. In headless mode, - * the GUI components are validated. + * the gui components are validated. * This must not be called from the event queue, because - * it uses eventQueueBlocker! - * - * @param width the width of the output in pixels. - * @param height the width of the output in pixels. - * - * @throws IllegalStateException if called from the event queue. - */ - public void prepareForOutput(int width, int height) { - if (SwingUtilities.isEventDispatchThread()) - throw new IllegalStateException("dasCanvas.prepareForOutput must not be called from event queue!"); - setPreferredWidth(width); - setPreferredHeight(height); - - if ( ! this.isShowing() || "true".equals(DasApplication.getProperty("java.awt.headless", "false")) ) { - this.addNotify(); - logger.log(Level.FINER, "setSize({0})", getPreferredSize()); - this.setSize(getPreferredSize()); - logger.finer("validate()"); - this.validate(); - resizeAllComponents(); - } - try { - waitUntilIdle(true); // wait for monitors. - } catch (InterruptedException ex) { - throw new RuntimeException(ex); - } - - } - - /** - * Creates a BufferedImage by blocking until the image is ready. This - * includes waiting for datasets to load, etc. Works by submitting - * an invokeAfter request to the RequestProcessor that calls - * {@link #writeToImageImmediately(Image)}. - * - * @param width - * @param height - * @return a BufferedImage - */ - public BufferedImage getImage(int width, int height) { - - long t0= System.currentTimeMillis(); - - String msg = "dasCanvas.getImage(" + width + "," + height + ")"; - logger.fine(msg); - - prepareForOutput(width, height); - - final BufferedImage image = getBackground().getAlpha() > 0 ? new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) : - new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB ); - - if (SwingUtilities.isEventDispatchThread()) { - writeToImageImmediately(image); - } else { - Runnable run = new Runnable() { - @Override - public void run() { - logger.fine("writeToImageImmediately"); - writeToImageImmediately(image); - } - }; - try { - SwingUtilities.invokeAndWait(run); - } catch (InvocationTargetException ex) { - application.getExceptionHandler().handle(ex); - } catch (InterruptedException ex) { - application.getExceptionHandler().handle(ex); - } + * it uses eventQueueBlocker! + * + * @param width the width of the output in pixels. + * @param height the width of the output in pixels. + * + * @throws IllegalStateException if called from the event queue. + */ + public void prepareForOutput(final int width, final int height) { + if (SwingUtilities.isEventDispatchThread()) throw new IllegalStateException("dasCanvas.prepareForOutput must not be called from event queue!"); + + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + setPreferredWidth(width); + setPreferredHeight(height); + + if ("true".equals(DasApplication.getProperty("java.awt.headless", "false"))) { + DasCanvas.this.addNotify(); + logger.finer("setSize(" + getPreferredSize() + ")"); + DasCanvas.this.setSize(getPreferredSize()); + logger.finer("validate()"); + DasCanvas.this.validate(); + + resizeAllComponents(); + } + } + }); + waitUntilIdle(); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); } + catch (InvocationTargetException ex) { + throw new RuntimeException(ex); + } - logger.log(Level.FINE, "time to getImage: {0}ms", (System.currentTimeMillis() - t0)); - return image; } - /** + /** * Creates a BufferedImage by blocking until the image is ready. This * includes waiting for datasets to load, etc. Works by submitting * an invokeAfter request to the RequestProcessor that calls * {@link #writeToImageImmediately(Image)}. - * - * Note, this calls writeToImageImmediatelyNonPrint, which avoids the usual overhead of - * revalidating the DasPlot elements we normally do when printing to a new device. - * + * * @param width * @param height - * @return an Image + * @return */ - public Image getImageNonPrint(int width, int height) { - - long t0= System.currentTimeMillis(); - + public Image getImage(int width, int height) { String msg = "dasCanvas.getImage(" + width + "," + height + ")"; logger.fine(msg); @@ -1536,13 +962,13 @@ public Image getImageNonPrint(int width, int height) { final Image image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); if (SwingUtilities.isEventDispatchThread()) { - writeToImageImmediatelyNonPrint(image); + writeToImageImmediately(image); } else { Runnable run = new Runnable() { - @Override + public void run() { logger.fine("writeToImageImmediately"); - writeToImageImmediatelyNonPrint(image); + writeToImageImmediately(image); } }; try { @@ -1554,79 +980,30 @@ public void run() { } } - logger.log(Level.FINE, "time to getImageNonPaint: {0}ms", (System.currentTimeMillis() - t0)); return image; } - /** - * Writes on to the image without waiting, using the print method. - * The graphics context is accessed with image.getGraphics. - * @param image the image + /** TODO + * @param image */ - public void writeToImageImmediately(Image image) { - Graphics2D graphics; + protected void writeToImageImmediately(Image image) { + Graphics graphics; try { synchronized (displayLockObject) { - while (displayLockCount != 0) { + if (displayLockCount != 0) { displayLockObject.wait(); } } } catch (InterruptedException ex) { } - graphics = (Graphics2D) image.getGraphics(); - if ( this.getBackground().getAlpha()>0 ) { - graphics.setColor(this.getBackground()); - graphics.fillRect(0, 0, image.getWidth(this), image.getHeight(this)); - graphics.setColor(this.getForeground()); - } - graphics.setBackground(this.getBackground()); + graphics = image.getGraphics(); + graphics.setColor(this.getBackground()); + graphics.fillRect(0, 0, image.getWidth(this), image.getHeight(this)); + graphics.setColor(this.getForeground()); print(graphics); } - /** - * This by passes the normal print method used in writeToImageImmedately, which sets the printing flags which - * tell the components, like DasPlot, to fully reset. This was introduced so that Autoplot could get thumbnails - * and an image of the canvas for its layout tab without having to reset. - * @param image the image - */ - public void writeToImageImmediatelyNonPrint( Image image ) { - long t0= System.currentTimeMillis(); - Graphics2D graphics; - try { - synchronized (displayLockObject) { - while (displayLockCount != 0) { - displayLockObject.wait(); - } - } - } catch (InterruptedException ex) { - } - graphics = (Graphics2D) image.getGraphics(); - - // code from print method. Here we do the print stuff, but don't turn on the printing flag. - setOpaque(false); - - Graphics2D g= (Graphics2D)graphics; - // if svg - g.setColor( this.getBackground() ); - g.fillRect(0,0,getWidth(),getHeight()); - g.setColor( this.getForeground() ); - g.setBackground( this.getBackground() ); - - // avoid calling the overrided print method, which turns on flags for printing. - super.print(g); - - logger.log(Level.FINE, "time to writeToImageImmediatelyNonPaint: {0}ms", (System.currentTimeMillis() - t0)); - } - - private transient PropertyChangeListener repaintListener= new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - repaint(); - } - }; - - /** - * This methods adds the specified DasCanvasComponent to this canvas. + /** This methods adds the specified DasCanvasComponent to this canvas. * @param c the component to be added to this canvas * Note that the canvas will need to be revalidated after the component * is added. @@ -1634,7 +1011,6 @@ public void propertyChange(PropertyChangeEvent evt) { * @param column DasColumn specifying the layout of the component. */ public void add(DasCanvasComponent c, DasRow row, DasColumn column) { - logger.log( Level.FINE, "adding DasCanvasComponent {0}", c); if (c.getRow() == DasRow.NULL || c.getRow().getParent() != this) { c.setRow(row); } @@ -1642,24 +1018,29 @@ public void add(DasCanvasComponent c, DasRow row, DasColumn column) { c.setColumn(column); } add(c); + PropertyChangeListener positionListener = new PropertyChangeListener() { - row.addPropertyChangeListener(repaintListener); - column.addPropertyChangeListener(repaintListener); + public void propertyChange(PropertyChangeEvent evt) { + repaint(); + } + }; + + if(row != null) + row.addPropertyChangeListener(positionListener); + + if(column != null) + column.addPropertyChangeListener(positionListener); } - /** - * This is doing something special setting the LAYER_PROPERTY. We - * check to see if the property is already set, and if it is not then - * we set it to the default value. (This is to allow client code to - * set it before adding the component.) + /** TODO * @param comp * @param constraints * @param index */ - @Override + @Override protected void addImpl(Component comp, Object constraints, int index) { if (comp == null) { - logger.log(Level.SEVERE,"NULL COMPONENT"); + org.das2.util.DasDie.println("NULL COMPONENT"); Thread.dumpStack(); return; } @@ -1691,7 +1072,6 @@ protected void addImpl(Component comp, Object constraints, int index) { * @param width the specified width. */ public void setPreferredWidth(int width) { - logger.log(Level.FINE, "setPreferredWidth({0})", width); Dimension pref = getPreferredSize(); pref.width = width; setPreferredSize(pref); @@ -1703,18 +1083,14 @@ public void setPreferredWidth(int width) { * @param height the specified height */ public void setPreferredHeight(int height) { - logger.log(Level.FINE, "setPreferredHeight({0})", height); Dimension pref = getPreferredSize(); pref.height = height; setPreferredSize(pref); if (getParent() != null) ((JComponent) getParent()).revalidate(); } - /** - * The font used should be the base font scaled based on the canvas size. - * If this is false, then the canvas font is simply the base font. - */ - protected boolean scaleFonts = true; + + protected boolean scaleFonts = true; /** * The font used should be the base font scaled based on the canvas size. @@ -1722,18 +1098,10 @@ public void setPreferredHeight(int height) { */ public static final String PROP_SCALEFONTS = "scaleFonts"; - /** - * true if the fonts should be rescaled as the window size is changed. - * @return true if the fonts should be rescaled as the window size is changed. - */ public boolean isScaleFonts() { return scaleFonts; } - /** - * true if the fonts should be rescaled as the window size is changed. - * @param scaleFonts true if the fonts should be rescaled as the window size is changed. - */ public void setScaleFonts(boolean scaleFonts) { boolean oldScaleFonts = this.scaleFonts; this.scaleFonts = scaleFonts; @@ -1741,19 +1109,10 @@ public void setScaleFonts(boolean scaleFonts) { firePropertyChange(PROP_SCALEFONTS, oldScaleFonts, scaleFonts); } - /** - * Property name for the base font. - */ - public static final String PROP_BASEFONT= "baseFont"; - - /** - * the base font, which is the font or the font which is scaled when scaleFont is true. - */ private Font baseFont = null; - /** - * the base font, which is the font or the font which is scaled when scaleFont is true. - * @return the base font, which is the font or the font which is scaled when scaleFont is true. + /** TODO + * @return */ public Font getBaseFont() { if (baseFont == null) { @@ -1763,24 +1122,21 @@ public Font getBaseFont() { } /** - * the base font, which is the font or the font which is scaled with canvas size when scaleFont is true. + * The base font is the font from which all other fonts should be derived. When the + * canvas is resized, the base font size is scaled. * @param font the font used to derive all other fonts. */ public void setBaseFont(Font font) { - logger.log(Level.FINE, "setBaseFont({0})", font); Font oldFont = getFont(); - Font oldBaseFont= baseFont; this.baseFont = font; - if ( scaleFonts ) { + if ( scaleFonts ) { setFont(getFontForSize(getWidth(), getHeight())); } else { setFont( font ); } firePropertyChange("font", oldFont, getFont()); //TODO: really? - firePropertyChange("baseFont", oldBaseFont, this.baseFont ); repaint(); } - private static final int R_1024_X_768 = 1024 * 768; private static final int R_800_X_600 = 800 * 600; private static final int R_640_X_480 = 640 * 480; @@ -1808,7 +1164,8 @@ private Font getFontForSize(int width, int height) { private ComponentListener createResizeListener() { return new ComponentAdapter() { - @Override + + @Override public void componentResized(ComponentEvent e) { Font aFont; if ( scaleFonts ) { @@ -1823,8 +1180,169 @@ public void componentResized(ComponentEvent e) { }; } - /** - * Returns the DasCanvasComponent that contains the (x, y) location. + /** TODO + * @return + * @param document + */ + @Override + public Element getDOMElement(Document document) { + Element element = document.createElement("canvas"); + Dimension size = getPreferredSize(); + element.setAttribute("name", getDasName()); + element.setAttribute("width", Integer.toString(size.width)); + element.setAttribute("height", Integer.toString(size.height)); + + for (int index = 0; index < devicePositionList.size(); index++) { + Object obj = devicePositionList.get(index); + if (obj instanceof DasRow) { + DasRow row = (DasRow) obj; + element.appendChild(row.getDOMElement(document)); + } else if (obj instanceof DasColumn) { + DasColumn column = (DasColumn) obj; + element.appendChild(column.getDOMElement(document)); + } + } + + Component[] components = getComponents(); + Map elementMap = new LinkedHashMap(); + + //THREE PASS ALGORITHM. + //1. Process all DasAxis components. + // Add all , , elements to elementList. + //2. Process all DasColorBar components. + // Remove all elements that correspond to axis property of colorbars. + // Add all elements to elementList. + //3. Process all DasSpectrogramPlot and DasPlot components. + // Remove all , , , and elements + // that correspond to xAxis, yAxis, and colorbar properties of + // plots spectrograms and spectrogram renderers. + // Add all elements to elementList. + + for (int index = 0; index < components.length; index++) { + if (components[index] instanceof DasAxis) { + DasAxis axis = (DasAxis) components[index]; + elementMap.put(axis.getDasName(), axis.getDOMElement(document)); + } + } + for (int index = 0; index < components.length; index++) { + if (components[index] instanceof DasColorBar) { + DasColorBar colorbar = (DasColorBar) components[index]; + elementMap.put(colorbar.getDasName(), colorbar.getDOMElement(document)); + } + } + for (int index = 0; index < components.length; index++) { + if (components[index] instanceof DasPlot) { + DasPlot plot = (DasPlot) components[index]; + elementMap.remove(plot.getXAxis().getDasName()); + elementMap.remove(plot.getYAxis().getDasName()); + Renderer[] renderers = plot.getRenderers(); + for (int i = 0; i < renderers.length; i++) { + if (renderers[i] instanceof SpectrogramRenderer) { + SpectrogramRenderer spectrogram = (SpectrogramRenderer) renderers[i]; + elementMap.remove(spectrogram.getColorBar().getDasName()); + } + } + elementMap.put(plot.getDasName(), plot.getDOMElement(document)); + } + } + + for (Iterator iterator = elementMap.values().iterator(); iterator.hasNext();) { + Element e = (Element) iterator.next(); + if (e != null) { + element.appendChild(e); + } + } + return element; + } + + /** Process a <canvas> element. + * + * @param form + * @param element The DOM tree node that represents the element + * @throws DasPropertyException + * @throws DasNameException + * @throws ParsedExpressionException + * @return + */ + public static DasCanvas processCanvasElement(Element element, FormBase form) + throws DasPropertyException, DasNameException, DasException, ParsedExpressionException, java.text.ParseException { + try { + Logger log = DasLogger.getLogger(DasLogger.DASML_LOG); + + String name = element.getAttribute("name"); + int width = Integer.parseInt(element.getAttribute("width")); + int height = Integer.parseInt(element.getAttribute("height")); + + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + + DasCanvas canvas = new DasCanvas(width, height); + + NodeList children = element.getChildNodes(); + int childCount = children.getLength(); + for (int index = 0; index < childCount; index++) { + Node node = children.item(index); + log.fine("node=" + node.getNodeName()); + if (node instanceof Element) { + String tagName = node.getNodeName(); + if (tagName.equals("row")) { + DasRow row = DasRow.processRowElement((Element) node, canvas, form); + } else if (tagName.equals("column")) { + DasColumn column = DasColumn.processColumnElement((Element) node, canvas, form); + } else if (tagName.equals("axis")) { + DasAxis axis = DasAxis.processAxisElement((Element) node, form); + canvas.add(axis); + } else if (tagName.equals("timeaxis")) { + DasAxis timeaxis = DasAxis.processTimeaxisElement((Element) node, form); + canvas.add(timeaxis); + } else if (tagName.equals("attachedaxis")) { + DasAxis attachedaxis = DasAxis.processAttachedaxisElement((Element) node, form); + canvas.add(attachedaxis); + } else if (tagName.equals("colorbar")) { + DasColorBar colorbar = DasColorBar.processColorbarElement((Element) node, form); + canvas.add(colorbar); + } else if (tagName.equals("plot")) { + DasPlot plot = DasPlot.processPlotElement((Element) node, form); + canvas.add(plot); + } else if (tagName.equals("spectrogram")) { + DasPlot plot = DasPlot.processPlotElement((Element) node, form); + canvas.add(plot); + } + + } + } + canvas.setDasName(name); + nc.put(name, canvas); + + return canvas; + } catch (org.das2.DasPropertyException dpe) { + if (!element.getAttribute("name").equals("")) { + dpe.setObjectName(element.getAttribute("name")); + } + throw dpe; + } + } + + /** + * @param name + * @param width + * @param height + * @return DasCanvas with a name. + */ + public static DasCanvas createFormCanvas(String name, int width, int height) { + DasCanvas canvas = new DasCanvas(width, height); + if (name == null) { + name = "canvas_" + Integer.toHexString(System.identityHashCode(canvas)); + } + try { + canvas.setDasName(name); + } catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + return canvas; + } + + /** Returns the DasCanvasComponent that contains the (x, y) location. * If there is no component at that location, this method * returns null * @param x the x coordinate @@ -1847,11 +1365,10 @@ public DasCanvasComponent getCanvasComponentAt(int x, int y) { /** * Removes the component, specified by index, - * from this container, calling its uninstallComponent - * method if it's a DasCanvasComponent. + * from this container. * @param index the index of the component to be removed. */ - @Override + @Override public void remove(int index) { Component comp = this.getComponent(index); super.remove(index); @@ -1860,25 +1377,16 @@ public void remove(int index) { } } - /** - * Removes the component, specified by index, - * from this container, calling its uninstallComponent - * method if it's a DasCanvasComponent. - * @param comp the component - */ - @Override - public void remove(Component comp) { - super.remove(comp); - if (comp instanceof DasCanvasComponent) { - ((DasCanvasComponent) comp).uninstallComponent(); - } - } - private class CanvasDnDSupport extends org.das2.util.DnDSupport { CanvasDnDSupport() { super(DasCanvas.this, DnDConstants.ACTION_COPY_OR_MOVE, null); } + private List acceptList = Arrays.asList(new DataFlavor[]{ + org.das2.graph.dnd.TransferableCanvasComponent.PLOT_FLAVOR, + org.das2.graph.dnd.TransferableCanvasComponent.AXIS_FLAVOR, + org.das2.graph.dnd.TransferableCanvasComponent.COLORBAR_FLAVOR + }); private Rectangle getAxisRectangle(Rectangle rc, Rectangle t, int x, int y) { if (t == null) { @@ -1924,7 +1432,7 @@ private int getAxisOrientation(Rectangle rc, int x, int y) { return (nx > ny ? (b ? DasAxis.TOP : DasAxis.RIGHT) : (b ? DasAxis.LEFT : DasAxis.BOTTOM)); } - @Override + @Override protected int canAccept(DataFlavor[] flavors, int x, int y, int action) { glassPane.setAccepting(true); List flavorList = java.util.Arrays.asList(flavors); @@ -1953,7 +1461,6 @@ protected int canAccept(DataFlavor[] flavors, int x, int y, int action) { } target = glassPane.target = cellBounds; if (cellBounds != null) { - assert target!=null; glassPane.repaint(target.x - 1, target.y - 1, target.width + 2, target.height + 2); } } @@ -1962,7 +1469,7 @@ protected int canAccept(DataFlavor[] flavors, int x, int y, int action) { return -1; } - @Override + @Override protected void done() { glassPane.setAccepting(false); if (glassPane.target != null) { @@ -1972,7 +1479,7 @@ protected void done() { } } - @Override + @Override protected boolean importData(Transferable t, int x, int y, int action) { boolean success = false; try { @@ -2016,7 +1523,7 @@ protected boolean importData(Transferable t, int x, int y, int action) { return success; } - @Override + @Override protected Transferable getTransferable(int x, int y, int action) { DasCanvasComponent component = DasCanvas.this.getCanvasComponentAt(x, y); if (component instanceof DasColorBar) { @@ -2030,7 +1537,7 @@ protected Transferable getTransferable(int x, int y, int action) { } } - @Override + @Override protected void exportDone(Transferable t, int action) { } } @@ -2077,19 +1584,13 @@ void setAccepting(boolean b) { } } - /** - * set the DragRenderer to be painted on the glasspane. - * @param r DragRenderer, for example the CrossHairRenderer or BoxZoomGesturesRenderer - * @param p1 the start point to render - * @param p2 the current point to render - */ public void setDragRenderer(DragRenderer r, Point p1, Point p2) { this.dragRenderer = r; this.p1 = p1; this.p2 = p2; } - @Override + @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g.create(); if (blocking) { @@ -2101,37 +1602,17 @@ protected void paintComponent(Graphics g) { if (accepting && target != null) { paintDnDTarget(g2); } - if (dragRenderer != null ) { - if ( p1!=null && p2!=null ) { - dragRenderer.renderDrag(g2, p1, p2); - } else { - logger.info("NullPointerException avoided, why is p1 or p2 null?"); - } - } - Painter[] decor; - decor= getCanvas().topDecorators.toArray(getCanvas().emptyPainterArray); - for ( Painter p : decor ) { - try { - long t0= System.currentTimeMillis(); - Graphics2D g22= (Graphics2D) g2.create(); // create a graphics object in case they reset colors, etc. - p.paint(g22); - long dt= System.currentTimeMillis()-t0; - if ( dt > 120 ) { // warn if painters are taking more than 120 ms to paint. - System.err.println("painter is taking too long to paint ("+dt+" ms): "+p ); - } - } catch ( Exception ex ) { - g.drawString( "topDecorator causes exception: "+ex.toString(), 20, 20 ); - ex.printStackTrace(); - } + if (dragRenderer != null) { + dragRenderer.renderDrag(g2, p1, p2); } - g2.dispose(); } private void paintRowColumn(Graphics2D g2) { g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); DasCanvas canvas = getCanvas(); - for (DasDevicePosition d : canvas.devicePositionList) { + for (Iterator i = canvas.devicePositionList.iterator(); i.hasNext();) { + DasDevicePosition d = (DasDevicePosition) i.next(); double minimum = d.getMinimum(); double maximum = d.getMaximum(); int cWidth = canvas.getWidth(); @@ -2174,58 +1655,55 @@ private void paintLoading(Graphics2D g2) { } } - @Override + @Override public void mouseClicked(MouseEvent e) { } - @Override + @Override public void mouseDragged(MouseEvent e) { } - @Override + @Override public void mouseEntered(MouseEvent e) { } - @Override + @Override public void mouseExited(MouseEvent e) { } - @Override + @Override public void mouseMoved(MouseEvent e) { } - @Override + @Override public void mousePressed(MouseEvent e) { } - @Override + @Override public void mouseReleased(MouseEvent e) { } /** Invoked when a key has been pressed. * See the class description for {@link KeyEvent} for a definition of * a key pressed event. - * @param e */ - @Override + @Override public void keyPressed(KeyEvent e) { } /** Invoked when a key has been released. * See the class description for {@link KeyEvent} for a definition of * a key released event. - * @param e */ - @Override + @Override public void keyReleased(KeyEvent e) { } /** Invoked when a key has been typed. * See the class description for {@link KeyEvent} for a definition of * a key typed event. - * @param e */ - @Override + @Override public void keyTyped(KeyEvent e) { } } @@ -2390,29 +1868,22 @@ private void removeColumn(DasColumn column) { } } - /** - * @param name - * @param width - * @param height - * @return DasCanvas with a name. + /** TODO + * @return */ - public static DasCanvas createFormCanvas(String name, int width, int height) { - DasCanvas canvas = new DasCanvas(width, height); - if (name == null) { - name = "canvas_" + Integer.toHexString(System.identityHashCode(canvas)); + @Override + public FormBase getForm() { + Component parent = getParent(); + if (parent instanceof FormComponent) { + return ((FormComponent) parent).getForm(); } - try { - canvas.setDasName(name); - } catch (org.das2.DasNameException dne) { - org.das2.util.DasExceptionHandler.handle(dne); - } - return canvas; + return null; } - /** TODO * @return */ + @Override public boolean getEditingMode() { return editable; } @@ -2420,6 +1891,7 @@ public boolean getEditingMode() { /** TODO * @param b */ + @Override public void setEditingMode(boolean b) { if (editable == b) { return; @@ -2431,6 +1903,7 @@ public void setEditingMode(boolean b) { /** TODO * @return */ + @Override public org.das2.util.DnDSupport getDnDSupport() { return dndSupport; } @@ -2442,6 +1915,7 @@ public org.das2.util.DnDSupport getDnDSupport() { * @param evt * @return */ + @Override public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt) { for (int i = 0; i < getComponentCount(); i++) { if (getComponent(i).getBounds().contains(x, y)) { @@ -2452,19 +1926,19 @@ public boolean startDrag(int x, int y, int action, java.awt.event.MouseEvent evt return false; } - /** - * return the name identifying the component. - * @return the name identifying the component. + /** TODO + * @return */ + @Override public String getDasName() { return dasName; } - /** - * set the name identifying the component. - * @param name the name identifying the component. - * @throws org.das2.DasNameException when the name is not a valid name ("[A-Za-z][A-Za-z0-9_]*") + /** TODO + * @param name + * @throws DasNameException */ + @Override public void setDasName(String name) throws org.das2.DasNameException { if (name.equals(dasName)) { return; @@ -2481,39 +1955,135 @@ public void setDasName(String name) throws org.das2.DasNameException { this.firePropertyChange("name", oldName, name); } - /** - * return the application object for this canvas. - * @return the application object for this canvas. - */ + @Override + public void deregisterComponent() { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + for (Iterator i = devicePositionList.iterator(); i.hasNext();) { + DasDevicePosition dp = (DasDevicePosition) i.next(); + try { + if (nc.get(dp.getDasName()) == dp) { + nc.remove(dp.getDasName()); + } + } catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + for (int index = 0; index < getComponentCount(); index++) { + Component c = getComponent(index); + if (c instanceof DasCanvasComponent) { + DasCanvasComponent cc = (DasCanvasComponent) c; + try { + if (nc.get(cc.getDasName()) == cc) { + nc.remove(cc.getDasName()); + } + } catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + } + try { + if (nc.get(getDasName()) == this) { + nc.remove(getDasName()); + } + } catch (DasPropertyException dpe) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(dpe.toString()); + se.initCause(dpe); + throw se; + } catch (java.lang.reflect.InvocationTargetException ite) { + //This exception would only occur due to some invalid state. + //So, wrap it and toss it. + IllegalStateException se = new IllegalStateException(ite.toString()); + se.initCause(ite); + throw se; + } + } + } + + @Override public DasApplication getDasApplication() { - return getApplication(); + Container p = getParent(); + if (p instanceof FormComponent) { + return ((FormComponent) p).getDasApplication(); + } else { + return null; + } + } + + @Override + public void registerComponent() throws org.das2.DasException { + try { + DasApplication app = getDasApplication(); + if (app != null) { + NameContext nc = app.getNameContext(); + for (Iterator i = devicePositionList.iterator(); i.hasNext();) { + DasDevicePosition dp = (DasDevicePosition) i.next(); + nc.put(dp.getDasName(), dp); + } + for (int index = 0; index < getComponentCount(); index++) { + Component c = getComponent(index); + if (c instanceof DasCanvasComponent) { + DasCanvasComponent cc = (DasCanvasComponent) c; + nc.put(cc.getDasName(), cc); + } + } + nc.put(getDasName(), this); + } + } catch (DasNameException dne) { + deregisterComponent(); + throw dne; + } } + /** Support reloading and refreshing all data on the canvas + */ + public void reload(){ + int nLen = getComponentCount(); + Component cmp; + for(int i = 0; i < nLen; i++){ + cmp = getComponent(i); + if( cmp instanceof DasCanvasComponent) + ((DasCanvasComponent)cmp).reload(); + } + } - /** - * return the component at the index. - * @param index the index - * @return the component at the index. - */ public DasCanvasComponent getCanvasComponents(int index) { return (DasCanvasComponent) getComponent(index + 1); } - /** - * return the components. - * @return the components. - */ public DasCanvasComponent[] getCanvasComponents() { - Component[] cc= getComponents(); - int n = cc.length - 1; + int n = getComponentCount() - 1; DasCanvasComponent[] result = new DasCanvasComponent[n]; for (int i = 0; i < n; i++) { - result[i] = (DasCanvasComponent) cc[i+1]; + result[i] = getCanvasComponents(i); } return result; } - @Override + @Override public String toString() { return "[DasCanvas " + this.getWidth() + "x" + this.getHeight() + " " + this.getDasName() + "]"; } @@ -2538,7 +2108,7 @@ public static class HotLine implements PropertyChangeListener { devicePosition.addPropertyChangeListener((minOrMax == MIN ? "dMinimum" : "dMaximum"), this); } - final void refresh() { + void refresh() { position = (minOrMax == MIN ? (int) Math.floor(devicePosition.getDMinimum() + 0.5) : (int) Math.floor(devicePosition.getDMaximum() + 0.5)); @@ -2551,7 +2121,10 @@ public void propertyChange(PropertyChangeEvent e) { refresh(); } - @Override + /** TODO + * @param o + * @return + */ public boolean equals(Object o) { if (o instanceof HotLine) { HotLine h = (HotLine) o; @@ -2560,12 +2133,16 @@ public boolean equals(Object o) { return false; } - @Override + /** TODO + * @return + */ public int hashCode() { return minOrMax * devicePosition.hashCode(); } - @Override + /** TODO + * @return + */ public String toString() { return "{" + devicePosition.getDasName() + (minOrMax == MIN ? ", MIN, " : ", MAX, ") + position + "}"; } @@ -2585,6 +2162,7 @@ public int getMinOrMax() { } } + /** TODO */ public static class Cell implements PropertyChangeListener { Rectangle rc; @@ -2605,6 +2183,10 @@ public static class Cell implements PropertyChangeListener { rc.height = (int) Math.floor(row.getDMaximum() + 0.5) - rc.y; } + /** TODO + * @param e + */ + @Override public void propertyChange(PropertyChangeEvent e) { if (e.getSource() == row) { rc.y = (int) Math.floor(row.getDMinimum() + 0.5); @@ -2615,15 +2197,11 @@ public void propertyChange(PropertyChangeEvent e) { } } - @Override - public int hashCode() { - int hash = 5; - hash = 79 * hash + (this.row != null ? this.row.hashCode() : 0); - hash = 79 * hash + (this.column != null ? this.column.hashCode() : 0); - return hash; - } - - @Override + /** TODO + * @param o + * @return + */ + @Override public boolean equals(Object o) { if (o instanceof Cell) { Cell box = (Cell) o; @@ -2632,19 +2210,22 @@ public boolean equals(Object o) { return false; } - @Override + /** TODO + * @return + */ + @Override public String toString() { return "{" + row.getDasName() + " x " + column.getDasName() + ": " + rc.toString() + "}"; } - /** get the bounds + /** TODO * @return */ public Rectangle getCellBounds() { return new Rectangle(rc); } - /** get the bounds + /** TODO * @param r * @return */ @@ -2656,14 +2237,14 @@ public Rectangle getCellBounds(Rectangle r) { return r; } - /** get the Row + /** TODO * @return */ public DasRow getRow() { return row; } - /** get the Column + /** TODO * @return */ public DasColumn getColumn() { @@ -2671,7 +2252,7 @@ public DasColumn getColumn() { } } /** - * printingTag is the string to use to tag printed images. + * printingTag is the DateFormat string to use to tag printed images. */ private String printingTag = "'UIOWA 'yyyyMMdd"; @@ -2684,43 +2265,30 @@ public String getPrintingTag() { } /** - * printingTag is the string to use to tag printed images. - * This can be 'yyyymmdd (SimpleDateFormat) or $Y$m$d, or just a string. + * printingTag is the DateFormat string to use to tag printed images. * @param printingTag New value of property printingTag. */ public void setPrintingTag(String printingTag) { String old = this.printingTag; - - if ( printingTag.trim().length()>0 ) { - if ( printingTag.contains("$Y") || printingTag.contains("$y") ) { - TimeParser tp= TimeParser.create(printingTag); - tp.format( TimeUtil.now(), TimeUtil.now() ); - } else if ( printingTag.contains("'yy") ) { - SimpleDateFormat dateFormat = new SimpleDateFormat(printingTag); - dateFormat.format(new Date()); - } else { - // no timetag is allowed as well. - } - } this.printingTag = printingTag; firePropertyChange("printingTag", old, printingTag); } /** - * if true if fonts will be fully rendered. + * Holds value of property textAntiAlias. */ private boolean textAntiAlias = true; /** - * return true if fonts will be fully rendered. - * @return true if fonts will be fully rendered. + * Getter for property textAntiAlias. + * @return Value of property textAntiAlias. */ public boolean isTextAntiAlias() { return this.textAntiAlias; } /** - * true if fonts will be fully rendered. - * @param textAntiAlias true if fonts will be fully rendered. + * Setter for property textAntiAlias. + * @param textAntiAlias New value of property textAntiAlias. */ public void setTextAntiAlias(boolean textAntiAlias) { boolean old = this.textAntiAlias; @@ -2728,21 +2296,21 @@ public void setTextAntiAlias(boolean textAntiAlias) { firePropertyChange("textAntiAlias", old, textAntiAlias); } /** - * true if data will be fully rendered with anti-aliasing. + * Holds value of property antiAlias. */ private boolean antiAlias = "on".equals(DasProperties.getInstance().get("antiAlias")); /** - * true if data will be fully rendered with anti-aliasing. - * @return true if data will be fully rendered with anti-aliasing. + * Getter for property antiAlias. + * @return Value of property antiAlias. */ public boolean isAntiAlias() { return this.antiAlias; } /** - * true if data will be fully rendered with anti-aliasing. - * @param antiAlias if data will be fully rendered with anti-aliasing. + * Setter for property antiAlias. + * @param antiAlias New value of property antiAlias. */ public void setAntiAlias(boolean antiAlias) { boolean old = this.antiAlias; @@ -2761,12 +2329,6 @@ public boolean isFitted() { return fitted; } - /** - * If true, and the canvas was added to a scrollpane, the canvas - * will size itself to fit within the scrollpane. - * - * @param fitted value of fitted property - */ public void setFitted(boolean fitted) { boolean oldValue = this.fitted; this.fitted = fitted; @@ -2774,19 +2336,12 @@ public void setFitted(boolean fitted) { revalidate(); } - /** - * make this the current canvas - */ - public final void makeCurrent() { - currentCanvas= this; - } - - @Override + @Override public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } - @Override + @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { switch (orientation) { case SwingConstants.HORIZONTAL: @@ -2798,7 +2353,7 @@ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, in } } - @Override + @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { switch (orientation) { case SwingConstants.HORIZONTAL: @@ -2810,71 +2365,29 @@ public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, i } } - @Override + @Override public boolean getScrollableTracksViewportWidth() { return fitted; } - @Override + @Override public boolean getScrollableTracksViewportHeight() { return fitted; } - /** - * indicate to the canvas that a change will be made soon. - * For example, the canvas should wait for the change to be performed before creating an image. - * @see ChangesSupport - * @param client the client registering the change - * @param lockObject an object identifying the change - */ public void registerPendingChange(Object client, Object lockObject) { stateSupport.registerPendingChange(client, lockObject); } - /** - * indicate to the canvas that a change is being performed. - * @see ChangesSupport - * @param client the client registering the change - * @param lockObject an object identifying the change - */ public void performingChange(Object client, Object lockObject) { stateSupport.performingChange(client, lockObject); } - /** - * indicate to the canvas that a change is now complete. - * @see ChangesSupport - * @param client the client registering the change - * @param lockObject an object identifying the change - */ public void changePerformed(Object client, Object lockObject) { stateSupport.changePerformed(client, lockObject); } - /** - * returns true if there are changes pending. - * @see ChangesSupport - * @return true if there are changes pending. - */ public boolean isPendingChanges() { return stateSupport.isPendingChanges(); } - - /** - * access the lock for an atomic operation. - * @see ChangesSupport - * @return the lock. - */ - public Lock mutatorLock() { - return stateSupport.mutatorLock(); - } - - /** - * returns true if an operation is being performed that should be treated as atomic. - * @see ChangesSupport - * @return true if an operation is being performed that should be treated as atomic. - */ - public boolean isValueAdjusting() { - return stateSupport.isValueAdjusting(); - } } diff --git a/dasCore/src/org/das2/graph/DasCanvasComponent.java b/dasCore/src/org/das2/graph/DasCanvasComponent.java index c8cd76daf..075f906f0 100755 --- a/dasCore/src/org/das2/graph/DasCanvasComponent.java +++ b/dasCore/src/org/das2/graph/DasCanvasComponent.java @@ -32,15 +32,12 @@ import javax.swing.*; import java.awt.*; import java.awt.event.*; -import java.util.HashSet; -import java.util.Set; import java.util.logging.*; import org.das2.components.propertyeditor.Editable; import org.das2.components.propertyeditor.PropertyEditor; /** - * Super class providing base functionality for all canvas components such as - * DasAxis, DasPlot, and DasLabel. + * * @author eew */ public abstract class DasCanvasComponent extends JComponent implements Editable { @@ -58,7 +55,6 @@ public static DasCanvasComponent getCurrentComponent() { } private static final MouseListener currentComponentListener = new MouseAdapter() { - @Override public void mousePressed(MouseEvent e) { DasCanvasComponent dcc; if (e.getSource() instanceof DasCanvasComponent) { @@ -68,15 +64,11 @@ public void mousePressed(MouseEvent e) { } CanvasComponentAction.currentCanvasComponent = dcc; DasCanvas canvas = dcc.getCanvas(); - canvas.makeCurrent(); + DasCanvas.CanvasAction.currentCanvas = canvas; } }; - /** - * action for entering the properties editor. - */ public static final Action PROPERTIES_ACTION = new CanvasComponentAction("Properties") { - @Override public void actionPerformed(ActionEvent e) { if (getCurrentComponent() != null) { getCurrentComponent().showProperties(); @@ -87,12 +79,7 @@ public void actionPerformed(ActionEvent e) { private DasRow row; private DasColumn column; private ResizeListener rl; - - /** - * the mouse adapter for handling mouse events. - */ protected DasMouseInputAdapter mouseAdapter; - private String dasName; /** @@ -121,8 +108,6 @@ public DasCanvasComponent() { * attached to the component via the DasMouseInputAdapter. * MouseModules will appear the in the order that they * are added. - * @param module the mouse module to add - * @see org.das2.event.MouseModule */ public void addMouseModule(MouseModule module) { mouseAdapter.addMouseModule(module); @@ -131,14 +116,12 @@ public void addMouseModule(MouseModule module) { /** * Remove the MouseModule from the list of MouseModules * attached to the component via the DasMouseInputAdapter. - * @param module the mouse module to remove - * @see org.das2.event.MouseModule */ public void removeMouseModule(MouseModule module) { mouseAdapter.removeMouseModule(module); } - - + + /** * accessor for the DasRow used for positioning the component. * @return DasRow used for positioning the component. @@ -160,7 +143,8 @@ public DasColumn getColumn() { */ public void resize() { if (column == DasColumn.NULL || row == DasRow.NULL ) { - logger.log(Level.WARNING, "Null row and/or column in resize: row={0} column={1}", new Object[]{row, column}); + logger.warning("Null row and/or column in resize: row=" + row + + " column=" + column); } else { setBounds(column.getDMinimum(),row.getDMinimum(), (column.getDMaximum()-column.getDMinimum()), @@ -168,14 +152,29 @@ public void resize() { } } + @Override + public void setBounds(int x, int y, int width, int height) { + if ( getDasName().startsWith("plot_") ) { + //new Exception().printStackTrace(); + //System.err.println( getDasName() + " setBounds(" + new Rectangle(x, y, width, height) + ")" ); + } + super.setBounds(x, y, width, height); + } + + @Override + public void setBounds(Rectangle r) { + //if ( getDasName().startsWith("plot_") ) System.err.println( getDasName() + " setBounds(" + r ); + super.setBounds(r); + } + + /** * class for handling resize events. */ private class ResizeListener implements DasUpdateListener { - @Override public void update(org.das2.graph.event.DasUpdateEvent e) { - logger.log(Level.FINE, "component row or column moved: {0}", e.getSource()); - markDirty("resize"); + logger.fine("component row or column moved: "+e.getSource()); + markDirty(); DasCanvasComponent.this.update(); } @@ -185,7 +184,6 @@ public void update(org.das2.graph.event.DasUpdateEvent e) { * set the DasRow for positioning the component vertically. * The current row is disconnected, and a propertyChange is * fired. - * @param r the DasRow */ public void setRow(DasRow r) { if (row == r) { @@ -193,15 +191,14 @@ public void setRow(DasRow r) { } Object oldValue = row; if (row != DasRow.NULL ) { - row.removeUpdateListener(rl); + row.removepwUpdateListener(rl); } row = r; if (row != DasRow.NULL ) { - row.addUpdateListener(rl); + row.addpwUpdateListener(rl); } /*else { throw new IllegalArgumentException("null row is not allowed for the meantime"); }*/ - if ( getRow()!=DasRow.NULL && getColumn()!=DasColumn.NULL ) resize(); firePropertyChange("row", oldValue, r); } @@ -209,7 +206,6 @@ public void setRow(DasRow r) { * set the DasColumn for positioning the component horizontally. * The current column is disconnected, and a propertyChange is * fired. - * @param c the DasColumn */ public void setColumn(DasColumn c) { if (column == c) { @@ -217,15 +213,14 @@ public void setColumn(DasColumn c) { } Object oldValue = column; if (column != DasColumn.NULL ) { - column.removeUpdateListener(rl); + column.removepwUpdateListener(rl); } column = c; if (column != DasColumn.NULL ) { - column.addUpdateListener(rl); + column.addpwUpdateListener(rl); } /*else { throw new IllegalArgumentException("null column is not allowed for the meantime"); }*/ - if ( getRow()!=DasRow.NULL && getColumn()!=DasColumn.NULL ) resize(); firePropertyChange("column", oldValue, c); } @@ -241,7 +236,6 @@ public void showProperties() { /** * @return a concise String representation of the object. */ - @Override public String toString() { return getClass().getName()+"'"+getDasName()+"'"; } @@ -254,7 +248,7 @@ public String toString() { * the AWT Event Queue can coalesce update events. */ protected void updateImmediately() { - logger.log(Level.FINER, "updateImmediately for {0}", this.getClass().getName()); + logger.finer("updateImmediately for "+this.getClass().getName() ); } private org.das2.event.DasUpdateEvent devt; @@ -264,12 +258,20 @@ protected void updateImmediately() { * done to get the get the component back into a valid state. */ public void update() { - logger.log(Level.FINER, "update for {0}", this.getClass().getName()); + logger.finer("update for "+this.getClass().getName() ); java.awt.EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); if (devt == null) devt = new org.das2.event.DasUpdateEvent(this); eventQueue.postEvent(devt); } + + /** Cause the component to reload, recalculate and repaint. + * This method is meant to support user-initiated data reloading, tick recalculation, + * etc. Override this to provide a refresh support. + */ + public void reload(){ + repaint(); + } /** Processes events occurring on this component. By default this * method calls the appropriate @@ -290,7 +292,6 @@ public void update() { * @see java.awt.Component#processMouseWheelEvent * @see #processDasUpdateEvent */ - @Override protected void processEvent(AWTEvent e) { super.processEvent(e); if (e instanceof org.das2.event.DasUpdateEvent) { @@ -298,12 +299,7 @@ protected void processEvent(AWTEvent e) { } } - /** - * like processEvent, but we also check the dirty status - * @param e the event - */ protected void processDasUpdateEvent(org.das2.event.DasUpdateEvent e) { - logger.fine("enter process DasUpdateEvent"); if (isDisplayable()) { if (isDirty()) { markClean(); @@ -334,7 +330,6 @@ protected void processDasUpdateEvent(org.das2.event.DasUpdateEvent e) { * @return a coalesced event, or null indicating that no * coalescing was done */ - @Override protected AWTEvent coalesceEvents(AWTEvent existingEvent, AWTEvent newEvent) { if (existingEvent instanceof org.das2.event.DasUpdateEvent && newEvent instanceof org.das2.event.DasUpdateEvent) { return existingEvent; @@ -342,24 +337,10 @@ protected AWTEvent coalesceEvents(AWTEvent existingEvent, AWTEvent newEvent) { return super.coalesceEvents(existingEvent, newEvent); } - /** - * currently does nothing. - */ protected void installComponent() {} - /** - * remove the component row and column update listener. - */ - protected void uninstallComponent() { - getRow().removeUpdateListener(rl); - getColumn().removeUpdateListener(rl); - } + protected void uninstallComponent() {} - /** - * return the font used to paint the component. - * @return the font. - */ - @Override public Font getFont() { return (getParent() == null ? super.getFont() : getParent().getFont()); } @@ -370,45 +351,27 @@ public Font getFont() { * @return the height of the component's font. */ public double getEmSize() { - Font f= getFont(); - return f==null ? 8 : f.getSize2D(); + return getFont().getSize2D(); } - private final Set dirty = new HashSet(); + boolean dirty = true; /** * set the dirty flag indicating the state has changed and work is to be * done to restore a valid state. For example, a DasAxis' minimum is * changed, so we will need to recalculate the ticks. (But we don't want * to recalculate the ticks immediately, since the maximum may change - * as well.) + * as well. */ void markDirty() { - dirty.add("??"); + dirty = true; } - /** - * mark the component as dirty, providing a reason that is useful - * when debugging. - * @param note a note useful for debugging. - */ - void markDirty( String note ) { - boolean loudDebug= false; // ( this instanceof DasAxis ) && ( ((DasAxis)this).isHorizontal() ); - if ( loudDebug ) { - logger.log(Level.WARNING, "{0}.markDirty({1})", new Object[]{this.dasName, note}); - } else { - logger.log(Level.FINE, "{0}.markDirty({1})", new Object[]{this.dasName, note}); - } - dirty.add(note); - } - - /** - * Returns true if the component has been marked as dirty, meaning + * @return true if the component has been marked as dirty, meaning * work needs to be done to restore it to a valid state. - * @return true if the component has been marked as dirty. */ boolean isDirty() { - return !dirty.isEmpty(); + return dirty; } /** @@ -416,13 +379,7 @@ boolean isDirty() { * state. */ void markClean() { - boolean loudDebug= false; // ( this instanceof DasAxis ) && ( ((DasAxis)this).isHorizontal() ); - if ( loudDebug ) { - logger.log(Level.WARNING, "{0}.markClean()", this.dasName); - } else { - logger.log(Level.FINE, "{0}.markClean()", this.dasName); - } - dirty.clear(); + dirty = false; } /** @@ -455,11 +412,10 @@ public String getDasName() { * the application. * @throws org.das2.DasNameException */ - public final void setDasName(String name) throws org.das2.DasNameException { + public void setDasName(String name) throws org.das2.DasNameException { if (name.equals(dasName)) { return; } - setName(name); // might as well set the component name. String oldName = dasName; dasName = name; DasApplication app = DasApplication.getDefaultApplication(); @@ -469,16 +425,11 @@ public final void setDasName(String name) throws org.das2.DasNameException { app.getNameContext().remove(oldName); } } - if ( getDasMouseInputAdapter()!=null ) { - getDasMouseInputAdapter().resetName(name); - } - this.firePropertyChange("name", oldName, name); } /** * returns the active region of the canvas component, which is not necessarily the bounds. - * @return the active region of the canvas component */ public Shape getActiveRegion() { int x = getColumn().getDMinimum(); @@ -495,18 +446,24 @@ public Shape getActiveRegion() { * was first introduced to support the annotation component, which draws a compact * background bubble around a message, which is typically smaller than its bounds, * plus an arrow. - * @param x the x location on the canvas, with (0,0) being the upper-left corner. - * @param y the y location on the canvas, with (0,0) being the upper-left corner. + * @param x + * @param y * @return true if the component accepts the context at this point. */ public boolean acceptContext( int x, int y ) { - return true; + return true; } /** - * return a list of actions. This is used by the DasMouseInputAdapter. - * @return the actions this provides. + * accessor to the DasMouseInputAdapter handling mouse input for the component. + * Note there is also getDasMouseInputAdapter. + * @return DasMouseInputAdaptor handling mouse input for the component. + * @deprecated use getDasMouseInputAdapter instead */ + public DasMouseInputAdapter getMouseAdapter() { + return mouseAdapter; + } + public Action[] getActions() { return new Action[] { PROPERTIES_ACTION, @@ -514,18 +471,18 @@ public Action[] getActions() { } /** - * Get the DasMouseInputAdapter, which handles mouse input for the component. - * @return the dasMouseInputAdapter. + * Getter for property dasMouseInputAdapter, the DasMouseInputAdapter handling mouse input for the component. + * @return Value of property dasMouseInputAdapter. */ public DasMouseInputAdapter getDasMouseInputAdapter() { return this.mouseAdapter; } /** - * Set the dasMouseInputAdapter, which handles mouse input for the component - * @param dasMouseInputAdapter the dasMouseInputAdapter. + * Setter for property dasMouseInputAdapter. + * @param dasMouseInputAdapter New value of property dasMouseInputAdapter. */ - private void setDasMouseInputAdapter(DasMouseInputAdapter dasMouseInputAdapter) { + public synchronized void setDasMouseInputAdapter(DasMouseInputAdapter dasMouseInputAdapter) { if ( mouseAdapter!=null ) { removeMouseListener(mouseAdapter); removeMouseMotionListener(mouseAdapter); diff --git a/dasCore/src/org/das2/graph/DasColorBar.java b/dasCore/src/org/das2/graph/DasColorBar.java index 7951bb90e..6257cd705 100644 --- a/dasCore/src/org/das2/graph/DasColorBar.java +++ b/dasCore/src/org/das2/graph/DasColorBar.java @@ -23,88 +23,74 @@ package org.das2.graph; -import java.awt.Color; -import java.awt.Container; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.Shape; import org.das2.components.propertyeditor.Displayable; +import org.das2.datum.DatumRange; import org.das2.datum.Units; import org.das2.datum.Datum; +import org.das2.datum.DatumRangeUtil; +import org.das2.datum.TimeUtil; +import org.das2.NameContext; +import org.das2.DasApplication; import org.das2.components.propertyeditor.Enumeration; +import org.das2.dasml.FormBase; +import org.das2.event.DataRangeSelectionEvent; +import org.das2.event.HorizontalSliceSelectionRenderer; +import org.das2.event.MouseModule; +import org.das2.event.MousePointSelectionEvent; import java.awt.image.IndexColorModel; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.awt.*; +import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; - -import javax.swing.ImageIcon; +import javax.swing.event.EventListenerList; /** - * Axis that converts to RGB color instead of horizontal or vertical - * position. + * * @author jbf */ public class DasColorBar extends DasAxis { - private static final long serialVersionUID = 1L; - - /** - * handle for the property "type". - */ public static final String PROPERTY_TYPE = "type"; - - /** - * handle for the property fillColor. - */ public static final String PROPERTY_FILL_COLOR= "fillColor"; private BufferedImage image; - private transient DasColorBar.Type type; + private DasColorBar.Type type; private static int fillColor= Color.LIGHT_GRAY.getRGB(); private int fillColorIndex; private int ncolor; - /** - * number of colors in each table, not including the grey for fill. - */ private static final int COLORTABLE_SIZE=240; - /** - * Create an color bar object, relating data and color. - * @param min the minimum value - * @param max the maximum value - * @param log if true then the axis is a log axis. - */ - public DasColorBar( Datum min, Datum max, boolean log) { - this(min, max, RIGHT, log); + public DasColorBar( Datum min, Datum max, boolean isLog) { + this(min, max, RIGHT, isLog); } - /** - * Create an color bar object, relating data and color. - * @param min the minimum value - * @param max the maximum value - * @param orientation the position relative to a plot, one of DasAxis.TOP, DasAxis.BOTTOM, DasAxis.LEFT, DasAxis.RIGHT. - * @param log if true then the axis is a log axis. - */ - public DasColorBar( Datum min, Datum max, int orientation, boolean log) { - super(min, max, orientation, log); + public DasColorBar( Datum min, Datum max, int orientation, boolean isLog) { + super(min, max, orientation, isLog); setLayout(new ColorBarLayoutManager()); setType(DasColorBar.Type.COLOR_WEDGE); } + + public DasColorBar( DatumRange range, int orientation, boolean isLog) { + super(range, orientation); + setLog(isLog); + setLayout(new ColorBarLayoutManager()); + setType(DasColorBar.Type.COLOR_WEDGE); + } + + public DasColorBar( DatumRange range, boolean isLog) { + this(range, RIGHT, isLog); + } - /** - * convert the double to an RGB color. - * @param data a data value - * @param units the units of the given data value. - * @return the combined RGB components - * @see Color#Color(int) - */ - public int rgbTransform(double data, Units units) { - int icolor= (int)transform(data,units,0, ncolor); + public int rgbTransform(double x, Units units) { + int icolor= (int)transform(x,units,0, ncolor); - if ( units.isFill(data) ) { + if ( units.isFill(x) ) { return fillColor; } else { icolor= (icolor<0)?0:icolor; @@ -113,53 +99,31 @@ public int rgbTransform(double data, Units units) { } } - /** - * convert the double to an indexed color. - * @param data a data value - * @param units the units of the given data value. - * @return the index into the color table. - * @see #getIndexColorModel() - */ - public int indexColorTransform( double data, Units units ) { - if ( units.isFill(data) ) { + public int indexColorTransform( double x, Units units ) { + if ( units.isFill(x) ) { return fillColorIndex; } else { - int icolor= (int)transform(data,units,0,ncolor); + int icolor= (int)transform(x,units,0,ncolor); icolor= (icolor<0)?0:icolor; icolor= (icolor>=ncolor)?(ncolor-1):icolor; return icolor; } } - /** - * return the color model so that indexed color model can be used. - * @return the color model - */ public IndexColorModel getIndexColorModel() { return new IndexColorModel( 8, type.getColorCount()+1, type.colorTable, 0, true, -1, DataBuffer.TYPE_BYTE ); } - /** - * return the index of the fill color in the indexed color model. - * @return the index of the fill color - */ public int getFillColorIndex() { return fillColorIndex; } - /** - * return the type of colorbar (e.g. DasColorBar.Type.GRAYSCALE or DasColorBar.Type.APL_RAINBOW_BLACK0) - * @return the type of colorbar - */ public DasColorBar.Type getType() { return type; } - /** - * set the type of colorbar - * @param type type of colorbar (e.g. DasColorBar.Type.GRAYSCALE or DasColorBar.Type.APL_RAINBOW_BLACK0) - */ - public final void setType(DasColorBar.Type type) { + + public void setType(DasColorBar.Type type) { if (this.type == type) { return; } @@ -169,69 +133,140 @@ public final void setType(DasColorBar.Type type) { image = null; fillColorIndex= getType().getColorCount(); fillColor= getType().getRGB(fillColorIndex); - markDirty("type"); + markDirty(); update(); firePropertyChange( PROPERTY_TYPE, oldValue,type); } - @Override protected void paintComponent(Graphics g) { - - if (getCanvas().isValueAdjusting()) { - return; + int x = (int)Math.round(getColumn().getDMinimum()); + int y = (int)Math.round(getRow().getDMinimum()); + int width = (int)Math.round(getColumn().getDMaximum()) - x; + int height = (int)Math.round(getRow().getDMaximum()) - y; + //if (image == null || image.getWidth() != width || image.getHeight() != height) { + if (isHorizontal()) { + image = type.getHorizontalScaledImage(width, height); + } else { + image = type.getVerticalScaledImage(width, height); } - - if ( showColorBar ) { - int x = getColumn().getDMinimum(); - int y = getRow().getDMinimum(); - int width = getColumn().getDMaximum() - x; - int height = getRow().getDMaximum() - y; - //if (image == null || image.getWidth() != width || image.getHeight() != height) { - if (isHorizontal()) { - image = type.getHorizontalScaledImage(width, height); - } else { - image = type.getVerticalScaledImage(width, height); - } - //} - g.translate(-getX(), -getY()); - if (!isHorizontal()) { - y++; - } - g.drawImage(image, x, y, this); - g.translate(getX(), getY()); + //} + g.translate(-getX(), -getY()); + if (!isHorizontal()) { + y++; } + g.drawImage(image, x, y, this); + g.translate(getX(), getY()); super.paintComponent(g); } - @Override protected Rectangle getAxisBounds() { - int x = getColumn().getDMinimum(); - int y = getRow().getDMinimum(); - int width = getColumn().getDMaximum() - x; - int height = getRow().getDMaximum() - y; + int x = (int)Math.round(getColumn().getDMinimum()); + int y = (int)Math.round(getRow().getDMinimum()); + int width = (int)Math.round(getColumn().getDMaximum()) - x; + int height = (int)Math.round(getRow().getDMaximum()) - y; Rectangle rc = new Rectangle(x, y, width, height); Rectangle bounds = super.getAxisBounds(); bounds.add(rc); return bounds; } - /** - * return a column suitable for the colorbar, based on the spectrogram - * column. - * @param column the column for the spectrogram described. - * @return the new column. - */ public static DasColumn getColorBarColumn(DasColumn column) { return new DasColumn( null, column, 1.0, 1.0, 1, 2, 0, 0 ); } + /** Process a <colorbar> element. + * + * @param element The DOM tree node that represents the element + */ + static DasColorBar processColorbarElement(Element element, FormBase form) throws org.das2.DasPropertyException,org.das2.DasNameException, java.text.ParseException { + String name = element.getAttribute("name"); + boolean log = element.getAttribute("log").equals("true"); + String unitStr = element.getAttribute("units"); + if (unitStr == null) { + unitStr = ""; + } + Datum dataMinimum; + Datum dataMaximum; + if (unitStr.equals("TIME")) { + String min = element.getAttribute("dataMinimum"); + String max = element.getAttribute("dataMaximum"); + dataMinimum = (min == null || min.equals("") ? TimeUtil.create("1979-02-26") : TimeUtil.create(min)); + dataMaximum = (max == null || max.equals("") ? TimeUtil.create("1979-02-27") : TimeUtil.create(max)); + } else { + Units units = Units.lookupUnits(unitStr); + String min = element.getAttribute("dataMinimum"); + String max = element.getAttribute("dataMaximum"); + dataMinimum = (min == null || min.equals("") ? Datum.create(1.0, units) : Datum.create(Double.parseDouble(min), units)); + dataMaximum = (max == null || max.equals("") ? Datum.create(10.0, units) : Datum.create(Double.parseDouble(max), units)); + } + int orientation = parseOrientationString(element.getAttribute("orientation")); + + DasColorBar cb = new DasColorBar(dataMinimum, dataMaximum, orientation, log); + + String rowString = element.getAttribute("row"); + if (!rowString.equals("")) { + DasRow row = (DasRow)form.checkValue(rowString, DasRow.class, ""); + cb.setRow(row); + } + String columnString = element.getAttribute("column"); + if (!columnString.equals("")) { + DasColumn column = (DasColumn)form.checkValue(columnString, DasColumn.class, ""); + cb.setColumn(column); + } + + cb.setLabel(element.getAttribute("label")); + cb.setOppositeAxisVisible(!element.getAttribute("oppositeAxisVisible").equals("false")); + cb.setTickLabelsVisible(!element.getAttribute("tickLabelsVisible").equals("false")); + cb.setType(DasColorBar.Type.parse(element.getAttribute(PROPERTY_TYPE))); + + cb.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, cb); + + return cb; + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("colorbar"); + String minimumStr = getDataMinimum().toString(); + element.setAttribute("dataMinimum", minimumStr); + String maximumStr = getDataMaximum().toString(); + element.setAttribute("dataMaximum", maximumStr); + + element.setAttribute("name", getDasName()); + element.setAttribute("row", getRow().getDasName()); + element.setAttribute("column", getColumn().getDasName()); + + element.setAttribute("label", getLabel()); + element.setAttribute("log", Boolean.toString(isLog())); + element.setAttribute("tickLabelsVisible", Boolean.toString(isTickLabelsVisible())); + element.setAttribute("oppositeAxisVisible", Boolean.toString(isOppositeAxisVisible())); + element.setAttribute("animated", Boolean.toString(isAnimated())); + element.setAttribute("orientation", orientationToString(getOrientation())); + element.setAttribute(PROPERTY_TYPE, getType().toString()); + + return element; + } + + public static DasColorBar createNamedColorBar(String name) { + DasColorBar cb = new DasColorBar(Datum.create(1.0, Units.dimensionless), Datum.create(10.0, Units.dimensionless), false); + if (name == null) { + name = "colorbar_" + Integer.toHexString(System.identityHashCode(cb)); + } + try { + cb.setDasName(name); + } catch (org.das2.DasNameException dne) { + org.das2.util.DasExceptionHandler.handle(dne); + } + return cb; + } - @Override public Shape getActiveRegion() { - int x = getColumn().getDMinimum(); - int y = getRow().getDMinimum(); - int width = getColumn().getWidth(); - int height = getRow().getHeight(); + int x = (int)Math.round(getColumn().getDMinimum()); + int y = (int)Math.round(getRow().getDMinimum()); + int width = (int)Math.round(getColumn().getDMaximum()) - x; + int height = (int)Math.round(getRow().getDMaximum()) - y; Rectangle bounds = primaryInputPanel.getBounds(); bounds.translate(getX(), getY()); Rectangle middleBounds = new Rectangle(x, y, width, height); @@ -243,39 +278,15 @@ public Shape getActiveRegion() { } return bounds; } - - private boolean showColorBar = true; - - public static final String PROP_SHOWCOLORBAR = "showColorBar"; - - public boolean isShowColorBar() { - return showColorBar; - } - - /** - * when set to false, this is basically an ordinary axis. Autoplot uses - * this to support StackedHistogram mode. - * @param showColorBar true if the colorbar should be drawn. - */ - public void setShowColorBar(boolean showColorBar) { - boolean oldShowColorBar = this.showColorBar; - this.showColorBar = showColorBar; - firePropertyChange(PROP_SHOWCOLORBAR, oldShowColorBar, showColorBar); - } - - /** - * TODO: Ed document me - */ protected class ColorBarLayoutManager extends AxisLayoutManager { - @Override public void layoutContainer(Container parent) { super.layoutContainer(parent); - int x = getColumn().getDMinimum(); - int y = getRow().getDMinimum(); - int width = getColumn().getWidth(); - int height = getRow().getHeight(); + int x = (int)Math.round(getColumn().getDMinimum()); + int y = (int)Math.round(getRow().getDMinimum()); + int width = (int)Math.round(getColumn().getDMaximum()) - x; + int height = (int)Math.round(getRow().getDMaximum()) - y; Rectangle rc = new Rectangle(x - getX(), y - getY(), width, height); Rectangle bounds = primaryInputPanel.getBounds(); bounds.add(rc); @@ -284,89 +295,14 @@ public void layoutContainer(Container parent) { } - /** - * enumeration of the types of colorbars. - */ public static final class Type implements Enumeration, Displayable { - /** - * Rainbow colorbar used by default. TODO: this is a misnomer, where - * color_wedge was the type of object in das1, not the instance of the type, - * and this should be renamed to "rainbow" - */ public static final Type COLOR_WEDGE = new Type("color_wedge"); - - /** - * rainbow colorbar used at APL that has black at the bottom. - */ - public static final Type APL_RAINBOW_BLACK0 = new Type("apl_rainbow_black0"); - - /** - * rainbow colorbar used at APL that has white at the bottom. - */ - public static final Type APL_RAINBOW_WHITE0 = new Type("apl_rainbow_white0"); - - /** - * rainbow colorbar introduced for use at Goddard Space Flight Center. - */ - public static final Type GSFC_RP_SPECIAL = new Type("gsfc_rp_special"); - - /** - * Mimic the default Matlab colorbar for comparison with Matlab-generated spectrograms. - */ - public static final Type MATLAB_JET = new Type("matlab_jet"); - - public static final Type BLUE_TO_ORANGE = new Type("blue_to_orange"); - - /** - * gray scale with white at the minimum (bottom) and black at the maximum. - */ + //public static final Type BLUE_TO_ORANGE = new Type("blue_to_orange"); public static final Type GRAYSCALE = new Type("grayscale"); - - /** - * gray scale with black at the minimum (bottom) and white at the maximum. - */ public static final Type INVERSE_GRAYSCALE = new Type("inverse_grayscale"); - - /** - * rainbow that wraps around so that the top and bottom are the same color, - * When used with care this is useful for spaces that wrap around (modulo), - * such as longitude. - */ public static final Type WRAPPED_COLOR_WEDGE = new Type("wrapped_color_wedge"); - /** - * colorbar with black in the middle, blue at the minimum and red at the maximum - * for showing deviations from the center. - */ - public static final Type BLUE_BLACK_RED_WEDGE = new Type("blue_black_red"); - - /** - * colorbar with white in the middle, blue at the minimum and red at the maximum - * for showing deviations from the center. - */ - public static final Type BLUE_WHITE_RED_WEDGE = new Type("blue_white_red"); - - /** - * black to red, introduced to show the red component of RGB images - */ - public static final Type BLACK_RED = new Type("black_red"); - - /** - * black to green, introduced to show the green component of RGB images - */ - public static final Type BLACK_GREEN = new Type("black_green"); - - /** - * black to blue, introduced to show the blue component of RGB images - */ - public static final Type BLACK_BLUE = new Type("black_blue"); - - /** - * Violet -Yellow - */ - public static final Type VIOLET_YELLOW = new Type("violet_yellow"); - private BufferedImage image; private int[] colorTable; private final String desc; @@ -375,66 +311,37 @@ public static final class Type implements Enumeration, Displayable { private Type(String desc) { this.desc = desc; } - - @Override - public void drawListIcon( Graphics2D g, int x, int y ) { - ImageIcon licon= (ImageIcon) getListIcon(); - g.drawImage(licon.getImage(), x, y, null); - } - - @Override + public javax.swing.Icon getListIcon() { maybeInitializeIcon(); return icon; } - /** - * initialize the icon representing this colorbar, if not done already. - */ public void maybeInitializeIcon() { if (icon == null) { icon = new javax.swing.ImageIcon(getVerticalScaledImage(16, 16)); } } - @Override public String toString() { return desc; } - @Override public String getListLabel() { return desc; } - /** - * Return the number of colors in the color bar. Fill (gray) is an additional - * color, and it's understood that the colors indeces from 0 to getColorCount()-1 - * are the color wedge, and getColorCount() is the fill color. - * @return the number of colors. - */ + /* It's understood that the colors indeces from 0 to getColorCount()-1 are the color wedge, and getColorCount() is the fill color. */ public int getColorCount() { maybeInitializeColorTable(); return colorTable.length-1; } - /** - * return the RGB encoded color for the index. - * @param index the index, from 0 to getColorCount(). - * @return the RGB color - * @see Color#Color(int) - */ public int getRGB(int index) { maybeInitializeColorTable(); return colorTable[index]; } - /** - * return an image showing the colors from left to right. - * @param width the width of the image - * @param height the height of the image - * @return the image - */ public BufferedImage getHorizontalScaledImage(int width, int height) { maybeInitializeImage(); BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); @@ -447,12 +354,6 @@ public BufferedImage getHorizontalScaledImage(int width, int height) { return scaled; } - /** - * return an image showing the colors from bottom to top. - * @param width the width of the image - * @param height the height of the image - * @return the image - */ public BufferedImage getVerticalScaledImage(int width, int height) { maybeInitializeImage(); BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); @@ -472,18 +373,7 @@ private void maybeInitializeImage() { } } - /** - * returns a color table with interpolated colors for the wedge from 0 to ncolor-1, and at ncolor, the fill color. - * @param index set of indeces that are control points for interpolation, including 0 and ncolor-1. - * @param red the red value from 0 to 255 at each index - * @param green the green value from 0 to 255 at each index - * @param blue the blue value from 0 to 255 at each index - * @param ncolor number of colors, typically COLORTABLE_SIZE=240. - * @param bottom the bottom, typically 0. - * @param top the top, typically COLORTABLE_SIZE=240. - * @return an array of RGB colors. - * @see Color#Color(int) - */ + // returns a color table with interpolated colors for the wedge from 0 to ncolor-1, and at ncolor, the fill color. private static int[] makeColorTable( int [] index, int[] red, int[] green, int[] blue, int ncolor, int bottom, int top ) { // index should go from 0-255. // truncate when ncolor>COLORTABLE_SIZE @@ -491,7 +381,7 @@ private static int[] makeColorTable( int [] index, int[] red, int[] green, int[] int ii= 0; for (int i = 0; i < ncolor-1; i++) { - int comp= ( i - bottom ) * 255 / ( top - bottom ); + float comp= ( i - bottom ) * 255 / ( top - bottom ); if ( comp > index[ii + 1]) { ii++; } @@ -524,34 +414,14 @@ private void maybeInitializeColorTable() { private void initializeColorTable( int size, int bottom, int top ) { if (this == COLOR_WEDGE) { initializeColorWedge(size, bottom, top); - } else if (this == APL_RAINBOW_WHITE0) { - initializeColorWedgeWhite(size, bottom, top); - } else if (this == APL_RAINBOW_BLACK0) { - initializeColorWedgeBlack(size, bottom, top); - } else if (this == GSFC_RP_SPECIAL ) { - initializeRPSpecial(size, bottom, top); - } else if (this == MATLAB_JET ) { - initializeMatlabJet(size, bottom, top); } else if (this == GRAYSCALE) { initializeGrayScale(size, bottom, top); } else if (this == INVERSE_GRAYSCALE) { initializeInverseGrayScale(size, bottom, top); } else if (this == WRAPPED_COLOR_WEDGE) { initializeWrappedColorWedge(size, bottom, top); - } else if (this == BLUE_BLACK_RED_WEDGE) { - initializeBlueBlackRedWedge(size, bottom, top); - } else if (this == BLUE_WHITE_RED_WEDGE) { - initializeBlueWhiteRedWedge(size, bottom, top); - } else if (this == BLACK_RED) { - initializeWhiteRed(size, bottom, top); - } else if (this == BLACK_GREEN ) { - initializeWhiteGreen(size, bottom, top); - } else if (this == VIOLET_YELLOW ) { - initializeVioletYellow(size, bottom, top); - } else if (this == BLACK_BLUE) { - initializeWhiteBlue(size, bottom, top); - } else if (this == BLUE_TO_ORANGE ) { - initializeBlueToOrange(size, bottom, top); + //} else if (this == BLUE_TO_ORANGE ) { + // initializeBlueToOrange(size, bottom, top); } } @@ -563,45 +433,7 @@ private void initializeColorWedge( int size, int bottom, int top ) { colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); colorTable[0] = ( colorTable[0] & 0xFFFFFF00 ) | 1; } - - private void initializeColorWedgeWhite( int size, int bottom, int top ) { - ColorWedgeColorSource ct = new ColorWedgeColorSource(true); - int[] index = ct.getIndex(); - int[] red = ct.getRed(); - int[] green = ct.getGreen(); - int[] blue = ct.getBlue(); - colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); - //colorTable[0] = ( colorTable[0] & 0xFFFFFF00 ) | 1; - } - - private void initializeColorWedgeBlack( int size, int bottom, int top ) { - ColorWedgeColorSource ct = new ColorWedgeColorSource(false); - int[] index = ct.getIndex(); - int[] red = ct.getRed(); - int[] green = ct.getGreen(); - int[] blue = ct.getBlue(); - colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); - colorTable[0] = ( colorTable[0] & 0xFFFFFF00 ) | 1; - } - - - private void initializeBlueBlackRedWedge( int size, int bottom, int top ) { - int[] index = { 0, 64, 128, 192, 255 }; - int[] red = { 0, 0, 0, 128, 255 }; - int[] green = { 0, 0, 0, 0, 0 }; - int[] blue = { 255, 128, 0, 0, 0 }; - colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); - } - - private void initializeBlueWhiteRedWedge( int size, int bottom, int top ) { - int[] index = { 0, 64, 128, 192, 255 }; - int[] red = { 0, 128, 255, 255, 255 }; - int[] green = { 0, 128, 255, 128, 0 }; - int[] blue = { 255, 255, 255, 128, 0 }; - colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); - } - private void initializeBlueToOrange( int size, int bottom, int top ) { // cat | awk '{ print $3 "," }' | xargs int[] index = { 0, 23, 46, 69, 92, 115, 139, 162, 185, 208, 231, 255 }; @@ -636,58 +468,6 @@ private void initializeGrayScale( int size, int bottom, int top ) { colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); } - private void initializeRPSpecial( int size, int bottom, int top ) { - int [] index= { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 }; - int [] red= { 64,64,64,63,62,62,62,62,62,62,61,61,61,61,61,61,61,61,61,61,62,62,62,62,62,63,63,63,63,63,64,64,64,64,65,65,65,65,65,66,66,66,66,66,67,67,67,67,68,68,68,68,69,69,69,69,69,69,70,70,70,70,70,71,71,71,71,72,72,73,73,74,74,75,76,76,77,78,78,79,79,80,80,81,81,82,82,83,83,84,84,85,85,86,86,88,89,91,93,95,96,98,99,100,101,103,104,106,107,109,110,111,113,114,114,114,114,114,115,115,115,115,116,116,116,116,116,116,116,116,117,118,120,121,122,124,125,127,128,129,131,133,137,141,145,149,153,156,159,161,164,166,171,177,183,189,195,201,207,211,215,218,222,225,229,233,237,241,245,249,250,250,251,251,252,252,253,253,254,254,254,255,255,255,255,255,254,254,254,253,253,253,253,252,252,252,251,250,249,249,248,247,246,245,245,244,243,242,242,241,240,239,238,238,237,236,235,235,234,233,232,232,231,230,229,228,227,226,225,224,223,222,221,219,218,217,215,214,212,210,208,206,204,202,200,200,200,200,200,200,200,200,200,200,200,200 }; - int [] green= { 85,85,85,83,82,82,82,81,81,81,81,81,81,81,81,81,81,81,81,82,83,83,84,85,86,87,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,108,109,110,111,112,113,114,115,116,117,118,119,120,120,120,121,121,122,122,123,124,124,125,126,127,128,130,131,133,134,136,137,139,140,142,143,145,146,147,149,151,153,155,158,160,162,164,166,169,171,173,177,180,183,187,190,194,195,196,197,198,199,201,203,205,207,210,212,213,214,215,216,217,218,220,221,223,225,227,229,229,230,231,232,233,234,235,236,238,239,241,242,243,243,244,244,245,246,247,248,249,250,250,251,251,252,252,252,253,253,253,254,254,254,255,255,255,255,255,255,253,252,251,250,249,248,247,246,244,243,242,240,238,236,234,231,229,227,226,225,224,223,221,218,215,212,209,206,203,200,198,196,192,189,185,182,178,175,171,168,164,159,156,152,149,145,141,138,134,130,126,122,118,114,110,107,103,99,95,92,89,85,82,79,76,72,69,66,63,59,55,51,47,42,40,38,36,35,33,31,30,30,30,30,30,30,30,30,30,30,30,30 }; - int [] blue= { 141,141,141,138,136,136,136,135,135,135,135,135,135,135,135,135,134,134,134,135,135,136,137,138,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,169,170,170,171,171,171,171,172,172,173,173,173,174,174,175,176,177,178,178,179,180,181,182,182,183,184,184,185,185,186,187,187,188,188,189,189,190,190,191,191,192,192,192,193,194,193,193,193,192,192,192,192,191,189,188,187,186,185,184,183,182,182,180,177,174,171,168,165,161,159,157,155,153,151,147,143,139,135,131,127,124,123,121,119,117,115,112,110,107,104,101,99,98,97,96,95,94,92,90,89,87,85,83,82,81,81,80,79,78,76,75,73,72,71,70,69,68,68,67,67,66,64,63,62,61,60,59,58,58,57,56,55,53,52,50,49,47,46,45,44,43,42,40,39,38,37,36,34,33,32,31,30,29,28,27,26,25,25,24,23,22,21,20,20,19,18,17,16,16,15,15,15,14,14,13,13,12,12,11,10,9,9,8,8,8,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6 }; - colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); - } - - private void initializeMatlabJet( int size, int bottom, int top ) { - int [] index= { 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 162, 166, 170, 174, 178, 182, 186, 190, 194, 198, 202, 206, 210, 214, 218, 222, 226, 230, 234, 238, 243, 247, 251, 255 }; - int [] red= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 223, 207, 191, 175, 159, 143, 127 }; - int [] green= { 0, 0, 0, 0, 0, 0, 0, 0, 15, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 223, 207, 191, 175, 159, 143, 127, 111, 95, 79, 63, 47, 31, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - int [] blue= { 143, 159, 175, 191, 207, 223, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 223, 207, 191, 175, 159, 143, 127, 111, 95, 79, 63, 47, 31, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); - } - - private void initializeWhiteRed( int size, int bottom, int top ) { - int [] index= { 0, 255 }; - int [] red= { 0, 255 }; - int [] green= { 0, 0 }; - int [] blue= { 0, 0 }; - colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); - } - private void initializeWhiteGreen( int size, int bottom, int top ) { - int [] index= { 0, 255 }; - int [] red= { 0, 0 }; - int [] green= { 0, 255 }; - int [] blue= { 0, 0 }; - colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); - } - private void initializeWhiteBlue( int size, int bottom, int top ) { - int [] index= { 0, 255 }; - int [] red= { 0, 0 }; - int [] green= { 0, 0 }; - int [] blue= { 0, 255 }; - colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); - } - - //Rob-Wilson JADE colorbar - private void initializeVioletYellow( int size, int bottom, int top ) { - int [] index= { 0, 27, 64, 95, 127, 159, 183, 202, 225, 246, 255 }; - int [] red= { 68, 71, 59, 45, 34, 36, 71, 110, 174, 233, 253 }; - int [] green= { 2, 36, 83, 111, 139, 171, 192, 206, 221, 229, 231 }; - int [] blue= { 86, 114, 139, 142, 141, 129, 110, 87, 46, 23, 37,}; - colorTable = makeColorTable( index, red, green, blue, size, bottom, top ); - } - - /** - * from the string, identify the type. - * @param s string like "apl_rainbow_black0" - * @return type like Type.APL_RAINBOW_BLACK0. - */ public static Type parse(String s) { if (s.equals("color_wedge")) { return COLOR_WEDGE; @@ -695,28 +475,8 @@ public static Type parse(String s) { return GRAYSCALE; } else if (s.equals("inverse_grayscale")) { return INVERSE_GRAYSCALE; - } else if (s.equals("blue_black_red")) { - return BLUE_BLACK_RED_WEDGE; - } else if (s.equals("blue_white_red")) { - return BLUE_WHITE_RED_WEDGE; - } else if (s.equals("apl_rainbow_black0")) { - return APL_RAINBOW_BLACK0; - } else if (s.equals("apl_rainbow_white0")) { - return APL_RAINBOW_WHITE0; - } else if (s.equals("gsfc_rp_special")) { - return GSFC_RP_SPECIAL; - } else if (s.equals("matlab_jet")) { - return MATLAB_JET; - } else if (s.equals("black_red")) { - return BLACK_RED; - } else if (s.equals("black_green")) { - return BLACK_GREEN; - } else if (s.equals("violet_yellow")) { - return VIOLET_YELLOW; - } else if (s.equals("black_blue")) { - return BLACK_BLUE; - } else if (s.equals("blue_to_orange")) { - return BLUE_TO_ORANGE; + //} else if (s.equals("blue_to_orange")) { + // return BLUE_TO_ORANGE; } else { throw new IllegalArgumentException("invalid DasColorBar.Type string: " + s); } @@ -724,31 +484,174 @@ public static Type parse(String s) { } - /* - * ColorBarRepalletteMouseModule removed because it is no longer useful. - */ + public MouseModule getRepaletteMouseModule( Renderer r ) { + return new ColorBarRepaletteMouseModule( r, this ); + } + + public class ColorBarRepaletteMouseModule extends MouseModule { + + DasColorBar colorBar; + Renderer parent; + DatumRange range0; + int lastTopColor; + int lastBottomColor; + + boolean animated0; + int state; + int STATE_IGNORE=300; + int STATE_TOP=200; + int STATE_BOTTOM=100; + + /** Utility field used by event firing mechanism. */ + private EventListenerList listenerList = null; + + public String getLabel() { return "Repalette"; }; + + public ColorBarRepaletteMouseModule( Renderer parent, DasColorBar colorBar ) { + if (colorBar.isHorizontal()) { + throw new IllegalArgumentException("Axis orientation is not vertical"); + } + if ( parent==null ) { + throw new IllegalArgumentException("Parent is null"); + } + this.parent= parent; + // this.dragRenderer= (DragRenderer)HorizontalRangeRenderer.renderer; + this.dragRenderer= new HorizontalSliceSelectionRenderer(parent.getParent()); + this.colorBar= colorBar; + } + + private void setColorBar( int y ) { + + int bottomColor, topColor; + + DatumRange dr; + DasRow row= colorBar.getRow(); + + double alpha= ( row.getDMaximum() - y ) / (1.*row.getHeight()); + + if ( state==STATE_TOP ) { + topColor= (int)( COLORTABLE_SIZE * alpha ); + topColor= Math.max( COLORTABLE_SIZE / 20 + 1, topColor ); + bottomColor= 0; + } else if ( state==STATE_BOTTOM ) { + topColor= COLORTABLE_SIZE; + bottomColor= (int)( COLORTABLE_SIZE * alpha ); + bottomColor= Math.min( COLORTABLE_SIZE * 19 / 20, bottomColor ); + } else { + return; + } + + System.err.println( ""+bottomColor + " "+topColor ); + lastTopColor= topColor; + lastBottomColor= bottomColor; + + colorBar.type.initializeColorTable( COLORTABLE_SIZE, bottomColor, lastTopColor ); + + colorBar.image= null; + colorBar.type.image= null; + colorBar.repaint(); + parent.refreshImage(); + } + + public void mouseReleased( MouseEvent e ) { + if ( state!=STATE_IGNORE ) { + colorBar.setAnimated(animated0); + int lastTopColor= this.lastTopColor; + + DatumRange dr; + double a0= lastBottomColor / ( 1.*COLORTABLE_SIZE ); + double a1= lastTopColor / ( 1.*COLORTABLE_SIZE); + + if ( isLog() ) { + dr= DatumRangeUtil.rescaleLog( range0, a0, a1 ); + } else { + dr= DatumRangeUtil.rescale( range0, a0, a1); + } + + colorBar.setDatumRange( dr ); + + colorBar.type.initializeColorTable( COLORTABLE_SIZE, 0, COLORTABLE_SIZE ); + + colorBar.image= null; + colorBar.type.image= null; + colorBar.repaint(); + parent.refreshImage(); + + } + } + + public void mousePointSelected(MousePointSelectionEvent e) { + setColorBar( e.getY() ); + } + + /** Registers DataRangeSelectionListener to receive events. + * @param listener The listener to register. + */ + public synchronized void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + if (listenerList == null ) { + listenerList = new EventListenerList(); + } + listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Removes DataRangeSelectionListener from the list of listeners. + * @param listener The listener to remove. + */ + public synchronized void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { + listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); + } + + /** Notifies all registered listeners about the event. + * + * @param event The event to be fired + */ + private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { + if (listenerList == null) return; + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { + ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); + } + } + } + + public void mousePressed(java.awt.event.MouseEvent e) { + super.mousePressed(e); + if ( DasColorBar.this.getColumn().contains(e.getX()+DasColorBar.this.getX()) ) { + if ( e.getY() + DasColorBar.this.getY() > DasColorBar.this.getRow().getDMiddle() ) { + state= STATE_BOTTOM; + } else { + state= STATE_TOP; + } + animated0= colorBar.isAnimated(); + colorBar.setAnimated(false); + range0= colorBar.getDatumRange(); + } else { + state= STATE_IGNORE; + } + } + + } /** - * get the color used to indicate fill, often gray or a transparent - * white. Note all instances use the same fillColor. - * @return the fill color. + * Getter for property fillColor. + * @return Value of property fillColor. */ public Color getFillColor() { return new Color( this.fillColor, true ); } /** - * set the color used to indicate fill, often gray or a transparent - * white. Note all instances use the same fillColor. - * @param fillColor the new fill color. + * Setter for property fillColor. + * @param fillColor New value of property fillColor. */ public void setFillColor(Color fillColor) { Color oldColor= new Color( this.fillColor ); this.fillColor = fillColor.getRGB(); this.type.initializeColorTable( COLORTABLE_SIZE, 0, this.type.getColorCount() ); - markDirty("fillColor"); + markDirty(); update(); firePropertyChange( PROPERTY_FILL_COLOR, oldColor,fillColor ); } - + } diff --git a/dasCore/src/org/das2/graph/DasColumn.java b/dasCore/src/org/das2/graph/DasColumn.java index 25af96d7a..5722c2bdb 100755 --- a/dasCore/src/org/das2/graph/DasColumn.java +++ b/dasCore/src/org/das2/graph/DasColumn.java @@ -1,8 +1,7 @@ /* File: DasColumn.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West + * Copyright (C) 2002-2014 The University of Iowa + * Created by: Jeremy Faden + * Edward E. West * * This file is part of the das2 library. * @@ -23,41 +22,56 @@ package org.das2.graph; +import org.das2.NameContext; +import org.das2.DasApplication; +import org.das2.DasException; +import org.das2.dasml.FormBase; import java.text.ParseException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; -/** - * DasColumn object represents the horizontal position on the canvas. - * @author jbf +/** Define a vertical region on a DasCanvas. + * Extends from the top to the bottom of the canvas. Canvas components such as + * plots, axes, colorbars and labels use DasColumns and DasRows for positioning. + * + * @author jbf + * @author eew + * @since 2.0 */ public class DasColumn extends DasDevicePosition { - - /** - * create a DasColumn with the normal position and no offsets. - * @param parent the canvas where this lives. - * @param nMin normal position of the left with respect to the canvas. - * @param nMax normal position of the right with respect to the canvas - */ - public DasColumn(DasCanvas parent, double nMin, double nMax) { - super(parent,nMin,nMax,true); + + /** Create a new vertical region on a DasCanvas. + * + * @param parent The canvas on which the region will be defined + * @param rLeft a value between 0.0 and 1.0 defining the left boundary of the column + * @param rRight a value between 0.0 and 1.0 defining the right boundary of the column + */ + public DasColumn(DasCanvas parent, double rLeft, double rRight) + { + super(parent,rLeft,rRight,true); } - /** - * create a DasColumn - * @param canvas the canvas where this lives. - * @param parent the parent row or null to which this is relative. - * @param nMin normal position of the left with respect to the canvas or parent if non-null. - * @param nMax normal position of the right with respect to the canvas or parent if non-null. - * @param emMin em offset of the left from the minimum position, in canvas font heights. - * @param emMax em offset of the right from the maximum position, in canvas font heights. - * @param ptMin point offset of the left from the minimum position, note points are the same as pixels. - * @param ptMax point offset of the right from the maximum position, note points are the same as pixels. - */ - public DasColumn( DasCanvas canvas, DasColumn parent, double nMin, double nMax, - double emMin, double emMax, int ptMin, int ptMax ) { - super( canvas, true, parent, nMin, nMax, emMin, emMax, ptMin, ptMax ); + /** Create a new vertical region on a DasCanvas + * + * @param canvas The canvas on which the region will be defined + * @param parent If not null, the column is defined relative to another column. So + * rMin = 0.0 is the left position of the parent column and rMax = 1.0 is the + * right position of the parent column. + * @param rLeft a value between 0.0 and 1.0 defining the left boundary of the column + * @param rRight a value between 0.0 and 1.0 defining the right boundary of the column + * @param emMin Offset from rLeft in "M's", adds with ptMin + * @param emMax Offset from rRight in "M's", adds with ptMax + * @param ptMin Offset from rLeft in pixels, adds with emMin + * @param ptMax Offset from rRight in pixels, adds with emMax + */ + public DasColumn(DasCanvas canvas, DasColumn parent, double rLeft, double rRight, + double emMin, double emMax, int ptMin, int ptMax ) + { + super( canvas, true, parent, rLeft, rRight, emMin, emMax, ptMin, ptMax ); } - /** + + /** Makes a new DasColumn by parsing a formatted string * makes a new DasColumn by parsing a string like "100%-5em+3pt" to get the offsets. * The three qualifiers are "%", "em", and "pt", but "px" is allowed as well * as surely people will use that by mistake. If an offset or the normal position @@ -67,69 +81,113 @@ public DasColumn( DasCanvas canvas, DasColumn parent, double nMin, double nMax, * @param parent if non-null, this DasColumn is specified with respect to parent. * @param minStr a string like "0%+5em" * @param maxStr a string like "100%-7em" - * @return a new DasColumn * @throws IllegalArgumentException if the strings cannot be parsed */ - public static DasColumn create( DasCanvas canvas, DasColumn parent, String minStr, String maxStr ) { + public static DasColumn create(DasCanvas canvas, DasColumn parent, String minStr, + String maxStr ) + { double[] min, max; try { - min= parseLayoutStr( minStr ); + min= parseFormatStr( minStr ); } catch ( ParseException e ) { throw new IllegalArgumentException("unable to parse min: \""+minStr+"\""); } try { - max= parseLayoutStr( maxStr ); + max= parseFormatStr( maxStr ); } catch ( ParseException e ) { throw new IllegalArgumentException("unable to parse max: \""+maxStr+"\""); } return new DasColumn( canvas, parent, min[0], max[0], min[1], max[1], (int)min[2], (int)max[2] ); } - /** - * placeholder for unassigned value. - */ public static final DasColumn NULL= new DasColumn(null,null,0,0,0,0,0,0); /** - * return the width in points (pixels) of the column. - * @return the width in points (pixels) of the column. + * @deprecated This created a column that was not attached to anything, so + * it was simply a convenience method that didn't save much effort. */ + public DasColumn createSubColumn( double pleft, double pright ) { + double left= getMinimum(); + double right= getMaximum(); + double delta= right-left; + return new DasColumn(getCanvas(),left+pleft*delta,left+pright*delta); + } + public int getWidth() { return getDMaximum()-getDMinimum(); } - /** - * create a DasColumn - * @param parent the canvas where this lives. - * @return DasColumn - */ public static DasColumn create(DasCanvas parent) { - return new DasColumn(parent,null,0.0,1.0,5,-3,0,0); + return new DasColumn(parent,null,0.0,1.0,5,-5,0,0); } - - /** - * create a column that is positioned relative to this column. - * @param pleft the normal position - * @param pright the normal position - * @return the new row. - */ + + public static DasColumn create( DasCanvas parent, int iplot, int nplot ) { + double min= 0.1 + iplot * ( 0.7 ) / nplot; + double max= 0.099 + ( iplot + 1 ) * ( 0.7 ) / nplot; + return new DasColumn( parent, min, max ); + } + public DasColumn createAttachedColumn(double pleft, double pright) { return new DasColumn(null,this,pleft,pright,0,0,0,0); } - + + /** + * create a child by parsing spec strings like "50%+3em" + * @throws IllegalArgumentException when the string is malformed. + * @param smin + * @param smax + * @return + */ + public DasColumn createChildColumn( String smin, String smax ) { + try { + double[] min= DasDevicePosition.parseFormatStr(smin); + double[] max= DasDevicePosition.parseFormatStr(smax); + return new DasColumn( null, this, min[0], max[0], min[1], max[1], (int)min[2], (int)max[2] ); + } catch ( ParseException ex ) { + throw new IllegalArgumentException(ex); + } + } + + /** Process a <column7gt; element. + * + * @param element The DOM tree node that represents the element + */ + static DasColumn processColumnElement(Element element, DasCanvas canvas, FormBase form) throws DasException { + String name = element.getAttribute("name"); + double minimum + = Double.parseDouble(element.getAttribute("minimum")); + double maximum + = Double.parseDouble(element.getAttribute("maximum")); + DasColumn column = new DasColumn(canvas, minimum, maximum); + column.setDasName(name); + DasApplication app = form.getDasApplication(); + NameContext nc = app.getNameContext(); + nc.put(name, column); + return column; + } + + public Element getDOMElement(Document document) { + Element element = document.createElement("column"); + element.setAttribute("name", getDasName()); + element.setAttribute("minimum", Double.toString(getMinimum())); + element.setAttribute("maximum", Double.toString(getMaximum())); + return element; + } + /** - * return pixel location of the left of the column. - * @return pixel location of the left of the column. + * return the left of the column. + * @return */ public int left() { return getDMinimum(); } /** - * return pixel location the right (non-inclusive) of the column. - * @return pixel location the right (non-inclusive) of the column. + * return the right (non-inclusive) of the column. + * @return */ public int right() { return getDMaximum(); } } + diff --git a/dasCore/src/org/das2/graph/DasDevicePosition.java b/dasCore/src/org/das2/graph/DasDevicePosition.java index 8fd979915..29877d791 100755 --- a/dasCore/src/org/das2/graph/DasDevicePosition.java +++ b/dasCore/src/org/das2/graph/DasDevicePosition.java @@ -33,36 +33,18 @@ import java.awt.event.ComponentEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; +import java.text.DecimalFormat; import java.text.ParseException; -import java.util.Locale; -import java.util.NoSuchElementException; import java.util.StringTokenizer; -import java.util.logging.Level; -import java.util.logging.Logger; import org.das2.components.propertyeditor.Editable; import org.das2.system.MutatorLock; -import org.das2.util.DebugPropertyChangeSupport; -import org.das2.util.LoggerManager; /** - * DasRows and DasColumns are both DasDevidePositions that lay out the - * canvas. Any object on the DasCanvas have a row and column object to indicate - * the position of the object. + * * @author jbf */ public abstract class DasDevicePosition implements Editable, java.io.Serializable { - private final static Logger logger= LoggerManager.getLogger("das2.graphics"); - - public static final String PROP_DMAXIMUM = "dMaximum"; - public static final String PROP_DMINIMUM = "dMinimum"; - public static final String PROP_EMMAXIMUM = "emMaximum"; - public static final String PROP_EMMINIMUM = "emMinimum"; - public static final String PROP_MAXIMUM = "maximum"; - public static final String PROP_MINIMUM = "minimum"; - public static final String PROP_PTMAXIMUM = "ptMaximum"; - public static final String PROP_PTMINIMUM = "ptMinimum"; - protected transient DasCanvas canvas; protected transient DasDevicePosition parent; @@ -76,8 +58,7 @@ public abstract class DasDevicePosition implements Editable, java.io.Serializabl protected EventListenerList listenerList = new EventListenerList(); - private final PropertyChangeListener canvasListener= new PropertyChangeListener() { - @Override + private PropertyChangeListener canvasListener= new PropertyChangeListener() { public void propertyChange( PropertyChangeEvent ev ) { if ( DasDevicePosition.this.emMinimum!=0 || DasDevicePosition.this.emMaximum!=0 ) { revalidate(); @@ -85,19 +66,6 @@ public void propertyChange( PropertyChangeEvent ev ) { } }; - /** - * create the DasDevicePosition. Typically the DasRow or DasColumn - * constructor is used. - * @param canvas the canvas to which the position refers. - * @param isWidth true if the DasDevicePosition is a column not a row. - * @param parent null or the parent to which this position is relative. - * @param minimum normal position with respect to the canvas or parent if non-null. - * @param maximum normal position with respect to the canvas or parent if non-null. - * @param emMinimum em offset from the minimum position, in canvas font heights. - * @param emMaximum em offset from the maximum position, in canvas font heights. - * @param ptMinimum point offset from the minimum position, note points are the same as pixels. - * @param ptMaximum point offset from the maximum position, note points are the same as pixels. - */ protected DasDevicePosition( DasCanvas canvas, boolean isWidth, DasDevicePosition parent, double minimum, double maximum, double emMinimum, double emMaximum, @@ -114,14 +82,10 @@ protected DasDevicePosition( DasCanvas canvas, boolean isWidth, DasDevicePositio isWidth= parent.isWidth; } - if ( canvas==null && ( ! isNull ) ) { + if ( canvas==null & ( ! isNull ) ) { throw new IllegalArgumentException("parent cannot be null"); } - if ( isNull ) { - logger.finest( "null canvas and null parent is allowed if you know what you are doing."); - } - this.canvas = canvas; this.parent= parent; this.minimum = minimum; @@ -133,10 +97,9 @@ protected DasDevicePosition( DasCanvas canvas, boolean isWidth, DasDevicePositio this.isWidth = isWidth; this.dasName = DasApplication.getDefaultApplication().suggestNameFor(this); - this.propertyChangeDelegate = new DebugPropertyChangeSupport(this); + this.propertyChangeDelegate = new PropertyChangeSupport(this); if ( parent!=null ) { parent.addPropertyChangeListener( new PropertyChangeListener() { - @Override public void propertyChange(PropertyChangeEvent evt) { revalidate(); } @@ -144,7 +107,6 @@ public void propertyChange(PropertyChangeEvent evt) { } else { if (canvas != null) { canvas.addComponentListener(new ComponentAdapter() { - @Override public void componentResized(ComponentEvent e) { revalidate(); } @@ -159,46 +121,12 @@ public void componentResized(ComponentEvent e) { } /** - * parse the format string into a pixel count. Convenience method. - * parseFormatStr(s) will throw a parse exception and should be used to - * verify strings. - * @param s The string, like "5em+3pt" - * @param em the em height of the font, - * @param widthHeight the width or height of the dimension. - * @param fail the value to return if the parsing fails. - * @return the length in pixels (or points). - */ - public static double parseLayoutStr( String s, double em, int widthHeight, double fail ) { - try { - double [] r= parseLayoutStr(s); - return widthHeight * r[0] + em * r[1] + r[2]; - } catch ( ParseException ex ) { - return fail; - } - } - - /** - * Calls parseLayoutStr - * @param s - * @return - * @deprecated use parseLayoutStr. - * @throws ParseException - */ - public static double[] parseFormatStr( String s ) throws ParseException { - return parseLayoutStr(s); - } - - /** - * parse position strings like "100%-5em+4pt" into [ npos, emoffset, pt_offset ]. + * parse position strings like "100%-5em+4pt" into npos, emoffset, pt_offset. * Note px is acceptable, but pt is proper. * Ems are rounded to the nearest hundredth. * Percents are returned as normal (0-1) and rounded to the nearest thousandth. - * @param s string containing the position string. - * @return three-element array of parsed [ npos, emoffset, pt_offset ] - * @throws java.text.ParseException - * @see formatFormatStr */ - public static double[] parseLayoutStr( String s ) throws ParseException { + public static double[] parseFormatStr( String s ) throws ParseException { double[] result= new double[] { 0, 0, 0 }; StringTokenizer tok= new StringTokenizer( s, "%emptx", true ); int pos=0; @@ -206,17 +134,9 @@ public static double[] parseLayoutStr( String s ) throws ParseException { String ds= tok.nextToken(); pos+=ds.length(); double d= Double.parseDouble(ds); - String u=null; - try { - u = tok.nextToken(); - } catch (NoSuchElementException e) { - if ( s.trim().equals("0") ) { - return new double[] { 0,0,0 }; - } else { - throw new ParseException("missing units in format string: "+s,0); - } - } + String u= tok.nextToken(); pos+=u.length(); + u.trim(); if ( u.charAt(0)=='%' ) { result[0]= d/100.; } else if ( u.equals("e") ) { @@ -235,71 +155,40 @@ public static double[] parseLayoutStr( String s ) throws ParseException { result[1]= Math.round(result[1]*10)/10.; return result; } - - /** - * formats the three position specifiers efficiently. - * @param arr three-element array [ npos, emoffset, pt_offset ]. - * @return String like "100%-5em+4pt" - * @see #parseFormatStr(java.lang.String) - * @see #formatLayoutStr(org.das2.graph.DasDevicePosition, boolean) which contains repeated code. - */ - public static String formatFormatStr( double[] arr ) { - StringBuilder buf= new StringBuilder(); - if ( arr[0]!=0 ) buf.append( String.format( Locale.US, "%.2f%%", arr[0]*100 ) ); - if ( arr[1]!=0 ) buf.append( String.format(Locale.US, "%+.1fem", arr[1] ) ); - if ( arr[2]!=0 ) buf.append( String.format(Locale.US, "%+dpt", (int)arr[2] ) ); - return buf.toString(); - } - - /** - * parses the layout string, which contains both the minimum and maximum - * positions, and configures the row or column. Note that for rows, - * 0% refers to the top of the canvas or parent row. - * @param pos row or column to assign values. - * @param spec string like "0%+2em,100%-5em+4pt" - * @throws ParseException when the string cannot be parsed. - */ + public static void parseLayoutStr( DasDevicePosition pos, String spec ) throws ParseException { String[] ss= spec.split(","); - double[] pmin= parseLayoutStr( ss[0] ); - double[] pmax= parseLayoutStr( ss[1] ); + double[] pmin= parseFormatStr( ss[0] ); + double[] pmax= parseFormatStr( ss[1] ); MutatorLock lock= pos.mutatorLock(); lock.lock(); - //try { - pos.setMinimum(pmin[0]); - pos.setEmMinimum(pmin[1]); - pos.setPtMinimum((int)pmin[2]); - pos.setMaximum(pmax[0]); - pos.setEmMaximum(pmax[1]); - pos.setPtMaximum((int)pmax[2]); - //} finally { - lock.unlock(); - //} + pos.setMinimum(pmin[0]); + pos.setEmMinimum(pmin[1]); + pos.setPtMinimum((int)pmin[2]); + pos.setMaximum(pmax[0]); + pos.setEmMaximum(pmax[1]); + pos.setPtMaximum((int)pmax[2]); + lock.unlock(); } - /** - * formats the row or column position into a string like - * @param pos the row or column - * @param min true if the minimum boundary is to be formatted, false if the maximum boundary is to be formatted. - * @return String like "100%-5em+4pt" - * @see #formatFormatStr(double[]) which contains repeated code. - */ public static String formatLayoutStr( DasDevicePosition pos, boolean min ) { - StringBuilder buf= new StringBuilder(); + StringBuffer buf= new StringBuffer(); + DecimalFormat nf2= new DecimalFormat("0.00"); + DecimalFormat nf1= new DecimalFormat("0.0"); + DecimalFormat nf0= new DecimalFormat("0"); if ( min ) { - if ( pos.getMinimum()!=0 ) buf.append( String.format( Locale.US, "%.2f%%", pos.getMinimum()*100 ) ); - if ( pos.getEmMinimum()!=0 ) buf.append( String.format(Locale.US, "%+.1fem", pos.getEmMinimum() ) ); - if ( pos.getPtMinimum()!=0 ) buf.append( String.format(Locale.US, "%+dpt", pos.getPtMinimum() ) ); + if ( pos.getMinimum()!=0 ) buf.append( nf2.format(pos.getMinimum()*100 )+"%" ); + if ( pos.getEmMinimum()!=0 ) buf.append( nf1.format(pos.getEmMinimum() )+"em" ); + if ( pos.getPtMinimum()!=0 ) buf.append( nf0.format(pos.getPtMinimum()) + "pt" ); } else { - if ( pos.getMaximum()!=0 ) buf.append( String.format(Locale.US, "%.2f%%", pos.getMaximum()*100 ) ); - if ( pos.getEmMaximum()!=0 ) buf.append( String.format(Locale.US, "%+.1fem", pos.getEmMaximum() ) ); - if ( pos.getPtMaximum()!=0 ) buf.append( String.format(Locale.US, "%+dpt", pos.getPtMaximum() ) ); + if ( pos.getMaximum()!=0 ) buf.append( nf2.format(pos.getMaximum()*100 )+"%" ); + if ( pos.getEmMaximum()!=0 ) buf.append( nf1.format(pos.getEmMaximum() )+"em" ); + if ( pos.getPtMaximum()!=0 ) buf.append( nf0.format(pos.getPtMaximum()) + "pt" ); } if ( buf.length()==0 ) return "0%"; return buf.toString(); } - public DasDevicePosition(DasCanvas parent, double minimum, double maximum, boolean width) { this( parent, width, null, minimum, maximum, 0., 0., 0, 0 ); @@ -309,11 +198,6 @@ protected DasCanvas getCanvas() { return this.canvas; } - /** - * set the name associated with this object. - * @param name - * @throws org.das2.DasNameException - */ public void setDasName(String name) throws org.das2.DasNameException { if (name.equals(dasName)) { return; @@ -330,17 +214,13 @@ public void setDasName(String name) throws org.das2.DasNameException { this.firePropertyChange("name", oldName, name); } - /** - * get the name associated with this object. - * @return - */ public String getDasName() { return dasName; } /** - * returns the em size for the canvas. We define the em size as the - * height of the font. + * returns the em size for the canvas. We define the em size as the height of the + * font. * @return the em height in points. */ public int getEmSize() { @@ -366,21 +246,17 @@ private int getParentMax() { private int dMinimum, dMaximum; /** - * recalculates dMinimum and dMaximum based on the new values, and - * checks for correctness. Note if dMaximum<=dMinimum, we define - * dMaximum= dMinimum+1. + * recalculates dMinimum and dMaximum becased on the new values, and checks for + * correctness. Note if dMaximum<=dMinimum, we define dMaximum= dMinimum+1. */ - protected final void revalidate() { - if ( parent!=null ) { - parent.revalidate(); - } + private void revalidate() { int oldmin= dMinimum; int oldmax= dMaximum; dMinimum= (int)( getParentMin() + minimum*getDeviceSize() + getEmSize() * emMinimum + ptMinimum ); dMaximum= (int)( getParentMin() + maximum*getDeviceSize() + getEmSize() * emMaximum + ptMaximum ); if ( dMaximum<=dMinimum ) dMaximum= dMinimum+1; - if ( dMinimum!=oldmin ) firePropertyChange( PROP_DMINIMUM, oldmin ,dMinimum); - if ( dMaximum!=oldmax ) firePropertyChange( PROP_DMAXIMUM, oldmax ,dMaximum); + if ( dMinimum!=oldmin ) firePropertyChange( "dMinimum", oldmin, dMinimum ); + if ( dMaximum!=oldmax ) firePropertyChange( "dMaximum", oldmax, dMaximum ); if ( dMinimum!=oldmin || dMaximum!=oldmax ) fireUpdate(); canvas.repaint(); } @@ -393,7 +269,7 @@ protected final void revalidate() { public int getDMinimum() { if ( canvas==null && parent==null ) { String type= isWidth ? "column" : "row"; - throw new RuntimeException( type+" was not set before layout, having no canvas or parent "+type ); + throw new RuntimeException("null "+type+", "+type+" was not set before layout"); } return dMinimum; } @@ -406,51 +282,39 @@ public int getDMinimum() { public int getDMaximum() { if ( canvas==null && parent==null ) { String type= isWidth ? "column" : "row"; - throw new RuntimeException( type+" was not set before layout, having no canvas or parent "+type); + throw new RuntimeException("null "+type+", "+type+" was not set before layout"); } return dMaximum; } /** * return the normal position control of the top/left. - * @return the normal position control of the top/left. + * @return */ public double getMinimum() { return minimum; } - - /** - * return the normal position control of the bottom/right. - * @return the normal position control of the bottom/right. - */ + public double getMaximum() { return maximum; } - - /** - * set the new normal location of both the min and max in one operation. - * @param minimum the top or left - * @param maximum the bottom or right - */ + private void setPosition(double minimum, double maximum) { double oldMin = this.minimum; double oldMax = this.maximum; + double doldMin = this.minimum; + double doldMax = this.maximum; this.minimum = Math.min(minimum, maximum); - this.maximum = Math.max(minimum, maximum); + this.maximum = Math.max(maximum, maximum); revalidate(); if (oldMin != this.minimum) { - firePropertyChange( PROP_MINIMUM, oldMin, this.minimum); + firePropertyChange("minimum", oldMin, this.minimum); } if (oldMax != this.maximum) { - firePropertyChange( PROP_MAXIMUM, oldMax, this.maximum); + firePropertyChange("maximum", oldMax, this.maximum); } } - - /** - * set the new pixel location of both the min and max in one operation. - * @param minimum the top or left - * @param maximum the bottom or right - */ + public void setDPosition( int minimum, int maximum) { int pmin= getParentMin(); int pmax= getParentMax(); @@ -461,11 +325,6 @@ public void setDPosition( int minimum, int maximum) { setPosition( nmin, nmax ); } - /** - * set the normal position of the minimum of the row or column. - * For a row, this is the bottom. For a column, this is the right side. - * @param maximum normal (0-1) position - */ public void setMaximum(double maximum) { if (maximum == this.maximum) { return; @@ -475,17 +334,15 @@ public void setMaximum(double maximum) { } else { double oldValue = this.maximum; this.maximum = maximum; - firePropertyChange( PROP_MAXIMUM, oldValue, maximum); + firePropertyChange("maximum", oldValue, maximum); revalidate(); } } - - + /** - * set the new pixel position of the bottom/right boundary. - * em and pt offsets are not modified, and the normal position - * is recalculated. - * @param maximum new pixel maximum + * set the new pixel position of the bottom/right boundry. em and pt offsets + * are not modified, and the normal position is recalculated. + * @param maximum */ public void setDMaximum( int maximum) { int pmin= getParentMin(); @@ -496,11 +353,6 @@ public void setDMaximum( int maximum) { setMaximum( n ); } - /** - * set the normal position of the minimum of the row or column. - * For a row, this is the top. For a column, this is the left side. - * @param minimum normal (0-1) position - */ public void setMinimum( double minimum) { if (minimum == this.minimum) { return; @@ -510,15 +362,15 @@ public void setMinimum( double minimum) { } else { double oldValue = this.minimum; this.minimum = minimum; - firePropertyChange( PROP_MINIMUM, oldValue, minimum); + firePropertyChange("minimum", oldValue, minimum); revalidate(); } } /** - * set the new pixel position of the top/left boundary. em and pt offsets + * set the new pixel position of the top/left boundry. em and pt offsets * are not modified, and the normal position is recalculated. - * @param minimum new pixel minimum + * @param minimum */ public void setDMinimum( int minimum) { int pmin= getParentMin(); @@ -529,18 +381,10 @@ public void setDMinimum( int minimum) { setMinimum( n ); } - /** - * return the parent canvas. - * @return the parent canvas. - */ public DasCanvas getParent() { return this.canvas; } - /** - * set the parent canvas. - * @param parent canvas. - */ public void setParent(DasCanvas parent) { this.canvas= parent; fireUpdate(); @@ -548,21 +392,14 @@ public void setParent(DasCanvas parent) { private boolean valueIsAdjusting= false; - /** - * get a lock for this object, used to mutate a number of properties - * as one atomic operation. - * @return a lock - */ protected synchronized MutatorLock mutatorLock() { return new MutatorLock() { - @Override public void lock() { if ( isValueIsAdjusting() ) { System.err.println("lock is already set!"); } valueIsAdjusting= true; } - @Override public void unlock() { valueIsAdjusting= false; propertyChangeDelegate.firePropertyChange( "mutatorLock", "locked", "unlocked"); @@ -570,32 +407,15 @@ public void unlock() { }; } - /** - * add an update listener - * @param l update listener - */ - public void addUpdateListener(DasUpdateListener l) { + + public void addpwUpdateListener(DasUpdateListener l) { listenerList.add(DasUpdateListener.class, l); - if ( listenerList.getListenerCount()>100 ) { - logger.log(Level.WARNING, "I think I found a leak in {0}", this.getDasName()); - } } - /** - * remove an update listener - * @param l update listener - */ - public void removeUpdateListener(DasUpdateListener l) { - int n0= listenerList.getListenerCount(); + public void removepwUpdateListener(DasUpdateListener l) { listenerList.remove(DasUpdateListener.class, l); - if ( listenerList.getListenerCount()==n0 ) { - logger.fine("nothing was removed..."); - } } - /** - * fire an update to all listeners. - */ protected void fireUpdate() { DasUpdateEvent e = new DasUpdateEvent(this); Object[] listeners = listenerList.getListenerList(); @@ -625,11 +445,11 @@ protected void firePropertyChange(String propertyName, boolean oldValue, boolean } protected void firePropertyChange(String propertyName, int oldValue, int newValue) { - firePropertyChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue) ); + firePropertyChange(propertyName, new Integer(oldValue), new Integer(newValue)); } protected void firePropertyChange(String propertyName, long oldValue, long newValue) { - firePropertyChange(propertyName, Long.valueOf(oldValue), Long.valueOf(newValue) ); + firePropertyChange(propertyName, new Long(oldValue), new Long(newValue)); } protected void firePropertyChange(String propertyName, float oldValue, float newValue) { @@ -644,21 +464,10 @@ protected void firePropertyChange(String propertyName, Object oldValue, Object n propertyChangeDelegate.firePropertyChange(propertyName, oldValue, newValue); } - /** - * return the size in pixels (or points) - * @return the size in pixels (or points) - */ protected int getDeviceSize() { return getParentMax() - getParentMin(); } - /** - * convenience method for creating a rectangle from a row and column. - * The rectangle will be in canvas pixel coordinates. - * @param row row describing the top and bottom of the box. - * @param column column describing the left and right sides of the box. - * @return rectangle in canvas pixel coordinates. - */ public static java.awt.Rectangle toRectangle(DasRow row, DasColumn column) { int xmin=column.getDMinimum(); int ymin=row.getDMinimum(); @@ -667,11 +476,6 @@ public static java.awt.Rectangle toRectangle(DasRow row, DasColumn column) { row.getDMaximum()-ymin); } - /** - * return a human-readable string representing the object for debugging. - * @return a human-readable string representing the object for debugging. - */ - @Override public String toString() { //String format="%.1f%%%+.1fem%+dpt"; //String smin= String.format(format, minimum*100, emMinimum, ptMinimum ); @@ -680,9 +484,9 @@ public String toString() { } /** - * returns true if ( getDMinimum() <= x ) && ( x <= getDMaximum() ); + * returns true if ( getDMinimum() <= x ) && ( x <= getDMaximum() ); * @param x the pixel position - * @return true if ( getDMinimum() <= x ) && ( x <= getDMaximum() ); + * @return */ public boolean contains( int x ) { return ( getDMinimum() <= x ) && ( x <= getDMaximum() ); @@ -690,7 +494,7 @@ public boolean contains( int x ) { /** * returns pixel position (device position) of the the middle of the row or column - * @return pixel position (device position) of the the middle of the row or column + * @return */ public int getDMiddle() { return (getDMinimum()+getDMaximum())/2; @@ -702,66 +506,50 @@ public int getDMiddle() { private double emMinimum; /** - * return the em offset that controls the position of the top/left boundary. - * @return the em offset that controls the position of the top/left boundary. + * return the em offset that controls the position of the top/left boundry. + * @return */ public double getEmMinimum() { return this.emMinimum; } - /** - * set the em offset that controls the position of the top/left boundary. - * @param emMinimum the em offset. - */ public void setEmMinimum(double emMinimum) { double oldValue= this.emMinimum; this.emMinimum = emMinimum; - firePropertyChange( PROP_EMMINIMUM, oldValue, emMinimum); + firePropertyChange("emMinimum", oldValue, emMinimum); revalidate(); } /** - * property emMaximum, the em (font height) offset from the maximum + * property emMaximum, the em (font height * 2/3) offset from the maximum */ private double emMaximum; - /** - * return the em offset that controls the position of the bottom/right boundary. - * @return the em offset that controls the position of the bottom/right boundary. - */ public double getEmMaximum() { return this.emMaximum; } - /** - * set the em offset that controls the position of the bottom/right boundary. - * @param emMaximum the em offset. - */ public void setEmMaximum(double emMaximum) { double oldValue= this.emMaximum; this.emMaximum = emMaximum; - firePropertyChange( PROP_EMMAXIMUM, oldValue, emMaximum); + firePropertyChange("emMaximum", oldValue, emMaximum); revalidate(); } private int ptMinimum; /** - * return the points offset that controls the position of the top/left boundary. - * @return the points offset + * return the points offset that controls the position of the top/left boundry. + * @return */ public int getPtMinimum() { return this.ptMinimum; } - /** - * set the points offset that controls the position of the top/left boundary. - * @param ptMinimum the points offset - */ public void setPtMinimum(int ptMinimum) { int oldValue= this.ptMinimum; this.ptMinimum = ptMinimum; - firePropertyChange( PROP_PTMINIMUM, oldValue, ptMinimum); + firePropertyChange("ptMinimum", oldValue, ptMinimum); revalidate(); } @@ -771,72 +559,23 @@ public void setPtMinimum(int ptMinimum) { private int ptMaximum=0; /** - * return the points offset that controls the position of the bottom/right boundary. - * @return the points offset that controls the position of the bottom/right boundary. + * return the points offset that controls the position of the bottom/right boundry. + * @return */ public int getPtMaximum() { return this.ptMaximum; } - - /** - * set the pt offset that controls the position of the bottom/right boundary. - * @param ptMaximum the em offset. - */ public void setPtMaximum(int ptMaximum) { int oldValue= this.ptMaximum; this.ptMaximum = ptMaximum; - firePropertyChange( PROP_PTMAXIMUM, oldValue, ptMaximum); - revalidate(); - } - - /** - * set all three as one atomic operation - * @param norm normal position from 0 to 1. - * @param em em offset from the normal position. - * @param pt points offset from the normal position. - */ - public void setMin( double norm, double em, int pt ) { - double[] old= new double[ ] { this.minimum, this.emMinimum, this.emMaximum }; - this.minimum= norm; - this.emMinimum= em; - this.ptMinimum= pt; - firePropertyChange(PROP_PTMINIMUM, old[2], ptMinimum ); - firePropertyChange(PROP_EMMINIMUM, old[1], em ); - firePropertyChange(PROP_MINIMUM, old[0], norm ); - revalidate(); - } - - /** - * set all three as one atomic operation - * @param norm normal position from 0 to 1. - * @param em em offset from the normal position. - * @param pt points offset from the normal position. - */ - public void setMax( double norm, double em, int pt ) { - double[] old= new double[ ] { this.maximum, this.emMaximum, this.emMaximum }; - this.maximum= norm; - this.emMaximum= em; - this.ptMaximum= pt; - firePropertyChange(PROP_PTMAXIMUM, old[2], ptMaximum ); - firePropertyChange(PROP_EMMAXIMUM, old[1], em ); - firePropertyChange(PROP_MAXIMUM, old[0], norm ); + firePropertyChange("ptMaximum", oldValue, ptMaximum); revalidate(); } - /** - * return the parent, or null. If parent is non-null, then position is - * relative to the parent. - * @return the parent, or null. - */ public DasDevicePosition getParentDevicePosition() { return this.parent; } - /** - * return true if the value is currently adjusting because a - * mutator lock is out. - * @return true if the value is currently adjusting. - */ public boolean isValueIsAdjusting() { return valueIsAdjusting; } diff --git a/dasCore/src/org/das2/graph/DasEventsIndicator.java b/dasCore/src/org/das2/graph/DasEventsIndicator.java new file mode 100755 index 000000000..2bfcddd4d --- /dev/null +++ b/dasCore/src/org/das2/graph/DasEventsIndicator.java @@ -0,0 +1,71 @@ +/* + * DasEventsIndicator.java + * + * Created on April 6, 2004, 10:39 AM + */ + +package org.das2.graph; + +import org.das2.dataset.DataSetUpdateEvent; +import org.das2.dataset.DataSetDescriptor; +import org.das2.dataset.DataSetUpdateListener; +import org.das2.datum.DatumRange; +import org.das2.datum.Units; + +/** + * + * @author Jeremy + * + * The DasEventsIndicator takes a DataSetDescriptor that produces VectorDataSets with a plane + * "xTagWidth." This plane should consist of Datums with the same Units as the xAxis offset Units. + * The y values of the DataSet will be toString'ed and this value will be the tooltip of the bar. + */ +public class DasEventsIndicator extends DasPlot implements DataSetUpdateListener { + EventsRenderer renderer; + + /** Creates a new instance of DasEventsIndicator */ + public DasEventsIndicator( DataSetDescriptor dsd, DasAxis axis, DasAxis yAxis, String planeId ) { + super( axis, yAxis ); + renderer= new EventsRenderer( dsd ) ; + addRenderer( renderer ); + } + + public DasEventsIndicator(DasAxis axis, DasAxis yAxis, String planeId ) { + super( axis, yAxis ); + renderer= new EventsRenderer( ) ; + addRenderer( renderer ); + } + + + /** Creates a new instance of a DasEventsIndicator that doesn't auto-request data + * + */ + public static DasEventsIndicator create(DasAxis xAxis, String planeId){ + DasAxis yAxis= new DasAxis( new DatumRange( 0,1, Units.dimensionless ), DasAxis.VERTICAL ); + yAxis.setVisible(false); + return new DasEventsIndicator(xAxis, yAxis, planeId ); + } + + /** + * This method replaces the old constructor. This is unavoidable + */ + public static DasEventsIndicator create( DataSetDescriptor dsd, DasAxis axis, String planeId ) { + DasAxis yAxis= new DasAxis( new DatumRange( 0,1, Units.dimensionless ), DasAxis.VERTICAL ); + yAxis.setVisible(false); + return new DasEventsIndicator( dsd, axis, yAxis, planeId ); + } + + + public void setDataSetDescriptor( DataSetDescriptor dsd ) { + renderer.setDataSetDescriptor(dsd); + } + + public DataSetDescriptor getDataSetDescriptor( ) { + return renderer.getDataSetDescriptor(); + } + + public void dataSetUpdated(DataSetUpdateEvent e) { + ((XAxisDataLoader)renderer.getDataLoader()).dataSetUpdated(e); + } + +} diff --git a/dasCore/src/org/das2/graph/DasLabelAxis.java b/dasCore/src/org/das2/graph/DasLabelAxis.java index 0854dfb43..78c7105ba 100755 --- a/dasCore/src/org/das2/graph/DasLabelAxis.java +++ b/dasCore/src/org/das2/graph/DasLabelAxis.java @@ -22,21 +22,18 @@ */ package org.das2.graph; -import java.awt.Font; -import java.awt.Graphics2D; -import java.text.DecimalFormat; -import java.util.Arrays; -import org.das2.datum.Datum; import org.das2.datum.DatumRange; -import org.das2.datum.DatumUtil; -import org.das2.datum.DatumVector; import org.das2.datum.Units; +import org.das2.datum.DatumVector; +import org.das2.datum.Datum; +import org.das2.datum.DatumUtil; +import org.das2.util.GrannyTextRenderer; import org.das2.datum.format.DatumFormatter; import org.das2.graph.event.DasUpdateEvent; import org.das2.graph.event.DasUpdateListener; -import org.das2.util.GrannyTextRenderer; -import org.virbo.dataset.DataSetUtil; -import org.virbo.dataset.QDataSet; +import java.awt.*; +import java.text.DecimalFormat; + public class DasLabelAxis extends DasAxis implements DasUpdateListener { @@ -85,12 +82,8 @@ public DasLabelAxis(DatumVector labels, int orientation) { getDataRange().addUpdateListener(this); } - public DasLabelAxis( QDataSet labels, int orientation ) { - this( DataSetUtil.asDatumVector(labels), orientation ); - } - public int[] getLabelPositions() { - return Arrays.copyOf( this.labelPositions, this.labelPositions.length ); + return this.labelPositions; } private void updateTickPositions() { @@ -126,21 +119,20 @@ private void updateTickPositions() { } } - - @Override + @Override public Datum findTick(Datum xDatum, double direction, boolean minor) { // somehow tickv.minor is set to non-zero, and Axis.findTick gets messed up. // This is a work-around... return xDatum; } - @Override + @Override public void updateTickV() { //super.updateTickV(); updateTickPositions(); } - @Override + @Override public TickVDescriptor getTickV() { TickVDescriptor result = new TickVDescriptor(); result.units = getUnits(); @@ -149,7 +141,7 @@ public TickVDescriptor getTickV() { return result; } - @Override + @Override public double transform(double value, Units units) { if (units != this.labelUnits) { throw new IllegalArgumentException("units don't match"); @@ -184,17 +176,12 @@ private int findClosestIndex(double[] data, double searchFor) { return iclose; } - @Override + @Override public Datum invTransform(double d) { int iclose = findClosestIndex(labelPositions, (int) d); return labels.get(iclose); } - /** - * override this to allow a single Datum. - * @param dr - * @return - */ @Override protected boolean rangeIsAcceptable(DatumRange dr) { return true; @@ -214,11 +201,6 @@ public int getInterItemSpace() { return (int) Math.abs(transform(labels.get(1)) - transform(labels.get(0))); } - /** - * get the minimum pixel location of the bin allocated to the Datum. - * @param d - * @return pixel location of the min. - */ public int getItemMin(Datum d) { Units units = d.getUnits(); double value = d.doubleValue(units); @@ -229,11 +211,6 @@ public int getItemMin(Datum d) { return tickPosition - w / 2; } - /** - * get the maximum pixel location of the bin allocated to the Datum. - * @param d - * @return pixel location of the max. - */ public int getItemMax(Datum d) { int w = getInterItemSpace(); return getItemMin(d) + w; @@ -244,11 +221,12 @@ public DasAxis createAttachedAxis(DasRow row, DasColumn column) { return result; } - @Override + @Override public DasAxis createAttachedAxis(int orientation) { return new DasLabelAxis(labels, getDataRange(), orientation); } + @Override public void update(DasUpdateEvent e) { double minimum = getDataRange().getMinimum(); double maximum = getDataRange().getMaximum(); @@ -284,6 +262,9 @@ protected void paintHorizontalAxis(java.awt.Graphics2D g) { Font labelFont = getTickLabelFont(); + double dataMax = dataRange.getMaximum(); + double dataMin = dataRange.getMinimum(); + TickVDescriptor ticks = getTickV(); if (bottomTicks) { @@ -294,6 +275,7 @@ protected void paintHorizontalAxis(java.awt.Graphics2D g) { } int tickLengthMajor = labelFont.getSize() * 2 / 3; + int tickLengthMinor = tickLengthMajor / 2; int tickLength; String[] llabels= tickFormatter( ticks.tickV, getDatumRange() ); @@ -363,6 +345,9 @@ protected void paintVerticalAxis(java.awt.Graphics2D g) { Font labelFont = getTickLabelFont(); + double dataMax = dataRange.getMaximum(); + double dataMin = dataRange.getMinimum(); + TickVDescriptor ticks = getTickV(); if (leftTicks) { @@ -373,12 +358,14 @@ protected void paintVerticalAxis(java.awt.Graphics2D g) { } int tickLengthMajor = labelFont.getSize() * 2 / 3; + int tickLengthMinor = tickLengthMajor / 2; int tickLength; - String[] llabels= tickFormatter( labels, getDatumRange() ); + String[] llabels= tickFormatter( ticks.tickV, getDatumRange() ); for (int i = 0; i < ticks.tickV.getLength(); i++) { Datum datum = ticks.tickV.get(i); + int w = getInterItemSpace(); int tickPosition = (getItemMax(datum) + getItemMin(datum)) / 2 - g.getFontMetrics().getAscent() / 5; if (getRow().contains(tickPosition)) { tickLength = tickLengthMajor; @@ -470,7 +457,7 @@ public void setFloppyItemSpacing(boolean floppyItemSpacing) { update(); } - @Override + @Override public java.awt.geom.AffineTransform getAffineTransform(Memento memento, java.awt.geom.AffineTransform at) { return at; //equals doesn't seem to work diff --git a/dasCore/src/org/das2/graph/DasNumberFormatter.java b/dasCore/src/org/das2/graph/DasNumberFormatter.java index 382f64a1b..ecdebe9da 100644 --- a/dasCore/src/org/das2/graph/DasNumberFormatter.java +++ b/dasCore/src/org/das2/graph/DasNumberFormatter.java @@ -49,25 +49,25 @@ public static void main(String[] args) { dFormatter = createDF(b); for(int i=0; i-1 ) { - r= (Renderer) renderers.get(ir); - } - if ( r==null ) { - for ( int i=renderers.size()-1; i>=0; i-- ) { - if ( renderers.get(i).isActive()==false ) { - r= renderers.get(i); - break; + if (editRendererMenuItem != null) { + //TODO: check out SwingUtilities, I think this is wrong: + int ir = findRendererAt(getX() + e.getX(), getY() + e.getY()); + editRendererMenuItem.setText("Renderer Properties"); + if (ir > -1) { + editRendererMenuItem.setEnabled(true); + Renderer r = renderers.get(ir); + if (r instanceof Displayable) { + Displayable d = (Displayable) r; + editRendererMenuItem.setIcon(d.getListIcon()); + } else { + editRendererMenuItem.setIcon(null); } + setFocusRenderer(r); + } else { + editRendererMenuItem.setEnabled(false); + editRendererMenuItem.setIcon(null); + setFocusRenderer(null); } } - setFocusRenderer(r); - } - if (editRendererMenuItem != null) { - //TODO: check out SwingUtilities, I think this is wrong: - editRendererMenuItem.setText("Renderer Properties"); - if ( ir>-1 && r!=null ) { - editRendererMenuItem.setEnabled(true); - editRendererMenuItem.setIcon(r.getListIcon()); - } else { - editRendererMenuItem.setEnabled(false); - editRendererMenuItem.setIcon(null); - } - } - //} + //} } }); @@ -225,7 +167,6 @@ public void mousePressed(MouseEvent e) { xAxis.addPropertyChangeListener("dataMaximum", rebinListener); xAxis.addPropertyChangeListener(DasAxis.PROPERTY_DATUMRANGE, rebinListener); xAxis.addPropertyChangeListener("log", rebinListener); - xAxis.addPropertyChangeListener(DasAxis.PROP_FLIPPED,rebinListener); xAxis.addPropertyChangeListener(DasAxis.PROPERTY_TICKS, ticksListener); } this.yAxis = yAxis; @@ -237,8 +178,7 @@ public void mousePressed(MouseEvent e) { yAxis.addPropertyChangeListener("dataMaximum", rebinListener); yAxis.addPropertyChangeListener(DasAxis.PROPERTY_DATUMRANGE, rebinListener); yAxis.addPropertyChangeListener("log", rebinListener); - yAxis.addPropertyChangeListener(DasAxis.PROP_FLIPPED,rebinListener); - yAxis.addPropertyChangeListener(DasAxis.PROPERTY_TICKS, ticksListener); + xAxis.addPropertyChangeListener(DasAxis.PROPERTY_TICKS, ticksListener); } if (!"true".equals(DasApplication.getProperty("java.awt.headless", "false"))) { @@ -250,387 +190,141 @@ public void mousePressed(MouseEvent e) { * returns the Renderer with the current focus. Clicking on a trace sets the focus. */ protected Renderer focusRenderer = null; - - /** - * property name for the current renderer that the operator has selected. - */ public static final String PROP_FOCUSRENDERER = "focusRenderer"; - /** - * get the current renderer that the operator has selected. - * @return the current renderer that the operator has selected. - */ public Renderer getFocusRenderer() { return focusRenderer; } - /** - * set the current renderer that the operator has selected. - * @param focusRenderer the current renderer that the operator has selected. - */ public void setFocusRenderer(Renderer focusRenderer) { Renderer oldFocusRenderer = this.focusRenderer; this.focusRenderer = focusRenderer; - firePropertyChange(PROP_FOCUSRENDERER, null, focusRenderer); - //firePropertyChange(PROP_FOCUSRENDERER, oldFocusRenderer, focusRenderer); - } - - /** - * for multiline labels, the horizontal alignment, where 0 is left, 0.5 is center, and 1.0 is right. - */ - private float multiLineTextAlignment = 0.f; - - /** - * property name for multiline labels, the horizontal alignment, where 0 is left, 0.5 is center, and 1.0 is right. - */ - public static final String PROP_MULTILINETEXTALIGNMENT = "multiLineTextAlignment"; - - /** - * get the horizontal alignment for multiline labels, where 0 is left, 0.5 is center, and 1.0 is right. - * @return the alignment - */ - public float getMultiLineTextAlignment() { - return multiLineTextAlignment; - } - - /** - * set the horizontal alignment for multiline labels, where 0 is left, 0.5 is center, and 1.0 is right. - * @param multiLineTextAlignment the alignment - */ - public void setMultiLineTextAlignment(float multiLineTextAlignment) { - float oldMultiLineTextAlignment = this.multiLineTextAlignment; - this.multiLineTextAlignment = multiLineTextAlignment; - firePropertyChange(PROP_MULTILINETEXTALIGNMENT, oldMultiLineTextAlignment, multiLineTextAlignment); - repaint(); + firePropertyChange(PROP_FOCUSRENDERER, oldFocusRenderer, focusRenderer); } - - private static final Icon NULL_ICON= new ImageIcon(new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB) ); - - - /** - * returns the bounds of the legend, or null if there is no legend. - * TODO: merge with drawLegend, so there are not two similar codes. - * @param graphics graphics context - * @param msgx the location of the box - * @param msgy the location of the box - * @param llegendElements the elements - * @return the bounds - */ - private Rectangle getLegendBounds( Graphics2D graphics, int msgx, int msgy, List llegendElements) { - int maxIconWidth = 0; - Rectangle mrect; - Rectangle boundRect=null; + private void drawLegend(Graphics2D graphics) { int em = (int) getEmSize(); - - if ( llegendElements==null ) return null; - if ( graphics==null ) return null; - - String contextStr= this.context==null ? "" : this.context.toString(); - for (LegendElement le : llegendElements) { - if ( ( le.renderer!=null && le.renderer.isActive() ) || le.icon!=null || drawInactiveInLegend ) { - Icon icon= le.icon!=null ? le.icon : le.renderer.getListIcon(); - if ( icon==null ) icon=NULL_ICON; - GrannyTextRenderer gtr = new GrannyTextRenderer(); - String theLabel= String.valueOf(le.label).trim().replaceAll("%\\{CONTEXT\\}",contextStr); - gtr.setString(graphics, theLabel); // protect from nulls, which seems to happen - mrect = gtr.getBounds(); - maxIconWidth = Math.max(maxIconWidth, icon.getIconWidth()); - if ( reluctantLegendIcons ) { - if ( llegendElements.size()==1 ) { - maxIconWidth = 0; - } - } - int theheight = Math.max(mrect.height, icon.getIconHeight()); - mrect.translate(msgx, msgy + (int) gtr.getAscent() ); - mrect.height= theheight; - if (boundRect == null) { - boundRect = mrect; - } else { - boundRect.add(mrect); - } - msgy += theheight; - } - } - - if ( boundRect==null ) return null; - int iconColumnWidth = maxIconWidth + em / 4; - mrect = new Rectangle(boundRect); - mrect.width += iconColumnWidth; - if ( legendPosition==LegendPosition.NE || legendPosition==LegendPosition.NW ) { - mrect.y= yAxis.getRow().getDMinimum() + em/2; - if ( legendPosition==LegendPosition.NE ) { - mrect.x = xAxis.getColumn().getDMaximum() - em - mrect.width; - - } else if ( legendPosition==LegendPosition.NW ) { - mrect.x = xAxis.getColumn().getDMinimum() + em ; - } - } else if ( legendPosition==LegendPosition.SE || legendPosition==LegendPosition.SW ) { - mrect.y= yAxis.getRow().getDMaximum() - boundRect.height - em; // note em not em/2 is intentional - if ( legendPosition==LegendPosition.SE ) { - mrect.x = xAxis.getColumn().getDMaximum() - em - mrect.width; - - } else if ( legendPosition==LegendPosition.SW ) { - mrect.x = xAxis.getColumn().getDMinimum() + em ; - } - - } else if ( legendPosition==LegendPosition.OutsideNE ) { - mrect.x = xAxis.getColumn().getDMaximum() + em + maxIconWidth; - boundRect.x = mrect.x; - mrect.y= yAxis.getRow().getDMinimum(); // em/5 determined by experiment. - - } else { - throw new IllegalArgumentException("not supported: "+legendPosition); - } - - Rectangle axisBounds= DasDevicePosition.toRectangle( getRow(), getColumn() ); - axisBounds.width= Math.max( axisBounds.width, mrect.x+mrect.width-axisBounds.x ); // don't limit width because of outside NE - Rectangle2D rr= mrect.createIntersection(axisBounds); - - return new Rectangle( (int)rr.getX(),(int)rr.getY(),(int)rr.getWidth(),(int)rr.getHeight() ); - } - - private void drawLegend(Graphics2D g, List llegendElements ) { - - Graphics2D graphics= (Graphics2D) g.create(); - - graphics.setFont( getFont().deriveFont( getFont().getSize2D() + legendRelativeFontSize ) ); - - int em; int msgx, msgy; Color backColor = GraphUtil.getRicePaperColor(); + Rectangle boundRect = null; Rectangle mrect; em = (int) getEmSize(); msgx = xAxis.getColumn().getDMiddle() + em; - msgy = yAxis.getRow().getDMinimum() + em/2; - - int maxIconWidth= 0; - for (LegendElement le : llegendElements) { - Icon icon= le.icon!=null ? le.icon : le.renderer.getListIcon(); - if ( icon==null ) icon=NULL_ICON; - maxIconWidth = Math.max(maxIconWidth, icon.getIconWidth()); - if ( reluctantLegendIcons ) { - if ( llegendElements.size()==1 ) { - maxIconWidth = 0; - } + msgy = yAxis.getRow().getDMinimum() + em; + + int maxIconWidth = 0; + for (int i = 0; i < legendElements.size(); i++) { + LegendElement le = legendElements.get(i); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(graphics, String.valueOf(le.label)); // protect from nulls, which seems to happen + mrect = gtr.getBounds(); + mrect.translate(msgx, msgy); + maxIconWidth = Math.max(maxIconWidth, le.icon.getIconWidth()); + mrect.height = Math.max(mrect.height, le.icon.getIconHeight()); + if (boundRect == null) { + boundRect = mrect; + } else { + boundRect.add(mrect); } + msgy += mrect.getHeight(); } - mrect= getLegendBounds(graphics,msgx,msgy, llegendElements); - if ( mrect==null ) return; // nothing is active - - msgx= mrect.x; - msgy= mrect.y; - if ( legendPosition!=LegendPosition.OutsideNE ) { - msgx+= maxIconWidth + em/4; - } + mrect = new Rectangle(boundRect); + int iconColumnWidth = maxIconWidth + em / 4; + mrect.width += iconColumnWidth; + mrect.x = xAxis.getColumn().getDMaximum() - em - mrect.width; + boundRect.x= mrect.x; - if ( legendPosition!=LegendPosition.OutsideNE ) { - graphics.setColor(backColor); - graphics.fillRoundRect(mrect.x - em / 4, mrect.y - em/4, mrect.width + em / 2, mrect.height + em/2, 5, 5); - graphics.setColor(getForeground()); - graphics.drawRoundRect(mrect.x - em / 4, mrect.y - em/4, mrect.width + em / 2, mrect.height + em/2, 5, 5); - } + graphics.setColor(backColor); + graphics.fillRoundRect( mrect.x - em / 4 , mrect.y, mrect.width + em / 2, mrect.height, 5, 5); + graphics.setColor(getForeground()); + graphics.drawRoundRect( mrect.x - em / 4 , mrect.y, mrect.width + em / 2, mrect.height, 5, 5); - String contextStr= this.context==null ? "" : this.context.toString(); - for (LegendElement le : llegendElements) { - if ( ( le.renderer!=null && le.renderer.isActive() ) || le.icon!=null || drawInactiveInLegend ) { - Icon icon= le.icon!=null ? le.icon : le.renderer.getListIcon(); - if ( icon==null ) icon=NULL_ICON; - if ( llegendElements.size()==1 && reluctantLegendIcons ) { - icon = NULL_ICON; - } - GrannyTextRenderer gtr = new GrannyTextRenderer(); - gtr.setAlignment( GrannyTextRenderer.LEFT_ALIGNMENT ); - String theLabel= String.valueOf(le.label).trim().replaceAll("%\\{CONTEXT\\}",contextStr); - gtr.setString(graphics, theLabel); // protect from nulls, which seems to happen - mrect = gtr.getBounds(); - mrect.translate(msgx, msgy + (int) gtr.getAscent()); - int theheight= Math.max(mrect.height, icon.getIconHeight()); - int icony= theheight/2 - icon.getIconHeight() / 2; // from top of rectangle - int texty= theheight/2 - (int)gtr.getHeight() / 2 + (int) gtr.getAscent(); - if ( reduceOutsideLegendTopMargin ) texty = theheight/2; - gtr.draw( graphics, msgx, msgy + texty ); - mrect.height = theheight; - Rectangle imgBounds = new Rectangle( - msgx - (icon.getIconWidth() + em / 4), - msgy + icony, - icon.getIconWidth(), - icon.getIconHeight() ); - if ( le.icon!=null ) { - graphics.drawImage( ((ImageIcon)icon).getImage(), imgBounds.x, imgBounds.y, null ); - } else { - if ( llegendElements.size()==1 && reluctantLegendIcons ) { - - } else { - le.drawIcon( graphics, msgx - (icon.getIconWidth() + em / 4), msgy + icony ); - } - } - msgy += mrect.getHeight(); - mrect.add(imgBounds); - if ( msgy > getRow().bottom() ) break; - le.bounds = mrect; - } - } + msgx = xAxis.getColumn().getDMaximum() - boundRect.width - em ; + msgy = yAxis.getRow().getDMinimum() + em; - graphics.dispose(); + for (int i = 0; i < legendElements.size(); i++) { + LegendElement le = legendElements.get(i); + GrannyTextRenderer gtr = new GrannyTextRenderer(); + gtr.setString(graphics, String.valueOf(le.label)); // protect from nulls, which seems to happen + mrect = gtr.getBounds(); + mrect.translate(msgx, msgy); + gtr.draw(graphics, msgx, msgy); + Rectangle imgBounds= new Rectangle( msgx - (le.icon.getIconWidth() + em / 4), + msgy - (int) (mrect.getHeight() / 2 + le.icon.getIconHeight() / 2), + le.icon.getIconWidth(), le.icon.getIconHeight() ); + graphics.drawImage( le.icon.getImage(), imgBounds.x, imgBounds.y, null ); + msgy += mrect.getHeight(); + mrect.add( imgBounds ); + le.bounds= mrect; + } } - - /** - * draw the message bubbles. For the printing thread, we no - * longer display the bubbles, unless the have Long.MAX_VALUE for the - * birthmilli. - * @param g the graphics context. - */ - private void drawMessages(Graphics2D g, List lmessages ) { - Graphics2D graphics= (Graphics2D) g.create(); - boolean isPrint= getCanvas().isPrintingThread(); - + private void drawMessages(Graphics2D graphics) { Font font0 = graphics.getFont(); int msgem = (int) Math.max(8, font0.getSize2D() / 2); graphics.setFont(font0.deriveFont((float) msgem)); int em = (int) getEmSize(); - - boolean rightJustify= false; + int msgx = xAxis.getColumn().getDMinimum() + em; int msgy = yAxis.getRow().getDMinimum() + em; - if ( legendPosition==LegendPosition.NW ) { - rightJustify= true; - msgx= xAxis.getColumn().getDMaximum() - em; - } - Color warnColor = new Color(255, 255, 100, 200); - Color severeColor = new Color(255, 140, 140, 200); - - List renderers1= Arrays.asList( getRenderers() ); + Color errorColor = new Color(255, 140, 140, 200); + for (int i = 0; i < messages.size(); i++) { + MessageDescriptor message = (MessageDescriptor) messages.get(i); - long tnow= System.currentTimeMillis(); - - boolean needRepaintSoon= false; - long repaintDelay= 0; - - for (MessageDescriptor lmessage : lmessages) { - MessageDescriptor message = (MessageDescriptor) lmessage; - if ( message.messageType
    - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dasCoreDatum/src/org/das2/datum/TimeTemplator.java b/dasCoreDatum/src/org/das2/datum/TimeTemplator.java deleted file mode 100644 index 6cf7ee32d..000000000 --- a/dasCoreDatum/src/org/das2/datum/TimeTemplator.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.das2.datum; - -import java.io.File; -import java.io.FilenameFilter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import javax.swing.JDialog; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; -import javax.swing.table.DefaultTableModel; - -/** - * Experiment with a tool for quickly creating time templates. This has - * a fair amount of work to be done, but I think it would be useful: - * 1. variable length fields need to be supported. - * 2. there's a bug where the indeces sent to the field picker are not adjusted for template lengths. - * 3. support for recognizing enums. - * 4. controls/columns added for enums in table. - * @author jbf - */ -public class TimeTemplator extends javax.swing.JPanel { - - private String[] formatted; - - /** - * Creates new form TimeTemplator - */ - public TimeTemplator() { - initComponents(); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - jScrollPane1 = new javax.swing.JScrollPane(); - jTable1 = new javax.swing.JTable(); - templateTextField = new javax.swing.JTextField(); - fieldTypeButton = new javax.swing.JButton(); - jButton1 = new javax.swing.JButton(); - - jTable1.setModel(new javax.swing.table.DefaultTableModel( - new Object [][] { - {null, null}, - {null, null}, - {null, null}, - {null, null} - }, - new String [] { - "Title 1", "Title 2" - } - )); - jScrollPane1.setViewportView(jTable1); - - templateTextField.setText("rte_2066281937_20151101_050235_jbf.xml"); - templateTextField.addMouseListener(new java.awt.event.MouseAdapter() { - public void mouseClicked(java.awt.event.MouseEvent evt) { - templateTextFieldMouseClicked(evt); - } - }); - - fieldTypeButton.setText("Field Type..."); - fieldTypeButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - fieldTypeButtonActionPerformed(evt); - } - }); - - jButton1.setText("Sort By"); - jButton1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton1ActionPerformed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 409, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(templateTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(fieldTypeButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 59, javax.swing.GroupLayout.PREFERRED_SIZE)) - ); - - layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {fieldTypeButton, jButton1}); - - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(templateTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(fieldTypeButton) - .addComponent(jButton1)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 275, Short.MAX_VALUE)) - ); - }// //GEN-END:initComponents - - private void templateTextFieldMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_templateTextFieldMouseClicked - if ( evt.getClickCount()==2 ) { - - } - }//GEN-LAST:event_templateTextFieldMouseClicked - - private void fieldTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fieldTypeButtonActionPerformed - int i0= templateTextField.getSelectionStart(); - int i1= templateTextField.getSelectionEnd(); - TimeTemplatorFieldPicker ttfp= new TimeTemplatorFieldPicker(); - ttfp.setFormatted(formatted); - ttfp.setTemplate(templateTextField.getText()); - ttfp.setSelection(i0,i1); - JDialog d= new JDialog((JDialog)SwingUtilities.getWindowAncestor(this),true); - d.getContentPane().add(ttfp); - d.pack(); - d.setVisible(true); - templateTextField.setText(ttfp.getTemplate()); - }//GEN-LAST:event_fieldTypeButtonActionPerformed - - private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - final int i0= templateTextField.getSelectionStart(); - final int i1= templateTextField.getSelectionEnd(); - int imax= Integer.MIN_VALUE; - int imin= Integer.MAX_VALUE; - if ( this.formatted!=null ) { - Map other= new LinkedHashMap(); - for ( String formatted1 : formatted ) { - String s = formatted1.substring(i0, i1); // extract the field - try { - int i= Integer.parseInt(s); - imin= Math.min( imin, i ); - imax= Math.max( imax, i ); - } catch ( NumberFormatException ex ) { - Integer i= other.get(s); - if ( i==null ) { - other.put( s, 1 ); - } else { - other.put( s, i+1 ); - } - } - } - - List sformatted= Arrays.asList(formatted); - Collections.sort( sformatted, new Comparator() { - @Override - public int compare(Object o1, Object o2) { - String s1= (String)o1; - String s2= (String)o2; - return s1.substring(i0,i1).compareTo(s2.substring(i0,i1) ); - } - } ); - - setFormattedTable( sformatted.toArray(new String[sformatted.size()]) ); - } - }//GEN-LAST:event_jButton1ActionPerformed - - private void setFormattedTable( String[] formatted ) { - DefaultTableModel dtm= new DefaultTableModel(formatted.length,2); - for ( int i=0; i - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    diff --git a/dasCoreDatum/src/org/das2/datum/TimeTemplatorFieldPicker.java b/dasCoreDatum/src/org/das2/datum/TimeTemplatorFieldPicker.java deleted file mode 100644 index 835c1f870..000000000 --- a/dasCoreDatum/src/org/das2/datum/TimeTemplatorFieldPicker.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.das2.datum; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import javax.swing.SwingUtilities; - -/** - * - * @author jbf - */ -public class TimeTemplatorFieldPicker extends javax.swing.JPanel { - private String[] formatted; - private String template; - private int selectionStart; - private int selectionEnd; - - /** - * Creates new form TimeTemplatorFieldPicker - */ - public TimeTemplatorFieldPicker() { - initComponents(); - } - - public void setFormatted( String[] formatted ) { - this.formatted= formatted; - } - - public void setTemplate( String template ) { - this.template= template; - this.templateTextField.setText(template); - } - - public String getTemplate() { - return this.template; - } - - public void setSelection( int i0, int i1 ) { - this.selectionStart= i0; - this.selectionEnd= i1; - int imax= Integer.MIN_VALUE; - int imin= Integer.MAX_VALUE; - if ( this.formatted!=null ) { - Map other= new LinkedHashMap(); - for ( String formatted1 : formatted ) { - String s = formatted1.substring(i0, i1); - try { - int i= Integer.parseInt(s); - imin= Math.min( imin, i ); - imax= Math.max( imax, i ); - } catch ( NumberFormatException ex ) { - Integer i= other.get(s); - if ( i==null ) { - other.put( s, 1 ); - } else { - other.put( s, i+1 ); - } - } - } - if ( other.isEmpty() && imax>Integer.MIN_VALUE ) { - statsLabel.setText( "Numbers vary from "+imin + " through " + imax + "."); - } else { - StringBuilder b= new StringBuilder(); - for ( Entry s: other.entrySet() ) { - b.append(s.getKey()); - b.append("(").append(s.getValue()).append(") "); - if ( b.length()>60 ) break; - } - statsLabel.setText( "Includes: "+b.toString() ); - } - statsLabel.setText("more"+statsLabel.getText()); - - } - this.templateTextField.setSelectionStart(selectionStart); - this.templateTextField.setSelectionEnd(selectionEnd); - } - - private void closeDialog() { - SwingUtilities.getWindowAncestor(this).setVisible(false); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - templateTextField = new javax.swing.JTextField(); - statsLabel = new javax.swing.JLabel(); - capyButton = new javax.swing.JButton(); - ybutton = new javax.swing.JButton(); - jLabel2 = new javax.swing.JLabel(); - jLabel3 = new javax.swing.JLabel(); - mButton = new javax.swing.JButton(); - jLabel4 = new javax.swing.JLabel(); - dButton = new javax.swing.JButton(); - jLabel5 = new javax.swing.JLabel(); - jButton = new javax.swing.JButton(); - jLabel6 = new javax.swing.JLabel(); - caphButton = new javax.swing.JButton(); - capmButton = new javax.swing.JButton(); - capsButton = new javax.swing.JButton(); - jLabel7 = new javax.swing.JLabel(); - jLabel8 = new javax.swing.JLabel(); - jLabel9 = new javax.swing.JLabel(); - jButton9 = new javax.swing.JButton(); - jLabel10 = new javax.swing.JLabel(); - jButton10 = new javax.swing.JButton(); - jLabel11 = new javax.swing.JLabel(); - capymdButton = new javax.swing.JButton(); - caphCapmCapsButton = new javax.swing.JButton(); - jLabel1 = new javax.swing.JLabel(); - jLabel12 = new javax.swing.JLabel(); - - templateTextField.setText("jTextField1"); - - statsLabel.setText("jLabel1"); - - capyButton.setText("$Y"); - capyButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - fieldButtonActionPerformed(evt); - } - }); - - ybutton.setText("$y"); - ybutton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - fieldButtonActionPerformed(evt); - } - }); - - jLabel2.setText("Four-Digit Year"); - - jLabel3.setText("Two-Digit Year"); - - mButton.setText("$m"); - mButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - fieldButtonActionPerformed(evt); - } - }); - - jLabel4.setText("Month"); - - dButton.setText("$d"); - dButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - fieldButtonActionPerformed(evt); - } - }); - - jLabel5.setText("Day of Month"); - - jButton.setText("$j"); - jButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - fieldButtonActionPerformed(evt); - } - }); - - jLabel6.setText("Day of Year"); - - caphButton.setText("$H"); - caphButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - fieldButtonActionPerformed(evt); - } - }); - - capmButton.setText("$M"); - capmButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - fieldButtonActionPerformed(evt); - } - }); - - capsButton.setText("$S"); - capsButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - fieldButtonActionPerformed(evt); - } - }); - - jLabel7.setText("Hour of day"); - - jLabel8.setText("Minute of hour"); - - jLabel9.setText("Second of hour"); - - jButton9.setText("$x"); - jButton9.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - fieldButtonActionPerformed(evt); - } - }); - - jLabel10.setText("Ignore"); - - jButton10.setText("$v"); - - jLabel11.setText("Version Number"); - - capymdButton.setText("$Y$m$d"); - capymdButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - capymdButtonActionPerformed(evt); - } - }); - - caphCapmCapsButton.setText("$H$M$S"); - caphCapmCapsButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - caphCapmCapsButtonActionPerformed(evt); - } - }); - - jLabel1.setText("Guess date"); - - jLabel12.setText("Guess time"); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(templateTextField) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(statsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(capyButton, javax.swing.GroupLayout.PREFERRED_SIZE, 57, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel2)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(dButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(mButton, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel4) - .addComponent(jLabel5))) - .addGroup(layout.createSequentialGroup() - .addComponent(jButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel6))) - .addGap(50, 50, 50) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(capmButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel8)) - .addGroup(layout.createSequentialGroup() - .addComponent(caphButton, javax.swing.GroupLayout.PREFERRED_SIZE, 43, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel7)) - .addGroup(layout.createSequentialGroup() - .addGap(27, 27, 27) - .addComponent(ybutton, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel3)) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(layout.createSequentialGroup() - .addComponent(caphCapmCapsButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel12) - .addGap(17, 17, 17)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(capymdButton, javax.swing.GroupLayout.PREFERRED_SIZE, 78, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel1))) - .addGroup(layout.createSequentialGroup() - .addComponent(capsButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel9)))) - .addGroup(layout.createSequentialGroup() - .addComponent(jButton9) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel10)) - .addGroup(layout.createSequentialGroup() - .addComponent(jButton10) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel11))) - .addGap(0, 24, Short.MAX_VALUE))) - .addContainerGap()) - ); - - layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {dButton, jButton, jButton10, jButton9, mButton}); - - layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {caphButton, capmButton, capsButton}); - - layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {caphCapmCapsButton, capymdButton}); - - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(templateTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(statsLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(capyButton) - .addComponent(ybutton) - .addComponent(jLabel2) - .addComponent(jLabel3)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(mButton) - .addComponent(jLabel4) - .addComponent(caphButton) - .addComponent(jLabel7)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(dButton) - .addComponent(jLabel5) - .addComponent(capmButton) - .addComponent(jLabel8)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton) - .addComponent(jLabel6) - .addComponent(capsButton) - .addComponent(jLabel9)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 22, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton10) - .addComponent(jLabel11) - .addComponent(capymdButton) - .addComponent(jLabel1)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton9) - .addComponent(jLabel10) - .addComponent(caphCapmCapsButton) - .addComponent(jLabel12)) - .addContainerGap()) - ); - }// //GEN-END:initComponents - - private void fieldButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fieldButtonActionPerformed - String field= evt.getActionCommand(); - template= template.substring(0,selectionStart) + field+ template.substring(selectionEnd); - closeDialog(); - }//GEN-LAST:event_fieldButtonActionPerformed - - private void capymdButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_capymdButtonActionPerformed - int nc= selectionEnd-selectionStart; - String field; - if ( nc==4 ) { - field= "$Y"; - } else if ( nc==5 ) { - field= "$y$j"; - } else if ( nc==6 ) { - field= "$Y$m"; - } else if ( nc==7 ) { - field= "$Y$j"; - } else if ( nc==8 ) { - field= "$Y$m$d"; - } else { - field= "$Y$m$d"; - } - template= template.substring(0,selectionStart) + field+ template.substring(selectionEnd); - closeDialog(); - }//GEN-LAST:event_capymdButtonActionPerformed - - private void caphCapmCapsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_caphCapmCapsButtonActionPerformed - int nc= selectionEnd-selectionStart; - String field; - if ( nc==4 ) { - field= "$H$M"; - } else if ( nc==6 ) { - field= "$H$M$S"; - } else { - field= "$H$M$S"; - } - template= template.substring(0,selectionStart) + field+ template.substring(selectionEnd); - closeDialog(); - }//GEN-LAST:event_caphCapmCapsButtonActionPerformed - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton caphButton; - private javax.swing.JButton caphCapmCapsButton; - private javax.swing.JButton capmButton; - private javax.swing.JButton capsButton; - private javax.swing.JButton capyButton; - private javax.swing.JButton capymdButton; - private javax.swing.JButton dButton; - private javax.swing.JButton jButton; - private javax.swing.JButton jButton10; - private javax.swing.JButton jButton9; - private javax.swing.JLabel jLabel1; - private javax.swing.JLabel jLabel10; - private javax.swing.JLabel jLabel11; - private javax.swing.JLabel jLabel12; - private javax.swing.JLabel jLabel2; - private javax.swing.JLabel jLabel3; - private javax.swing.JLabel jLabel4; - private javax.swing.JLabel jLabel5; - private javax.swing.JLabel jLabel6; - private javax.swing.JLabel jLabel7; - private javax.swing.JLabel jLabel8; - private javax.swing.JLabel jLabel9; - private javax.swing.JButton mButton; - private javax.swing.JLabel statsLabel; - private javax.swing.JTextField templateTextField; - private javax.swing.JButton ybutton; - // End of variables declaration//GEN-END:variables - -} diff --git a/dasCoreDatum/src/org/das2/datum/TimeUtil.java b/dasCoreDatum/src/org/das2/datum/TimeUtil.java deleted file mode 100755 index 14e22801c..000000000 --- a/dasCoreDatum/src/org/das2/datum/TimeUtil.java +++ /dev/null @@ -1,1441 +0,0 @@ -/* File: TimeUtil.java - * Copyright (C) 2002-2003 The University of Iowa - * - * Created on September 22, 2003, 11:00 AM by Jeremy Faden - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum; - -import java.util.*; - -import java.text.ParseException; -import org.das2.datum.format.TimeDatumFormatter; - -/** - * Various time utilities - * @author jbf - */ -public final class TimeUtil { - - private TimeUtil() { - } - - private final static int[][] daysInMonth = { - {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}, - {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0} - }; - - private final static int[][] dayOffset = { - {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, - {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} - }; - - public static int daysInMonth(int month, int year) { - return daysInMonth[isLeapYear(year)?1:0][month]; - } - - /** - * calculation of julianDay based on http://www.imcce.fr/en/grandpublic/temps/jour_julien.php - * This is slightly slower because of a cusp at 1582, but is accurate - * before these times. - * @param YY Gregorian year - * @param MM Gregorian month - * @param DD Gregorian day - * @return - */ - public static int julianDayIMCCE( int YY, int MM, int DD ) { - int GGG = 1; - if( YY < 1582 ) GGG = 0; - if( YY <= 1582 && MM < 10 ) GGG = 0; - if( YY <= 1582 && MM == 10 && DD < 5 ) GGG = 0; - int JD = -1 * (7 * ( ((MM + 9) / 12) + YY) / 4); - int S = 1; - if ((MM - 9) < 0) S = -1; - int A = Math.abs(MM - 9); - int J1 = (YY + S * (A / 7)); - J1 = -1 * (((J1 / 100) + 1) * 3 / 4); - JD = JD + (275 * MM / 9) + DD + (GGG * J1); - JD = JD + 1721027 + 2 * GGG + 367 * YY; - return JD; - } - - /** - * return the julianDay for the year month and day. This was verified - * against another calculation (julianDayWP, commented out above) from - * http://en.wikipedia.org/wiki/Julian_day. Both calculations have 20 operations. - * Note this does not match - * @see julianToGregorian - * @param year calendar year greater than 1582. - * @param month - * @param day day of month. For day of year, use month=1 and doy for day. - * @return the Julian day - */ - public static int julianDay( int year, int month, int day ) { - if ( year<=1582 ) { - throw new IllegalArgumentException("year must be more than 1582"); - } - int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - - 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + - 275 * month / 9 + day + 1721029; - return jd; - } - - /** - * return the day of year for the month and day, for the given year - * @param month the month, january=1, february=2, etc. - * @param day day of month - * @param year four-digit year - * @return the day of year - */ - public static int dayOfYear( int month, int day, int year ) { - return day + dayOffset[isLeapYear(year)?1:0][month]; - } - - - public final static class TimeStruct { - /** - * year containing the time datum - */ - public int year; - /** - * month containing the time datum - */ - public int month; - /** - * day of month containing the time datum - */ - public int day; - /** - * day of year containing the time datum - */ - public int doy; - /** - * hour containing the time datum - */ - public int hour; - /** - * minute containing the time datum - */ - public int minute; - /** - * seconds since the last minute boundary of the time datum - */ - public double seconds; // remaining number of seconds past minute boundary - - /** - * additional milliseconds since minute boundary - */ - public int millis; - - /** - * additional microseconds since minute boundary - */ - public int micros; - - public boolean isLocation= false; - - @Override - public String toString() { - if ( isLocation ) { - int dayOfYear= dayOfYear( month, day, year ); - return String.format( "%4d/%02d/%02d %02d:%02d:%06.3f (doy=%03d)", year,month,day,hour,minute,seconds+millis/1000.+micros/1000000.,dayOfYear ); - } else { - int intSeconds= (int)seconds; - int nanos= (int)( 1000000000 * ( seconds - intSeconds ) ); - nanos+=micros*1000; - nanos+=millis*1000000; - return DatumRangeUtil.formatISO8601Duration( new int[] { year,month,day,hour,minute,intSeconds,nanos } ); //TODO: test this. - } - } - public boolean[] want; - - public TimeStruct copy() { - TimeStruct result= new TimeStruct(); - result.year= this.year; - result.month= this.month; - result.day= this.day; - result.hour= this.hour; - result.minute= this.minute; - result.seconds= this.seconds; - result.millis= this.millis; - result.micros= this.micros; - result.isLocation= this.isLocation; - return result; - } - - public TimeStruct add( TimeStruct offset ) { - if ( offset.isLocation && this.isLocation ) throw new IllegalArgumentException("can't add two times!"); - TimeStruct result= new TimeStruct(); - - result.year= this.year + offset.year; - result.month= this.month + offset.month; - result.day= this.day + offset.day; - result.hour= this.hour + offset.hour; - result.minute= this.minute + offset.minute; - result.seconds= this.seconds + offset.seconds; - result.millis= this.millis + offset.millis ; - result.micros= this.micros + offset.micros ; - - result.isLocation= this.isLocation || offset.isLocation; - - return result; - } - } - - public static final int YEAR = 1; - public static final int MONTH = 2; - public static final int DAY = 3; - public static final int HOUR = 4; - public static final int MINUTE = 5; - public static final int SECOND = 6; - public static final int MILLI= 7; - public static final int MICRO = 8; - public static final int NANO = 9; - public static final int WEEK = 97; - public static final int QUARTER = 98; - public static final int HALF_YEAR= 99; - - // introduced to aid in debugging. - public static class TimeDigit { - int ordinal; // YEAR, MONTH, etc. - String label; - int divisions; // approximate - private static final TimeDigit[] digits= new TimeDigit[10]; - @Override - public String toString(){ - return label; - } - private TimeDigit( int ordinal, String label, int divisions ) { - this.ordinal= ordinal; - this.label= label; - this.divisions= divisions; - digits[ordinal]= this; - } - public int getOrdinal() { - return ordinal; - } - public int divisions() { - return divisions; - } - public static TimeDigit fromOrdinal( int ordinal ) { - return digits[ordinal]; - } - } - - public static final TimeDigit TD_YEAR = new TimeDigit( 1, "YEAR", 12 ); - public static final TimeDigit TD_MONTH = new TimeDigit( 2, "MONTH", 30 ); - public static final TimeDigit TD_DAY = new TimeDigit( 3, "DAY", 24 ); - public static final TimeDigit TD_HOUR = new TimeDigit( 4, "HOUR", 60 ); - public static final TimeDigit TD_MINUTE = new TimeDigit( 5, "MINUTE", 60 ); - public static final TimeDigit TD_SECOND = new TimeDigit( 6, "SECOND", 1000 ); - public static final TimeDigit TD_MILLI= new TimeDigit( 7, "MILLISECONDS", 1000 ); - public static final TimeDigit TD_MICRO = new TimeDigit( 8, "MICROSECONDS", 1000 ); - public static final TimeDigit TD_NANO = new TimeDigit( 9, "NANOSECONDS", 1000 ); - - /** - * return the number of seconds elapsed since the last midnight. - * @param datum - * @return - */ - public static double getSecondsSinceMidnight(Datum datum) { - double xx= datum.doubleValue(Units.t2000); - if (xx<0) { - xx= xx % 86400; - if (xx==0) { - return 0; - } else { - return 86400+xx; - } - } else { - return xx % 86400; - } - } - - /** - * return the number of microseconds elapsed since the last midnight. - * @param datum - * @return - */ - public static double getMicroSecondsSinceMidnight(Datum datum) { - double xx= datum.doubleValue( Units.us2000 ); - if (xx<0) { - xx= xx % 86400e6; - if (xx==0) { - return 0; - } else { - return 86400e6+xx; - } - } else { - return xx % 86400e6; - } - } - - /** - * return the the integer number of days that have elapsed since roughly Monday, January 1, 4713 BC. Julian Day - * is defined as starting at noon UT, here is is defined starting at midnight. - * - * @param datum - * @return - */ - public static int getJulianDay( Datum datum ) { - double xx= datum.doubleValue(Units.mj1958); - return (int)Math.floor( xx ) + 2436205; - } - - /** - * return the the integer number of days that have elapsed since roughly Monday, January 1, 4713 BC. Julian Day - * is defined as starting at noon UT, here is is defined starting at midnight. - * - * @param val the magnitude - * @param units Units object identifying the units of the val, such as Units.us2000. - * @return - */ - public static int getJulianDay( long val, Units units ) { - if ( units==Units.us2000 ) { - return (int)( val / 86400000000L ) + 15340 + 2436205; - } else if ( units==Units.t2000 ) { - return (int)( val / 86400L ) + 15340 + 2436205; - } else if ( units==Units.mj1958 ) { - return (int)( val ) + 2436205; - } else { - UnitsConverter uc= units.getConverter(Units.mj1958); - return (int)Math.floor(uc.convert(val)) + 2436205; - } - } - - /** - * convert to Datum without regard to the type of unit used to represent time. - * This will use the canonical Units.us2000, which does not represent leap - * seconds. Note this may change. - * @param d the decomposed time. - * @return the Datum. - */ - public static Datum toDatum( TimeStruct d ) { - int year = (int)d.year; - int month = (int)d.month; - int day = (int)d.day; - int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - - 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + - 275 * month / 9 + day + 1721029; - double us2000= ( jd - 2451545 ) * 86400e6; // TODO: leap seconds - return Datum.create( d.hour * 3600.0e6 + d.minute * 60e6 + d.seconds * 1e6 + d.millis * 1000 + d.micros + us2000, Units.us2000 ); - } - - /** - * convert to a Datum with the given units. - * @param d the decomposed time - * @param u the target units. - * @return the Datum in the units specified. - */ - public static Datum toDatum( TimeStruct d, Units u ) { - int year = (int)d.year; - int month = (int)d.month; - int day = (int)d.day; - int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - - 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + - 275 * month / 9 + day + 1721029; - if ( u==Units.cdfTT2000 ) { - double us2000= ( jd - 2451545 ) * 86400e6; - double tt2000= Units.us2000.convertDoubleTo( Units.cdfTT2000, us2000 ); - Units.cdfTT2000.createDatum(tt2000); - Datum rtt2000= Datum.create( d.hour * 3600.0e9 + d.minute * 60e9 + d.seconds * 1e9 + d.millis * 1e6 + d.micros*1e3 + tt2000, Units.cdfTT2000 ); - return rtt2000; - } else if ( u!=Units.us2000 ) { // TODO: sub-optimal implementation... - double us2000= ( jd - 2451545 ) * 86400e6; - Datum rus2000= Datum.create( d.hour * 3600.0e6 + d.minute * 60e6 + d.seconds * 1e6 + d.millis * 1000 + d.micros + us2000, Units.us2000 ); - return rus2000.convertTo(u); - } else { - double us2000= ( jd - 2451545 ) * 86400e6; // TODO: leap seconds - return Datum.create( d.hour * 3600.0e6 + d.minute * 60e6 + d.seconds * 1e6 + d.millis * 1000 + d.micros + us2000, Units.us2000 ); - } - } - - - - /** - *Break the Julian day apart into month, day year. This is based on - *http://en.wikipedia.org/wiki/Julian_day (GNU Public License), and - *was introduced when toTimeStruct failed when the year was 1886. - *@see julianDay( int year, int mon, int day ) - *@param julian the (integer) number of days that have elapsed since the initial epoch at noon Universal Time (UT) Monday, January 1, 4713 BC - *@return a TimeStruct with the month, day and year fields set. - */ - public static TimeStruct julianToGregorian( int julian ) { - int j = julian + 32044; - int g = j / 146097; - int dg = j % 146097; - int c = (dg / 36524 + 1) * 3 / 4; - int dc = dg - c * 36524; - int b = dc / 1461; - int db = dc % 1461; - int a = (db / 365 + 1) * 3 / 4; - int da = db - a * 365; - int y = g * 400 + c * 100 + b * 4 + a; - int m = (da * 5 + 308) / 153 - 2; - int d = da - (m + 4) * 153 / 5 + 122; - int Y = y - 4800 + (m + 2) / 12; - int M = (m + 2) % 12 + 1; - int D = d + 1; - TimeStruct result= new TimeStruct(); - result.year= Y; - result.month= M; - result.day= D; - result.isLocation= true; - return result; - } - - /** - * splits the time location datum into y,m,d,etc components. Note that - * seconds is a double, and micros will be 0. - * @param datum with time location units. - * @return TimeStruct containing the time components. - */ - public static TimeStruct toTimeStruct( Datum datum ) { - Units u= datum.getUnits(); - double d= datum.doubleValue(u); - - int mjd1958= (int)datum.doubleValue( Units.mj1958 ); - if ( mjd1958 < -714781 ) { // year 0001 - throw new IllegalArgumentException( "invalid time: mjd1958="+mjd1958 ); - } - if ( mjd1958 > 2937613 ) { // year 9999 - throw new IllegalArgumentException( "invalid time: mjd1958="+mjd1958 ); - } - double midnight= Units.mj1958.convertDoubleTo( u, mjd1958 ); - double sinceMidnight= d-midnight; - - if ( u==Units.cdfTT2000 && sinceMidnight<0.0 ) { - mjd1958= mjd1958-1; - sinceMidnight= sinceMidnight+86401e9; - } - - int jd= 2436205 + mjd1958; - double nanoseconds= u.getOffsetUnits().convertDoubleTo( Units.nanoseconds, sinceMidnight ); - - if ( jd<0 ) { - throw new IllegalArgumentException("julian day is negative."); - } - - if ( nanoseconds<0 ) { - jd= jd-1; - nanoseconds += 86400e9; // no leap - } - - if ( nanoseconds>=86400e9 && u!=Units.cdfTT2000 ) { - jd= jd+1; - nanoseconds -= 86400e9; // no leap - } - - TimeStruct result= julianToGregorian( jd ); - - int hour = (int)(nanoseconds/3600.0e9); - if ( hour>23 ) hour= 23; - int minute = (int)((nanoseconds - hour*3600.0e9)/60.0e9); - if ( minute>59 ) minute= 59; - double justNanoSeconds = nanoseconds - hour*3600.0e9 - minute*60.0e9; - - result.doy = dayOfYear(result.month, result.day, result.year); - result.hour= hour; - result.minute= minute; - result.seconds= justNanoSeconds / 1e9; - - result.isLocation= true; - - return result; - } - - public static TimeStruct add( TimeStruct a, TimeStruct b ) { - if ( b.year>1000 && a.year>1000 ) { - throw new IllegalArgumentException("cannot add more than 1000 years at a time. Did you attempt to add two time locations?"); - } - TimeStruct result= new TimeStruct(); - result.year= a.year + b.year; - result.month= a.month + b.month; - result.day= a.day + b.day; - result.doy= a.doy + b.doy; - result.hour= a.hour + b.hour; - result.minute= a.minute + b.minute; - result.seconds= a.seconds + b.seconds; - result.millis= a.millis + b.millis; - result.micros= a.micros + b.micros; - result.isLocation= a.isLocation || b.isLocation; - return result; - } - - public static TimeStruct subtract( TimeStruct a, TimeStruct b ) { - TimeStruct result= new TimeStruct(); - result.year= a.year - b.year; - result.month= a.month - b.month; - result.day= a.day - b.day; - result.doy= a.doy - b.doy; - result.hour= a.hour - b.hour; - result.minute= a.minute - b.minute; - result.seconds= a.seconds - b.seconds; - result.millis= a.millis - b.millis; - result.micros= a.micros - b.micros; - return result; - } - - /** - * splits the time location datum into y,m,d,etc components. Note that - * seconds is a double, and micros will be 0. - * @param datum with time location units. - * @return TimeStruct containing the time components. - */ -// public static TimeStruct toTimeStruct(Datum datum) { -// int hour, minute; -// int jdOffset=0; -// double justNanoSeconds; -// -// Units u= Units.us2000; -// -// double microseconds= datum.doubleValue( Units.us2000 ); -// -// if ( microseconds<-6.31152E14 ) { -// u= Units.us1980; -// microseconds= datum.doubleValue( u ); -// } -// -// long nanoseconds; -// long lns2000= (long)(1000*microseconds); -// if (lns2000<0) { -// long xx= lns2000 % 86400000000000L; -// if (xx==0) { -// nanoseconds= 0; -// } else { -// nanoseconds= 86400000000000L+xx; -// } -// } else { -// nanoseconds= lns2000 % 86400000000000L; -// } -// -// long sansNanos= lns2000 - nanoseconds; -// -// int jd= getJulianDay(sansNanos/1000,u); -// -// if ( jd<0 ) { -// throw new IllegalArgumentException("julian day is negative."); -// } -// -// TimeStruct result= julianToGregorian( jd ); -// -// hour = (int)(nanoseconds/3600.0e9); -// minute = (int)((nanoseconds - hour*3600.0e9)/60.0e9); -// justNanoSeconds = nanoseconds - hour*3600.0e9 - minute*60.0e9; -// -// result.doy = dayOfYear(result.month, result.day, result.year); -// result.hour= hour; -// result.minute= minute; -// result.seconds= justNanoSeconds / 1e9; -// -// return result; -// } - - /** - * returns int[] { year, month, day, hour, minute, second, millis, micros } - * @param time the time - * @return the time decomposed into year, month, day, hour, minute, second, millis, micros. - * @deprecated use 7-element fromDatum instead, which is consistent with toDatum. Note the array elements are different! - */ - public static int[] toTimeArray( Datum time ) { - TimeStruct ts= toTimeStruct( time ); - int seconds= (int)( ts.seconds+0.0000005 ); - int micros= (int)( ( ts.seconds+0.0000005 - seconds ) * 1000000 ); - int millis= micros / 1000; - micros= micros - millis * 1000 + ts.micros + ts.millis*1000; - return new int[] { ts.year, ts.month, ts.day, ts.hour, ts.minute, seconds, millis, micros }; - } - - - /** - * returns the 7-element array of components from the time location datum: - * 0:year, 1:month, 2:day, 3:hour, 4:minute, 5:second, 6:nanoseconds - * @param time - * @return seven-element int array. - */ - public static int[] fromDatum( Datum time ) { - TimeStruct ts= toTimeStruct( time ); - int seconds= (int)( ts.seconds+0.0000000005 ); - int nanos= (int)( ( ts.seconds+0.0000000005 - seconds ) * 100000000 ); - return new int[] { ts.year, ts.month, ts.day, ts.hour, ts.minute, seconds, nanos }; - } - - /** - * get the datum from the 6 or 7 element timeArray. The elements are: - * 0:year, 1:month, 2:day, 3:hour, 4:minute, 5:second, [ 6:nanoseconds ] - * - * @param timeArray, an int[6] or int[7]. - * @return a datum representing the time. - */ - public static Datum toDatum( int[] timeArray ) { - int year = timeArray[0]; - int month = timeArray[1]; - int day = timeArray[2]; - int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - - 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + - 275 * month / 9 + day + 1721029; - int hour = (int)timeArray[3]; - int minute = (int)timeArray[4]; - double seconds = timeArray[5] + hour*(float)3600.0 + minute*(float)60.0 ; - if ( timeArray.length>6 ) seconds+= timeArray[6]/1e9; - double us2000= UnitsConverter.getConverter(Units.mj1958,Units.us2000).convert(( jd - 2436205 ) + seconds / 86400. ); - return Datum.create( us2000, Units.us2000 ); - } - - /** - * return the leap year for years 1901-2099. - * @param year - * @return - */ - public static boolean isLeapYear( int year ) { - return (year % 4)==0 && ( year%400==0 || year%100!=0 ); - } - - /** - * Normalize the TimeStruct by incrementing higher digits. For - * example, 2002-01-01T24:00 --> 2002-01-02T00:00. - * This will only carry one to the next higher place, so 70 seconds is handled but not 130. - * 2015-09-08: this now supports leap seconds. - * @param t a time structure - * @return a time structure where extra minutes are moved into hours, etc. - */ - public static TimeStruct carry(TimeStruct t) { - TimeStruct result= t; - - if (result.seconds>=60 && ( result.hour<23 || result.minute<59 ) ) { - result.seconds-=60; - result.minute++; - } - if (result.minute>=60) { - result.minute-=60; - result.hour++; - } - if (result.hour>=24) { - result.hour-=24; - result.day++; - } - - int daysThisMonth= daysInMonth(result.month,result.year); - if (result.day>daysThisMonth) { - result.day-=daysThisMonth; - result.month++; - } - if (result.month>12) { - result.month-=12; - result.year++; - } - return result; - } - - /** - * Normalize the TimeStruct by decrementing higher digits. - * @throws IllegalArgumentException if t.day<0 or t.month<1 - * @param t the time. - * @return the normalized TimeStruct - * @see #normalize(org.das2.datum.TimeUtil.TimeStruct) - */ - public static TimeStruct borrow(TimeStruct t) { - TimeStruct result= t; - - if (result.seconds<0.) { - result.seconds+=60.; - result.minute--; - } - if (result.minute<0) { - result.minute+=60; - result.hour--; - } - if (result.hour<0) { - result.hour+=24; - result.day--; - } - - if (result.day<0 || result.month<1) { - // we're going to abort here. The problem is how to decrement the month? - // What does MONTH=-1, DAY=31 mean? The "digits" are not independent as - // they are in HOURS,MINUTES,SECONDS... I don't think we are going to - // run into this case anyway. jbf - throw new IllegalArgumentException("Borrow operation not defined for months<1 or days<0"); - } - if (result.day==0) { - int daysLastMonth; - if (result.month>1) { - daysLastMonth= daysInMonth(result.month-1,result.year); - } else { - daysLastMonth= 31; - } - result.day+=daysLastMonth; - result.month--; - } - - if (result.month==0) { - result.month+=12; - result.year--; - } - - return result; - } - - /** - * convert times like "2000-01-01T24:00" to "2000-01-02T00:00" and - * "2000-002T00:00" to "2000-01-02T00:00". - * This will only carry one to the next higher place, so 70 seconds is handled but not 130. - * @param t - * @return - * @see #borrow(org.das2.datum.TimeUtil.TimeStruct) - */ - public static TimeStruct normalize( TimeStruct t ) { - if ( t.doy>0 && t.day==0 ) { - int leap= isLeapYear(t.year) ? 1: 0; - if ( t.doy>dayOffset[leap][13] ) throw new IllegalArgumentException("doy>"+dayOffset[leap][13]+")"); - int month= 12; - while ( dayOffset[leap][month] > t.doy ) { - month--; - } - t.day= t.doy - dayOffset[leap][month]; - t.month= month; - } - return carry(borrow(t)); - } - - /** - * return the next boundary - * @param td the boundary, e.g. TimeUtil.TD_HALF_YEAR - * @param count the number of boundaries - * @param datum the starting point. - * @return the boundary - */ - public static Datum next( TimeDigit td, int count, Datum datum ) { - if ( td==TD_NANO ) throw new IllegalArgumentException("not supported nanos"); - TimeStruct array= toTimeStruct(datum); - int step= td.getOrdinal(); - switch (td.getOrdinal()) { - case MILLI: - array.millis+= count; - break; - case SECOND: - array.seconds+= count; - break; - case MINUTE: - array.minute+= count; - break; - case HOUR: - array.hour+= count; - break; - case DAY: - array.day+= count; - break; - case MONTH: - array.month+=count; - array.day=1; - break; - case YEAR: - array.year+= count; - array.month= 1; - array.day= 1; - break; - default: - break; - } - - if ( step < MILLI ) array.millis= 0; - if ( step < SECOND ) array.seconds=0; - if ( step < MINUTE ) array.minute=0; - if ( step < HOUR ) array.hour=0; - - if (array.month>12) { - array.year++; - array.month-=12; - } - Datum result= toDatum(array); - - return result; - } - - /** - * introduced as a way to increase the efficiency of the time axis tick calculation, this wasn't - * used because datums need to be created anyway. So this is private for now. - * @param step, e.g. SECOND, MONTH, QUARTER, YEAR - * @param array the decomposed time. - * @return the next boundary. - */ - private static TimeStruct next( int step, TimeStruct array ) { - switch (step) { - case SECOND: - array.seconds= array.seconds+1; - break; - case MINUTE: - array.minute= array.minute+1; - break; - case HOUR: - array.hour= array.hour+1; - break; - case DAY: - array.day= array.day+1; - break; - case MONTH: - array.month=array.month+1; - array.day=1; - break; - case QUARTER: - array.month= ((array.month-1)+3)/3*3+1; - array.day=1; - break; - case HALF_YEAR: - array.month= ((array.month-1)+6)/6*6+1; - array.day=1; - break; - case YEAR: - array.year=array.year+1; - array.month=1; - array.day=1; - break; - default: - break; - } - if ( step < HOUR ) { - array.hour=0; - array.minute=0; - array.seconds=0.; - } - - if (array.month>12) { - array.year++; - array.month-=12; - } - return array; - } - - /** - * step to the next ordinal. If the datum is already at an ordinal - * boundary, then step to the next by one ordinal. - * @param step the ordinal location, such as TimeUtil.DAY or TimeUtil.HALF_YEAR - * @param datum the location. - * @return the next boundary location. - */ - public static Datum next( int step, Datum datum ) { - if ( step==NANO ) throw new IllegalArgumentException("not supported nanos"); - return toDatum( next( step, toTimeStruct(datum) ) ); - } - - /** - * return the next ordinal boundary if we aren't at one already. - * @param step the ordinal location, such as TimeUtil.DAY or TimeUtil.HALF_YEAR - * @param datum the location. - * @return the next ordinal boundary if we aren't at one already. - */ - public static Datum ceil( int step, Datum datum ) { - Datum next= next( step, datum ); - Datum t1= prev( step, next ); - if ( t1.equals(datum) ) { - return datum; - } else { - return next; - } - } - - /** - * return the previous ordinal boundary if we aren't at one already. - * @param step the ordinal location, such as TimeUtil.DAY or TimeUtil.HALF_YEAR - * @param datum the location. - * @return the previous ordinal boundary if we aren't at one already. - */ - public static Datum floor( int step, Datum datum ) { - Datum prev= prev( step, datum ); - Datum t1= next( step, prev ); - if ( t1.equals(datum) ) { - return datum; - } else { - return prev; - } - } - - /** - * step to the next month. - * @param datum - * @return the next month. - * @deprecated. Use next(MONTH,datum) instead - */ - public static Datum nextMonth(Datum datum) { - return next(MONTH,datum); - } - - /** - * decrement by 7 days. - * @param datum - * @return the datum - */ - public static Datum prevWeek( Datum datum ) { - TimeStruct t= toTimeStruct(datum); - t.day= t.day-7; - if ( t.day<1 ) { - t.month--; - t.day+= daysInMonth( t.month, t.year ); - } - return toDatum(t); - } - - /** - * step down the previous ordinal. If the datum is already at an ordinal - * boundary, then step down by one ordinal. - * @param step the ordinal location, such as TimeUtil.DAY or TimeUtil.HALF_YEAR - * @param datum the location. - * @return the prev boundary location. - */ - public static Datum prev( int step, Datum datum ) { - - TimeStruct t= toTimeStruct(datum); - - switch(step) { - case WEEK: - throw new IllegalArgumentException("not supported, use prevWeek"); - case YEAR: - t.month=1; - case HALF_YEAR: - t.month= ((t.month-1)/6*6)+1; - case QUARTER: - t.month= ((t.month-1)/3*3)+1; - case MONTH: - t.day= 1; - case DAY: - t.hour= 0; - case HOUR: - t.minute= 0; - case MINUTE: - t.seconds= 0.; - case SECOND: - t.seconds= (int)t.seconds; - } - - Datum result= toDatum(t); - if ( result.equals(datum) ) { - Datum d= datum.subtract( 500, Units.milliseconds ); - if ( d.equals(result) ) { - throw new IllegalStateException("aborting to avoid stack overflow!"); - } - return prev(step,d ); //TODO: yuck! - } else { - return result; - } - - } - - /** - * return the current time as a Datum. - * @return the current time as a Datum. - */ - public static Datum now() { - double us2000= ( System.currentTimeMillis() - 946684800e3 ) * 1000; - return Units.us2000.createDatum(us2000); - } - - /** - * convert the month components to a double in the given units. - * @param year the year - * @param month the month - * @param day the day of month, unless month==0, then day is day of year. - * @param hour additional hours - * @param minute additional minutes - * @param second additional seconds - * @param units the Units in which to return the result. - * @return a double in the given units. - */ - public static double convert(int year, int month, int day, int hour, int minute, double second, TimeLocationUnits units) { - // if month==0, then day is doy (day of year). - int jd; - if ( month>0 ) { - jd = julianDay(year,month,day); - } else { - // if month==0 then day is doy (TODO: why? Clients should use 1 for doy.) - int month1= 1; - int day1= 1; - jd = julianDay(year,month1,day1); - jd+= ( day - 1 ); - } - - second+= hour*3600.0 + minute*60.0; - - double us2000 = (jd-2451545)*86400000000. + second * 1000000; - - if ( units==Units.us2000 ) { - return us2000; - } else { - return Units.us2000.convertDoubleTo(units, us2000); - } - } - - private final static String[] mons= { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - - /** - * returns 1..12 for the English month name. (Sorry, rest of world...) - * - * @param s the three-letter month name, jan,feb,...,nov,dec - * @return 1,2,...,11,12 for the English month name - * @throws ParseException if the name isn't recognized - */ - public static int monthNumber( String s ) throws ParseException { - if ( s.length()<3 ) throw new ParseException("need at least three letters",0); - s= s.substring(0,3); - for ( int i=0; i<12; i++ ) { - if ( s.equalsIgnoreCase( mons[i] ) ) return i+1; - } - throw new ParseException("Unable to parse month", 0 ); - } - - /** - * returns "Jan", "Feb", ... for month number (1..12). - * @param mon integer from 1 to 12. - * @return three character English month name. - */ - public static String monthNameAbbrev( int mon ) { - if ( mon<1 || mon>12 ) throw new IllegalArgumentException("invalid month number: "+mon); - return mons[mon-1]; - } - - /** - * parse the time into a timestruct. - * @param s - * @see also createValid which creates a Datum. - * @return - * @throws java.text.ParseException - */ - public static TimeStruct parseTime(String s) throws java.text.ParseException { - int year, month, day_month, day_year, hour, minute; - //String s; - double second; - int c; - - final int DATE = 0; - final int YEAR = 1; - final int MONTH = 2; - final int DAY = 3; - final int HOUR = 4; - final int MINUTE = 5; - final int SECOND = 6; - - final String DELIMITERS = " \t/-:,_;"; - final String PDSDELIMITERS = " \t/-T:,_;"; - final String[] months = { - "january", "febuary", "march", "april", "may", "june", - "july", "august", "september", "october", "november", "december" - }; - - String delimiters; - int end_of_date; - //GregorianCalendar curdate; - int i, j, len, n; - String[] tok = new String[10]; - boolean[] want = new boolean[7]; - int[] format= new int[7]; - - java.util.Arrays.fill(want, false); - int ptr; - int number; - double value; - int hold; - int leap; - int tokIndex; - - StringTokenizer st; - - /* handl PDS time format */ - - delimiters = DELIMITERS; - if ((c = s.indexOf((int)'Z')) != -1) s = s.substring(0, c); - end_of_date = s.indexOf((int)'T'); - if (end_of_date > 1) { // don't mistake "T" in "TIME" - c = end_of_date - 1; - if (Character.isDigit(s.charAt(c))) delimiters = PDSDELIMITERS; - else end_of_date = -1; - } - - /* if not PDS then count out 3 non-space delimiters */ - - if (end_of_date == -1) { - n = 0; - len = s.length(); - for (i = 0; i < len; i++) { - if ((c = (delimiters.substring(2)).indexOf(s.charAt(i))) != -1) n++; - if (n == 3) { - end_of_date = i; - break; - } - } - } - - /* default to current year */ - - //curdate = new GregorianCalendar(); - //curdate.setTime(new Date()); - - year = 0; - month = 0; - day_month = 0; - day_year = 0; - hour = 0; - minute = 0; - second= 0.0; - - /* tokenize the time string */ - st = new StringTokenizer(s, delimiters); - - if (!st.hasMoreTokens()) throw new java.text.ParseException( "No tokens in '"+s+"'", 0 ); - - for (n = 0; n < 10 && st.hasMoreTokens(); n++) tok[n] = st.nextToken(); - - want[DATE] = want[YEAR] = want[MONTH] = want[DAY] = true; - hold = 0; - - tokIndex = -1; - - for (i = 0; i < n; i++) { - tokIndex = s.indexOf(tok[i], tokIndex+1); - if ((end_of_date != -1) && want[DATE] && tokIndex > end_of_date) { - want[DATE] = false; - want[HOUR] = want[MINUTE] = want[SECOND] = true; - } - - len = tok[i].length(); - - try { - if ( tok[i].length()>0 && Character.isLetter(tok[i].charAt(0) ) ) { - throw new NumberFormatException("must start with a number: "+tok[i]); // caught immediately. - } - value = Double.parseDouble(tok[i]); - } catch (NumberFormatException e) { - if (len < 3 || !want[DATE]) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - for (j = 0; j < 12; j++) { - if (tok[i].equalsIgnoreCase(months[j]) || tok[i].equalsIgnoreCase(mons[j])) { - month = j + 1; - want[MONTH] = false; - if (hold > 0) { - if (day_month > 0) throw new java.text.ParseException( "Ambiguous dates in token '"+tok[i]+"' in '"+s+"'", 0 ); - day_month = hold; - hold = 0; - want[DAY] = false; - } - break; - } - } - if (want[MONTH]) { - throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - } - continue; - } - - if (Math.IEEEremainder(value, 1.0) != 0.0) { - if (want[SECOND]) { - second = value; - break; - } else throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - } - - number = (int)value; - if (number < 0) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - - if (want[DATE]) { - - if (number == 0) throw new java.text.ParseException( "m,d, or y can't be 0 in '"+s+"'", 0 ); - - if ( number >= 10000000 && want[YEAR] ) { // %Y%m%d - year= number / 10000; - want[YEAR]= false; - month= number / 100 % 100; - want[MONTH]= false; - day_month= number % 100; - day_year= 0; - want[DAY]= false; - } else if (number >= 1000000 && want[YEAR] ) { //%Y%j - year= number / 1000; - want[YEAR]= false; - day_year= number % 1000; - month= 0; - want[MONTH]= false; - want[DAY]= false; - - } else if (number > 31) { - - if (want[YEAR]) { - if ( hold!=0 && year<100 && year>50 ) { - throw new ParseException("Held digit ("+hold+") before two-digit year ("+year+"): "+s,0); - } - year = number; - if (year < 1000) year += 1900; - want[YEAR] = false; - } else if (want[MONTH]) { - want[MONTH] = false; - month = 0; - day_year = number; - want[DAY] = false; - } else throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - - } else if (number > 12) { - - if (want[DAY]) { - if (hold > 0) { - month = hold; - want[MONTH] = false; - } - if (len == 3) { - if (month > 0) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - day_year = number; - day_month = 0; - want[MONTH] = false; - } else day_month = number; - want[DAY] = false; - } else throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - - } else if (!want[MONTH]) { - - if (month > 0) { - day_month = number; - day_year = 0; - } else { - day_year = number; - day_month = 0; - } - want[DAY] = false; - - } else if (!want[DAY]) { - - if (day_year > 0) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - month = number; - want[MONTH] = false; - - } else if (!want[YEAR]) { - - if (len == 3) { - if (month > 0) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - day_year = number; - day_month = 0; - want[DAY] = false; - } else { - if (day_year > 0) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - month = number; - if (hold > 0) { - day_month = hold; - want[DAY] = false; - } - } - want[MONTH] = false; - - } else if (hold > 0) { - - month = hold; - hold = 0; - want[MONTH] = false; - day_month = number; - want[DAY] = false; - - } else hold = number; - - if (!(want[YEAR] || want[MONTH] || want[DAY])) { - want[DATE] = false; - want[HOUR] = want[MINUTE] = want[SECOND] = true; - } - - } else if (want[HOUR]) { - - if (len == 4) { - hold = number / 100; - // TODO: handle times like Jan-1-2001T24:00 --> Jan-2-2001T00:00, for ease of modifying times - if (hold > 24) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - hour = hold; - hold = number % 100; - if (hold > 60) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - minute = hold; - want[MINUTE] = false; - } else { - if (number > 24) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - hour = number; - } - want[HOUR] = false; - - } else if (want[MINUTE]) { - // TODO: handle times like 0:90 --> 1:30, for ease of modifying times - if (number > 60) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - minute = number; - want[MINUTE] = false; - - } else if (want[SECOND]) { - - if (number > 61) throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - second = number; - want[SECOND] = false; - - } else throw new java.text.ParseException( "Error at token '"+tok[i]+"' in '"+s+"'", 0 ); - - } /* for all tokens */ - - - if ( want[YEAR] ) { - throw new java.text.ParseException("This doesn't appear to contain a year: '"+s+"'",0); - } - - if (month > 12) throw new java.text.ParseException("Month is greater than 12 in '"+s+"'",0); - if (month > 0 && day_month <= 0) day_month = 1; - - leap = ((year & 3) > 0 ? 0 : ((year % 100) > 0 ? 1 : ((year % 400) > 0 ? 0 : 1)) ); - - if ((month > 0) && (day_month > 0) && (day_year == 0)) { - if (day_month > daysInMonth[leap][month]) throw new java.text.ParseException( "day of month too high in '"+s+"'",0 ); - day_year = dayOffset[leap][month] + day_month; - } else if ((day_year > 0) && (month == 0) && (day_month == 0)) { - if (day_year > (365 + leap)) throw new java.text.ParseException( "day of year too high in '"+s+"'",0 ); - for (i = 2; i < 14 && day_year > dayOffset[leap][i]; i++); - i--; - month = i; - day_month = day_year - dayOffset[leap][i]; - } else { - if ( month==0 ) month= 1; - day_month= 1; - } - - TimeStruct result= new TimeStruct(); - result.year = year; - result.month = month; - result.day = day_month; - result.doy = day_year; - result.hour = hour; - result.minute = minute; - result.seconds = second; - result.isLocation= true; - - result.want= want; - - return result; - } - - /** Creates a datum from a string - * @param s - * @throws ParseException - * @return - */ - public static Datum create(String s) throws java.text.ParseException { - TimeStruct ts= parseTime(s); - return toDatum(ts); - } - - /** creates a Datum from a string which is known to contain - * a valid time format. Throws a RuntimeException if the - * string is not valid. - * @param validString - * @return - */ - public static Datum createValid(String validString ) { - try { - return create( validString ); - } catch ( java.text.ParseException ex ) { - throw new RuntimeException( ex ); - } - } - - public static boolean isValidTime( String string ) { - try { - create( string ); - return true; - } catch ( java.text.ParseException ex ) { - return false; - } - } - - public static void main(String[] args) throws Exception { - //System.out.println( "TimeUtil.parse="+TimeUtil.parseTime("1")); - System.out.println(""+isLeapYear(1900) ); //logger okay - System.out.println(""+isLeapYear(2000) ); //logger okay - System.out.println(""+isLeapYear(1996) ); //logger okay - System.out.println(""+isLeapYear(1999) ); //logger okay - System.out.println(""+isLeapYear(2100) ); //logger okay - - System.out.println( "TimeUtil.parse="+TimeUtil.parseTime("2010")); //logger okay - System.out.println( TimeUtil.now() ); //logger okay - System.out.println( Datum.create( TimeUtil.convert(2000,1,2, 0, 0, 0, Units.us2000 ), Units.us2000 )); //logger okay - Datum x=create( "2000-1-1 0:00:33.45" ); - System.out.println( x ); //logger okay - - TimeStruct ts= TimeUtil.toTimeStruct(x); - System.out.println( TimeUtil.toDatum(ts) ); //logger okay - - TimeDatumFormatter tf = TimeDatumFormatter.DEFAULT; - - for ( int i=0; i<44; i++ ) { - System.out.println(tf.format(x)+"\t"+(long)x.doubleValue(Units.us2000)); //logger okay - x= TimeUtil.prev(SECOND,x); - } - - Units[] uu= new Units[] { Units.cdfEpoch, Units.us1980, Units.us2000, Units.mj1958 }; - - for ( int i=0; i= 1060. - * @param month integer month, 1..12. - * @param day integer day of month. - * @param hour additional hours - * @param minute additional minutes - * @param second additional seconds - * @param nano additional nanoseconds - * @return a Datum with units Units.us2000. - */ - public static Datum createTimeDatum( int year, int month, int day, int hour, int minute, int second, int nano ) { - if ( year<1000 ) throw new IllegalArgumentException("year must not be < 1000, and 2 digit years are not allowed(year="+year+")"); - if ( year>9001 ) throw new IllegalArgumentException("year must be smaller than 9000"); - int jd = 367 * year - 7 * (year + (month + 9) / 12) / 4 - - 3 * ((year + (month - 9) / 7) / 100 + 1) / 4 + - 275 * month / 9 + day + 1721029; - double microseconds = second*1e6 + hour*3600e6 + minute*60e6 + nano/1e3; - double us2000= UnitsConverter.getConverter(Units.mj1958,Units.us2000).convert(( jd - 2436205 )) + microseconds; - return Datum.create( us2000, Units.us2000 ); - } - -} diff --git a/dasCoreDatum/src/org/das2/datum/Units.java b/dasCoreDatum/src/org/das2/datum/Units.java deleted file mode 100755 index 962111034..000000000 --- a/dasCoreDatum/src/org/das2/datum/Units.java +++ /dev/null @@ -1,827 +0,0 @@ -/* File: Units.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum; - -import java.text.ParseException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.das2.datum.format.DatumFormatterFactory; - -/** - * Class for indicating physical units, and other random units. - * @author jbf - */ -public abstract class Units { - - private static final Logger logger= Logger.getLogger("datum.units"); - - private static Map unitsMap = new HashMap(); - - public static final Units dimensionless= new NumberUnits("","dimensionless quantities"); - - public static final Units radians= new NumberUnits("radian"); - public static final Units degrees= new NumberUnits("degrees"); - public static final Units deg= new NumberUnits("deg"); - static { - degrees.registerConverter(radians, new UnitsConverter.ScaleOffset(Math.PI/180.0,0.0) ); - degrees.registerConverter(deg, UnitsConverter.IDENTITY); - } - - /** - * - */ - public static final Units rgbColor= new NumberUnits("rgbColor","256*256*red+256*green+blue"); - - /** - * this is left in in case legacy code needs to see the conversion from dB to dimensionless offset. - */ - private static final class dBConverter extends UnitsConverter { - @Override - public double convert(double value) { - return 10 * Math.log10(value); - } - @Override - public UnitsConverter getInverse() { - if (inverse == null) { - inverse = new UnitsConverter() { - @Override - public double convert(double value) { - return Math.pow(10.0, value / 10.0); - } - @Override - public UnitsConverter getInverse() { - return dBConverter.this; - } - }; - } - return inverse; - } - } - - public static final Units celciusDegrees= new NumberUnits("celcius degrees"); // disambiguate from "deg C" which is the temperature scale - public static final Units fahrenheitDegrees= new NumberUnits("fahrenheit degrees"); // disambiguate from "deg F" which is the temperature scale - - public static final Units hours= new NumberUnits("hr"); - public static final Units minutes= new NumberUnits("min"); - public static final Units seconds= new NumberUnits("s"); - public static final Units seconds2= new NumberUnits("sec"); - //public static final Units seconds3= new NumberUnits("seconds"); // note s was not convertible to seconds. - public static final Units milliseconds= new NumberUnits("ms","milliseconds"); - public static final Units milliseconds2= new NumberUnits("msec"); - public static final Units microseconds= new NumberUnits("microseconds"); - public static final Units microseconds2= new NumberUnits("\u00B5s"); - - public static final Units nanoseconds= new NumberUnits("nanoseconds"); - public static final Units ns= new NumberUnits("ns","nanoseconds"); - public static final Units picoseconds= new NumberUnits("picoseconds"); - public static final Units days= new NumberUnits("days"); - static { - seconds.registerConverter(milliseconds, UnitsConverter.MILLI); - seconds.registerConverter(microseconds, UnitsConverter.MICRO); - seconds.registerConverter(nanoseconds,UnitsConverter.NANO); - seconds.registerConverter(ns,UnitsConverter.NANO); - nanoseconds.registerConverter( ns, UnitsConverter.IDENTITY ); - seconds.registerConverter(picoseconds,UnitsConverter.PICO); - seconds.registerConverter(seconds2,UnitsConverter.IDENTITY); - microseconds.registerConverter(nanoseconds, UnitsConverter.MILLI); // to support time formatting, often from us2000 to microseconds offset. - microseconds.registerConverter(microseconds2, UnitsConverter.IDENTITY); - milliseconds.registerConverter(milliseconds2, UnitsConverter.IDENTITY); - hours.registerConverter(seconds, new UnitsConverter.ScaleOffset( 3600.,0.0)); - minutes.registerConverter(seconds, new UnitsConverter.ScaleOffset( 60.,0.0)); - days.registerConverter(seconds, new UnitsConverter.ScaleOffset(8.64e4, 0.0)); - } - - public static final Units bytesPerSecond= new NumberUnits("bytes/s"); - public static final Units kiloBytesPerSecond= new NumberUnits("KBytes/s"); - public static final Units bytes= new NumberUnits( "bytes" ); - public static final Units kiloBytes= new NumberUnits( "KBytes" ); - static { - bytesPerSecond.registerConverter( kiloBytesPerSecond, UnitsConverter.KILO ); - bytes.registerConverter( kiloBytes, UnitsConverter.KILO ); - } - - public static final Units hertz= new NumberUnits("Hz"); - public static final Units kiloHertz = new NumberUnits("kHz"); // I verified that this should be lower case k. I wonder why... - public static final Units megaHertz = new NumberUnits("MHz"); - public static final Units gigaHertz = new NumberUnits("GHz"); - static { - hertz.registerConverter(kiloHertz, UnitsConverter.KILO); - hertz.registerConverter(megaHertz, UnitsConverter.MEGA); - hertz.registerConverter(gigaHertz, UnitsConverter.GIGA); - } - - public static final Units eV= new NumberUnits("eV"); - public static final Units ev= new NumberUnits("ev"); // Mike at LANL had run into these... - public static final Units keV= new NumberUnits("keV"); - public static final Units MeV= new NumberUnits("MeV"); - static { - eV.registerConverter(Units.ev, UnitsConverter.IDENTITY); - eV.registerConverter(Units.keV, UnitsConverter.KILO); - eV.registerConverter(Units.MeV, UnitsConverter.MEGA); - } - - /** - * 1 / cm3 - */ - public static final Units pcm3= new NumberUnits("cm!a-3!n"); - - public static final Units kelvin= new NumberUnits("K"); - public static final Units cmps= new NumberUnits("cm/s"); - - public static final Units cm_2s_1keV_1= new NumberUnits( "cm!U-2!N s!U-1!N keV!U-1!N" ); - public static final Units cm_2s_1MeV_1= new NumberUnits( "cm!U-2!N s!U-1!N MeV!U-1!N" ); - static { - cm_2s_1keV_1.registerConverter( Units.cm_2s_1MeV_1, UnitsConverter.KILO ); - } - /** - * Volts 2 m-2 Hz-1 - */ - public static final Units v2pm2Hz= new NumberUnits("V!a2!nm!a-2!nHz!a-1"); - - /** - * Watts / m2 - */ - public static final Units wpm2= new NumberUnits("W/m!a-2!n"); - - - public static final Units meters = new NumberUnits("m"); - public static final Units millimeters = new NumberUnits("mm"); - public static final Units centimeters = new NumberUnits("cm"); - public static final Units kiloMeters = new NumberUnits("km"); - public static final Units inches = new NumberUnits("inch"); - public static final Units typographicPoints = new NumberUnits("points"); - static { - meters.registerConverter(kiloMeters, UnitsConverter.KILO); - meters.registerConverter(centimeters, UnitsConverter.CENTI ); - meters.registerConverter(millimeters, UnitsConverter.MILLI ); - inches.registerConverter( meters, new UnitsConverter.ScaleOffset(0.0254,0.0) ); - inches.registerConverter( typographicPoints, new UnitsConverter.ScaleOffset(72,0.0) ); - } - - /**** begin of LocationUnits. These must be defined after the physical units to support Basis. ****/ - - public static final Units centigrade= new LocationUnits( "centigrade", "centigrade", Units.celciusDegrees, Basis.centigrade ); - public static final Units fahrenheitScale= new LocationUnits("deg F", "deg F", Units.fahrenheitDegrees, Basis.fahrenheit ); - - static { - centigrade.registerConverter(fahrenheitScale, new UnitsConverter.ScaleOffset(1.8, 32)); - celciusDegrees.registerConverter(fahrenheitDegrees, new UnitsConverter.ScaleOffset(1.8,0) ); - } - - /** - * currencies for demonstration purposes. - */ - public static final Units dollars= new CurrencyUnits("dollars","$","United States Dollars"); - public static final Units euros= new CurrencyUnits("euros","\u20AC", "Euro Dollars"); - public static final Units yen= new CurrencyUnits("yen","\uFFE5","Japanese Yen"); - public static final Units rupee= new CurrencyUnits("rupee","\u20B9", "Indian Rupee"); - - /** - * Microseconds since midnight Jan 1, 2000, excluding those within a leap second. Differences across leap - * second boundaries do not represent the number of microseconds elapsed. - */ - public static final TimeLocationUnits us2000= new TimeLocationUnits("us2000", "Microseconds since midnight Jan 1, 2000.", - Units.microseconds, Basis.since2000); - - /** - * Microseconds since midnight Jan 1, 1980, excluding those within a leap second. - */ - public static final TimeLocationUnits us1980= new TimeLocationUnits("us1980", "Microseconds since midnight Jan 1, 1980.", - Units.microseconds, Basis.since1980 ); - - /** - * Seconds since midnight Jan 1, 2010, excluding leap seconds. - */ - public static final TimeLocationUnits t2010= new TimeLocationUnits("t2010","Seconds since midnight Jan 1, 2010.", - Units.seconds, Basis.since2010 ); - - /** - * Seconds since midnight Jan 1, 2000, excluding leap seconds. - */ - public static final TimeLocationUnits t2000= new TimeLocationUnits("t2000","Seconds since midnight Jan 1, 2000.", - Units.seconds, Basis.since2000 ); - - /** - * seconds since midnight Jan 1, 1970, excluding leap seconds. - */ - public static final TimeLocationUnits t1970= new TimeLocationUnits("t1970","Seconds since midnight Jan 1, 1970", - Units.seconds, Basis.since1970 ); - - /** - * milliseconds since midnight Jan 1, 1970, excluding leap seconds. - */ - public static final TimeLocationUnits ms1970= new TimeLocationUnits("ms1970","Milliseconds since midnight Jan 1, 1970", - Units.milliseconds, Basis.since1970 ); - - /** - * roughly days since noon on some day in 1958, Julian - 2436204.5 to be more precise. - */ - public static final TimeLocationUnits mj1958= new TimeLocationUnits("mj1958","Julian - 2436204.5", - Units.days, Basis.since1958 ); - - /** - * The Modified Julian Day (MJD) is the number of days (with decimal fraction of the day) that have elapsed since midnight at the beginning of Wednesday November 17, 1858. - * Julian - 2400000.5 - */ - public static final TimeLocationUnits mjd= new TimeLocationUnits("mjd", "days since midnight November 17, 1858.", - Units.days , Basis.modifiedJulian ); - - /** - * cdf epoch milliseconds since midnight, 01-Jan-0000, excluding those with a leap second. There must be skipped days, because this doesn't yield 01-Jan-0000 for 0., - * but works fine at 1-1-2000., excluding those within a leap second - */ - public static final TimeLocationUnits cdfEpoch= new TimeLocationUnits("cdfEpoch","milliseconds since 01-Jan-0000", - Units.milliseconds, Basis.since0000 ); - - /** - * the number of nanoseconds since 01-Jan-2000T12:00, roughly. This includes leap seconds, so conversion is more than a scale,offset. - */ - public static final TimeLocationUnits cdfTT2000= new TimeLocationUnits("cdfTT2000","nanoseconds since 01-Jan-2000, including leap seconds", - Units.nanoseconds, Basis.since2000 ); - - static { - ((Units)t2000).registerConverter(us2000, UnitsConverter.MICRO); - ((Units)us1980).registerConverter(us2000, new UnitsConverter.ScaleOffset(1.0, -631152000000000L ) ); - ((Units)us2000).registerConverter(cdfEpoch, new UnitsConverter.ScaleOffset( 1/1000.,63113904000000L )); - ((Units)us2000).registerConverter(cdfTT2000, new LeapSecondsConverter( true ) ); - ((Units)t2000).registerConverter(t1970, new UnitsConverter.ScaleOffset(1.0, 9.466848e8)); - ((Units)t1970).registerConverter(ms1970, UnitsConverter.MILLI ); - ((Units)t2000).registerConverter(t2010, new UnitsConverter.ScaleOffset(1.0, -3.1561920e+8 )); - ((Units)t2000).registerConverter(mj1958, new UnitsConverter.ScaleOffset(1.0/8.64e4, 15340 )); - ((Units)t2000).registerConverter(mjd, new UnitsConverter.ScaleOffset(1.0/8.64e4, 51544 )); - } - - /**** ratiometric units ***********/ - - public static final Units percent= new NumberUnits("%",""); - - /** - * Define a set of units to describe ratiometric (logarithmic) spacing. Note that Units.percent - * is no longer the defacto ratiometric spacing, and Units.percentIncrease takes its place. - * Note the log10Ratio is the preferred method for expressing spacing, but all are convertible - * See logERatio, log10Ratio and google for "fold change." - */ - - /* percentIncrease is defined as ( b-a )*100. / a. So { 1,2,4,8 } has a spacing of 100 % diff. */ - public static final Units dB = new NumberUnits("dB","decibels"); - public static final Units ampRatio= new NumberUnits("ampratio","amplitude ratio"); - public static final Units percentIncrease= new NumberUnits("% diff","Special dimensionless number, useful for expressing on logarithmic scale. 100% indicates a doubling"); - public static final Units log10Ratio= new NumberUnits("log10Ratio", "Special dimensionless number, useful for expressing distances on a log10 scale" ); - public static final Units logERatio= new NumberUnits("logERatio", "Special dimensionless number, useful for expressing distances on a logE scale" ); - private static class PercentRatioConverter extends UnitsConverter { - @Override - public double convert(double value) { - return ( Math.exp(value) - 1.0 ) * 100; - } - @Override - public UnitsConverter getInverse() { - if (inverse == null) { - inverse = new UnitsConverter() { - @Override - public double convert(double value) { - return Math.log( value / 100 + 1. ); - } - @Override - public UnitsConverter getInverse() { - return PercentRatioConverter.this; - } - }; - } - return inverse; - } - } - - /** - * see http://en.wikipedia.org/wiki/Decibel - */ - private static class AmpRatioConverter extends UnitsConverter { - @Override - public double convert(double value) { - return ( Math.pow(10,value/20.) ); - } - @Override - public UnitsConverter getInverse() { - if (inverse == null) { - inverse = new UnitsConverter() { - @Override - public double convert(double value) { - return 20 * Math.log10( value ); - } - @Override - public UnitsConverter getInverse() { - return AmpRatioConverter.this; - } - }; - } - return inverse; - } - } - - static { - log10Ratio.registerConverter( logERatio, new UnitsConverter.ScaleOffset( Math.log(10), 0. ) ); - logERatio.registerConverter( percentIncrease, new PercentRatioConverter() ); - dB.registerConverter( log10Ratio, new UnitsConverter.ScaleOffset( 10, 0 ) ); - dB.registerConverter( ampRatio, new AmpRatioConverter() ); - } - - /* static { - unitsMap.put("mj1958", Units.mj1958); - unitsMap.put("t1970", Units.t1970); - unitsMap.put("t2000", Units.t2000); - unitsMap.put("us2000", Units.us2000); - unitsMap.put("seconds", Units.seconds); - unitsMap.put("s", Units.seconds); - unitsMap.put("days", Units.days); - unitsMap.put("microseconds", Units.microseconds); - unitsMap.put("", Units.dimensionless); - unitsMap.put("dB", Units.dB); - - unitsMap.put("Hz", Units.hertz); - unitsMap.put("kHz", Units.kiloHertz); - unitsMap.put("MHz", Units.megaHertz); - }*/ - - private String id; - private String description; - private final Map conversionMap = new ConcurrentHashMap(); - - protected Units( String id ) { - this( id, "" ); - }; - - protected Units( String id, String description ) { - this.id= id; - this.description= description; - unitsMap.put( id, this ); - }; - - /** - * get the id uniquely identifying the units. Note the id may contain - * special tokens, like "since" for time locations. - * @return the id. - */ - public String getId() { - return this.id; - } - - /** - * register a converter between the units. Note these converters can be - * changed together to derive conversions. (A to B, B to C defines A to C.) - * @param toUnits the target units - * @param converter the converter that goes from this unit to target units. - */ - public void registerConverter(Units toUnits, UnitsConverter converter) { - conversionMap.put(toUnits, converter); - UnitsConverter inverse = (UnitsConverter)toUnits.conversionMap.get(this); - if (inverse == null || inverse.getInverse() != converter) { - toUnits.registerConverter(this, converter.getInverse()); - } - } - - /** - * return the units to which this unit is convertible. - * @return the units to which this unit is convertible. - */ - public Units[] getConvertableUnits() { - Set result= new HashSet(); - LinkedList queue = new LinkedList(); - queue.add(this); - while (!queue.isEmpty()) { - Units current = (Units)queue.removeFirst(); - for (Map.Entry entry : current.conversionMap.entrySet()) { - Units next = (Units)entry.getKey(); - if (!result.contains(next)) { - queue.add(next); - result.add(next); - } - } - } - return (Units[])result.toArray( new Units[result.size()] ); - } - - /** - * return true if the unit can be converted to toUnits. - * @deprecated use isConvertibleTo (which does not contain spelling error) - * @param toUnits Units object. - * @return true if the unit can be converted to toUnits. - */ - public boolean isConvertableTo( Units toUnits ) { - UnitsConverter result= getConverterInternal(this, toUnits); - return result!=null; - } - - /** - * return true if the unit can be converted to toUnits. - * @param toUnits Units object. - * @return true if the unit can be converted to toUnits. - */ - public boolean isConvertibleTo( Units toUnits ) { - UnitsConverter result= getConverterInternal(this, toUnits); - return result!=null; - } - - /** - * lookup the UnitsConverter object that takes numbers from fromUnits to toUnits. - * This will chain together UnitsConverters registered via units.registerConverter. - * @param fromUnits units instance that is the source units. - * @param toUnits units instance that is the target units. - * @return UnitsConverter object - * @throws InconvertibleUnitsException when the conversion is not possible. - */ - public static UnitsConverter getConverter( final Units fromUnits, final Units toUnits ) { - logger.log(Level.FINER, "getConverter( {0} to {1} )", new Object[]{fromUnits, toUnits}); //TODO: THIS IS CALLED WITH EVERY REPAINT!!! - UnitsConverter result= getConverterInternal(fromUnits, toUnits); - if ( result==null ) { - throw new InconvertibleUnitsException( fromUnits, toUnits ); - } - return result; - } - - /** - * lookup the UnitsConverter object that takes numbers from fromUnits to toUnits. - * This will chain together UnitsConverters registered via units.registerConverter. - * @param fromUnits - * @param toUnits - * @return UnitsConverter object - * @throws InconvertibleUnitsException when the conversion is not possible. - */ - private static UnitsConverter getConverterInternal( final Units fromUnits, final Units toUnits ) { - logger.log(Level.FINE, "fromUnits={0} {1} toUnits={2} {3}", new Object[]{fromUnits,fromUnits.hashCode(), toUnits,toUnits.hashCode()}); - if (fromUnits == toUnits) { - return UnitsConverter.IDENTITY; - } - - UnitsConverter o = fromUnits.conversionMap.get(toUnits); - if ( o != null) { - return o; - } - - Map visited = new HashMap(); - visited.put(fromUnits, null); - LinkedList queue = new LinkedList(); - queue.add(fromUnits); - while (!queue.isEmpty()) { - Units current = (Units)queue.removeFirst(); - for ( Map.Entry entry : current.conversionMap.entrySet() ) { - Units next = (Units)entry.getKey(); - if (!visited.containsKey(next)) { - visited.put(next, current); - queue.add(next); - if (next == toUnits) { - logger.log(Level.FINE, "build conversion from {0} to {1}", new Object[]{fromUnits, toUnits}); - return buildConversion(fromUnits, toUnits, visited); - } - } - } - } - return null; - } - - private static UnitsConverter buildConversion(Units fromUnits, Units toUnits, Map parentMap) { - ArrayList list = new ArrayList(); - Units current = toUnits; - while (current != null) { - list.add(current); - current = (Units)parentMap.get(current); - } - UnitsConverter converter = UnitsConverter.IDENTITY; - for (int i = list.size() - 1; i > 0; i--) { - Units a = (Units)list.get(i); - Units b = (Units)list.get(i - 1); - UnitsConverter c = (UnitsConverter)a.conversionMap.get(b); - converter = converter.append(c); - } - fromUnits.registerConverter(toUnits, converter); - return converter; - } - - /** - * Get the converter that goes from this Unit to toUnits. E.g. - * Units.meters.getConverter(Units.centimeters) yields a converter that - * multiplies by 100. - * @param toUnits - * @return a converter from this unit to toUnits. - * @throws IllegalArgumentException if conversion between units is not possible - */ - public UnitsConverter getConverter( Units toUnits ) { - return getConverter( this, toUnits ); - } - - /** - * convert the double in this units' space to toUnits' space. - * @param toUnits the units. - * @param value the value in toUnits. - * @return the double in the new units system. - */ - public double convertDoubleTo( Units toUnits, double value ) { - if ( this==toUnits ) { - return value; - } else { - return getConverter(this,toUnits).convert(value); - } - } - - @Override - public String toString() { - return id; - } - - /** - * return the units from the Basis for the unit, such as "seconds" in - * "seconds since midnight, Jan 1, 1970" - * @return this units offsets. - */ - public Units getOffsetUnits() { - return this; - } - - /** - * return the Basis which defines the meaning of zero and the direction of positive values, such as - * "since midnight, Jan 1, 1970" - * @return the Basis object, which simply identifies a basis. - */ - public Basis getBasis() { - return Basis.physicalZero; - } - - public abstract Datum createDatum( double value ); - public abstract Datum createDatum( int value ); - public abstract Datum createDatum( long value ); - public abstract Datum createDatum( Number value ); - - public abstract Datum createDatum( double value, double resolution ); - - private final static double FILL_DOUBLE= -1e31; - - public double getFillDouble() { return FILL_DOUBLE; } - public Datum getFillDatum() { return this.createDatum(FILL_DOUBLE); } - - public boolean isFill( double value ) { return valueFILL_DOUBLE/10 ; - } - - public abstract DatumFormatterFactory getDatumFormatterFactory(); - - public abstract Datum parse(String s) throws ParseException; - public String format( Datum datum ) { - return getDatumFormatterFactory().defaultFormatter().format(datum); - } - public String grannyFormat( Datum datum ) { - return getDatumFormatterFactory().defaultFormatter().grannyFormat(datum); - } - - public abstract Datum add( Number a, Number b, Units bUnits ); - public abstract Datum subtract( Number a, Number b, Units bUnits ); - public abstract Datum multiply( Number a, Number b, Units bUnits ); - public abstract Datum divide( Number a, Number b, Units bUnits ); - - /** - * return all the known units. - * @return list of all the known units. - */ - public static List getAllUnits() { - return new ArrayList(unitsMap.keySet()); - } - - /** - * returns a Units object with the given string representation that is stored in the unitsMap. - * - * @param s units identifier - * @return units object - * @throws IllegalArgumentException if the unit is not recognized. - */ - public static Units getByName(String s) { - Units units = (Units)unitsMap.get(s); - if (units == null) { - throw new IllegalArgumentException("Unrecognized units: "+s); - } else return units; - } - - /** - * return canonical das2 unit for colloquial time. - * @param s string containing time unit like s, sec, millisec, etc. - * @return - */ - public static Units lookupTimeLengthUnit(String s) throws ParseException { - s= s.toLowerCase().trim(); - if ( s.startsWith("sec") || s.equals("s") ) { - return Units.seconds; - } else if ( s.startsWith("ms") || s.startsWith("millisec") || s.startsWith("milliseconds") ) { - return Units.milliseconds; - } else if ( s.equals("hr") || s.startsWith("hour") ) { - return Units.hours; - } else if ( s.equals("mn") || s.startsWith("min") ) { - return Units.minutes; - } else if ( s.startsWith("us") || s.startsWith("\u00B5s" ) || s.startsWith("micros")) { - return Units.microseconds; - } else if ( s.startsWith("ns") || s.startsWith("nanos" ) ) { - return Units.nanoseconds; - } else if ( s.startsWith("d") ) { //TODO: yikes... - return Units.days; - } else { - throw new ParseException("failed to identify unit: "+s,0); - } - } - - /** - * lookupUnits canonical units object, or allocate one. If one is - * allocated, then parse for "<unit> since <datum>" and add conversion to - * "microseconds since 2000-001T00:00." Note leap seconds are ignored! - * @param base the base time, for example 2000-001T00:00. - * @param offsetUnits the offset units for example microseconds. Positive values of the units will be since the base time. - * @return the unit. - */ - public static synchronized Units lookupTimeUnits( Datum base, Units offsetUnits ) { - Units result; - String canonicalName = "" + offsetUnits + " since "+ base; - try { - result= Units.getByName(canonicalName); - return result; - } catch ( IllegalArgumentException ex ) { - Basis basis= new Basis( "since "+ base, "since "+ base, Basis.since2000, base.doubleValue(Units.us2000), Units.us2000.getOffsetUnits() ); - result= new TimeLocationUnits( canonicalName, canonicalName, offsetUnits, basis ); - result.registerConverter( Units.us2000, - new UnitsConverter.ScaleOffset( - offsetUnits.convertDoubleTo(Units.microseconds, 1.0), - base.doubleValue(Units.us2000) ) ); - return result; - } - } - - /** - * lookupUnits canonical units object, or allocate one. If one is - * allocated, then parse for "<unit> since <datum>" and add conversion to - * "microseconds since 2000-001T00:00" (us2000). Note leap seconds are ignored - * in the returned units, so each day is 86400 seconds long, and differences in - * times should not include leap seconds. Note this contains a few kludges - * as this for datasets encountered by Autoplot. - * @param units string like "microseconds since 2000-001T00:00" which will be the id. - * @return a units object that implements. - * @throws java.text.ParseException if the time cannot be parsed, etc. - */ - public static synchronized Units lookupTimeUnits( String units ) throws ParseException { - - Units result; - - //see if it's already registered. - try { - result= Units.getByName(units); - return result; - } catch ( IllegalArgumentException ex ) { - //do nothing until later - } - - if(units.trim().equalsIgnoreCase("UTC")) return us2000; - - String[] ss= units.split("since"); - Units offsetUnits= lookupTimeLengthUnit(ss[0]); - Datum datum; - - if ( ss[1].equals(" 1-1-1 00:00:00" ) ) { // make this into something that won't crash. - //datum= Units.mj1958.createDatum(-714779); - ss[1]= "1901-01-01 00:00:00"; // /media/mini/data.backup/examples/netcdf/sst.ltm.1961-1990.nc - } - if ( ss[1].contains("1970-01-01 00:00:00.0 0:00") ) { - ss[1]= "1970-01-01 00:00:00"; - } - if ( ss[1].endsWith(" UTC") ) { // http://www.ngdc.noaa.gov/stp/satellite/dmsp/f16/ssj/2011/01/f16_20110101_ssj.h5?TIME - ss[1]= ss[1].substring(0,ss[1].length()-4); - } - datum= TimeUtil.create(ss[1]); - return lookupTimeUnits( datum, offsetUnits ); - } - - /** - * lookupUnits canonical units object, or allocate one. - * Examples include: - * "nT" where it's already allocated, - * "apples" where it allocates a new one, and - * "seconds since 2011-12-21T00:00" where it uses lookupTimeUnits. - * @param sunits string identifier. - * @return canonical units object. - */ - public static synchronized Units lookupUnits(String sunits) { - Units result; - sunits= sunits.trim(); - try { - result= Units.getByName(sunits); - - } catch ( IllegalArgumentException ex ) { - if ( sunits.contains(" since ") || sunits.equalsIgnoreCase("UTC") ) { - try { - result = lookupTimeUnits(sunits); - } catch (ParseException ex1) { - result= new NumberUnits( sunits ); - } - } else if ( sunits.equals("sec") ) { // begin, giant table of kludges - result= Units.seconds; - } else if ( sunits.equals("msec") ) { // CDF - result= Units.milliseconds; - } else if ( sunits.contains("(All Qs)")) { //themis files have this annotation on the units. Register a converter. TODO: solve this in a nice way. The problem is I wouldn't want to assume nT(s) doesn't mean nT * sec. - result= new NumberUnits( sunits ); - Units targetUnits= lookupUnits( sunits.replace("(All Qs)","").trim() ); - result.registerConverter( targetUnits, UnitsConverter.IDENTITY ); - } else { - Pattern multPattern= Pattern.compile("([.0-9]+)\\s*([a-zA-Z]+)"); - Matcher m= multPattern.matcher(sunits); - if ( m.matches() ) { // kludge for ge_k0_mgf which has "0.1nT" for units. We register a converter when we see these. Note this is going to need more attention - try { - Units convTo; - convTo = lookupUnits(m.group(2)); - if ( convTo!=null ) { - double fact= Double.parseDouble(m.group(1)); - result= new NumberUnits( sunits ); - result.registerConverter( convTo, new UnitsConverter.ScaleOffset(fact,0.0) ); - } else { - result= lookupUnits(sunits); - } - } catch ( NumberFormatException ex2 ) { - result= lookupUnits(sunits); - } - } else { - result= new NumberUnits( sunits ); - } - } - } - - // look to see if there is a standard unit for this and register a converter if so. E.g. [ms]<-->ms - String stdunits= sunits; - if ( stdunits.startsWith("[") && stdunits.endsWith("]") ) { // we can't just pop these off. Hudson has case where this causes problems. We need to make units in vap files canonical as well. - stdunits= stdunits.substring(1,stdunits.length()-1); - } - if ( stdunits.startsWith("(") && stdunits.endsWith(")") ) { // often units get [] or () put around them. Pop these off. - stdunits= stdunits.substring(1,stdunits.length()-1); - } - if ( !stdunits.equals(sunits) ) { - Units stdUnit= lookupUnits(stdunits); // we need to register "foo" when "[foo]" so that order doesn't matter. - if ( !stdUnit.isConvertibleTo(result) ) { - logger.log(Level.FINE, "registering identity converter {0} -> {1}", new Object[]{stdUnit, result}); - stdUnit.registerConverter( result, UnitsConverter.IDENTITY ); - stdUnit.getConverter(result); - } - } - return result; - } - - public static void main( String[] args ) throws java.text.ParseException { - //Datum ratio = Datum.create(100); - Datum ratio = Units.ampRatio.createDatum(100); - Datum db = ratio.convertTo(dB); - System.out.println("ratio: " + ratio); - System.out.println("dB: " + db); - - Datum Hz = Datum.create(1000000.0, hertz); - Datum kHz = Hz.convertTo(kiloHertz); - Datum MHz = kHz.convertTo(megaHertz); - System.out.println("Hz: " + Hz); - System.out.println("kHz: " + kHz); - System.out.println("MHz: " + MHz); - - System.err.println( Units.ms1970.createDatum(1000) ); - } -} diff --git a/dasCoreDatum/src/org/das2/datum/UnitsConverter.java b/dasCoreDatum/src/org/das2/datum/UnitsConverter.java deleted file mode 100644 index e7f275908..000000000 --- a/dasCoreDatum/src/org/das2/datum/UnitsConverter.java +++ /dev/null @@ -1,306 +0,0 @@ -/* File: UnitsConverter.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum; - -/** - * Units Converter object performs scale/offset conversions, but can - * also be used any double-to-double conversion, and contains an - * implementation to chain multiple conversions together. - * - * @author jbf - */ -public abstract class UnitsConverter { - - /** - * No conversion, where convert trivially returns the value. - */ - public static final UnitsConverter IDENTITY = new UnitsConverter() { - @Override - public UnitsConverter getInverse() { - return this; - } - @Override - public double convert(double value) { - return value; - } - - @Override - public UnitsConverter append(UnitsConverter that) { - return that; - } - - @Override - public String toString() { - return "IDENTITY UnitsConverter"; - } - }; - - /** - * Allow conversion, but this is a flag that indicates the result - * should be dimensionless because the Ratiometric units were - * not convertible. - */ - public static final UnitsConverter LOOSE_IDENTITY = new UnitsConverter() { - @Override - public UnitsConverter getInverse() { - return this; - } - @Override - public double convert(double value) { - return value; - } - - @Override - public UnitsConverter append(UnitsConverter that) { - return that; - } - - @Override - public String toString() { - return "LOOSE_IDENTITY UnitsConverter"; - } - }; - - public static final UnitsConverter TERA = new ScaleOffset(1e-12, 0.0); - public static final UnitsConverter GIGA = new ScaleOffset(1e-9, 0.0); - public static final UnitsConverter MEGA = new ScaleOffset(1e-6, 0.0); - public static final UnitsConverter KILO = new ScaleOffset(1e-3, 0.0); - public static final UnitsConverter MILLI = new ScaleOffset(1e3, 0.0); - public static final UnitsConverter CENTI = new ScaleOffset(1e2, 0.0); - public static final UnitsConverter MICRO = new ScaleOffset(1e6, 0.0); - public static final UnitsConverter NANO = new ScaleOffset(1e9, 0.0); - public static final UnitsConverter PICO = new ScaleOffset(1e12, 0.0); - - protected UnitsConverter inverse; - - protected UnitsConverter() { - } - - protected UnitsConverter(UnitsConverter inverse) { - this.inverse = inverse; - } - - public abstract UnitsConverter getInverse(); - - /** - * convert the value in the source units to the target units. - * @param value value in source units. - * @return value in target units. - */ - public abstract double convert(double value); - - /** - * convert the value in the source units to the target units, - * preserving the data type. - * - * TODO: study where this is used, because it seems like the unit - * should determine its use. For example, Units.CDF_TT2000 might want Long. - * - * @param number value in source units. - * @return value in target units. - */ - public Number convert( Number number ) { - double value = number.doubleValue(); - value = convert(value); - if (number instanceof Integer) { - return (int)value; - } - else if (number instanceof Long) { - return (long)value; - } - else { - return value; - } - } - - public UnitsConverter append(UnitsConverter that) { - return new Appended(this, that); - } - - public static class ScaleOffset extends UnitsConverter { - private final double offset; - private final double scale; - private final int hashCode; - - /** - * Creates a new UnitsConverter.ScaleOffset. This - * converter multiplies by scale and adds offset, so - * offset is in the target Units. For example, - * deg C to deg F would be - * {@code new UnitsConverter.ScaleOffset( 9./5, 32 )}. - * @param scale the scale to apply to the value. - * @param offset the offset to apply after the scale - */ - public ScaleOffset(double scale, double offset) { - this(scale, offset, null); - } - - private ScaleOffset(double scale, double offset, UnitsConverter inverse) { - super(inverse); - this.scale = scale; - this.offset = offset; - hashCode = computeHashCode(); - } - - private int computeHashCode() { - long scaleBits = Double.doubleToLongBits(scale); - long offsetBits = Double.doubleToLongBits(offset); - long code = (11 * 13 * 13) + (13 * scaleBits) + offsetBits; - int a = (int)(code >> 32); - int b = (int)(0xFFFFFFFFL & code); - return a + b; - } - - @Override - public UnitsConverter getInverse() { - if (((UnitsConverter)this).inverse == null) { - ((UnitsConverter)this).inverse = new ScaleOffset(1.0 / scale, -(offset / scale), this); - } - return ((UnitsConverter)this).inverse; - } - - @Override - public double convert( double value ) { - return scale * value + offset; - } - - @Override - public UnitsConverter append(UnitsConverter that) { - if (that==IDENTITY) { - return this; - } else if (that instanceof ScaleOffset) { - ScaleOffset so = (ScaleOffset)that; - double aScale = this.scale * so.scale; - double aOffset = this.offset * so.scale + so.offset; - return new ScaleOffset(aScale, aOffset); - } - else { - return super.append(that); - } - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof ScaleOffset)) { - return false; - } - ScaleOffset that = (ScaleOffset)o; - return this.scale == that.scale && this.offset == that.offset; - } - - @Override - public String toString() { - return getClass().getName() + "[scale=" + scale + ",offset=" + offset + "]"; - } - - @Override - public int hashCode() { - return hashCode; - } - - } - - public static class Appended extends UnitsConverter{ - - UnitsConverter[] converters; - - public Appended(UnitsConverter uc1, UnitsConverter uc2) { - UnitsConverter[] a1 = ucToArray(uc1); - UnitsConverter[] a2 = ucToArray(uc2); - converters = new UnitsConverter[a1.length + a2.length]; - System.arraycopy(a1, 0, converters, 0, a1.length); - System.arraycopy(a2, 0, converters, a1.length, a2.length); - } - - private Appended(UnitsConverter[] array, UnitsConverter inverse) { - super(inverse); - converters = array; - } - - @Override - public double convert(double value) { - for (UnitsConverter converter : converters) { - value = converter.convert(value); - } - return value; - } - - @Override - public Number convert(Number value) { - for (UnitsConverter converter : converters) { - value = converter.convert(value); - } - return value; - } - - @Override - public UnitsConverter getInverse() { - if (inverse == null) { - int length = converters.length; - UnitsConverter[] inverseArray = new UnitsConverter[length]; - for (int i = 0; i < length; i++) { - inverseArray[i] = converters[length - i - 1].getInverse(); - } - inverse = new Appended(inverseArray, this); - } - return inverse; - } - - private static UnitsConverter[] ucToArray(UnitsConverter uc) { - if (uc instanceof Appended) { - return ((Appended)uc).converters; - } - else { - return new UnitsConverter[] {uc}; - } - } - - @Override - public String toString() { - StringBuilder result= new StringBuilder("UnitsConverted$Appended["); - for ( UnitsConverter uc1: converters ) { - result.append(" ").append(uc1).append(" "); - } - result.append("]"); - return result.toString(); - } - } - - /** - * lookup the UnitsConverter object that takes numbers from fromUnits to toUnits. - * This will chain together UnitsConverters registered via units.registerConverter. - * @param fromUnits - * @param toUnits - * @return UnitsConverter object - * @throws InconvertibleUnitsException when the conversion is not possible. - */ - public static UnitsConverter getConverter(Units fromUnits, Units toUnits) { - if ( fromUnits==toUnits ) { - return UnitsConverter.IDENTITY; - } else { - return Units.getConverter(fromUnits,toUnits); - } - } - -} diff --git a/dasCoreDatum/src/org/das2/datum/UnitsUtil.java b/dasCoreDatum/src/org/das2/datum/UnitsUtil.java deleted file mode 100644 index 7f7c1d60a..000000000 --- a/dasCoreDatum/src/org/das2/datum/UnitsUtil.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * UnitsUtil.java - * - * Created on December 1, 2004, 10:25 PM - */ - -package org.das2.datum; - -import java.text.DecimalFormat; - -/** - * Useful operations for units, and tests for Steven's Levels of Measurement. - * @author Jeremy - */ -public class UnitsUtil { - - /** - * returns true if the unit is used to measure distance in a logarithmic - * space, such as decibels or percent increase. Note Units.dimensionless - * are not considered ratiometric. (Of course, all ratiometic - * units are dimensionless...) - * - * Do not confuse this with isRatioMeasurement. "5kg" is ratio measurement. - * "105%" is ratiometric. - */ - public static boolean isRatiometric( Units unit ) { - return unit!=Units.dimensionless && unit.isConvertibleTo(Units.logERatio); - } - - /** - * returns true if the unit describes a location in time, as in us2000. - */ - public static boolean isTimeLocation( Units unit ) { - return unit==Units.us2000 || unit.isConvertibleTo(Units.us2000); - } - - /** - * returns true if the unit is a ratio measurement, meaning there is a physical zero - * and you can make meaningful ratios between arbitrary numbers. All operations - * like add, multiply and divide are allowed. (What about negative numbers? We - * need a statistician!) - * Examples include "5 km" or "0.2/cc" and "15 counts" - * See http://en.wikipedia.org/wiki/Level_of_measurement - * @param unit - * @return - */ - public static boolean isRatioMeasurement( Units unit ) { - return !(unit instanceof EnumerationUnits) && unit.getOffsetUnits()==unit; - } - - /** - * returns true if the unit is a interval measurement, meaning the choice of - * zero is arbitrary. Subtraction and comparison are allowed, but addition, - * multiplication and division are invalid operators. - * Examples include "2008-04-09T14:27:00Z" and 15 deg W Longitude. - * See http://en.wikipedia.org/wiki/Level_of_measurement - * @param unit - * @return - */ - public static boolean isIntervalMeasurement( Units unit ) { - return !(unit instanceof EnumerationUnits) && unit.getOffsetUnits()!=unit; - } - - /** - * returns true if the unit is a interval measurement or is a ratio measurement, - * and not a nominal or ordinal measurement. These are things that are plotted - * by showing a location on an axis. - * See http://en.wikipedia.org/wiki/Level_of_measurement - * Examples include "2008-04-09T14:27:00Z" and "5 km" - * @param unit - * @return - */ - public static boolean isIntervalOrRatioMeasurement( Units unit ) { - return !(unit instanceof EnumerationUnits); - } - - /** - * returns true if the unit is nominal, meaning that Datums with this unit - * can only be equal or not equal. Currently all nominal data is stored - * as ordinal data, so this always returns false. - * Examples include "Iowa City", and "Voyager 1". - * See http://en.wikipedia.org/wiki/Level_of_measurement - * @param unit - * @return true if the unit is nominal. - */ - public static boolean isNominalMeasurement( Units unit ) { - return unit instanceof EnumerationUnits; - } - - /** - * returns true if the unit is ordinal, meaning that Datums with this unit - * can only be equal or not equal, or compared. subtract, add, multiply, - * divide are invalid. - * Examples include energy bin labels and quality measures. - * See http://en.wikipedia.org/wiki/Level_of_measurement - * @param unit - * @return true if the unit is ordinal. - */ - public static boolean isOrdinalMeasurement( Units unit ) { - return unit instanceof EnumerationUnits; - } - - /** - * returns the unit whose product with the parameter unit is unity. - * @throws IllegalArgumentException if the units inversion is not known. - * (Presently this is only time units). - * - */ - public static Units getInverseUnit( Units unit ) { - if ( unit==Units.seconds ) { - return Units.hertz; - } else if ( unit==Units.hertz ) { - return Units.seconds; - } else if ( unit==Units.dimensionless ) { - return Units.dimensionless; - } else if ( unit==Units.milliseconds ) { - return Units.kiloHertz; - } else if ( unit==Units.microseconds ) { - return Units.megaHertz; - } else if ( unit==Units.nanoseconds ) { - return Units.gigaHertz; - } else { - if ( unit.isConvertibleTo(Units.seconds ) ) { - UnitsConverter uc= unit.getConverter(Units.seconds); - if ( uc==UnitsConverter.IDENTITY ) { - return Units.hertz; - } - uc= unit.getConverter(Units.milliseconds); // there's no way to check for scale=1000... - if ( uc==UnitsConverter.IDENTITY ) { - return Units.kiloHertz; - } - uc= unit.getConverter(Units.microseconds); // there's no way to check for scale=1000... - if ( uc==UnitsConverter.IDENTITY ) { - return Units.megaHertz; - } - uc= unit.getConverter(Units.nanoseconds); // there's no way to check for scale=1000... - if ( uc==UnitsConverter.IDENTITY ) { - return Units.gigaHertz; - } else { - throw new IllegalArgumentException( "units not supported: "+unit ); - } - } else if ( unit.isConvertibleTo(Units.hertz ) ) { - UnitsConverter uc= unit.getConverter(Units.hertz); - if ( uc==UnitsConverter.IDENTITY ) { - return Units.seconds; - } - uc= unit.getConverter(Units.kiloHertz); - if ( uc==UnitsConverter.IDENTITY ) { - return Units.milliseconds ; - } - uc= unit.getConverter(Units.megaHertz); - if ( uc==UnitsConverter.IDENTITY ) { - return Units.microseconds; - } - uc= unit.getConverter(Units.gigaHertz); - if ( uc==UnitsConverter.IDENTITY ) { - return Units.nanoseconds; - } else { - throw new IllegalArgumentException( "units not supported: "+unit ); - } - } else { - throw new IllegalArgumentException( "units not supported: "+unit ); - } - } - } - - /** - * Special division operation that either does the Datum division if - * possible, or returns the division of the magnitude parts of the - * Datums plus the unit names "A/B", suitable for human consumption. - */ - public static String divideToString( Datum aDatum, Datum bDatum ) { - try { - Datum result= divide( aDatum, bDatum ); - return String.valueOf(result); - } catch ( IllegalArgumentException e ) { - Units aUnits= aDatum.getUnits(); - Units bUnits= bDatum.getUnits(); - double a= aDatum.doubleValue(aUnits); - double b= bDatum.doubleValue(bUnits); - DecimalFormat df= new DecimalFormat("0.000E0"); - return ""+df.format(a/b)+" "+aUnits+" / " +bUnits; - } - } - - /** - * attempt to perform the division of two Datums by looking for - * convertible units or dimensionless. - */ - public static Datum divide( Datum aDatum, Datum bDatum ) { - Units bUnits= bDatum.getUnits(); - Units aUnits= aDatum.getUnits(); - - Units bInvUnits; - try{ - bInvUnits= getInverseUnit(bUnits); - } catch ( IllegalArgumentException e ) { - bInvUnits= null; - } - - double a= aDatum.doubleValue(aUnits); - double b= bDatum.doubleValue(bUnits); - - if ( bUnits==Units.dimensionless ) { - return aUnits.createDatum( a/b ); - } else if ( aUnits==Units.dimensionless ) { - if ( bInvUnits==null ) { - throw new IllegalArgumentException("unable to calculate, b units not convertable to a"); - } else { - return bInvUnits.createDatum(a/b); - } - } else { - if ( !bUnits.isConvertibleTo(aUnits) ) { - throw new IllegalArgumentException("unable to calculate, b units not convertable to a"); - } else { - UnitsConverter uc= bUnits.getConverter(aUnits); - return Units.dimensionless.createDatum( a / uc.convert(b) ); - } - } - } -} diff --git a/dasCoreDatum/src/org/das2/datum/format/DateTimeDatumFormatter.java b/dasCoreDatum/src/org/das2/datum/format/DateTimeDatumFormatter.java deleted file mode 100755 index 81b63768d..000000000 --- a/dasCoreDatum/src/org/das2/datum/format/DateTimeDatumFormatter.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.datum.format; - -import org.das2.datum.Datum; -import org.das2.datum.DatumRange; -import org.das2.datum.DatumRangeUtil; -import org.das2.datum.DatumVector; -import org.das2.datum.TimeUtil; -import org.das2.datum.Units; - -/** - * - * @author jbf - */ -public class DateTimeDatumFormatter extends DatumFormatter { - - boolean dayOfYear= false; - - public boolean isDayOfYear() { - return dayOfYear; - } - - public void setDayOfYear(boolean dayOfYear) { - this.dayOfYear = dayOfYear; - } - - public static final int OPT_DOY= 1; - - /** - * create a formatter with the given options. Options should be or'd together a|b. - * @param opts - */ - public DateTimeDatumFormatter( int opts ) { - setDayOfYear( (opts&OPT_DOY)==OPT_DOY ); - } - - public DateTimeDatumFormatter() { - this(0); - } - - @Override - public String format(Datum datum) { - if ( !datum.getUnits().isConvertibleTo(Units.us2000 ) ) { - return "!Ktime!C!kexpected"; - } - double ssm= TimeUtil.getSecondsSinceMidnight(datum); - String date= null; - String time= TimeDatumFormatter.MINUTES.format(datum); - if ( ssm==0 ) { - if ( dayOfYear ) { - date= TimeDatumFormatter.DAY_OF_YEAR.format(datum); - } else { - date= TimeDatumFormatter.DAYS.format(datum); - } - } - return date==null ? time : date + " " + time; - } - - @Override - public String grannyFormat(Datum datum) { - double ssm= TimeUtil.getSecondsSinceMidnight(datum); - String date= null; - String time= TimeDatumFormatter.MINUTES.format(datum); - if ( ssm==0 ) { - if ( dayOfYear ) { - date= TimeDatumFormatter.DAY_OF_YEAR.format(datum); - } else { - date= TimeDatumFormatter.DAYS.format(datum); - } - } - return date==null ? time : time + "!c" + date; - } - - @Override - public String[] axisFormat( DatumVector datums, DatumRange context ) { - boolean haveMidnight= false; - boolean haveNonMidnight= false; - - int firstIndex= -1; - String[] result= new String[datums.getLength()]; - - if ( !datums.getUnits().isConvertibleTo(Units.us2000 ) || !context.getUnits().isConvertibleTo(Units.us2000 ) ) { - for ( int i=0; i=60e6 ) { - scale= TimeUtil.MINUTE; - } else if ( width>=1e6 ) { - scale= TimeUtil.SECOND; - } else if ( width>=1e3 ) { - scale= TimeUtil.MILLI; - } else { - scale= TimeUtil.MICRO; - } - } - - TimeDatumFormatter delegate= TimeDatumFormatter.formatterForScale(scale, context); - - for ( int i=0; i-1 ) { - Datum datum= datums.get(firstIndex); - String date; - if ( dayOfYear ) { - date= TimeDatumFormatter.DAY_OF_YEAR.format(datum); - } else { - date= TimeDatumFormatter.DAYS.format(datum); - } - result[firstIndex]= result[firstIndex] + "!c" + date; - } - } else { - for ( int i=0; i - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum.format; - -import java.util.logging.Logger; -import org.das2.datum.Datum; -import org.das2.datum.DatumRange; -import org.das2.datum.DatumVector; -import org.das2.datum.LoggerManager; -import org.das2.datum.Units; - -/** - * Formats Datum objects for printing and parses strings to Datum objects. - * @author Edward West - */ -public abstract class DatumFormatter { - - protected static final Logger logger= LoggerManager.getLogger("datum.formatter"); - - protected DatumFormatter() {} - - /* - * format the Datum so that it is understood out-of-context. For - * example, "4.5 seconds" - */ - public abstract String format( Datum datum ); - - /* - * format the Datum in the context of a given unit. For example, - * "4.5". It is acceptable to return the fully-qualified Datum, which - * is also the default class behavior. This will give somewhat undesirable - * results on axes. - */ - public String format( Datum datum, Units units ) { - return format( datum ); - } - - /** Returns the datum formatted as a String with special formatting - * characters. As with format, this should be out-of-context and should - * be tagged with the Units. - * - * The default implementation just returns the result of - * {@link #format(org.das2.datum.Datum)} - */ - public String grannyFormat(Datum datum) { - return format(datum); - } - - - /** formats the Datum in the context of the units. - */ - public String grannyFormat( Datum datum, Units units ) { - return format( datum, units ); - } - - /** - * format the set of Datums using a consistent and optimized format. - * First introduced to support DasAxis, where tighter coupling between - * the two is required to efficiently provide context. - * @param datums - * @param context visible range, context should be provided. - * @return - */ - public String[] axisFormat( DatumVector datums, DatumRange context ) { - String [] result= new String[datums.getLength()]; - for ( int i=0; i - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package org.das2.datum.format; - -import java.text.*; -import org.das2.datum.Datum; -import org.das2.datum.DatumRange; -import org.das2.datum.DatumVector; -import org.das2.datum.Units; - -/** Formats Datum objects for printing and parses strings to Datum objects. - * - * @author Edward West - */ -public class DefaultDatumFormatter extends DatumFormatter { - - private String formatString; - private NumberFormat format; - - /** Available for use by subclasses */ - protected DefaultDatumFormatter() { - } - - public DefaultDatumFormatter(String formatString) throws ParseException { - if (formatString.equals("")) { - this.formatString = ""; - format = null; - } else { - this.formatString = formatString; - format = NumberFormatUtil.getDecimalFormat(formatString); - } - } - - @Override - public String format(Datum datum) { - if ( datum.isFill() ) { - return "fill"; - } else { - return format(datum, datum.getUnits()) + " " + datum.getUnits(); - } - } - - @Override - public String format(Datum datum, Units units) { - double d = datum.doubleValue(units); - if (Double.isInfinite(d) || Double.isNaN(d)) { - return "" + d; - } - String result; - if (format == null) { - double resolution = datum.getResolution(units.getOffsetUnits()); - result = formatLimitedResolution(d, resolution); - } else { - result = format.format(datum.doubleValue(units)); - } - return result; - } - - @Override - public String grannyFormat(Datum datum, Units units) { - String formt = format(datum, units); - if (formt.contains("E")) { - int iE = formt.indexOf("E"); - StringBuilder granny = new StringBuilder(formt.length() + 4); - String mant = formt.substring(0, iE); - if (Double.parseDouble(mant) != 1.0) { - granny.append(mant).append("\u00d7"); - } - granny.append("10").append("!A").append(formt.substring(iE + 1)).append("!N"); - formt = granny.toString(); - } - return formt; - } - - @Override - public String[] axisFormat(DatumVector datums, DatumRange context ) { - Units units= context.getUnits(); - String[] result = new String[datums.getLength()]; - for (int i = 0; i < result.length; i++) { - result[i] = format(datums.get(i), units); - } - boolean hasMant = false; - for (String res1 : result) { - if (res1.contains("E")) { - int iE = res1.indexOf("E"); - String mant = res1.substring(0, iE); - if (Double.parseDouble(mant) != 1.0) { - hasMant = true; - } - } - } - for (int i = 0; i < result.length; i++) { - String res1 = result[i]; - if (res1.contains("E")) { - int iE = res1.indexOf("E"); - StringBuilder granny = new StringBuilder(res1.length() + 4); - String mant = res1.substring(0, iE); - - if (hasMant) { - granny.append(mant).append("\u00d7"); - } - - granny.append("10").append("!A").append(res1.substring(iE + 1)).append("!N"); - result[i] = granny.toString(); - } - - } - return result; - } - - @Override - public String grannyFormat(Datum datum) { - return grannyFormat(datum, datum.getUnits()) + " " + datum.getUnits(); - } - - @Override - public String toString() { - return formatString; - } - - private String formatLimitedResolution(double d, double resolution) { - String result; - if (resolution == 0. && Double.toString(d).length() > 7) { - // make the default resolution be 0.01%. - resolution = d / 10000; - } - if (resolution > 0) { - // 28 --> scale = -1 - // 2.8 --> scale = 0 - int scale = (int) Math.ceil(-1 * Math.log10(resolution) - 0.00001); - int exp; - if (d != 0.) { - exp = (int) Math.log10(Math.abs(d)); - } else { - exp = 0; - } - if (scale >= 0) { - DecimalFormat f; - if (exp <= -5 || exp >= 5) { - f = NumberFormatUtil.getDecimalFormat("0E0"); - f.setMinimumFractionDigits(scale + exp - 1); - f.setMaximumFractionDigits(scale + exp - 1); - } else { - f = NumberFormatUtil.getDecimalFormat("0"); - f.setMinimumFractionDigits(scale); - f.setMaximumFractionDigits(scale); - } - result = f.format(d); - } else { - double round = Math.pow( 10, -1*scale); - d = Math.round(d / round) * round; - DecimalFormat f; - if (exp <= -5 || exp >= 5) { - f = NumberFormatUtil.getDecimalFormat("0E0"); - f.setMinimumFractionDigits(scale + exp + 1); - f.setMaximumFractionDigits(scale + exp + 1); - } else { - f = NumberFormatUtil.getDecimalFormat("0"); - } - result = f.format(d); - } - } else { - result = Double.toString(d); - } - return result; - } -} diff --git a/dasCoreDatum/src/org/das2/datum/format/DefaultDatumFormatterFactory.java b/dasCoreDatum/src/org/das2/datum/format/DefaultDatumFormatterFactory.java deleted file mode 100644 index b286bb1cb..000000000 --- a/dasCoreDatum/src/org/das2/datum/format/DefaultDatumFormatterFactory.java +++ /dev/null @@ -1,60 +0,0 @@ -/* File: DatumFormatterFactory.java - * Copyright (C) 2002-2003 The University of Iowa - * - * Created on September 25, 2003, 3:35 PM - * by Edward West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum.format; - -/** - * - * @author Edward West - */ -public final class DefaultDatumFormatterFactory extends DatumFormatterFactory { - - private static DatumFormatterFactory factory; - - /** provided for use by subclasses */ - protected DefaultDatumFormatterFactory() { - } - - public DatumFormatter newFormatter(String format) throws java.text.ParseException { - return new DefaultDatumFormatter(format); - } - - public static DatumFormatterFactory getInstance() { - //This isn't thread safe, but who cares. Instances are small and - //functionally identical. - if (factory == null) { - factory = new DefaultDatumFormatterFactory(); - } - return factory; - } - - public DatumFormatter defaultFormatter() { - try { - return newFormatter(""); - } - catch (java.text.ParseException pe) { - throw new RuntimeException(pe); - } - } - -} diff --git a/dasCoreDatum/src/org/das2/datum/format/EnumerationDatumFormatter.java b/dasCoreDatum/src/org/das2/datum/format/EnumerationDatumFormatter.java deleted file mode 100644 index e8e5c9934..000000000 --- a/dasCoreDatum/src/org/das2/datum/format/EnumerationDatumFormatter.java +++ /dev/null @@ -1,52 +0,0 @@ -/* File: EnumerationDatumFormatter.java - * Copyright (C) 2002-2003 The University of Iowa - * - * Created on September 29, 2003, 4:34 PM - * by Edward West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum.format; - -import org.das2.datum.Datum; -import org.das2.datum.EnumerationUnits; - -/** - * - * @author Edward West - */ -public class EnumerationDatumFormatter extends DatumFormatter { - - public EnumerationDatumFormatter() { - } - - @Override - public String toString() { - return getClass().getName(); - } - - public String format(Datum datum) { - Object o= ((EnumerationUnits)datum.getUnits()).getObject(datum); - if ( o==null ) { - logger.severe("bad enumeration datum contains ordinal with no mapping to object"); - } - String s= String.valueOf( o ); - return s; - } - -} diff --git a/dasCoreDatum/src/org/das2/datum/format/EnumerationDatumFormatterFactory.java b/dasCoreDatum/src/org/das2/datum/format/EnumerationDatumFormatterFactory.java deleted file mode 100644 index 9b95e7d12..000000000 --- a/dasCoreDatum/src/org/das2/datum/format/EnumerationDatumFormatterFactory.java +++ /dev/null @@ -1,52 +0,0 @@ -/* File: EnumerationDatumFormatterFactory.java - * Copyright (C) 2002-2003 The University of Iowa - * - * Created on September 29, 2003, 4:34 PM - * by Edward West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum.format; - -/** - * - * @author Edward West - */ -public class EnumerationDatumFormatterFactory extends DatumFormatterFactory { - - private final static EnumerationDatumFormatterFactory INSTANCE - = new EnumerationDatumFormatterFactory(); - - private final static EnumerationDatumFormatter form= new EnumerationDatumFormatter(); - - private EnumerationDatumFormatterFactory() { - } - - public DatumFormatter defaultFormatter() { - return form; - } - - public DatumFormatter newFormatter(String format) throws java.text.ParseException { - return defaultFormatter(); - } - - public static EnumerationDatumFormatterFactory getInstance() { - return INSTANCE; - } - -} diff --git a/dasCoreDatum/src/org/das2/datum/format/ExponentialDatumFormatter.java b/dasCoreDatum/src/org/das2/datum/format/ExponentialDatumFormatter.java deleted file mode 100644 index a9d5f2bb1..000000000 --- a/dasCoreDatum/src/org/das2/datum/format/ExponentialDatumFormatter.java +++ /dev/null @@ -1,105 +0,0 @@ -/* File: DefaultDatumFormatter.java - * Copyright (C) 2002-2003 The University of Iowa - * - * Created on October 1, 2003, 4:45 PM - * by Edward West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum.format; - -import java.text.NumberFormat; -import org.das2.datum.Datum; -import org.das2.datum.Units; - -/** Formats Datums forcing a given exponent and number of decimal places. - * This is useful for axes where each of the labels should have the same - * exponent. Zero is treated specially, just "0" is returned. This helps - * one to quickly identify zero on the axis. - * - * @author Jeremy Faden - */ -public class ExponentialDatumFormatter extends DatumFormatter { - - int digits; - int exponent; - NumberFormat mantFormat; - String mantFormatString; - - /* print with digits in the mantissa, use exponent for the exponent */ - /* mEe */ - public ExponentialDatumFormatter(int digits, int exponent) { - this.digits= digits; - this.exponent= exponent; - StringBuffer buff = new StringBuffer(digits+2).append("0"); - if ( digits>1 ) buff.append('.'); - for (int i = 1; i< digits; i++) { - buff.append('0'); - } - mantFormatString= buff.toString(); - this.mantFormat= NumberFormatUtil.getDecimalFormat( buff.toString() ); - } - - public String format(Datum datum) { - return format( datum, datum.getUnits() ) + " " + datum.getUnits(); - } - - @Override - public String format( Datum datum, Units units ) { - double x= datum.doubleValue(datum.getUnits()); - if ( x == 0. ) return "0."; - double exp= Math.pow(10,exponent); - double mant= x/exp; - double tenToN= Math.pow(10,digits); - mant= Math.round( mant * tenToN ) / tenToN; - return mantFormat.format(mant)+"E"+exponent; - } - - @Override - public String grannyFormat( Datum datum, Units units ) { - String format= format(datum,units); - if ( format.indexOf("E")!=-1 ) { - int iE= format.indexOf("E"); - StringBuffer granny = new StringBuffer(format.length() + 4); - String mant= format.substring(0,iE); - granny.append(mant).append("\u00d7"); - granny.append("10").append("!A").append(format.substring(iE+1)).append("!N"); - format = granny.toString(); - } - return format; - } - - @Override - public String grannyFormat( Datum datum ) { - String format= format(datum,datum.getUnits()); - if ( format.indexOf("E")!=-1 ) { - int iE= format.indexOf("E"); - StringBuffer granny = new StringBuffer(format.length() + 4); - String mant= format.substring(0,iE); - granny.append(mant).append("\u00d7"); - granny.append("10").append("!A").append(format.substring(iE+1)).append("!N"); - format = granny.toString(); - } - return format + " " +datum.getUnits(); - } - - public String toString() { - return mantFormatString + "E"+exponent; - } - -} diff --git a/dasCoreDatum/src/org/das2/datum/format/FormatStringFormatter.java b/dasCoreDatum/src/org/das2/datum/format/FormatStringFormatter.java deleted file mode 100644 index 9c5c2b7a2..000000000 --- a/dasCoreDatum/src/org/das2/datum/format/FormatStringFormatter.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.datum.format; - -import java.util.IllegalFormatException; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.das2.datum.Datum; -import org.das2.datum.LoggerManager; -import org.das2.datum.Units; -import org.das2.datum.UnitsUtil; - -/** - * This is based on the C-style format strings introduced in Java 5 that - * we can now use. Note this should not be used for times. In the future this - * may be supported. - * @author jbf - */ -public class FormatStringFormatter extends DefaultDatumFormatter { - - private String format; - private boolean units; - private boolean integer; - - private static final Logger logger= LoggerManager.getLogger("datum.format"); - - /** - * create a new instance based on the Java format string. - * @param formatStr see http://download.oracle.com/javase/1.5.0/docs/api/java/util/Formatter.html#syntax - * @param units if true, append the units after the formatted string - */ - public FormatStringFormatter( String formatStr, boolean units ) { - if ( !formatStr.contains("%") ) { - throw new IllegalArgumentException("formatStr doesn't contain percent (%)"); - } - this.format= formatStr; - this.units= units; - - // attempt to use the string - try { - String s= String.format( format, 0. ); - logger.log( Level.FINEST, "format string results in {0}", s); - integer= false; - } catch ( IllegalFormatException ex ) { - integer= true; - } - } - - @Override - public String format(Datum datum) { - String s= format( datum, datum.getUnits() ); - if ( units ) { - s+= " " + datum.getUnits().toString(); - } - return s; - } - - - @Override - public String format(Datum datum, Units units) { - if ( UnitsUtil.isTimeLocation( datum.getUnits() ) ) { - throw new IllegalArgumentException("times not formatted"); - } else { - if ( integer ) { - return String.format( format, (int)datum.doubleValue(units) ); - } else { - return String.format( format, datum.doubleValue(units) ); - } - } - } - - @Override - public String toString() { - return String.format( "FormatStringFormatter(%s)", format ); - } -} diff --git a/dasCoreDatum/src/org/das2/datum/format/LatinPrefixDatumFormatter.java b/dasCoreDatum/src/org/das2/datum/format/LatinPrefixDatumFormatter.java deleted file mode 100644 index 43f4ab5f1..000000000 --- a/dasCoreDatum/src/org/das2/datum/format/LatinPrefixDatumFormatter.java +++ /dev/null @@ -1,91 +0,0 @@ -/* File: DefaultDatumFormatter.java - * Copyright (C) 2002-2003 The University of Iowa - * - * Created on October 1, 2003, 4:45 PM - * by Edward West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum.format; - -import java.text.NumberFormat; -import org.das2.datum.Datum; -import org.das2.datum.Units; - -/** Formats Datums using K and M, etc labels and a specified precision. - * - * @author Jeremy Faden - */ -public class LatinPrefixDatumFormatter extends DatumFormatter { - - int digits; - int exponent; - - /* print with digits in the mantissa */ - /* m.mmK */ - public LatinPrefixDatumFormatter( int digits ) { - this.digits= digits; - } - - public String format(Datum datum) { - return format( datum, datum.getUnits() ) + " " + datum.getUnits(); - } - - @Override - public String format( Datum datum, Units units ) { - double x= datum.doubleValue(units); - if ( x == 0. ) return "0."; - int expon= (int) Math.log10(1.000001*Math.abs(x)) / 3 * 3; - - String expString; - switch (expon) { - case -18: expString="a"; break; - case -15: expString="f"; break; - case -12: expString="p"; break; - case -9: expString="n";break; - case -6: expString="\u03BC";break; // micro - case -3: expString="m";break; - case 0: expString="";break; - case 3: expString="k";break; - case 6: expString="M";break; - case 9: expString="G";break; - case 12: expString="T"; break; - default: expString=""; expon=0; break; - } - - double exp= Math.pow(10,expon); - double mant= x / exp; - - int mantFracDigits= digits - (int)Math.log10(mant); - - StringBuffer buff = new StringBuffer(digits+2).append("0"); - if ( digits>1 ) buff.append('.'); - for (int i = 0; i< mantFracDigits; i++) { - buff.append('0'); - } - NumberFormat mantFormat= NumberFormatUtil.getDecimalFormat(buff.toString()); - - return mantFormat.format(mant) + expString; - } - - @Override - public String toString() { - return "EngineeringFormatter("+digits+" sig fig)"; - } - -} diff --git a/dasCoreDatum/src/org/das2/datum/format/TimeDatumFormatter.java b/dasCoreDatum/src/org/das2/datum/format/TimeDatumFormatter.java deleted file mode 100644 index 086de1862..000000000 --- a/dasCoreDatum/src/org/das2/datum/format/TimeDatumFormatter.java +++ /dev/null @@ -1,401 +0,0 @@ -/* File: TimeDatumFormatter.java - * Copyright (C) 2002-2003 The University of Iowa - * - * Created on September 25, 2003, 1:47 PM - * by Edward West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum.format; - -import java.text.Format; -import java.text.MessageFormat; -import java.text.ParseException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.das2.datum.Datum; -import org.das2.datum.DatumRange; -import org.das2.datum.TimeUtil; -import org.das2.datum.Units; - -/** - * Formatter is configured with strings of SimpleDateFormat (yyyy-MM-DD) - * or % percent format (%Y-%m-%d) specifiers. - * SimpleDateFormat is discouraged because most other parts of das2 use the - * shorter form. - * @author Edward West - */ -public class TimeDatumFormatter extends DatumFormatter { - - /** Private constants for referencing an array of timestamp fields */ - private static final int YEAR_FIELD_INDEX = 0; - private static final int MONTH_FIELD_INDEX = 1; - private static final int DAY_FIELD_INDEX = 2; - private static final int DOY_FIELD_INDEX = 3; - private static final int HOUR_FIELD_INDEX = 4; - private static final int MINUTE_FIELD_INDEX = 5; - private static final int SECONDS_FIELD_INDEX = 6; - private static final int TIMESTAMP_FIELD_COUNT = 7; - - /** - * yyyy-MM-dd'T'HH:mm:ss.SSS'Z - */ - public static final TimeDatumFormatter DEFAULT; - /** - * yyyy-MM-dd - */ - public static final TimeDatumFormatter DAYS; - - /** - * yyyy-JJJ - */ - public static final TimeDatumFormatter DAY_OF_YEAR; - - /** - * yyyy-MM-dd (JJJ) - */ - public static final TimeDatumFormatter DOY_DAYS; - - /** - * yyyy - */ - public static final TimeDatumFormatter YEARS; - /** - * yyyy-MM - */ - public static final TimeDatumFormatter MONTHS; - /** - * yyyy-MM-dd HH:'00' - */ - public static final TimeDatumFormatter HOURS; - /** - * HH:mm - */ - public static final TimeDatumFormatter MINUTES; - /** - * HH:mm:ss - */ - public static final TimeDatumFormatter SECONDS; - /** - * HH:mm:ss.SSS - */ - public static final TimeDatumFormatter MILLISECONDS; - /** - * HH:mm:ss.SSSSSS - */ - public static final TimeDatumFormatter MICROSECONDS; - /** - * HH:mm:ss.SSSSSSSSS - */ - public static final TimeDatumFormatter NANOSECONDS; - - //Initialize final constants - static { - try { - DEFAULT = new TimeDatumFormatter("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - YEARS= new TimeDatumFormatter("yyyy"); - MONTHS= new TimeDatumFormatter("yyyy-MM"); - DAYS = new TimeDatumFormatter("yyyy-MM-dd"); - DAY_OF_YEAR = new TimeDatumFormatter("%Y-%j"); - DOY_DAYS = new TimeDatumFormatter("%Y-%m-%d (%j)"); - HOURS = new TimeDatumFormatter("yyyy-MM-dd HH:'00'"); - MINUTES = new TimeDatumFormatter("HH:mm"); - SECONDS = new TimeDatumFormatter("HH:mm:ss"); - MILLISECONDS = new TimeDatumFormatter("HH:mm:ss.SSS"); - MICROSECONDS = new TimeDatumFormatter("HH:mm:ss.SSSSSS"); - NANOSECONDS = new TimeDatumFormatter("HH:mm:ss.SSSSSSSSS"); - } catch (ParseException pe) { - throw new RuntimeException(pe); - } - } - - private final String formatString; - - private final MessageFormat format; - - private int[] scaleSeconds; - - /** - * Creates a new instance of TimeDatumFormatter - * @param formatString - * @throws java.text.ParseException - */ - public TimeDatumFormatter(String formatString) throws ParseException { - this.formatString = formatString; - if ( formatString.contains( "%" ) ) { - format = new MessageFormat(parseTimeFormatStringPercent(formatString)); - } else { - format = new MessageFormat(parseTimeFormatString(formatString)); - } - } - - /** - * returns a TimeDatumFormatter suitable for the specified scale and context. - * Context may be null to indicate that the formatted string will be interpretted - * outside of any context. - * @param scale the length we wish to represent, such as TimeUtil.HOUR - * @param context the context for the formatter, or null if the formatted string - * will be interpreted outside of any context. - * @return the formatter. - * @throws IllegalArgumentException if the scale is TimeUtil.NANOS or is not found in TimeUtil. - */ - public static TimeDatumFormatter formatterForScale( int scale, DatumRange context ) { - try { - if ( context!=null ) { - switch ( scale ) { - case TimeUtil.YEAR: return YEARS; - case TimeUtil.MONTH: return MONTHS; - case TimeUtil.DAY: return DAYS; - case TimeUtil.HOUR: return MINUTES; - case TimeUtil.MINUTE: return MINUTES; - case TimeUtil.SECOND: return SECONDS; - case TimeUtil.MILLI: return MILLISECONDS; - case TimeUtil.MICRO: return MICROSECONDS; - case TimeUtil.NANO: return NANOSECONDS; - default: throw new IllegalArgumentException("unsupported scale: "+scale); - } - } else { - switch ( scale ) { - case TimeUtil.YEAR: return YEARS; - case TimeUtil.MONTH: return MONTHS; - case TimeUtil.DAY: return DAYS; - case TimeUtil.HOUR: return HOURS; - case TimeUtil.MINUTE: return new TimeDatumFormatter("yyyy-MM-dd HH:mm"); - case TimeUtil.SECOND: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss"); - case TimeUtil.MILLI: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss.SSS"); - case TimeUtil.MICRO: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss.SSSSSS"); - case TimeUtil.NANO: return new TimeDatumFormatter("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"); - default: throw new IllegalArgumentException("unsupported scale: "+scale); - } - } - } catch ( ParseException e ) { - throw new RuntimeException(e); - } - } - - @Override - public String toString() { - return formatString; - } - - @Override - public String format(Datum datum) { - if ( datum.isFill() ) return "fill"; - int mjd1958= (int)datum.doubleValue( Units.mj1958 ); - TimeUtil.TimeStruct ts; - if ( mjd1958 < -349909 ) { // year 1000 - ts= TimeUtil.toTimeStruct( Units.mj1958.createDatum(-349909) ); - } else if ( mjd1958 > 2937279 ) { // year 9999 // should be 2937613 rounding errors? - ts= TimeUtil.toTimeStruct( Units.mj1958.createDatum(2937279) ); - } else { - ts= TimeUtil.toTimeStruct(datum); - } - Number[] array = timeStructToArray(ts); - return format.format(array); - } - - protected Format getFormat() { - return format; - } - - /** - * parse message format strings like yyyy-MM-DD - * @param input - * @return - * @throws ParseException - */ - protected final String parseTimeFormatString(String input) throws ParseException { - final String formatPattern = "(([yMDdHmsS])\\2*)"; - final String delimiterPattern = "([-/:.,_ \t]+)"; - final String literalPattern = "('(?:[^']|'')*')"; - Pattern token = Pattern.compile( - formatPattern + "|" + delimiterPattern + "|" + literalPattern - ); - int from = 0; - StringBuilder frmtString = new StringBuilder(); - Matcher matcher = token.matcher(input); - while (matcher.find(from)) { - int start = matcher.start(); - if (start > from) { - char[] dots = new char[start + 1]; - java.util.Arrays.fill(dots, from, start, '.'); - dots[from] = '^'; - dots[start] = '^'; - StringBuilder errorString = new StringBuilder("Unrecognized sub-pattern\n"); - errorString.append(input).append("\n"); - errorString.append(dots); - throw new ParseException(errorString.toString(), from); - } - String frmt = matcher.group(1); - String delimiter = matcher.group(3); - String literal = matcher.group(4); - if (frmt != null) { - switch (frmt.charAt(0)) { - case 'y': { - appendSubFormat(frmtString, YEAR_FIELD_INDEX, frmt.length()); - } break; - case 'M': { - appendSubFormat(frmtString, MONTH_FIELD_INDEX, frmt.length()); - } break; - case 'D': { - appendSubFormat(frmtString, DOY_FIELD_INDEX, frmt.length()); - } break; - case 'd': { - appendSubFormat(frmtString, DAY_FIELD_INDEX, frmt.length()); - } break; - case 'H': { - appendSubFormat(frmtString, HOUR_FIELD_INDEX, frmt.length()); - } break; - case 'm': { - appendSubFormat(frmtString, MINUTE_FIELD_INDEX, frmt.length()); - } break; - case 's': { - appendSubFormat(frmtString, SECONDS_FIELD_INDEX, frmt.length()); - }break; - case 'S': { - int digitCount = frmt.length(); - int fieldIndex = addScaleFactor(digitCount); - appendSubFormat(frmtString, fieldIndex, digitCount); - } - break; - default: break; - } - } else if (delimiter != null) { - frmtString.append(delimiter); - } else if (literal != null) { - literal = literal.substring(1, literal.length() - 1); - literal = literal.replaceAll("''", "'"); - frmtString.append(literal); - } - from = matcher.end(); - } - return frmtString.toString(); - } - - /** - * create the message format, based on %Y, %m, %d format specification - * of the UNIX date command. - * @param format the format spec, such as %Y%m%dT%H%M - * @return a SimpleDateFormat string. - * @throws java.text.ParseException - */ - protected final String parseTimeFormatStringPercent(String format) throws ParseException { - StringBuilder frmtString= new StringBuilder(); - String[] ss= format.split("%"); - frmtString.append(ss[0]); - int offset= ss[0].length(); - - for ( int i=1; i - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.datum.format; - -/** - * - * @author Edward West - */ -public class TimeDatumFormatterFactory extends DatumFormatterFactory { - - private static TimeDatumFormatterFactory factory; - - private TimeDatumFormatterFactory() {} - - public DatumFormatter defaultFormatter() { - return TimeDatumFormatter.DEFAULT; - } - - public DatumFormatter newFormatter(String format) throws java.text.ParseException { - return new TimeDatumFormatter(format); - } - - public static TimeDatumFormatterFactory getInstance() { - //This isn't thread safe, but who cares. Instances are small and - //functionally identical. - if (factory == null) { - factory = new TimeDatumFormatterFactory(); - } - return factory; - } - -} diff --git a/dasCoreDatum/src/org/das2/datum/package.html b/dasCoreDatum/src/org/das2/datum/package.html deleted file mode 100644 index ffb995b89..000000000 --- a/dasCoreDatum/src/org/das2/datum/package.html +++ /dev/null @@ -1,27 +0,0 @@ - -

    Provides classes representing physical quanties. The Units class -defines an enumeration of physical units such as Units.hertz and Units.microseconds. It -also contains a convention for identifying locations in time with units like -Units.t1970, the number of microseconds elapsed since midnight, January 1, 1970. -The Units class also contains a registry of Units and the conversions between them. -

    -

    A Datum is a number in the context of a Unit, for example "15 microseconds." - A Datum object has methods for formatting itself as a String, representing - itself as a double in the context of another Unit, and mathematical -operators that allow simple calculations to be made at the physical quantities. -Also, a Datum's precision can be limited to improve formatting. -

    -

    A DatumRange is a pair of Datums that identify a range in the physical space -of a Unit. An example of a formatted DatumRange is "January, 2005" or "0 to 50Hz".

    - -

    Time ranges are a special case of DatumRange that are used often. These include "March 2010", - "2012-12-14T00:00/2012-12-14T01:00", and "2012-12-14T00:00:00.001/2012-12-14T00:00:00.002". - Note that DatumRanges provide a next() and previous() method, so "April 2010" would be the - result of next called on the datum range for "March 2010". There are also special cases - of time ranges like orbit ranges. These are formatted like "orbit:rbspa-pp:5" is the time interval - for orbit 5 of the RBSP-A (Van Allen) probe.

    - - -

    Also there are utility classes for parsing and formatting times.

    - - \ No newline at end of file diff --git a/dasCoreDatum/src/test/TestTimeParser.java b/dasCoreDatum/src/test/TestTimeParser.java deleted file mode 100644 index dc4adc744..000000000 --- a/dasCoreDatum/src/test/TestTimeParser.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package test; - -import java.text.ParseException; -import org.das2.datum.DatumRangeUtil; - -/** - * - * @author jbf - */ -public class TestTimeParser { - - private static void test1( String test, String norm ) throws ParseException { - if ( ! DatumRangeUtil.parseISO8601Range(norm).equals( DatumRangeUtil.parseISO8601Range(test) ) ) { - throw new RuntimeException("fails to match: "+test); - } - } - public static void main( String[] args ) throws ParseException { - DatumRangeUtil.parseTimeRangeValid("P5D"); - test1( "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z", "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z" ); - test1( "2007-03-01T13:00:00Z/P1Y2M10DT2H30M", "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z" ); - test1( "P1Y2M10DT2H30M/2008-05-11T15:30:00Z", "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z" ); - test1( "2007-03-01T00:00Z/P1D", "2007-03-01T00:00:00Z/2007-03-02T00:00:00Z" ); - - } -} diff --git a/dasCoreUtil/build.xml b/dasCoreUtil/build.xml deleted file mode 100644 index 1c3fcb5bf..000000000 --- a/dasCoreUtil/build.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - Builds, tests, and runs the project dasCoreUtil. - - - diff --git a/dasCoreUtil/lib/CopyLibs-2/org-netbeans-modules-java-j2seproject-copylibstask.jar b/dasCoreUtil/lib/CopyLibs-2/org-netbeans-modules-java-j2seproject-copylibstask.jar deleted file mode 100644 index 04089d5f8..000000000 Binary files a/dasCoreUtil/lib/CopyLibs-2/org-netbeans-modules-java-j2seproject-copylibstask.jar and /dev/null differ diff --git a/dasCoreUtil/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar b/dasCoreUtil/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar deleted file mode 100644 index 5ee71d2e3..000000000 Binary files a/dasCoreUtil/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar and /dev/null differ diff --git a/dasCoreUtil/lib/batik-awt-util.jar b/dasCoreUtil/lib/batik-awt-util.jar deleted file mode 100644 index 8064779fa..000000000 Binary files a/dasCoreUtil/lib/batik-awt-util.jar and /dev/null differ diff --git a/dasCoreUtil/lib/batik-svggen.jar b/dasCoreUtil/lib/batik-svggen.jar deleted file mode 100644 index e732b5499..000000000 Binary files a/dasCoreUtil/lib/batik-svggen.jar and /dev/null differ diff --git a/dasCoreUtil/lib/batik-util.jar b/dasCoreUtil/lib/batik-util.jar deleted file mode 100644 index d153e4bb6..000000000 Binary files a/dasCoreUtil/lib/batik-util.jar and /dev/null differ diff --git a/dasCoreUtil/lib/commons-net-2.0.jar b/dasCoreUtil/lib/commons-net-2.0.jar deleted file mode 100644 index dd7a52a69..000000000 Binary files a/dasCoreUtil/lib/commons-net-2.0.jar and /dev/null differ diff --git a/dasCoreUtil/lib/commons-net-ftp-2.0.jar b/dasCoreUtil/lib/commons-net-ftp-2.0.jar deleted file mode 100644 index f697534cf..000000000 Binary files a/dasCoreUtil/lib/commons-net-ftp-2.0.jar and /dev/null differ diff --git a/dasCoreUtil/lib/commons-vfs-1.0.jar b/dasCoreUtil/lib/commons-vfs-1.0.jar deleted file mode 100644 index 7992d21b0..000000000 Binary files a/dasCoreUtil/lib/commons-vfs-1.0.jar and /dev/null differ diff --git a/dasCoreUtil/lib/itext-1.2.jar b/dasCoreUtil/lib/itext-1.2.jar deleted file mode 100644 index 8d82f9ac9..000000000 Binary files a/dasCoreUtil/lib/itext-1.2.jar and /dev/null differ diff --git a/dasCoreUtil/lib/itextpdf-5.4.3.jar b/dasCoreUtil/lib/itextpdf-5.4.3.jar deleted file mode 100644 index 478af455d..000000000 Binary files a/dasCoreUtil/lib/itextpdf-5.4.3.jar and /dev/null differ diff --git a/dasCoreUtil/lib/junit/junit-3.8.2-api.zip b/dasCoreUtil/lib/junit/junit-3.8.2-api.zip deleted file mode 100644 index 6d792fdaa..000000000 Binary files a/dasCoreUtil/lib/junit/junit-3.8.2-api.zip and /dev/null differ diff --git a/dasCoreUtil/lib/junit/junit-3.8.2.jar b/dasCoreUtil/lib/junit/junit-3.8.2.jar deleted file mode 100644 index d83587261..000000000 Binary files a/dasCoreUtil/lib/junit/junit-3.8.2.jar and /dev/null differ diff --git a/dasCoreUtil/lib/junit_4/junit-4.5-api.zip b/dasCoreUtil/lib/junit_4/junit-4.5-api.zip deleted file mode 100644 index 5748c444d..000000000 Binary files a/dasCoreUtil/lib/junit_4/junit-4.5-api.zip and /dev/null differ diff --git a/dasCoreUtil/lib/junit_4/junit-4.5-src.jar b/dasCoreUtil/lib/junit_4/junit-4.5-src.jar deleted file mode 100644 index 18774a573..000000000 Binary files a/dasCoreUtil/lib/junit_4/junit-4.5-src.jar and /dev/null differ diff --git a/dasCoreUtil/lib/junit_4/junit-4.5.jar b/dasCoreUtil/lib/junit_4/junit-4.5.jar deleted file mode 100644 index 83f8bc793..000000000 Binary files a/dasCoreUtil/lib/junit_4/junit-4.5.jar and /dev/null differ diff --git a/dasCoreUtil/lib/nblibraries.properties b/dasCoreUtil/lib/nblibraries.properties deleted file mode 100644 index 7ed6915ef..000000000 --- a/dasCoreUtil/lib/nblibraries.properties +++ /dev/null @@ -1,24 +0,0 @@ -libs.ApacheCommons.classpath=\ - ${base}/commons-net-2.0.jar:\ - ${base}/commons-net-ftp-2.0.jar:\ - ${base}/commons-vfs-1.0.jar -libs.BatikSVGGen1.5.classpath=\ - ${base}/batik-awt-util.jar:\ - ${base}/batik-svggen.jar:\ - ${base}/batik-util.jar -libs.CopyLibs.classpath=\ - ${base}/CopyLibs-2/org-netbeans-modules-java-j2seproject-copylibstask.jar -libs.CopyLibs.displayName=CopyLibs Task -libs.CopyLibs.prop-version=2.0 -libs.iText1.3.classpath=\ - ${base}/itextpdf-5.4.3.jar -libs.junit.classpath=\ - ${base}/junit/junit-3.8.2.jar -libs.junit.javadoc=\ - ${base}/junit/junit-3.8.2-api.zip -libs.junit_4.classpath=\ - ${base}/junit_4/junit-4.5.jar -libs.junit_4.javadoc=\ - ${base}/junit_4/junit-4.5-api.zip -libs.junit_4.src=\ - ${base}/junit_4/junit-4.5-src.jar diff --git a/dasCoreUtil/nbproject/build-impl.xml b/dasCoreUtil/nbproject/build-impl.xml deleted file mode 100644 index ac305bdbf..000000000 --- a/dasCoreUtil/nbproject/build-impl.xml +++ /dev/null @@ -1,919 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set src.dir - Must set test.src.dir - Must set build.dir - Must set dist.dir - Must set build.classes.dir - Must set dist.javadoc.dir - Must set build.test.classes.dir - Must set build.test.results.dir - Must set build.classes.excludes - Must set dist.jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - - - - - - java -cp "${run.classpath.with.dist.jar}" ${main.class} - - - - - - - - - - - - To run this application from the command line without Ant, try: - - java -jar "${dist.jar.resolved}" - - - - - - - - To run this application from the command line without Ant, try: - - java -jar "${dist.jar.resolved}" - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - Must select one file in the IDE or set run.class - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set debug.class - - - - - Must select one file in the IDE or set debug.class - - - - - Must set fix.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - Some tests failed; see details above. - - - - - - - - - Must select some files in the IDE or set test.includes - - - - Some tests failed; see details above. - - - - - Must select one file in the IDE or set test.class - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dasCoreUtil/nbproject/genfiles.properties b/dasCoreUtil/nbproject/genfiles.properties deleted file mode 100644 index 441aab18e..000000000 --- a/dasCoreUtil/nbproject/genfiles.properties +++ /dev/null @@ -1,8 +0,0 @@ -build.xml.data.CRC32=502b5976 -build.xml.script.CRC32=cec4e0d3 -build.xml.stylesheet.CRC32=28e38971@1.38.2.45 -# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. -# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=502b5976 -nbproject/build-impl.xml.script.CRC32=cf5d3436 -nbproject/build-impl.xml.stylesheet.CRC32=229523de@1.38.3.45 diff --git a/dasCoreUtil/nbproject/private/private.properties b/dasCoreUtil/nbproject/private/private.properties deleted file mode 100644 index c5afcd8d8..000000000 --- a/dasCoreUtil/nbproject/private/private.properties +++ /dev/null @@ -1,6 +0,0 @@ -compile.on.save=true -do.depend=false -do.jar=true -javac.debug=true -javadoc.preview=true -user.properties.file=/home/jbf/.netbeans/6.9/build.properties diff --git a/dasCoreUtil/nbproject/project.properties b/dasCoreUtil/nbproject/project.properties deleted file mode 100644 index 7c3d3bfa8..000000000 --- a/dasCoreUtil/nbproject/project.properties +++ /dev/null @@ -1,74 +0,0 @@ -annotation.processing.enabled=true -annotation.processing.enabled.in.editor=false -annotation.processing.run.all.processors=true -annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output -application.title=dasCoreUtil -application.vendor=jbf -build.classes.dir=${build.dir}/classes -build.classes.excludes=**/*.java,**/*.form -# This directory is removed when the project is cleaned: -build.dir=build -build.generated.dir=${build.dir}/generated -build.generated.sources.dir=${build.dir}/generated-sources -# Only compile against the classpath explicitly listed here: -build.sysclasspath=ignore -build.test.classes.dir=${build.dir}/test/classes -build.test.results.dir=${build.dir}/test/results -# Uncomment to specify the preferred debugger connection transport: -#debug.transport=dt_socket -debug.classpath=\ - ${run.classpath} -debug.test.classpath=\ - ${run.test.classpath} -# This directory is removed when the project is cleaned: -dist.dir=dist -dist.jar=${dist.dir}/dasCoreUtil.jar -dist.javadoc.dir=${dist.dir}/javadoc -endorsed.classpath= -excludes= -includes=** -jar.compress=false -javac.classpath=\ - ${libs.BatikSVGGen1.5.classpath}:\ - ${libs.iText1.3.classpath}:\ - ${libs.ApacheCommons.classpath} -# Space-separated list of extra javac options -javac.compilerargs= -javac.deprecation=false -javac.processorpath=\ - ${javac.classpath} -javac.source=1.6 -javac.target=1.6 -javac.test.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir}:\ - ${libs.junit.classpath}:\ - ${libs.junit_4.classpath} -javac.test.processorpath=\ - ${javac.test.classpath} -javadoc.additionalparam= -javadoc.author=false -javadoc.encoding=${source.encoding} -javadoc.noindex=false -javadoc.nonavbar=false -javadoc.notree=false -javadoc.private=false -javadoc.splitindex=true -javadoc.use=true -javadoc.version=false -javadoc.windowtitle= -meta.inf.dir=${src.dir}/META-INF -platform.active=default_platform -run.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -# Space-separated list of JVM arguments used when running the project -# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value -# or test-sys-prop.name=value to set system properties for unit tests): -run.jvmargs= -run.test.classpath=\ - ${javac.test.classpath}:\ - ${build.test.classes.dir} -source.encoding=UTF-8 -src.dir=src -test.src.dir=test diff --git a/dasCoreUtil/nbproject/project.xml b/dasCoreUtil/nbproject/project.xml deleted file mode 100644 index 81091c88b..000000000 --- a/dasCoreUtil/nbproject/project.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - org.netbeans.modules.java.j2seproject - - - dasCoreUtil - - - - - - - - - ./lib/nblibraries.properties - - - diff --git a/dasCoreUtil/src/org/das2/util/AboutUtil.java b/dasCoreUtil/src/org/das2/util/AboutUtil.java deleted file mode 100644 index 69f3c768d..000000000 --- a/dasCoreUtil/src/org/das2/util/AboutUtil.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package org.das2.util; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * method for getting useful build and version information. - * TODO: Splash should call this to get version, not the other way around. - * @author jbf - */ -public class AboutUtil { - - private static final Logger logger= LoggerManager.getLogger("das2"); - - public static String getAboutHtml() { - String dasVersion = Splash.getVersion(); - String javaVersion = System.getProperty("java.version"); // applet okay - String buildTime = "???"; - java.net.URL buildURL = AboutUtil.class.getResource("/buildTime.txt"); - if (buildURL != null) { - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(buildURL.openStream())); - buildTime = reader.readLine(); - reader.close(); - } catch (IOException ex) { - logger.log( Level.WARNING, ex.getMessage(), ex ); - } - } - String arch = System.getProperty("os.arch"); // applet okay - DecimalFormat nf = new DecimalFormat("0.0"); - String mem = nf.format(Runtime.getRuntime().maxMemory() / (1024 * 1024)); - StringBuilder aboutContent = new StringBuilder("" + - //"
    " + - "release version: " + dasVersion + - "\n
    build time: " + buildTime + - "\n
    java version: " + javaVersion + - "\n
    max memory (Mb): " + mem + - "\n
    arch: " + arch + - "\n
    \n"); - - try { - List bis = getBuildInfos(); - for (String bi : bis) { - aboutContent.append( "
    " ).append(bi).append("\n"); - } - } catch (IOException ex) { - logger.log( Level.WARNING, ex.getMessage(), ex ); - } - - aboutContent.append( "" ); - - return aboutContent.toString(); - } - - /** - * searches class path for META-INF/build.txt, returns nice strings - * @return one line per jar - * @throws IOException when META-INF/build.txt cannot be loaded. - */ - public static List getBuildInfos() throws IOException { - ClassLoader loader= AboutUtil.class.getClassLoader(); - if ( loader==null ) loader= ClassLoader.getSystemClassLoader(); - Enumeration urls = loader.getResources("META-INF/build.txt"); - - List result = new ArrayList(); - - while (urls.hasMoreElements()) { - URL url = urls.nextElement(); - - String jar = url.toString(); - - int i = jar.indexOf(".jar"); - int i0 = jar.lastIndexOf("/", i - 1); - - String name; - if (i != -1) { - name = jar.substring(i0 + 1, i + 4); - } else { - name = jar.substring(6); - } - - Properties props = new Properties(); - props.load(url.openStream()); - - String cvsTagName = props.getProperty("build.tag"); - String version; - if (cvsTagName == null || cvsTagName.length() <= 9) { - version = "untagged_version"; - } else { - version = cvsTagName.substring(6, cvsTagName.length() - 2); - } - - String ch= name + ": " + version + "(" + props.getProperty("build.timestamp") + " " + props.getProperty("build.user.name") + ")"; - if ( result.contains(ch) ) { - - } else { - result.add(ch); - } - - } - return result; - - } - - public static String getReleaseTag( Class clas ) throws IOException { - Properties props = new Properties(); - String clasFile= clas.getName().replaceAll("\\.","/")+".class"; - URL url = clas.getClassLoader().getResource( clasFile ); - if ( url!=null ) { - String surl= url.toString(); - url= new URL( new URL( surl.substring(0,surl.length()-clasFile.length() ) ), "META-INF/build.txt" ); - props.load(url.openStream()); - String tagName = props.getProperty("build.tag"); - if ( tagName!=null && tagName.trim().length()>0 ) { - return tagName; - } - } - return "(dev)"; - } - - /** - * Identify the release version by looking a non-null build.tag. It's expected - * that the build script will insert build.tag into META-INF/build.txt - * @return build tag, which should not contain spaces, or - * (dev) if no tag is found. - * @throws java.io.IOException - */ - public static String getReleaseTag() throws IOException { - Enumeration urls = AboutUtil.class.getClassLoader().getResources("META-INF/build.txt"); - Properties props = new Properties(); - while (urls.hasMoreElements()) { - URL url = urls.nextElement(); - props.load(url.openStream()); - String tagName = props.getProperty("build.tag"); - if ( tagName!=null && tagName.trim().length()>0 ) { - return tagName; - } - } - return "(dev)"; - } - - /** - * Identify the release by looking for build.jenkinsURL . It's expected - * that the build script will insert build.tag into META-INF/build.txt - * @return build URL, or "" if one is not found. - * @throws java.io.IOException - */ - public static String getJenkinsURL() throws IOException { - Enumeration urls = AboutUtil.class.getClassLoader().getResources("META-INF/build.txt"); - Properties props = new Properties(); - while (urls.hasMoreElements()) { - URL url = urls.nextElement(); - props.load(url.openStream()); - String tagName = props.getProperty("build.jenkinsURL"); - if ( tagName!=null && tagName.trim().length()>0 ) { - return tagName; - } - } - return ""; - } - -} diff --git a/dasCoreUtil/src/org/das2/util/ArgumentList.java b/dasCoreUtil/src/org/das2/util/ArgumentList.java deleted file mode 100755 index f08d644d3..000000000 --- a/dasCoreUtil/src/org/das2/util/ArgumentList.java +++ /dev/null @@ -1,605 +0,0 @@ -package org.das2.util; - -import java.io.File; -import java.util.*; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.prefs.AbstractPreferences; -import java.util.prefs.BackingStoreException; -import java.util.prefs.Preferences; - -/** - * Utility class for processing the String[] arguments passed into the main routine, - * handing positional and switch parameters. Also automatically generates the - * usage documentation. - * - * Note in Autoplot's pngwalk, we add a parameter and positional argument with the - * same name. This should continue to be supported. - */ -public class ArgumentList { - - int nposition; - - String programName; - - String[] positionKeys; - - Map values; - - Map descriptions; - - Map names; - - Map reverseNames; - - Map formUsed; - - Map abbrevs; - - ArrayList requireOneOfList; - - /** - * if false, then any unrecognized switch is an error. - */ - boolean allowUndefinedSwitch= false; - - private String UNSPECIFIED = "__unspecified__"; - - private String REFERENCEWITHOUTVALUE = "__referencewithoutvalue__"; - - private String UNDEFINED_SWITCH = "__undefinedSwitch__"; - - public String FALSE = "__false__"; - - public String TRUE = "__true__"; - - private static final Logger logger= Logger.getLogger( "das2.util" ); - - /** - * creates the processor for the program. programName is provided - * for the usage statement. After creating the object, arguments are - * specified one by one, and then the process method is called. - */ - public ArgumentList(String programName) { - this.programName= programName; - positionKeys= new String[10]; - values= new HashMap(); - descriptions= new HashMap(); - names= new LinkedHashMap(); - reverseNames= new HashMap(); - abbrevs= new HashMap(); - formUsed= new HashMap(); - requireOneOfList= new ArrayList(); - } - - /** - * get the value for this parameter - * @param key the argument identifier. - * @return the parameter's value. - * @throws IllegalArgumentException if the parameter name was never described. - */ - public String getValue(String key) { - if ( values.containsKey(key) ) { - return (String)values.get( key ); - } else { - throw new IllegalArgumentException( "No such key: "+key ); - } - } - - /** - * make a standard way to make file names absolute - * @param ref e.g. files/afile/foo.txt - * @return /home/jbf/data/files/afile/foo.txt because PWD is /home/jbf/data - */ - public static String makeFileReferenceAbsolute( String ref ) { - if ( ref.startsWith("file://") ) ref= ref.substring(7); - if ( ref.startsWith("file:" ) ) ref= ref.substring(5); - if ( ref.startsWith("http://") || ref.startsWith("https://") || ref.startsWith("sftp://") || ref.startsWith("ftp://") ) { - throw new IllegalArgumentException("local file reference expected"); - } else { - File ff= new File(ref); - if ( !ff.isAbsolute() ) { - return ff.getAbsolutePath(); - } else { - return ref; - } - } - } - - /** - * returns the options as a java.util.prefs.Preferences object, for - * batch processes. The idea is that a process which grabs default - * settings from the user Preferences can instead get them from the command - * line, to support batch processes. See the Vg1pws app for an example of - * how this is used. - * - * @return a Preferences object, loaded with the command line values. - */ - public Preferences getPreferences() { - return new AbstractPreferences(null,"") { - protected void putSpi(String key, String value) { - formUsed.put(key,value); - values.put(key,value); - } - - protected String getSpi(String key) { - if ( formUsed.containsKey(key) ) { - return (String) values.get(key); - } else { - return null; - } - } - - protected void removeSpi(String key) { - // do nothing - } - - protected void removeNodeSpi() throws BackingStoreException { - // do nothing - } - - protected String[] keysSpi() throws BackingStoreException { - return (String[])values.keySet().toArray(new String[values.size()]); - } - - protected String[] childrenNamesSpi() throws BackingStoreException { - return new String[0]; - } - - protected AbstractPreferences childSpi(String name) { - return null; - } - - protected void syncSpi() throws BackingStoreException { - // do nothing - } - - protected void flushSpi() throws BackingStoreException { - // do nothing - } - }; - } - - public boolean getBooleanValue(String key) { - return values.get( key ) .equals( this.TRUE ); - } - - /** - * Specify the ith positional argument. - * - * @param position the position number, 0 is the first argument position after the class name. - * @param key the internal reference name to get the value specified. - * @param description a short (40 character) description of the argument. - */ - public void addPositionArgument(int position, String key, String description) { - if ( position>nposition ) { - throw new IllegalArgumentException( "Position arguments must be specified 0,1,2,3: position="+position ); - } - if ( position>positionKeys.length ) { - throw new IllegalArgumentException( "Position too big: position="+position ); - } - nposition= position+1; - positionKeys[position]= key; - descriptions.put(key, description); - values.put( key, UNSPECIFIED ); - } - - /** - * requires the user specify one of these values, otherwise the usage - * statement is printed. - * @param keyNames an array of internal key names that identify parameters. - */ - public void requireOneOf( String[] keyNames ) { - requireOneOfList.add(keyNames); - } - - /** - * Specify the ith positional argument, which may be left unspecified by - * the user. Note that all positional arguments after this one must also be - * optional. - * - * @param position the position number, 0 is the first argument position after the class name. - * @param key the internal reference name to get the value specified. - * @param defaultValue the value that is returned if a value is not provided by the user. - * @param description a short (40 character) description of the argument. - */ - public void addOptionalPositionArgument(int position, String key, String defaultValue, String description) { - if ( key==null ) throw new IllegalArgumentException("null key"); - addPositionArgument( position, key, description ); - values.put(key,defaultValue); - if ( defaultValue==null ) { - if ( false ) System.err.println("breakpoint"); - } - } - - /** - * specify a named switch argument that must be specified by the user. For example, --level=3 or -l=3 - * @param name the long parameter name, which the user may enter. e.g. "level" - * @param abbrev short (one letter) parameter version. e.g. "l" for -l=3 - * @param key the internal reference name to get the value specified, not necessarily but often the same as name. - * @param description a short (40 character) description of the argument. - */ - public void addSwitchArgument(String name, String abbrev, String key, String description) { - if ( abbrev==null && name==null ) { - throw new IllegalArgumentException( "both abbrev and name are null, one must be specified" ); - } - if ( key==null ) throw new IllegalArgumentException("null key"); - descriptions.put( key, description ); - if ( abbrev!=null ) { - if ( abbrevs.containsKey(abbrev) ) { - throw new IllegalArgumentException( "abbrev already used: "+abbrev ); - } - abbrevs.put( abbrev, key ); - } - if ( name!=null ) { - names.put( name, key ); - reverseNames.put( key, name ); - } - values.put( key, UNSPECIFIED ); - } - - /** - * specify a named switch argument that may be specified by the user. For example, --level=3 or -l=3 - * @param name the long parameter name, which the user may enter. e.g. "level" - * @param abbrev short (one letter) parameter version. e.g. "l" for -l=3 - * @param defaultValue value to return if the user doesn't specify. If TRUE or FALSE is used, then the - * user may use a number of different inputs such as "T" or "true", and getBooleanValue can be used to read the value - * @param key the internal reference name to get the value specified, not necessarily but often the same as name. - * @param description a short (40 character) description of the argument. - */ - public void addOptionalSwitchArgument(String name, String abbrev, String key, String defaultValue, String description) { - addSwitchArgument( name, abbrev, key, description ); - if ( key==null ) throw new IllegalArgumentException("null key"); - values.put( key, defaultValue ); - if ( defaultValue==null ) { - if ( false ) System.err.println("breakpoint"); - } - } - - /** - * specify a named switch argument that is named, and we only care whether it was used or not. e.g. --debug - * @param name the long parameter name, which the user may enter. e.g. "level" - * @param abbrev short (one letter) parameter version. e.g. "l" for -l=3 - * @param key the internal reference name to get the value specified, not necessarily but often the same as name. - * @param description a short (40 character) description of the argument. - */ - public void addBooleanSwitchArgument(String name, String abbrev, String key, String description) { - if ( key.equals("commandLinePrefs") ) allowUndefinedSwitch=true; - addOptionalSwitchArgument( name, abbrev, key, FALSE, description ); - } - - /** - * print the usage statement out to stderr. - */ - public void printUsage() { - StringBuilder s; - s= new StringBuilder( "Usage: " ); - s.append(this.programName).append(" "); - for ( int i=0; i "); - } - } - - System.err.println(s.toString()); - - Set set= names.keySet(); - Iterator i= set.iterator(); - - Map abbrevsCopy= new HashMap(abbrevs); - - while ( i.hasNext() ) { - String name= i.next(); - String key= names.get(name); - String abbrev=null; - for ( Entry se: abbrevsCopy.entrySet() ) { - if ( se.getValue().equals(name) ) { - abbrev= se.getKey(); - break; - } - } - if ( abbrev!=null ) { - abbrevsCopy.remove(abbrev); - } - - s= new StringBuilder(" "); - String description= descriptions.get(key); - String value= values.get(key); - - if ( abbrev==null ) { - if ( !this.UNSPECIFIED.equals(value) ) { - if ( this.FALSE.equals(value) || this.TRUE.equals(value) ) { - s.append( String.format( "--%s \t%s", name, description ) ); - } else { - s.append( String.format( "--%s= \t%s ", name, description ) ); - } - } else { - s.append( String.format( "--%s= \t%s (required)", name, description ) ); - } - } else { - if ( !this.UNSPECIFIED.equals(value) ) { - if ( this.FALSE.equals(value) || this.TRUE.equals(value) ) { - s.append( String.format( "-%s, --%s \t%s", abbrev, name, description ) ); - } else { - s.append( String.format( "-%s, --%s= \t%s ", abbrev, name, description ) ); - } - } else { - s.append( String.format( "-%s, --%s= \t%s (required)", abbrev, name, description ) ); - } - } - System.err.println(s.toString()); - } - - set= abbrevsCopy.keySet(); - i= set.iterator(); - - while ( i.hasNext() ) { - String abbrev= i.next(); - String key= abbrevs.get(abbrev); - s= new StringBuilder(" "); - String description= descriptions.get(key); - if ( !this.UNSPECIFIED.equals(values.get(key) ) ) { - if ( this.FALSE.equals(values.get(key)) || this.TRUE.equals(values.get(key)) ) { - s.append( String.format( "-%s \t%s", abbrev, description ) ); - } else { - s.append( String.format( "-%s=%s ", abbrev, description ) ); - } - } else { - s.append( String.format( "-%s=%s (required)", abbrev, description ) ); - } - System.err.println(s); - } - } - - /** - * check that the user's specified arguments are valid. - */ - private void checkArgs() { - boolean error= false; - java.util.List errorList= new java.util.ArrayList(); // add strings to here - for ( int i=0; !error & i i= values.keySet().iterator(); - while ( i.hasNext() ) { - String key= i.next(); - if ( key==null ) { - System.err.println("TODO: handle this case whereever it's coming from: key==null"); - continue; - } - if ( key.equals("help") || key.equals("--help" ) ) { // kludge - printUsage(); - System.exit(-1); //Findbugs correctly points out that this is a bad idea. For example, a typo on a web server could bring the whole thing down. - } - if ( values.get( key ) == this.UNSPECIFIED ) { - errorList.add( "Argument needed: --" + reverseNames.get( key ) ); - } - if ( values.get( key ) == this.REFERENCEWITHOUTVALUE ) { - errorList.add( "Switch requires argument: "+formUsed.get(key)); - } - if ( values.get( key ) == this.UNDEFINED_SWITCH && !allowUndefinedSwitch ) { - errorList.add( "Not a valid switch: "+formUsed.get(key) ); - } - } - } - - if ( !error ) { - for ( int i=0; i0 ) { - printUsage(); - System.err.println( "" ); - for ( int ii=0; ii getOptions() { - HashMap result= new HashMap(); - List exclude= Arrays.asList( positionKeys ); - for ( Iterator i=values.keySet().iterator(); i.hasNext(); ) { - String key= (String)i.next(); - if( !exclude.contains(key) && formUsed.containsKey(key) ) { - result.put( key, values.get(key) ); - } - } - return result; - } - - private int processSwitch( String[] args, int i ) { - String key; - if ( args[i].startsWith("--") ) { - String name= args[i].substring(2); - if ( name.indexOf('=') != -1 ) { - name= name.substring(0,name.indexOf('=')); - } - key= (String)names.get(name); - } else { - // TODO: should support several abbrevs: e.g., -xvf (Throw exception if ambiguous) - String abbrev= args[i].substring(1); - if ( abbrev.indexOf('=') != -1 ) { - abbrev= abbrev.substring(0,abbrev.indexOf('=')); - } - key= (String)abbrevs.get(abbrev); - } - - if ( key==null ) { - key= args[i]; - values.put( key, this.UNDEFINED_SWITCH ); - formUsed.put( key, args[i] ); - logger.log(Level.FINER, "undefined switch: {0}", key); - } else { - String value; - formUsed.put( key,args[i] ); - if ( values.get(key)==this.FALSE || values.get(key)==this.TRUE ) { // is boolean - value= TRUE; - if ( args[i].indexOf('=')!= -1 ) { - value= args[i].substring( args[i].indexOf('=')+1 ); - if ( value.equals("t") || value.equals("true") || value.equals("y") || value.equals("yes") ) { - value= TRUE; - } else if ( value.equals("f") || value.equals("false") || value.equals("n") || value.equals("no") ) { - value= FALSE; - } - } - values.put( key, value ); - } else { - if ( args[i].indexOf('=') != -1 ) { - value= args[i].substring( args[i].indexOf('=')+1 ); - } else { - if ( i+1 < args.length && ( ! args[i+1].startsWith("-") ) ) { - value= args[i+1]; - i= i+1; - } else { - value= this.REFERENCEWITHOUTVALUE; - } - } - if ( value.startsWith("\"") ) { - value= value.substring(1,value.length()-2); - } - logger.log(Level.FINER, "switch key: {0}={1}", new Object[]{key, value}); - values.put( key, value ); - } - } - return i; - } - - /** - * given the specification, process the argument list. If the list is in error, the - * usage statement is generated, and the System.exit is called (sorry!). Otherwise - * the method returns and getValue() may be used to retrieve the values. - * - * Again, note that System.exit may be called. This is probably a bad idea and another - * method will probably be added that would return true if processing was successful. - * - * @param args as in public static void main( String[] args ). - */ - public void process(String[] args) { - - StringBuilder sb= new StringBuilder(); - for ( int i=0; i set= names.keySet(); - Iterator i= set.iterator(); - - while ( i.hasNext() ) { - String name= i.next(); - String key= names.get(name); - String value= (String)formUsed.get(key); - if ( value !=null ) { - if ( value.equals(this.TRUE) ) { - s.append( "--" ).append( name ); - } if ( value.equals(this.FALSE ) ) { - // do nothing - } else { - s.append( value ).append(" "); - } - } - } - - return s.toString(); - - } - - /** - * dump the configuration to the given logger at Level.CONFIG. - * @param logger - */ - public void logPrefsSettings( Logger logger ) { - String s= getPrefsSettings(); - logger.log(Level.CONFIG,s); - } - -} - diff --git a/dasCoreUtil/src/org/das2/util/Base64.java b/dasCoreUtil/src/org/das2/util/Base64.java deleted file mode 100644 index a1d9e3e7a..000000000 --- a/dasCoreUtil/src/org/das2/util/Base64.java +++ /dev/null @@ -1,1772 +0,0 @@ -/** - *

    Encodes and decodes to and from Base64 notation.

    - *

    Homepage: http://iharder.net/base64.

    - * - *

    - * Change Log: - *

    - *
      - *
    • v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug - * when using very small files (~< 40 bytes).
    • - *
    • v2.2 - Added some helper methods for encoding/decoding directly from - * one file to the next. Also added a main() method to support command line - * encoding/decoding from one file to the next. Also added these Base64 dialects: - *
        - *
      1. The default is RFC3548 format.
      2. - *
      3. Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates - * URL and file name friendly format as described in Section 4 of RFC3548. - * http://www.faqs.org/rfcs/rfc3548.html
      4. - *
      5. Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates - * URL and file name friendly format that preserves lexical ordering as described - * in http://www.faqs.org/qa/rfcc-1940.html
      6. - *
      - * Special thanks to Jim Kellerman at http://www.powerset.com/ - * for contributing the new Base64 dialects. - *
    • - * - *
    • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added - * some convenience methods for reading and writing to and from files.
    • - *
    • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems - * with other encodings (like EBCDIC).
    • - *
    • v2.0.1 - Fixed an error when decoding a single byte, that is, when the - * encoded data was a single byte.
    • - *
    • v2.0 - I got rid of methods that used booleans to set options. - * Now everything is more consolidated and cleaner. The code now detects - * when data that's being decoded is gzip-compressed and will decompress it - * automatically. Generally things are cleaner. You'll probably have to - * change some method calls that you were making to support the new - * options format (ints that you "OR" together).
    • - *
    • v1.5.1 - Fixed bug when decompressing and decoding to a - * byte[] using decode( String s, boolean gzipCompressed ). - * Added the ability to "suspend" encoding in the Output Stream so - * you can turn on and off the encoding if you need to embed base64 - * data in an otherwise "normal" stream (like an XML file).
    • - *
    • v1.5 - Output stream pases on flush() command but doesn't do anything itself. - * This helps when using GZIP streams. - * Added the ability to GZip-compress objects before encoding them.
    • - *
    • v1.4 - Added helper methods to read/write files.
    • - *
    • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
    • - *
    • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream - * where last buffer being read, if not completely full, was not returned.
    • - *
    • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
    • - *
    • v1.3.3 - Fixed I/O streams which were totally messed up.
    • - *
    - * - *

    - * I am placing this code in the Public Domain. Do with it as you will. - * This software comes with no guarantees or warranties but with - * plenty of well-wishing instead! - * Please visit http://iharder.net/base64 - * periodically to check for updates or to contribute improvements. - *

    - * - * @author Robert Harder - * @author rob@iharder.net - * @version 2.2.1 - */ -package org.das2.util; - -public class Base64 -{ - -/* ******** P U B L I C F I E L D S ******** */ - - - /** No options specified. Value is zero. */ - public final static int NO_OPTIONS = 0; - - /** Specify encoding. */ - public final static int ENCODE = 1; - - - /** Specify decoding. */ - public final static int DECODE = 0; - - - /** Specify that data should be gzip-compressed. */ - public final static int GZIP = 2; - - - /** Don't break lines when encoding (violates strict Base64 specification) */ - public final static int DONT_BREAK_LINES = 8; - - /** - * Encode using Base64-like encoding that is URL- and Filename-safe as described - * in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * It is important to note that data encoded this way is not officially valid Base64, - * or at the very least should not be called Base64 without also specifying that is - * was encoded using the URL- and Filename-safe dialect. - */ - public final static int URL_SAFE = 16; - - - /** - * Encode using the special "ordered" dialect of Base64 described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - public final static int ORDERED = 32; - - -/* ******** P R I V A T E F I E L D S ******** */ - - - /** Maximum line length (76) of Base64 output. */ - private final static int MAX_LINE_LENGTH = 76; - - - /** The equals sign (=) as a byte. */ - private final static byte EQUALS_SIGN = (byte)'='; - - - /** The new line character (\n) as a byte. */ - private final static byte NEW_LINE = (byte)'\n'; - - - /** Preferred encoding. */ - private final static String PREFERRED_ENCODING = "UTF-8"; - - - // I think I end up not using the BAD_ENCODING indicator. - //private final static byte BAD_ENCODING = -9; // Indicates error in encoding - private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding - private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - - -/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ - - /** The 64 valid Base64 values. */ - //private final static byte[] ALPHABET; - /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ - private final static byte[] _STANDARD_ALPHABET = - { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' - }; - - - /** - * Translates a Base64 value to either its 6-bit reconstruction value - * or a negative number indicating some other meaning. - **/ - private final static byte[] _STANDARD_DECODABET = - { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9,-9,-9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - -/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ - - /** - * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." - */ - private final static byte[] _URL_SAFE_ALPHABET = - { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' - }; - - /** - * Used in decoding URL- and Filename-safe dialects of Base64. - */ - private final static byte[] _URL_SAFE_DECODABET = - { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 62, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9, // Decimal 91 - 94 - 63, // Underscore at decimal 95 - -9, // Decimal 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - - -/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ - - /** - * I don't get the point of this technique, but it is described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - private final static byte[] _ORDERED_ALPHABET = - { - (byte)'-', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', - (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'_', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' - }; - - /** - * Used in decoding the "ordered" dialect of Base64. - */ - private final static byte[] _ORDERED_DECODABET = - { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 0, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' - 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' - -9,-9,-9,-9, // Decimal 91 - 94 - 37, // Underscore at decimal 95 - -9, // Decimal 96 - 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' - 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - -/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ - - - /** - * Returns one of the _SOMETHING_ALPHABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URLSAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getAlphabet( int options ) - { - if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET; - else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET; - else return _STANDARD_ALPHABET; - - } // end getAlphabet - - - /** - * Returns one of the _SOMETHING_DECODABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URL_SAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getDecodabet( int options ) - { - if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET; - else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET; - else return _STANDARD_DECODABET; - - } // end getAlphabet - - - - /** Defeats instantiation. */ - private Base64(){} - - - /** - * Encodes or decodes two files from the command line; - * feel free to delete this method (in fact you probably should) - * if you're embedding this code into a larger program. - */ - public final static void main( String[] args ) - { - if( args.length < 3 ){ - usage("Not enough arguments."); - } // end if: args.length < 3 - else { - String flag = args[0]; - String infile = args[1]; - String outfile = args[2]; - if( flag.equals( "-e" ) ){ - Base64.encodeFileToFile( infile, outfile ); - } // end if: encode - else if( flag.equals( "-d" ) ) { - Base64.decodeFileToFile( infile, outfile ); - } // end else if: decode - else { - usage( "Unknown flag: " + flag ); - } // end else - } // end else - } // end main - - /** - * Prints command line usage. - * - * @param msg A message to include with usage info. - */ - private final static void usage( String msg ) - { - System.err.println( msg ); - System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" ); - } // end usage - - -/* ******** E N C O D I N G M E T H O D S ******** */ - - - /** - * Encodes up to the first three bytes of array threeBytes - * and returns a four-byte array in Base64 notation. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * The array threeBytes needs only be as big as - * numSigBytes. - * Code can reuse a byte array by passing a four-byte array as b4. - * - * @param b4 A reusable byte array to reduce array instantiation - * @param threeBytes the array to convert - * @param numSigBytes the number of significant bytes in your array - * @return four byte array in Base64 notation. - * @since 1.5.1 - */ - private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) - { - encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); - return b4; - } // end encode3to4 - - - /** - *

    Encodes up to three bytes of the array source - * and writes the resulting four Base64 bytes to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 3 for - * the source array or destOffset + 4 for - * the destination array. - * The actual number of significant bytes in your array is - * given by numSigBytes.

    - *

    This is the lowest level of the encoding methods with - * all possible parameters.

    - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param numSigBytes the number of significant bytes in your array - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the destination array - * @since 1.3 - */ - private static byte[] encode3to4( - byte[] source, int srcOffset, int numSigBytes, - byte[] destination, int destOffset, int options ) - { - byte[] ALPHABET = getAlphabet( options ); - - // 1 2 3 - // 01234567890123456789012345678901 Bit position - // --------000000001111111122222222 Array position from threeBytes - // --------| || || || | Six bit groups to index ALPHABET - // >>18 >>12 >> 6 >> 0 Right shift necessary - // 0x3f 0x3f 0x3f Additional AND - - // Create buffer with zero-padding if there are only one or two - // significant bytes passed in the array. - // We have to shift left 24 in order to flush out the 1's that appear - // when Java treats a value as negative that is cast from a byte to an int. - int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) - | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) - | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); - - switch( numSigBytes ) - { - case 3: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; - return destination; - - case 2: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - case 1: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = EQUALS_SIGN; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - default: - return destination; - } // end switch - } // end encode3to4 - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - * The object is not GZip-compressed before being encoded. - * - * @param serializableObject The object to encode - * @return The Base64-encoded object - * @since 1.4 - */ - public static String encodeObject( java.io.Serializable serializableObject ) - { - return encodeObject( serializableObject, NO_OPTIONS ); - } // end encodeObject - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - *

    - * Valid options:

    -     *   GZIP: gzip-compresses object before encoding it.
    -     *   DONT_BREAK_LINES: don't break lines at 76 characters
    -     *     Note: Technically, this makes your encoding non-compliant.
    -     * 
    - *

    - * Example: encodeObject( myObj, Base64.GZIP ) or - *

    - * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * @param serializableObject The object to encode - * @param options Specified options - * @return The Base64-encoded object - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeObject( java.io.Serializable serializableObject, int options ) - { - // Streams - java.io.ByteArrayOutputStream baos = null; - java.io.OutputStream b64os = null; - java.io.ObjectOutputStream oos = null; - java.util.zip.GZIPOutputStream gzos = null; - - // Isolate options - int gzip = (options & GZIP); - //int dontBreakLines = (options & DONT_BREAK_LINES); - - try - { - // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | options ); - - // GZip? - if( gzip == GZIP ) - { - gzos = new java.util.zip.GZIPOutputStream( b64os ); - oos = new java.io.ObjectOutputStream( gzos ); - } // end if: gzip - else - oos = new java.io.ObjectOutputStream( b64os ); - - oos.writeObject( serializableObject ); - } // end try - catch( java.io.IOException e ) - { - e.printStackTrace(); - return null; - } // end catch - finally - { - try{ oos.close(); } catch( Exception e ){ e.printStackTrace(); } - try{ gzos.close(); } catch( Exception e ){ e.printStackTrace(); } - try{ b64os.close(); } catch( Exception e ){ e.printStackTrace(); } - try{ baos.close(); } catch( Exception e ){ e.printStackTrace(); } - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - - } // end encode - - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @since 1.4 - */ - public static String encodeBytes( byte[] source ) - { - return encodeBytes( source, 0, source.length, NO_OPTIONS ); - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

    - * Valid options:

    -     *   GZIP: gzip-compresses object before encoding it.
    -     *   DONT_BREAK_LINES: don't break lines at 76 characters
    -     *     Note: Technically, this makes your encoding non-compliant.
    -     * 
    - *

    - * Example: encodeBytes( myData, Base64.GZIP ) or - *

    - * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @param source The data to convert - * @param options Specified options - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int options ) - { - return encodeBytes( source, 0, source.length, options ); - } // end encodeBytes - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @since 1.4 - */ - public static String encodeBytes( byte[] source, int off, int len ) - { - return encodeBytes( source, off, len, NO_OPTIONS ); - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

    - * Valid options:

    -     *   GZIP: gzip-compresses object before encoding it.
    -     *   DONT_BREAK_LINES: don't break lines at 76 characters
    -     *     Note: Technically, this makes your encoding non-compliant.
    -     * 
    - *

    - * Example: encodeBytes( myData, Base64.GZIP ) or - *

    - * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options Specified options, alphabet type is pulled from this - * (standard, url-safe, ordered) - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int off, int len, int options ) - { - // Isolate options - int dontBreakLines = ( options & DONT_BREAK_LINES ); - int gzip = ( options & GZIP ); - - // Compress? - if( gzip == GZIP ) - { - java.io.ByteArrayOutputStream baos = null; - java.util.zip.GZIPOutputStream gzos = null; - Base64.OutputStream b64os = null; - - - try - { - // GZip -> Base64 -> ByteArray - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | options ); - gzos = new java.util.zip.GZIPOutputStream( b64os ); - - gzos.write( source, off, len ); - gzos.close(); - } // end try - catch( java.io.IOException e ) - { - e.printStackTrace(); - return null; - } // end catch - finally - { - try{ gzos.close(); } catch( Exception e ){ e.printStackTrace(); } - try{ b64os.close(); } catch( Exception e ){ e.printStackTrace(); } - try{ baos.close(); } catch( Exception e ){ e.printStackTrace(); } - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - } // end if: compress - - // Else, don't compress. Better not to use streams at all then. - else - { - // Convert option to boolean in way that code likes it. - boolean breakLines = dontBreakLines == 0; - - int len43 = len * 4 / 3; - byte[] outBuff = new byte[ ( len43 ) // Main 4:3 - + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding - + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines - int d = 0; - int e = 0; - int len2 = len - 2; - int lineLength = 0; - for( ; d < len2; d+=3, e+=4 ) - { - encode3to4( source, d+off, 3, outBuff, e, options ); - - lineLength += 4; - if( breakLines && lineLength == MAX_LINE_LENGTH ) - { - outBuff[e+4] = NEW_LINE; - e++; - lineLength = 0; - } // end if: end of line - } // en dfor: each piece of array - - if( d < len ) - { - encode3to4( source, d+off, len - d, outBuff, e, options ); - e += 4; - } // end if: some padding needed - - - // Return value according to relevant encoding. - try - { - return new String( outBuff, 0, e, PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( outBuff, 0, e ); - } // end catch - - } // end else: don't compress - - } // end encodeBytes - - - - - -/* ******** D E C O D I N G M E T H O D S ******** */ - - - /** - * Decodes four bytes from array source - * and writes the resulting bytes (up to three of them) - * to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 4 for - * the source array or destOffset + 3 for - * the destination array. - * This method returns the actual number of bytes that - * were converted from the Base64 encoding. - *

    This is the lowest level of the decoding methods with - * all possible parameters.

    - * - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @param options alphabet type is pulled from this (standard, url-safe, ordered) - * @return the number of decoded bytes converted - * @since 1.3 - */ - private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options ) - { - byte[] DECODABET = getDecodabet( options ); - - // Example: Dk== - if( source[ srcOffset + 2] == EQUALS_SIGN ) - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - return 1; - } - - // Example: DkL= - else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); - return 2; - } - - // Example: DkLE - else - { - try{ - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) - // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) - | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); - - - destination[ destOffset ] = (byte)( outBuff >> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); - destination[ destOffset + 2 ] = (byte)( outBuff ); - - return 3; - }catch( Exception e){ - System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); - System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); - System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); - System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); - return -1; - } // end catch - } - } // end decodeToBytes - - - - - /** - * Very low-level access to decoding ASCII characters in - * the form of a byte array. Does not support automatically - * gunzipping or any other "fancy" features. - * - * @param source The Base64 encoded data - * @param off The offset of where to begin decoding - * @param len The length of characters to decode - * @return decoded data - * @since 1.3 - */ - public static byte[] decode( byte[] source, int off, int len, int options ) - { - byte[] DECODABET = getDecodabet( options ); - - int len34 = len * 3 / 4; - byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output - int outBuffPosn = 0; - - byte[] b4 = new byte[4]; - int b4Posn = 0; - int i = 0; - byte sbiCrop = 0; - byte sbiDecode = 0; - for( i = off; i < off+len; i++ ) - { - sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits - sbiDecode = DECODABET[ sbiCrop ]; - - if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better - { - if( sbiDecode >= EQUALS_SIGN_ENC ) - { - b4[ b4Posn++ ] = sbiCrop; - if( b4Posn > 3 ) - { - outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); - b4Posn = 0; - - // If that was the equals sign, break out of 'for' loop - if( sbiCrop == EQUALS_SIGN ) - break; - } // end if: quartet built - - } // end if: equals sign or better - - } // end if: white space, equals sign or better - else - { - System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); - return null; - } // end else: - } // each input character - - byte[] out = new byte[ outBuffPosn ]; - System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); - return out; - } // end decode - - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @return the decoded data - * @since 1.4 - */ - public static byte[] decode( String s ) - { - return decode( s, NO_OPTIONS ); - } - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @param options encode options such as URL_SAFE - * @return the decoded data - * @since 1.4 - */ - public static byte[] decode( String s, int options ) - { - byte[] bytes; - try - { - bytes = s.getBytes( PREFERRED_ENCODING ); - } // end try - catch( java.io.UnsupportedEncodingException uee ) - { - bytes = s.getBytes(); - } // end catch - // - - // Decode - bytes = decode( bytes, 0, bytes.length, options ); - - - // Check to see if it's gzip-compressed - // GZIP Magic Two-Byte Number: 0x8b1f (35615) - if( bytes != null && bytes.length >= 4 ) - { - - int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) - { - java.io.ByteArrayInputStream bais = null; - java.util.zip.GZIPInputStream gzis = null; - java.io.ByteArrayOutputStream baos = null; - byte[] buffer = new byte[2048]; - int length = 0; - - try - { - baos = new java.io.ByteArrayOutputStream(); - bais = new java.io.ByteArrayInputStream( bytes ); - gzis = new java.util.zip.GZIPInputStream( bais ); - - while( ( length = gzis.read( buffer ) ) >= 0 ) - { - baos.write(buffer,0,length); - } // end while: reading input - - // No error? Get new bytes. - bytes = baos.toByteArray(); - - } // end try - catch( java.io.IOException e ) - { - e.printStackTrace(); - // Just return originally-decoded bytes - } // end catch - finally - { - try{ - baos.close(); - gzis.close(); - bais.close(); - } catch( Exception e ){ - e.printStackTrace(); - } - } // end finally - - } // end if: gzipped - } // end if: bytes.length >= 2 - - return bytes; - } // end decode - - - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * - * @param encodedObject The Base64 data to decode - * @return The decoded and deserialized object - * @since 1.5 - */ - public static Object decodeToObject( String encodedObject ) - { - // Decode and gunzip if necessary - byte[] objBytes = decode( encodedObject ); - - java.io.ByteArrayInputStream bais = null; - java.io.ObjectInputStream ois = null; - Object obj = null; - - try - { - bais = new java.io.ByteArrayInputStream( objBytes ); - ois = new java.io.ObjectInputStream( bais ); - - obj = ois.readObject(); - } // end try - catch( java.io.IOException e ) - { - e.printStackTrace(); - obj = null; - } // end catch - catch( java.lang.ClassNotFoundException e ) - { - e.printStackTrace(); - obj = null; - } // end catch - finally - { - try{ bais.close(); } catch( Exception e ){} - try{ ois.close(); } catch( Exception e ){} - } // end finally - - return obj; - } // end decodeObject - - - - /** - * Convenience method for encoding data to a file. - * - * @param dataToEncode byte array of data to encode in base64 form - * @param filename Filename for saving encoded data - * @return true if successful, false otherwise - * - * @since 2.1 - */ - public static boolean encodeToFile( byte[] dataToEncode, String filename ) - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.ENCODE ); - bos.write( dataToEncode ); - success = true; - } // end try - catch( java.io.IOException e ) - { - - success = false; - } // end catch: IOException - finally - { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - return success; - } // end encodeToFile - - - /** - * Convenience method for decoding data to a file. - * - * @param dataToDecode Base64-encoded data as a string - * @param filename Filename for saving decoded data - * @return true if successful, false otherwise - * - * @since 2.1 - */ - public static boolean decodeToFile( String dataToDecode, String filename ) - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.DECODE ); - bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); - success = true; - } // end try - catch( java.io.IOException e ) - { - success = false; - } // end catch: IOException - finally - { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - return success; - } // end decodeToFile - - - - - /** - * Convenience method for reading a base64-encoded - * file and decoding it. - * - * @param filename Filename for reading encoded data - * @return decoded byte array or null if unsuccessful - * - * @since 2.1 - */ - public static byte[] decodeFromFile( String filename ) - { - byte[] decodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = null; - int length = 0; - int numBytes = 0; - - // Check for size of file - if( file.length() > Integer.MAX_VALUE ) - { - System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." ); - return null; - } // end if: file too big for int index - buffer = new byte[ (int)file.length() ]; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.DECODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - decodedData = new byte[ length ]; - System.arraycopy( buffer, 0, decodedData, 0, length ); - - } // end try - catch( java.io.IOException e ) - { - System.err.println( "Error decoding from file " + filename ); - } // end catch: IOException - finally - { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return decodedData; - } // end decodeFromFile - - - - /** - * Convenience method for reading a binary file - * and base64-encoding it. - * - * @param filename Filename for reading binary data - * @return base64-encoded string or null if unsuccessful - * - * @since 2.1 - */ - public static String encodeFromFile( String filename ) - { - String encodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1) - int length = 0; - int numBytes = 0; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.ENCODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); - - } // end try - catch( java.io.IOException e ) - { - System.err.println( "Error encoding from file " + filename ); - } // end catch: IOException - finally - { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return encodedData; - } // end encodeFromFile - - /** - * Reads infile and encodes it to outfile. - * - * @param infile Input file - * @param outfile Output file - * @since 2.2 - */ - public static void encodeFileToFile( String infile, String outfile ) - { - String encoded = Base64.encodeFromFile( infile ); - java.io.OutputStream out = null; - try{ - out = new java.io.BufferedOutputStream( - new java.io.FileOutputStream( outfile ) ); - out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output. - } // end try - catch( java.io.IOException ex ) { - ex.printStackTrace(); - } // end catch - finally { - try { out.close(); } - catch( Exception ex ){} - } // end finally - } // end encodeFileToFile - - - /** - * Reads infile and decodes it to outfile. - * - * @param infile Input file - * @param outfile Output file - * @since 2.2 - */ - public static void decodeFileToFile( String infile, String outfile ) - { - byte[] decoded = Base64.decodeFromFile( infile ); - java.io.OutputStream out = null; - try{ - out = new java.io.BufferedOutputStream( - new java.io.FileOutputStream( outfile ) ); - out.write( decoded ); - } // end try - catch( java.io.IOException ex ) { - ex.printStackTrace(); - } // end catch - finally { - try { out.close(); } - catch( Exception ex ){} - } // end finally - } // end decodeFileToFile - - - /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.InputStream} will read data from another - * java.io.InputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class InputStream extends java.io.FilterInputStream - { - private boolean encode; // Encoding or decoding - private int position; // Current position in the buffer - private byte[] buffer; // Small buffer holding converted data - private int bufferLength; // Length of buffer (3 or 4) - private int numSigBytes; // Number of meaningful bytes in the buffer - private int lineLength; - private boolean breakLines; // Break lines at less than 80 characters - private int options; // Record options used to create the stream. - //private byte[] alphabet; // Local copies to avoid extra method calls - private byte[] decodabet; // Local copies to avoid extra method calls - - - /** - * Constructs a {@link Base64.InputStream} in DECODE mode. - * - * @param in the java.io.InputStream from which to read data. - * @since 1.3 - */ - public InputStream( java.io.InputStream in ) - { - this( in, DECODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.InputStream} in - * either ENCODE or DECODE mode. - *

    - * Valid options:

    -         *   ENCODE or DECODE: Encode or Decode as data is read.
    -         *   DONT_BREAK_LINES: don't break lines at 76 characters
    -         *     (only meaningful when encoding)
    -         *     Note: Technically, this makes your encoding non-compliant.
    -         * 
    - *

    - * Example: new Base64.InputStream( in, Base64.DECODE ) - * - * - * @param in the java.io.InputStream from which to read data. - * @param options Specified options - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public InputStream( java.io.InputStream in, int options ) - { - super( in ); - this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & ENCODE) == ENCODE; - this.bufferLength = encode ? 4 : 3; - this.buffer = new byte[ bufferLength ]; - this.position = -1; - this.lineLength = 0; - this.options = options; // Record for later, mostly to determine which alphabet to use - //this.alphabet = getAlphabet(options); - this.decodabet = getDecodabet(options); - } // end constructor - - /** - * Reads enough of the input stream to convert - * to/from Base64 and returns the next byte. - * - * @return next byte - * @since 1.3 - */ - public int read() throws java.io.IOException - { - // Do we need to get data? - if( position < 0 ) - { - if( encode ) - { - byte[] b3 = new byte[3]; - int numBinaryBytes = 0; - for( int i = 0; i < 3; i++ ) - { - try - { - int b = in.read(); - - // If end of stream, b is -1. - if( b >= 0 ) - { - b3[i] = (byte)b; - numBinaryBytes++; - } // end if: not end of stream - - } // end try: read - catch( java.io.IOException e ) - { - // Only a problem if we got no data at all. - if( i == 0 ) - throw e; - - } // end catch - } // end for: each needed input byte - - if( numBinaryBytes > 0 ) - { - encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); - position = 0; - numSigBytes = 4; - } // end if: got data - else - { - return -1; - } // end else - } // end if: encoding - - // Else decoding - else - { - byte[] b4 = new byte[4]; - int i = 0; - for( i = 0; i < 4; i++ ) - { - // Read four "meaningful" bytes: - int b = 0; - do{ b = in.read(); } - while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); - - if( b < 0 ) - break; // Reads a -1 if end of stream - - b4[i] = (byte)b; - } // end for: each needed input byte - - if( i == 4 ) - { - numSigBytes = decode4to3( b4, 0, buffer, 0, options ); - position = 0; - } // end if: got four characters - else if( i == 0 ){ - return -1; - } // end else if: also padded correctly - else - { - // Must have broken out from above. - throw new java.io.IOException( "Improperly padded Base64 input." ); - } // end - - } // end else: decode - } // end else: get data - - // Got data? - if( position >= 0 ) - { - // End of relevant data? - if( /*!encode &&*/ position >= numSigBytes ) - return -1; - - if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) - { - lineLength = 0; - return '\n'; - } // end if - else - { - lineLength++; // This isn't important when decoding - // but throwing an extra "if" seems - // just as wasteful. - - int b = buffer[ position++ ]; - - if( position >= bufferLength ) - position = -1; - - return b & 0xFF; // This is how you "cast" a byte that's - // intended to be unsigned. - } // end else - } // end if: position >= 0 - - // Else error - else - { - // When JDK1.4 is more accepted, use an assertion here. - throw new java.io.IOException( "Error in Base64 code reading stream." ); - } // end else - } // end read - - - /** - * Calls {@link #read()} repeatedly until the end of stream - * is reached or len bytes are read. - * Returns number of bytes read into array or -1 if - * end of stream is encountered. - * - * @param dest array to hold values - * @param off offset for array - * @param len max number of bytes to read into array - * @return bytes read into array or -1 if end of stream is encountered. - * @since 1.3 - */ - public int read( byte[] dest, int off, int len ) throws java.io.IOException - { - int i; - int b; - for( i = 0; i < len; i++ ) - { - b = read(); - - //if( b < 0 && i == 0 ) - // return -1; - - if( b >= 0 ) - dest[off + i] = (byte)b; - else if( i == 0 ) - return -1; - else - break; // Out of 'for' loop - } // end for: each byte read - return i; - } // end read - - } // end inner class InputStream - - - - - - - /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.OutputStream} will write data to another - * java.io.OutputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class OutputStream extends java.io.FilterOutputStream - { - private boolean encode; - private int position; - private byte[] buffer; - private int bufferLength; - private int lineLength; - private boolean breakLines; - private byte[] b4; // Scratch used in a few places - private boolean suspendEncoding; - private int options; // Record for later - //private byte[] alphabet; // Local copies to avoid extra method calls - private byte[] decodabet; // Local copies to avoid extra method calls - - /** - * Constructs a {@link Base64.OutputStream} in ENCODE mode. - * - * @param out the java.io.OutputStream to which data will be written. - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out ) - { - this( out, ENCODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.OutputStream} in - * either ENCODE or DECODE mode. - *

    - * Valid options:

    -         *   ENCODE or DECODE: Encode or Decode as data is read.
    -         *   DONT_BREAK_LINES: don't break lines at 76 characters
    -         *     (only meaningful when encoding)
    -         *     Note: Technically, this makes your encoding non-compliant.
    -         * 
    - *

    - * Example: new Base64.OutputStream( out, Base64.ENCODE ) - * - * @param out the java.io.OutputStream to which data will be written. - * @param options Specified options. - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DONT_BREAK_LINES - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out, int options ) - { - super( out ); - this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & ENCODE) == ENCODE; - this.bufferLength = encode ? 3 : 4; - this.buffer = new byte[ bufferLength ]; - this.position = 0; - this.lineLength = 0; - this.suspendEncoding = false; - this.b4 = new byte[4]; - this.options = options; - //this.alphabet = getAlphabet(options); - this.decodabet = getDecodabet(options); - } // end constructor - - - /** - * Writes the byte to the output stream after - * converting to/from Base64 notation. - * When encoding, bytes are buffered three - * at a time before the output stream actually - * gets a write() call. - * When decoding, bytes are buffered four - * at a time. - * - * @param theByte the byte to write - * @since 1.3 - */ - public void write(int theByte) throws java.io.IOException - { - // Encoding suspended? - if( suspendEncoding ) - { - super.out.write( theByte ); - return; - } // end if: supsended - - // Encode? - if( encode ) - { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) // Enough to encode. - { - out.write( encode3to4( b4, buffer, bufferLength, options ) ); - - lineLength += 4; - if( breakLines && lineLength >= MAX_LINE_LENGTH ) - { - out.write( NEW_LINE ); - lineLength = 0; - } // end if: end of line - - position = 0; - } // end if: enough to output - } // end if: encoding - - // Else, Decoding - else - { - // Meaningful Base64 character? - if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) - { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) // Enough to output. - { - int len = Base64.decode4to3( buffer, 0, b4, 0, options ); - out.write( b4, 0, len ); - //out.write( Base64.decode4to3( buffer ) ); - position = 0; - } // end if: enough to output - } // end if: meaningful base64 character - else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) - { - throw new java.io.IOException( "Invalid character in Base64 data." ); - } // end else: not white space either - } // end else: decoding - } // end write - - - - /** - * Calls {@link #write(int)} repeatedly until len - * bytes are written. - * - * @param theBytes array from which to read bytes - * @param off offset for array - * @param len max number of bytes to read into array - * @since 1.3 - */ - public void write( byte[] theBytes, int off, int len ) throws java.io.IOException - { - // Encoding suspended? - if( suspendEncoding ) - { - super.out.write( theBytes, off, len ); - return; - } // end if: supsended - - for( int i = 0; i < len; i++ ) - { - write( theBytes[ off + i ] ); - } // end for: each byte written - - } // end write - - - - /** - * Method added by PHIL. [Thanks, PHIL. -Rob] - * This pads the buffer without closing the stream. - */ - public void flushBase64() throws java.io.IOException - { - if( position > 0 ) - { - if( encode ) - { - out.write( encode3to4( b4, buffer, position, options ) ); - position = 0; - } // end if: encoding - else - { - throw new java.io.IOException( "Base64 input not properly padded." ); - } // end else: decoding - } // end if: buffer partially full - - } // end flush - - - /** - * Flushes and closes (I think, in the superclass) the stream. - * - * @since 1.3 - */ - public void close() throws java.io.IOException - { - // 1. Ensure that pending characters are written - flushBase64(); - - // 2. Actually close the stream - // Base class both flushes and closes. - super.close(); - - buffer = null; - out = null; - } // end close - - - - /** - * Suspends encoding of the stream. - * May be helpful if you need to embed a piece of - * base640-encoded data in a stream. - * - * @since 1.5.1 - */ - public void suspendEncoding() throws java.io.IOException - { - flushBase64(); - this.suspendEncoding = true; - } // end suspendEncoding - - - /** - * Resumes encoding of the stream. - * May be helpful if you need to embed a piece of - * base640-encoded data in a stream. - * - * @since 1.5.1 - */ - public void resumeEncoding() - { - this.suspendEncoding = false; - } // end resumeEncoding - - - - } // end inner class OutputStream - - -} // end class Base64 diff --git a/dasCoreUtil/src/org/das2/util/ClassMap.java b/dasCoreUtil/src/org/das2/util/ClassMap.java deleted file mode 100644 index c867aac29..000000000 --- a/dasCoreUtil/src/org/das2/util/ClassMap.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * ClassMap.java - * - * Created on April 28, 2006, 2:11 PM - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -package org.das2.util; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; - -/** - * Map that takes a Class for keys, and the get method finds the closest matching class. - * @author Jeremy - */ -public class ClassMap implements Map { - HashMap map; - - /** Creates a new instance of ClassMap */ - public ClassMap() { - map= new HashMap(); - } - - public int size() { - return map.size(); - } - - public boolean isEmpty() { - return map.isEmpty(); - } - - public boolean containsKey(Object key) { - Object close= closestKey((Class)key); - return close!=null; - } - - public boolean containsValue(Object value) { - return map.containsValue(value); - } - - private Object closestKey( Object key ) { - Object result= key; - if ( map.containsKey(result) ) return result; - Class clas= (Class)key; - while ( clas!=null ) { - if ( map.containsKey(clas) ) return clas; - clas= clas.getSuperclass(); - } - return clas; - } - - public T get(Object key) { - Object close= closestKey(key); - return ( close==null ) ? null : map.get(close); - } - - public T put(Class key, T value) { - if ( key.isInterface() ) { - // System.err.println("interfaces not supported"); - } - T result= map.get(key); - map.put( key, value ); - return result; - } - - public T remove(Object key) { - return map.remove(key); - } - - public void putAll(Map t) { - if ( t instanceof ClassMap ) map.putAll(t); - } - - public void clear() { - map.clear(); - } - - public Set keySet() { - return map.keySet(); - } - - public Collection values() { - return map.values(); - } - - public Set entrySet() { - return map.entrySet(); - } - - /** - * return the object or null for this string "RED" -> Color.RED - * @param c the class defining the target type - * @param ele the string representation to be interpreted for this type. - * @return the instance of the type. - */ - public static Object getEnumElement( Class c, String ele ) { - int PUBLIC_STATIC_FINAL = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; - //List lvals; - if (c.isEnum()) { - Object[] vals = c.getEnumConstants(); - for (Object o : vals) { - Enum e = (Enum) o; - if ( e.toString().equalsIgnoreCase(ele) ) return e; - } - //lvals= Arrays.asList(vals); - } else { - Field[] fields = c.getDeclaredFields(); - //lvals= new ArrayList(); - for ( Field f: fields ) { - try { - String name = f.getName(); - if ( ( ( f.getModifiers() & PUBLIC_STATIC_FINAL) == PUBLIC_STATIC_FINAL ) ) { - Object value = f.get(null); - if ( value!=null && c.isInstance(value) ) { - //lvals.add(value); - if ( name.equalsIgnoreCase(ele) || value.toString().equalsIgnoreCase(ele) ) { - return value; - } - } - } - } catch (IllegalAccessException iae) { - IllegalAccessError err = new IllegalAccessError(iae.getMessage()); - err.initCause(iae); - throw err; - } - } - } - //logger.log( Level.INFO, "looking for {0}, found {1}\n", new Object[]{ele, lvals.toString()}); - return null; - } - -} diff --git a/dasCoreUtil/src/org/das2/util/CombinedTreeModel.java b/dasCoreUtil/src/org/das2/util/CombinedTreeModel.java deleted file mode 100644 index d60689a83..000000000 --- a/dasCoreUtil/src/org/das2/util/CombinedTreeModel.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * DataSetTreeModel.java - * - * Created on July 25, 2006, 2:17 PM - * - * - */ -package org.das2.util; - -import java.awt.EventQueue; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.WeakHashMap; -import javax.swing.event.TreeModelEvent; -import javax.swing.event.TreeModelListener; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; - -/** - * Forms a tree by connecting sub-trees. - * @author Jeremy - */ -public class CombinedTreeModel implements TreeModel { - - List treeModels; - List treeModelRoots; - List treeModelSortIndexes; - Object root = null; - private WeakHashMap sourceMap; - List listeners; // generally, the model should only be modified on the event thread. - // this is useful for debugging. - private static final boolean checkEvent = false; - - public CombinedTreeModel(Object root) { - this.root = root; - treeModels = new ArrayList(); - treeModelRoots = new ArrayList(); - treeModelSortIndexes = new ArrayList(); - sourceMap = new WeakHashMap(); - listeners = new ArrayList(); - } - - class SubTreeModelListener implements TreeModelListener { - - private TreeModelEvent prependTreeModelEvent(TreeModelEvent e) { - Object[] path = e.getPath(); - Object[] path2 = new Object[path.length + 1]; - path2[0] = root; - System.arraycopy(path, 0, path2, 1, path.length); - TreeModelEvent result = new TreeModelEvent(this, path2, e.getChildIndices(), e.getChildren()); - return result; - } - - public void treeNodesChanged(TreeModelEvent e) { - fireTreeNodesChanged(prependTreeModelEvent(e)); - } - - public void treeNodesInserted(TreeModelEvent e) { - fireTreeNodesInserted(prependTreeModelEvent(e)); - } - - public void treeNodesRemoved(TreeModelEvent e) { - fireTreeNodesRemoved(prependTreeModelEvent(e)); - } - - public void treeStructureChanged(TreeModelEvent e) { - fireTreeStructureChanged(prependTreeModelEvent(e)); - } - } - SubTreeModelListener myListener = new SubTreeModelListener(); - - public Object getRoot() { - return root; - } - - public void mountTree(TreeModel treeModel) { - mountTree(treeModel, -1); - } - - /** - * mounts the tree. Note each treeModel must have a unique root. - */ - @SuppressWarnings("unchecked") - public void mountTree(TreeModel treeModel, int sortIndex) { - if (checkEvent && !EventQueue.isDispatchThread()) - throw new IllegalArgumentException("must be called from AWT thread"); // useful for debugging concurrent exception - - if (treeModelRoots.contains(treeModel.getRoot())) { - int index = treeModelRoots.indexOf(treeModel.getRoot()); - treeModels.set(index, treeModel); - treeModelRoots.set(index, treeModel.getRoot()); - treeModelSortIndexes.set(index, (float)sortIndex ); - TreePath path = new TreePath(root); - treeModel.addTreeModelListener(myListener); - fireTreeNodesChanged(path, new int[]{index}, new Object[]{treeModel.getRoot()}); - } else { - int index= Collections.binarySearch( treeModelSortIndexes, sortIndex+0.5f ); - index= -1 - index; - treeModels.add( index, treeModel ); - treeModelRoots.add( index, treeModel.getRoot() ); - treeModelSortIndexes.add( index, (float)sortIndex ); - TreePath path = new TreePath(root); - treeModel.addTreeModelListener(myListener); - fireTreeNodesInserted(path, new int[]{ index }, new Object[]{treeModel.getRoot()}); - } - } - - public void unmountTree(TreeModel treeModel) { - if (checkEvent && !EventQueue.isDispatchThread()) - throw new IllegalArgumentException("must be called from AWT thread"); // useful for debugging concurrent exception - - int index = treeModelRoots.indexOf(treeModel.getRoot()); - if ( index!=-1 ) { - treeModels.remove(index); - treeModelRoots.remove(index); - treeModelSortIndexes.remove(index); - TreePath path = new TreePath(root); - fireTreeNodesRemoved( new TreeModelEvent(this, path, new int[] { index }, new Object[] { treeModel.getRoot() } ) ); - } - - while ( true ) { - Object rm= null; - for ( Entry e: sourceMap.entrySet() ) { - if ( e.getValue().equals(treeModel) ) { - rm= e.getKey(); - } - } - if ( rm==null) { - break; - } else { - sourceMap.remove(rm); - } - } - - } - - public synchronized Object getChild(Object parent, int index) { - if (checkEvent && !EventQueue.isDispatchThread()) - throw new IllegalArgumentException("must be called from AWT thread"); // useful for debugging concurrent exception - Object result; - TreeModel mt; - if (parent == root) { - mt = (TreeModel) treeModels.get(index); - result = mt.getRoot(); - } else { - mt = (TreeModel) sourceMap.get(parent); - result = mt.getChild(parent, index); - } - sourceMap.put(result, mt); - return result; - } - - public int getChildCount(Object parent) { - if (checkEvent && !EventQueue.isDispatchThread()) - throw new IllegalArgumentException("must be called from AWT thread"); // useful for debugging concurrent exception - if (parent == root) { - return this.treeModels.size(); - } else { - TreeModel mt = (TreeModel) sourceMap.get(parent); - return mt.getChildCount(parent); - } - } - - public boolean isLeaf(Object node) { - if (node == root) { - return treeModels.isEmpty(); - } else { - TreeModel mt = (TreeModel) sourceMap.get(node); - if ( mt==null ) { - System.err.println("null on "+node); - return true; - } - return mt.isLeaf(node); - } - } - - public void valueForPathChanged(TreePath path, Object newValue) { - Object parent = path.getPathComponent(path.getPathCount() - 2); - Object child = path.getPathComponent(path.getPathCount() - 1); - int index = getIndexOfChild(parent, child); - fireTreeNodesChanged(path, new int[]{index}, new Object[]{newValue}); - } - - public int getIndexOfChild(Object parent, Object child) { - if (parent == root) { - for (int i = 0; i < treeModels.size(); i++) { - if (child == ((TreeModel) treeModels.get(i)).getRoot()) - return i; - } - return -1; - } else { - TreeModel mt = (TreeModel) sourceMap.get(parent); - return mt.getIndexOfChild(parent, child); - } - } - - public void addTreeModelListener(TreeModelListener l) { - listeners.add(l); - } - - public void removeTreeModelListener(TreeModelListener l) { - listeners.remove(l); - } - - private void fireTreeNodesInserted(TreePath path, - int[] insertedIndeces, Object[] children) { - TreeModelEvent e = new TreeModelEvent(this, path, insertedIndeces, children); - fireTreeNodesInserted(e); - } - - private void fireTreeNodesChanged(TreePath path, - int[] changedIndeces, Object[] children) { - TreeModelEvent e = new TreeModelEvent(this, path, changedIndeces, children); - fireTreeNodesChanged(e); - } - - private void fireTreeNodesChanged(TreeModelEvent e) { - for (Iterator i = listeners.iterator(); i.hasNext();) { - ((TreeModelListener) i.next()).treeNodesChanged(e); - } - } - - private void fireTreeNodesInserted(TreeModelEvent e) { - for (Iterator i = listeners.iterator(); i.hasNext();) { - ((TreeModelListener) i.next()).treeNodesInserted(e); - } - } - - public void fireTreeNodesRemoved(TreeModelEvent e) { - for (Iterator i = listeners.iterator(); i.hasNext();) { - ((TreeModelListener) i.next()).treeNodesRemoved(e); - } - } - - public void fireTreeStructureChanged(TreeModelEvent e) { - for (Iterator i = listeners.iterator(); i.hasNext();) { - ((TreeModelListener) i.next()).treeStructureChanged(e); - } - } -} diff --git a/dasCoreUtil/src/org/das2/util/ConsoleExceptionHandler.java b/dasCoreUtil/src/org/das2/util/ConsoleExceptionHandler.java deleted file mode 100644 index 7a33c293f..000000000 --- a/dasCoreUtil/src/org/das2/util/ConsoleExceptionHandler.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * ConsoleExceptionHandler.java - * - * Created on November 16, 2006, 12:34 PM - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -package org.das2.util; - -/** - * ExceptionHandler that prints stack traces out to the stderr. - * @author jbf - */ -public class ConsoleExceptionHandler implements ExceptionHandler { - - /** Creates a new instance of ConsoleExceptionHandler */ - public ConsoleExceptionHandler() { - } - - public void handle(Throwable t) { - t.printStackTrace(); - } - - public void handleUncaught(Throwable t) { - t.printStackTrace(); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/CredentialsDialog.java b/dasCoreUtil/src/org/das2/util/CredentialsDialog.java deleted file mode 100644 index 1c2c6109e..000000000 --- a/dasCoreUtil/src/org/das2/util/CredentialsDialog.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.das2.util; - -import java.awt.Dialog; -import java.awt.event.KeyEvent; -import javax.swing.Icon; -import javax.swing.JDialog; -import javax.swing.JOptionPane; - -/** - * - * @author cwp - */ -public class CredentialsDialog extends JDialog{ - - protected int m_nRet; - protected String m_sUser; - protected String m_sPasswd; - - /** Creates new form CredentialsDialog */ - public CredentialsDialog(java.awt.Frame parent){ - super(parent, "Authentication Required", Dialog.ModalityType.APPLICATION_MODAL); - m_nRet = 0; - setResizable(true); - if(parent != null){ - setLocationRelativeTo(parent); - } - initComponents(); - } - - /** Show the dialog, get user input, and then close */ - public int runDialog(String sDesc, Icon icon, String sUser, String sPasswd){ - m_nRet = JOptionPane.CANCEL_OPTION; - - // Code halts here until some other location calls setVisible(false) on this - // dialog object - if(icon != null) lblIcon.setIcon(icon); - lblDesc.setText(sDesc); - tfUser.setText(sUser); - tfPasswd.setText(sPasswd); - - setDefaultCloseOperation(DISPOSE_ON_CLOSE); - - - pack(); // Recalculate internal size of sub components - validate(); - - setVisible(true); - m_sUser = tfUser.getText(); - m_sPasswd = new String( tfPasswd.getPassword() ); - - return m_nRet; - }; - - int getReturn(){return m_nRet;} - String getUser(){return m_sUser;} - String getPasswd(){return m_sPasswd;} - - /** This method is called from within the constructor to initialize the form. WARNING: - * Do NOT modify this code. The content of this method is always regenerated by the - * Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - java.awt.GridBagConstraints gridBagConstraints; - - lblIcon = new javax.swing.JLabel(); - lblDesc = new javax.swing.JLabel(); - jLabel3 = new javax.swing.JLabel(); - jLabel4 = new javax.swing.JLabel(); - tfUser = new javax.swing.JTextField(); - tfPasswd = new javax.swing.JPasswordField(); - jSeparator1 = new javax.swing.JSeparator(); - btnOK = new javax.swing.JButton(); - btnCancel = new javax.swing.JButton(); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - setTitle("Authorization Required"); - setModalityType(java.awt.Dialog.ModalityType.APPLICATION_MODAL); - setName("Authorization"); // NOI18N - getContentPane().setLayout(new java.awt.GridBagLayout()); - - lblIcon.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/das2logo-64.png"))); // NOI18N - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; - gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 0); - getContentPane().add(lblIcon, gridBagConstraints); - - lblDesc.setText("

    Some Long Complete Site Name

    Server: some.server.org/das
    Data Set: Some Dataset"); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridwidth = 4; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); - getContentPane().add(lblDesc, gridBagConstraints); - - jLabel3.setText("User name:"); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 2; - gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; - getContentPane().add(jLabel3, gridBagConstraints); - - jLabel4.setText("Password:"); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 3; - gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; - getContentPane().add(jLabel4, gridBagConstraints); - - tfUser.setText("someone"); - tfUser.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - tfUserActionPerformed(evt); - } - }); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 2; - gridBagConstraints.gridwidth = java.awt.GridBagConstraints.RELATIVE; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; - gridBagConstraints.weightx = 0.67; - gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); - getContentPane().add(tfUser, gridBagConstraints); - - tfPasswd.setText("jPasswor"); - tfPasswd.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - tfPasswdActionPerformed(evt); - } - }); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 3; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; - gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); - getContentPane().add(tfPasswd, gridBagConstraints); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.gridwidth = 5; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); - getContentPane().add(jSeparator1, gridBagConstraints); - - btnOK.setText("OK"); - btnOK.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnOKActionPerformed(evt); - } - }); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 4; - gridBagConstraints.gridy = 4; - gridBagConstraints.ipadx = 10; - gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); - getContentPane().add(btnOK, gridBagConstraints); - - btnCancel.setText("Cancel"); - btnCancel.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnCancelActionPerformed(evt); - } - }); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 3; - gridBagConstraints.gridy = 4; - gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; - gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); - getContentPane().add(btnCancel, gridBagConstraints); - - pack(); - }// //GEN-END:initComponents - - private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed - m_nRet = JOptionPane.OK_OPTION; - setVisible(false); - }//GEN-LAST:event_btnOKActionPerformed - - private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed - setVisible(false); - }//GEN-LAST:event_btnCancelActionPerformed - - private void tfPasswdActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tfPasswdActionPerformed - btnOKActionPerformed(evt); - }//GEN-LAST:event_tfPasswdActionPerformed - - private void tfUserActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tfUserActionPerformed - btnOKActionPerformed(evt); - }//GEN-LAST:event_tfUserActionPerformed - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton btnCancel; - private javax.swing.JButton btnOK; - private javax.swing.JLabel jLabel3; - private javax.swing.JLabel jLabel4; - private javax.swing.JSeparator jSeparator1; - private javax.swing.JLabel lblDesc; - private javax.swing.JLabel lblIcon; - private javax.swing.JPasswordField tfPasswd; - private javax.swing.JTextField tfUser; - // End of variables declaration//GEN-END:variables -} diff --git a/dasCoreUtil/src/org/das2/util/CredentialsManager.java b/dasCoreUtil/src/org/das2/util/CredentialsManager.java deleted file mode 100644 index a31660cdb..000000000 --- a/dasCoreUtil/src/org/das2/util/CredentialsManager.java +++ /dev/null @@ -1,315 +0,0 @@ -/* Part of the Das2 libraries, which the LGPL with class-path exception license */ -package org.das2.util; - -import java.awt.Frame; -import java.awt.Window; -import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.logging.Logger; -import javax.swing.ImageIcon; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; - - -/** Provides per-resource login credentials - * - * This class maintains a map of login credentials by resource ID. The resource ID's - * themselves are just strings. The only expectation on resource ID strings is that they - * should be suitable for use as the Keys to a hash map. Otherwise no formation rules are - * assumed nor expected. User names and passwords for multiple web-sites, ftp sites, etc. - * are maintained by a single instance of this class. Call: - * - * CredentialsManager.getManager() - * - * to get a reference to that single instance. - * - * In a graphical environment this class handles presenting dialogs to the user to gather - * logon credentials. In a shell environment it will interact with the TTY to get user - * information. - * - * An example of using this class to handle Das 2.1 server authentication which html - * location information formatting follows: - * - * - * CredentialsManage cm = CrentialsManager.getMannager(); - * String sLocId = "planet.physics.uiowa.edu/das/das2Server|voyager1/pwi/SpecAnalyzer-4s-Efield"; - * - * if(!cm.hasCredentials(sLocId)){ - * DasServer svr = DasServer.create(sDsdf); - * String sDesc = String.Format("

    %s

    Server: %s

    DataSource: %s

    ", - * DasServer.getName(), "planet.physics.uiowa.edu", - * "voyager1 > pwi > SpecAnalyzer-4s-Efield"); - * cm.setDescription(sLocId, sDesc, DasServer.getLogo()); - * } - * - * String sHash = getHttpBasicHash(sLocId) - * - *
    - * - * Two previous classes, org.das2.util.filesystem.KeyChain (autoplot) and - * org.das2.client.Authenticator have approached this problem as well. However both of - * those classes make assumptions that are not valid in general. The first assumes that - * the caller somehow knows the username. The second assumes that you are talking to - * a first generation Das 2.1 server. Details of server communication are beyond the - * scope of this class. - * - * @author cwp - */ -public class CredentialsManager{ - - /////////////////////////////////////////////////////////////////////////////////// - // Static Section - - // A map of credentials managers versus lookup key - static final HashMap g_dManagers = new HashMap(); - static{ - g_dManagers.put(null, new CredentialsManager(null)); - } - - /** Get a reference to the authentication manager. - * - * Typically this is the function you want to use to get started. - */ - public static CredentialsManager getMannager(){ - return g_dManagers.get(null); - } - - /** Get an authentication manager associated with an associated tracking string. - * This is probably not the function you are looking for. It only exists for - * odd cases where two different credential managers are active in a single - * application at the same time. - * - * @param sWhich - A string used to differentiate this credentials manager from the - * default instance. - */ - public static CredentialsManager getMannager(String sWhich){ - if(!g_dManagers.containsKey(sWhich)){ - synchronized(g_dManagers) { - g_dManagers.put(sWhich, new CredentialsManager(sWhich)); - } - } - return g_dManagers.get(sWhich); - } - - //////////////////////////////////////////////////////////////////////////////////// - // Instance Section - - protected class Location{ - String sLocId; - String sDesc; - ImageIcon iconLogo; - String sUser; - String sPasswd; - - protected Location(String sLocationId, String sDescription, ImageIcon icon){ - sLocId = sLocationId; - sDesc = sDescription; - iconLogo = icon; - sUser = null; - sPasswd = null; - } - - protected boolean hasCredentials(){ - return (sUser != null)||(sPasswd != null); - } - } - - String m_sName; - HashMap m_dLocs; - CredentialsDialog m_dlg; - - protected CredentialsManager(String sName){ - m_sName = sName; - m_dLocs = new HashMap(); - m_dlg = null; - } - - /** Provide a description of a location for use in authentication dialogs. - * - * Use this function to tie a string description to a location id. This description - * will be used when interacting with the user. If no description is present, then just - * the location ID itself will be used to identify the site to the end user. - * Usually location strings aren't that easy to read so use of this function or the - * version with an icon argument is recommended, though not required. - * - * @param sLocationId The location to describe, can not be null. - * @param sDescription A string to present to a user when prompting for a credentials - * for this location, may be a basic HTML string. - */ - public void setDescription(String sLocationId, String sDescription){ - setDescription(sLocationId, sDescription, null); - } - - /** Provide a description of a location with an Image Icon - * - * Use this function to tie a string description and an Icon to a location. - * - * @param sLocationId The location to describe, can not be null. - * @param sDescription The description, may be a simply formatted HTML string. - * @param icon An icon to display for the server. - */ - public synchronized void setDescription(String sLocationId, String sDescription, - ImageIcon icon) - { - if(!m_dLocs.containsKey(sLocationId)){ - m_dLocs.put(sLocationId, new Location(sLocationId, sDescription, icon)); - } - else{ - Location loc = m_dLocs.get(sLocationId); - loc.sDesc = sDescription; - loc.iconLogo = icon; - } - } - - /** Determine if there are any stored credentials for this location - * - * If either a username or a password have been provided for the location - * then it is considered to have credentials - * - * @param sLocationId The location to describe, can not be null. - * @return true if there are stored credentials, false otherwise - */ - public boolean hasCredentials(String sLocationId){ - if(!m_dLocs.containsKey(sLocationId)) return false; - Location loc = m_dLocs.get(sLocationId); - return loc.hasCredentials(); - } - - /** Determine if a given location has been described - * - * Gathering descriptive information about a remote location may trigger communication - * with a remote site. Use this function to see if such communication is needed. - * - * @param sLocationId The location in question - * @return true if the location has been described, false otherwise - */ - public boolean hasDescription(String sLocationId){ - if(!m_dLocs.containsKey(sLocationId)) return false; - Location loc = m_dLocs.get(sLocationId); - return (loc.sDesc != null)&&(!loc.sDesc.isEmpty()); - } - - /** Determine is a site image has been set for a location ID. - * - * This function is provided because retrieving the logo for a site may trigger remote - * host communication. Use this function to see if such communication is needed. - * @param sLocationId the location in question - * @return true if the location has as attached icon logo - */ - public boolean hasIcon(String sLocationId){ - if(!m_dLocs.containsKey(sLocationId)) return false; - Location loc = m_dLocs.get(sLocationId); - return loc.iconLogo != null; - } - - /** Get credentials in the form of a hashed HTTP Basic authentication string - * - * If there are no credentials stored for the given location id, this function may - * trigger interaction with the user, such as presenting modal dialogs, or changing the - * TTY to non-echo. - * - * @param sLocationId A unique string identifying a location. There are no formation - * rules on the string, but convenience functions are provided if a uniform naming - * convention is desired. - * - * @return The string USERNAME + ":" + PASSWORD that is then run through a base64 - * encoding. If no credentials are available for the given location ID and none can be - * gathered from the user (possibly due to the java.awt.headless being set or the - * user pressing cancel), null is returned. - */ - public String getHttpBasicHash(String sLocationId){ - String sHash; - - if(!m_dLocs.containsKey(sLocationId)){ - synchronized(this){ - //Check again. Though unlikely, the key could have been added between - //the call above and the start of the sychronized section - if(!m_dLocs.containsKey(sLocationId)) - m_dLocs.put(sLocationId, new Location(sLocationId, null, null)); - } - } - - Location loc = m_dLocs.get(sLocationId); - if(!hasCredentials(sLocationId)){ - if("true".equals( System.getProperty("java.awt.headless"))){ - if(!getCredentialsCmdLine(loc)) return null; - } - else{ - if(!getCredentialsGUI(loc)) return null; - } - } - - String sTmp = loc.sUser + ":" + loc.sPasswd; - sHash = Base64.encodeBytes( sTmp.getBytes()); - return sHash; - } - - /** Let the credentials manager know that stored credentials for a location are invalid - * - * @param sLocationId - * @return - */ - public synchronized void invalidate(String sLocationId){ - if(!m_dLocs.containsKey(sLocationId)) return; - Location loc = m_dLocs.get(sLocationId); - loc.sUser = null; - loc.sPasswd = null; - } - - ////////////////////////// User Interaction //////////////////////////////////// - - /** Gather User Credentials - * - * @param loc The Location in question - * @return True if user hit OK, False if user canceled the operation - */ - protected synchronized boolean getCredentialsGUI(final Location loc) { - - // Check again to see if another thread managed to set the credentials before - // this method started. Need to avoid the double-authenticate dialogs problem - // I'm not sure how to prevent the double cancel problem at this time. --cwp - if( loc.hasCredentials()) return true; - - try{ - SwingUtilities.invokeAndWait( - new Runnable(){ - @Override - public void run(){ - // make the dialog if it doesn't exist - if(m_dlg == null){ - Frame wParent = null; - Window[] lTopWnds = Window.getOwnerlessWindows(); - for(Window wnd: lTopWnds){ - if(wnd.isVisible() && wnd instanceof Frame){ - wParent = (Frame)wnd; - break; - } - } - m_dlg = new CredentialsDialog(wParent); - } - String sTmp = loc.sDesc; - if((sTmp == null)||(sTmp.isEmpty())) sTmp = loc.sLocId; - m_dlg.runDialog(sTmp, loc.iconLogo, loc.sUser, loc.sPasswd); - } - } - ); - } catch(InterruptedException ex) { - LoggerManager.getLogger("das2.util").severe(ex.toString()); - return false; - } catch ( InvocationTargetException ex){ - LoggerManager.getLogger("das2.util").severe(ex.toString()); - return false; - } - - if(m_dlg.getReturn() == JOptionPane.CANCEL_OPTION) return false; - loc.sUser = m_dlg.getUser(); - loc.sPasswd = m_dlg.getPasswd(); - - return true; - } - - protected synchronized boolean getCredentialsCmdLine(Location loc){ - throw new UnsupportedOperationException("Command Line Credential collection is " - + "not yet implemented"); - } -} diff --git a/dasCoreUtil/src/org/das2/util/Crypt.java b/dasCoreUtil/src/org/das2/util/Crypt.java deleted file mode 100644 index 5e102f166..000000000 --- a/dasCoreUtil/src/org/das2/util/Crypt.java +++ /dev/null @@ -1,71 +0,0 @@ -/* File: crypt.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author jbf - */ -public class Crypt { - - /** Creates a new instance of crypt */ - private Crypt() { - } - - public static String crypt(java.lang.String s) { - return JCrypt.crypt("do", s); -// try { -// return new String(MessageDigest.getInstance("MD5").digest(s.getBytes())); -// } catch ( NoSuchAlgorithmException ex ) { -// RuntimeException e= new IllegalStateException( "MD5 algorythm not available" ); -// e.initCause(ex); -// throw e; -// } - } - - public static void main(String args[]) throws Exception { - String arg; - if(args.length >= 1) { - arg= args[0]; - } else { - arg= "ask1st"; - Logger.getLogger("das2.anon").finest("java crypt "); - Logger.getLogger("das2.anon").log(Level.FINEST, " using {0}", arg); - } - System.out.println( - "[" + arg + "] => [" + - Crypt.crypt(arg) + "]" - ); - - //byte[] bytes= MessageDigest.getInstance("MD5").digest(arg.getBytes()); - //String[] hex= new String[] { "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f" }; - //for ( int i=0; i>4] + hex[( (bytes[i]&0x0F) )] + " "); - //} - - } -} diff --git a/dasCoreUtil/src/org/das2/util/DasExceptionHandler.java b/dasCoreUtil/src/org/das2/util/DasExceptionHandler.java deleted file mode 100755 index 79dc3a8ce..000000000 --- a/dasCoreUtil/src/org/das2/util/DasExceptionHandler.java +++ /dev/null @@ -1,174 +0,0 @@ -/* File: DasExceptionHandler.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.*; -import java.io.PrintWriter; -import java.io.StringWriter; - -/** - * - * @author jbf - */ -public final class DasExceptionHandler { - - //private static JDialog dialog; - //private static JTextArea messageArea; - //private static JTextArea traceArea; - private static final String UNCAUGHT = "An unexpected error has occurred. " + - "The system may not be able to recover properly. Please report this " + - "error to the Das2 bug database at http://bugs-pw.physics.uiowa.edu/." + - " Please include all error information and a description of how you" + - " encountered the error. For your convenience, you may click the " + - "\"Show Details\" button then click the \"Save to file\" button to save" + - " all the relevant error messages to a file.\n"; - - /* this is public so that the AWT thread can create it */ - public DasExceptionHandler() { - } - - public static void handle(Throwable t) { - if ( "true".equals( System.getProperty("java.awt.headless","false") ) ) { - t.printStackTrace(); - } - else { - showExceptionDialog(t, ""); - } - } - - public static void handleUncaught(Throwable t) { - if ( "true".equals( System.getProperty("java.awt.headless","false") ) ) { - t.printStackTrace(); - } - else { - showExceptionDialog(t, UNCAUGHT); - } - } - - private static void showExceptionDialog(final Throwable t, String extraInfo) { - String errorMessage = extraInfo + t.getClass().getName() + "\n" - + (t.getMessage() == null ? "" : t.getMessage()); - final JDialog dialog = new JDialog( ); // DasApplication.getDefaultApplication().getMainFrame() ); - dialog.setTitle("Error in das2"); - dialog.setModal(false); - dialog.setResizable(false); - dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - final JTextArea messageArea = new JTextArea(10, 40); - messageArea.setLineWrap(true); - messageArea.setWrapStyleWord(true); - messageArea.setEditable(false); - messageArea.setText(errorMessage); - JScrollPane message = new JScrollPane(messageArea); - message.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - JPanel mainPanel = new JPanel(new BorderLayout()); - mainPanel.add(message, BorderLayout.CENTER); - - JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - JButton ok = new JButton("Ok"); - final JToggleButton details = new JToggleButton("Show Details"); - buttonPanel.add(ok); - buttonPanel.add(details); - mainPanel.add(buttonPanel, BorderLayout.SOUTH); - - dialog.getContentPane().add(mainPanel, BorderLayout.CENTER); - - final JTextArea traceArea = new JTextArea(10, 40); - traceArea.setLineWrap(false); - traceArea.setEditable(false); - traceArea.setTabSize(4); - - StringWriter writer = new StringWriter(); - t.printStackTrace(new PrintWriter(writer)); - traceArea.setText(writer.toString()); - - final JPanel stackPane = new JPanel(new BorderLayout()); - stackPane.add(new JScrollPane(traceArea), BorderLayout.NORTH); - stackPane.setBorder(new javax.swing.border.EmptyBorder(10, 10, 10, 10)); - JPanel buttonPanel2 = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - buttonPanel2.setBorder(new javax.swing.border.EmptyBorder(10, 0, 0, 0)); - JButton dump = new JButton("Dump to STDERR"); - JButton save = new JButton("Save to file"); - buttonPanel2.add(dump); - buttonPanel2.add(save); - stackPane.add(buttonPanel2, BorderLayout.SOUTH); - Dimension size = message.getPreferredSize(); - size.width = stackPane.getPreferredSize().width; - message.setPreferredSize(size); - - ok.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - dialog.dispose(); - } - }); - - details.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (details.isSelected()) { - details.setText("Less Details"); - dialog.getContentPane().add(stackPane, BorderLayout.SOUTH); - dialog.pack(); - } - else { - details.setText("More Details"); - dialog.getContentPane().remove(stackPane); - dialog.pack(); - } - } - }); - - dump.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - String text = traceArea.getText(); - System.err.print(text); - } - }); - - save.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - try { - JFileChooser chooser = new JFileChooser(); - int result = chooser.showSaveDialog(dialog); - if (result == chooser.APPROVE_OPTION) { - File selected = chooser.getSelectedFile(); - PrintWriter out = new PrintWriter(new FileOutputStream(selected)); - t.printStackTrace(out); - out.close(); - } - } - catch (IOException ioe) { - handle(ioe); - } - } - }); - - dialog.pack(); - dialog.setLocationRelativeTo(null); - dialog.setVisible(true); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/DasMath.java b/dasCoreUtil/src/org/das2/util/DasMath.java deleted file mode 100644 index c37a21fd1..000000000 --- a/dasCoreUtil/src/org/das2/util/DasMath.java +++ /dev/null @@ -1,263 +0,0 @@ -/* File: DasMath.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author jbf - */ - -public class DasMath { - - /** Creates a new instance of DasMath */ - public DasMath() { - } - - private static final double log10=Math.log(10); - - public static double log10(double x) { - return Math.log(x)/log10; - } - - public static double exp10(double x) { - double result= Math.pow(10,x); - return result; - } - - public static double exp10(int x) { - double result= Math.pow(10,x); - return result; - } - - public static double roundNFractionalDigits(double x,int n) { - double tenToN= Math.pow(10,n-1); - return Math.round( x * tenToN ) / tenToN; - } - - public static double roundNSignificantDigits(double x,int n) { - double sign= x<0 ? -1 : 1; - double exp= Math.pow(10,Math.floor(Math.log10(sign*x))); - double mant= x/exp; - double tenToN= Math.pow(10,n-1); - mant= Math.round( mant * tenToN ) / tenToN; - return mant*exp; - } - - public static double tanh( double x ) { - double sinh = ( Math.exp(x) - Math.exp(-x) )/2; - double cosh = ( Math.exp(x) + Math.exp(-x) )/2; - double tanh = sinh / cosh; - return tanh; - } - - /** Interpolate just one point */ - public static double interpolate( double[] datay, double findex ) { - int index= (int)findex; - double alpha= findex - index; - double result; - if ( findex<0. ) return datay[0]; - if ( findex>datay.length-1. ) return datay[datay.length-1]; - if ( alpha == 0. ) { // optimization and handle findex=(data.length-1); - result= datay[index]; - } else { - result= datay[index] * ( 1.0 - alpha ) + datay[index+1] * alpha ; - } - return result; - } - - /** interpolate an array of points. */ - public static double[] interpolate( double[] datay, double[] findex ) { - double[] result= new double[ findex.length ]; - for ( int i=0; i0 && datax[index]>x ) index--; - if ( index==datax.length-1 ) index--; - return index + ( x - datax[index] ) / ( datax[index+1] - datax[index] ); - } - - public static void main(String[] args) { - double x= 1e-18; - Logger.getLogger("das2.anon").log(Level.FINEST, "x:{0}", x); - Logger.getLogger("das2.anon").log(Level.FINEST, "roundNDigits:{0}", roundNSignificantDigits(x,3)); - - double[] x1= new double[] { 1,2,3,4,5 }; - double[] y1= new double[] { 4,6,7,3,1 }; - double[] interpx= new double [] { 1.0, 1.5, 4.5, 5.0, 1.5 }; - double[] interpy= interpolate( y1, findex( x1, interpx ) ); - for ( int i=0; i= 0 ? result : t + result; - } - - /** - * just like modulo (%) function, but negative numbers return positive phase. - */ - public static int modp( int x, int t) { - int result= x % t; - return result >= 0 ? result : t + result; - } - - public static double biggerOf(double x1, double x2) { - return ( x1>x2 ) ? x1 : x2; - } - - private static double gcd( double a, double d, double error ) { - - if ( error>0 ) { - a= Math.round( a/error ); - d= Math.round( d/error ); - } - - if ( a0 ) { - return a * error; - } else { - return a; - } - } - - double r= a % d; - - int iterations=0; - - while ( r > 0 && iterations<15 ) { - d= r; - r= a % d; - iterations++; - } - - if ( error>0 ) { - return d * error; - } else { - return d; - } - } - - - /* - * Returns the greatest common divisor of a group of numbers. This is useful for - * a number of visualization techniques, for instance when you need to integerize - * your data, the binsize should be the gcd. An error parameter is provided to - * avoid numerical noise, and in case there is a granularity that needn't be - * surpassed. - * - * org.das2.datum.DatumUtil has a private copy of this code. - */ - public static double gcd( double[] A, double error ) { - double guess= A[0]; - - double result= guess; - - for ( int i=1; i A[i] ? max : A[i] ); - } - return max; - } - - public static double min( double[] A ) { - double min= A[0]; - for ( int i=0; i - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -/** - * - * @author eew - */ -public abstract class DasPNGConstants { - - /* - * Chunk type constants - */ - static final String CHUNK_TYPE_IHDR = "IHDR"; - static final String CHUNK_TYPE_PLTE = "PLTE"; - static final String CHUNK_TYPE_IDAT = "IDAT"; - static final String CHUNK_TYPE_IEND = "IEND"; - static final String CHUNK_TYPE_bKGD = "bKGD"; //CURRENTLY UNSUPPORTED - static final String CHUNK_TYPE_cHRM = "cHRM"; //CURRENTLY UNSUPPORTED - static final String CHUNK_TYPE_gAMA = "gAMA"; //CURRENTLY UNSUPPORTED - static final String CHUNK_TYPE_hIST = "hIST"; //CURRENTLY UNSUPPORTED - static final String CHUNK_TYPE_pHYs = "pHYs"; //CURRENTLY UNSUPPORTED - static final String CHUNK_TYPE_sBIT = "sBIT"; //CURRENTLY UNSUPPORTED - static final String CHUNK_TYPE_tEXT = "tEXt"; - static final String CHUNK_TYPE_tIME = "tIME"; //CURRENTLY UNSUPPORTED - static final String CHUNK_TYPE_tRNS = "tRNS"; //CURRENTLY UNSUPPORTED - static final String CHUNK_TYPE_zTXT = "zTXt"; //CURRENTLY UNSUPPORTED - - public static final int DEFAULT_GAMMA = 45000; //gamma of .45 (multiplied by 100000) - - public static final String KEYWORD_TITLE = "Title"; - public static final String KEYWORD_AUTHOR = "Author"; - public static final String KEYWORD_DESCRIPTION = "Description"; - public static final String KEYWORD_COPYRIGHT = "Copyright"; - public static final String KEYWORD_CREATION_TIME = "Creation Time"; - public static final String KEYWORD_SOFTWARE = "Software"; - public static final String KEYWORD_DISCLAIMER = "Disclaimer"; - public static final String KEYWORD_WARNING = "Warning"; - public static final String KEYWORD_SOURCE = "Source"; - public static final String KEYWORD_COMMENT = "Comment"; - - /** - * dasCanvas has a method for generating a JSON string describing the plots, - * and this is the tag that data should be inserted with. - */ - public static final String KEYWORD_PLOT_INFO = "plotInfo"; - - protected HashMap textMap = new HashMap(); - - protected int gamma = DEFAULT_GAMMA; - - DasPNGConstants() { - } - - /** Returns an unmodifiable java.util.List containing the contents of - * all of the tEXT chunks with the specified keyword. The return value - * is guaranteed to be non-null. If no tEXT chunks with the specified - * keyword exist, then an empty list is returned. - * @param keyword the specified keyword - * @return a java.util.List of the contents of tEXT chunks. - */ - public List getText(String keyword) { - List list = (List)textMap.get(keyword); - if (list == null) { - return Collections.EMPTY_LIST; - } - return Collections.unmodifiableList(list); - } - - protected static byte[] getISO8859_1Bytes(String header) { - try { - return header.getBytes("ISO-8859-1"); - } - catch (java.io.UnsupportedEncodingException uee) { - throw new AssertionError(uee); - } - } - - public int getGamma() { - return gamma; - } - -} diff --git a/dasCoreUtil/src/org/das2/util/DasPNGEncoder.java b/dasCoreUtil/src/org/das2/util/DasPNGEncoder.java deleted file mode 100644 index 6db8ce8a8..000000000 --- a/dasCoreUtil/src/org/das2/util/DasPNGEncoder.java +++ /dev/null @@ -1,343 +0,0 @@ -/* File: DasPNGEncoder.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import java.awt.image.BufferedImage; -import java.awt.image.IndexColorModel; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.zip.CRC32; -import java.util.zip.Deflater; - -/** - * - * @author eew - */ -public class DasPNGEncoder extends DasPNGConstants { - - - /** Creates a new instance of DasPNGEncoder */ - public DasPNGEncoder() { - } - - /** Adds a tEXT chunk with the specified keyword and content. - * @param keyword the specified keyword - * @param content the content for the tEXT chunk - */ - public void addText(String keyword, String content) { - List list = (List)textMap.get(keyword); - if (list == null) { - list = new ArrayList(); - textMap.put(keyword, list); - } - list.add(content); - } - - /** Removes the tEXT chunk with the specified keyword and content. - * @param keyword the specified keyword - * @param content the specified content to be removed - */ - public void removeText(String keyword, String content) { - List list = (List)textMap.get(keyword); - if (list != null) { - list.remove(content); - } - } - - /** Removes all tEXT chunk with the specified keyword - * @param keyword the specified keyword. - */ - public void removeAllText(String keyword) { - textMap.remove(keyword); - } - - public void setGamma(int gamma) { - this.gamma = gamma; - } - - public void write(BufferedImage image, OutputStream out) throws IOException { - LinkedList chunkList = new LinkedList(); - int totalSize = 0; - chunkList.add(getHeaderBytes()); - chunkList.add(getIHDRBytes(image)); - gettEXtBytes(chunkList); - chunkList.add(getgAMABytes()); - chunkList.add(getPLTEBytes(image)); - chunkList.add(getIDATBytes(image)); - chunkList.add(getIENDBytes()); - Iterator iterator = chunkList.iterator(); - while (iterator.hasNext()) { - totalSize += ((byte[])iterator.next()).length; - } - ByteBuffer buffer = ByteBuffer.allocate(totalSize); - iterator = chunkList.iterator(); - while (iterator.hasNext()) { - buffer.put((byte[])iterator.next()); - } - out.write(buffer.array()); - } - - private byte[] getHeaderBytes() { - return new byte[] { - (byte)137, (byte)80, (byte)78, (byte)71, - (byte)13, (byte)10, (byte)26, (byte)10 - }; - } - - /** - * Width: 4 bytes - * Height: 4 bytes - * Bit depth: 1 byte (allowed values: 1, 2, 4, 8, 16) - * Color type: 1 byte - Color Allowed Interpretation - Type Bit Depths - 0 1,2,4,8,16 Each pixel is a grayscale sample. - 2 8,16 Each pixel is an R,G,B triple. - 3 1,2,4,8 Each pixel is a palette index; - a PLTE chunk must appear. - 4 8,16 Each pixel is a grayscale sample, - followed by an alpha sample. - 6 8,16 Each pixel is an R,G,B triple, - followed by an alpha sample. - * Compression method: 1 byte (must be 0) - * Filter method: 1 byte (must be 0) - * Interlace method: 1 byte (interlacing not supported, will be 0) - */ - private byte[] getIHDRBytes(BufferedImage image) { - byte bitDepth; - byte colorType; - int imageType = image.getType(); - switch (imageType) { - //24 bit image types - case BufferedImage.TYPE_3BYTE_BGR: - case BufferedImage.TYPE_INT_BGR: - case BufferedImage.TYPE_INT_RGB: - case BufferedImage.TYPE_USHORT_555_RGB: - case BufferedImage.TYPE_USHORT_565_RGB: - bitDepth = 8; - colorType = 2; - break; - - //32 bit alpha - case BufferedImage.TYPE_4BYTE_ABGR: - case BufferedImage.TYPE_INT_ARGB: - bitDepth = 8; - colorType = 6; - break; - - case BufferedImage.TYPE_BYTE_INDEXED: - bitDepth = 8; - colorType = 3; - break; - - case BufferedImage.TYPE_BYTE_GRAY: - bitDepth = 8; - colorType = 0; - break; - - case BufferedImage.TYPE_USHORT_GRAY: - bitDepth = 16; - colorType = 0; - break; - - //Currently unsupported types - case BufferedImage.TYPE_BYTE_BINARY: - case BufferedImage.TYPE_4BYTE_ABGR_PRE: - case BufferedImage.TYPE_INT_ARGB_PRE: - case BufferedImage.TYPE_CUSTOM: - default: - throw new RuntimeException("Unsupported image type"); - } - byte compressionMethod = 0; - byte filterMethod = 0; - byte interlaceMethod = 0; - - byte[] array = new byte[25]; - ByteBuffer buffer = ByteBuffer.wrap(array); - buffer.putInt(13); - buffer.put(getISO8859_1Bytes(CHUNK_TYPE_IHDR)); - buffer.putInt(image.getWidth()); - buffer.putInt(image.getHeight()); - buffer.put(bitDepth); - buffer.put(colorType); - buffer.put(compressionMethod); - buffer.put(filterMethod); - buffer.put(interlaceMethod); - CRC32 crc = new CRC32(); - crc.update(array, 4, 17); - buffer.putInt((int)crc.getValue()); - return array; - } - - private byte[] getgAMABytes() { - byte[] array = new byte[16]; - ByteBuffer buffer = ByteBuffer.wrap(array); - buffer.putInt(4); - buffer.put(getISO8859_1Bytes(CHUNK_TYPE_gAMA)); - buffer.putInt(gamma); - CRC32 crc = new CRC32(); - crc.update(array, 4, 8); - buffer.putInt((int)crc.getValue()); - return array; - } - - private byte[] getPLTEBytes(BufferedImage image) { - if (image.getType() != BufferedImage.TYPE_BYTE_INDEXED) { - return new byte[0]; - } - //IndexColorModel cm = (IndexColorModel)image.getColorModel(); - //int colorCount = cm.getMapSize(); - throw new UnsupportedOperationException(); - } - - private byte[] getIDATBytes(BufferedImage image) { - byte[] imageData; - int imageType = image.getType(); - switch (imageType) { - case BufferedImage.TYPE_3BYTE_BGR: - case BufferedImage.TYPE_INT_BGR: - case BufferedImage.TYPE_INT_RGB: - case BufferedImage.TYPE_USHORT_555_RGB: - case BufferedImage.TYPE_USHORT_565_RGB: - imageData = getRGBBytes(image); - break; - - //32 bit alpha - case BufferedImage.TYPE_4BYTE_ABGR: - throw new UnsupportedOperationException("ABGR mode not supported"); - case BufferedImage.TYPE_INT_ARGB: - throw new UnsupportedOperationException("ARGB mode not supported"); - case BufferedImage.TYPE_BYTE_INDEXED: - case BufferedImage.TYPE_BYTE_GRAY: - throw new UnsupportedOperationException("8 bit mode not supported"); - - case BufferedImage.TYPE_USHORT_GRAY: - throw new UnsupportedOperationException("16bit mode not supported"); - - //Currently unsupported types - case BufferedImage.TYPE_BYTE_BINARY: - case BufferedImage.TYPE_4BYTE_ABGR_PRE: - case BufferedImage.TYPE_INT_ARGB_PRE: - case BufferedImage.TYPE_CUSTOM: - default: - throw new RuntimeException("Unsupported image type"); - } - - byte[] compressedImageData = new byte[imageData.length]; - Deflater deflater = new Deflater(); - deflater.setInput(imageData); - deflater.finish(); - int compressedSize = deflater.deflate(compressedImageData); - - byte[] array = new byte[compressedSize + 12]; - ByteBuffer buffer = ByteBuffer.wrap(array); - buffer.putInt(compressedSize); - buffer.put(getISO8859_1Bytes(CHUNK_TYPE_IDAT)); - buffer.put(compressedImageData, 0, compressedSize); - CRC32 crc = new CRC32(); - crc.update(array, 4, compressedSize + 4); - buffer.putInt((int)crc.getValue()); - return array; - } - - private byte[] getRGBBytes(BufferedImage image) { - int width = image.getWidth(); - int height = image.getHeight(); - int[] intPixels = new int[width * height]; - image.getRGB(0, 0, width, height, intPixels, 0, width); - byte[] bytePixels = new byte[intPixels.length * 3 + height]; - - for (int line = 0; line < height; line++) { - int offset = (width * 3 + 1) * line; - bytePixels[offset] = (byte)0; - for (int pixel = 0; pixel < width; pixel++) { - int intIndex = line * width + pixel; - int byteIndex = offset + (pixel * 3 + 1); - bytePixels[byteIndex] = (byte)((0xFF0000 & intPixels[intIndex]) >> 16); - bytePixels[byteIndex + 1] = (byte)((0x00FF00 & intPixels[intIndex]) >> 8); - bytePixels[byteIndex + 2] = (byte)(0x0000FF & intPixels[intIndex]); - } - } - return bytePixels; - } - -// private byte[] getARGBBytes(BufferedImage image) { -// throw new UnsupportedOperationException("ARGB mode not supported"); -// } -// -// private byte[] getABGRBytes(BufferedImage image) { -// throw new UnsupportedOperationException("ABGR mode not supported"); -// } -// -// private byte[] get8BitSampleBytes(BufferedImage image) { -// throw new UnsupportedOperationException("8 bit mode not supported"); -// } -// -// private byte[] get16BitSampleBytes(BufferedImage image) { -// throw new UnsupportedOperationException("16bit mode not supported"); -// } -// - private byte[] getIENDBytes() { - byte[] array = new byte[12]; - byte[] typeBytes = getISO8859_1Bytes(CHUNK_TYPE_IEND); - ByteBuffer buffer = ByteBuffer.wrap(array); - buffer.putInt(0); - buffer.put(typeBytes); - CRC32 crc = new CRC32(); - crc.update(typeBytes); - buffer.putInt((int)crc.getValue()); - return array; - } - - private void gettEXtBytes(List list) { - Iterator entries = textMap.entrySet().iterator(); - while (entries.hasNext()) { - Map.Entry entry = (Map.Entry)entries.next(); - List contentList = (List)entry.getValue(); - Iterator content = contentList.iterator(); - while (content.hasNext()) { - list.add(gettEXtBytes((String)entry.getKey(), (String)content.next())); - } - } - } - - private byte[] gettEXtBytes(String keyword, String content) { - byte[] keywordBytes = getISO8859_1Bytes(keyword); - byte[] contentBytes = getISO8859_1Bytes(content); - byte[] array = new byte[keywordBytes.length + contentBytes.length + 13]; - ByteBuffer buffer = ByteBuffer.wrap(array); - buffer.putInt(keywordBytes.length + contentBytes.length + 1); - buffer.put(getISO8859_1Bytes(CHUNK_TYPE_tEXT)); - buffer.put(keywordBytes); - buffer.put((byte)0); - buffer.put(contentBytes); - CRC32 crc = new CRC32(); - crc.update(array, 4, keywordBytes.length + contentBytes.length + 5); - buffer.putInt((int)crc.getValue()); - return array; - } - -} diff --git a/dasCoreUtil/src/org/das2/util/DasProgressMonitorInputStream.java b/dasCoreUtil/src/org/das2/util/DasProgressMonitorInputStream.java deleted file mode 100755 index 455aa6e39..000000000 --- a/dasCoreUtil/src/org/das2/util/DasProgressMonitorInputStream.java +++ /dev/null @@ -1,214 +0,0 @@ -/* File: DasProgressMonitorInputStream.java - * Copyright (C) 2002-2003 The University of Iowa - * - * Created on September 23, 2003, 5:00 PM - * by Edward West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InterruptedIOException; -import java.text.DecimalFormat; -import org.das2.util.monitor.ProgressMonitor; - -/** - * - * @author Edward West - */ -public class DasProgressMonitorInputStream extends java.io.FilterInputStream { - - private ProgressMonitor monitor; - private boolean started = false; - private int bytesRead = 0; - long birthTimeMilli; - long deathTimeMilli; - DecimalFormat transferRateFormat; - boolean enableProgressPosition= true; - - private long streamLength= 1000000; // this is usually close because of server side averaging. - private long taskSize= streamLength/1000; - - /** Creates a new instance of DasProgressMonitorInputStream */ - public DasProgressMonitorInputStream( InputStream in, ProgressMonitor monitor ) { - super(in); - this.monitor = monitor; - this.birthTimeMilli= System.currentTimeMillis(); - this.deathTimeMilli= -1; - } - - private void reportTransmitSpeed() { - if (transferRateFormat==null ) { - transferRateFormat= new DecimalFormat(); - transferRateFormat.setMaximumFractionDigits(2); - transferRateFormat.setMinimumFractionDigits(2); - } - monitor.setProgressMessage("("+ transferRateFormat.format(calcTransmitSpeed()/1024) +"kB/s)"); - if ( enableProgressPosition ) monitor.setTaskProgress(bytesRead/1000); - } - - - private double calcTransmitSpeed() { - // return speed in bytes/second. - long totalBytesRead= bytesRead; - long timeElapsed; - if ( deathTimeMilli>-1 ) { - timeElapsed= deathTimeMilli-birthTimeMilli; - } else { - timeElapsed= System.currentTimeMillis()-birthTimeMilli; - } - if ( timeElapsed==0 ) return Double.POSITIVE_INFINITY; - return 1000. * totalBytesRead / timeElapsed; - } - - public int read() throws IOException { - checkCancelled(); - int result = super.read(); - if (monitor != null) { - if (!started) { - started = true; - monitor.setTaskSize(taskSize); - monitor.started(); - } - if (bytesRead == -1) { - if ( !monitor.isFinished() ) monitor.finished(); - } - else { - bytesRead++; - checkCancelled(); - reportTransmitSpeed(); - } - } - return result; - } - - public int read(byte[] b) throws IOException { - checkCancelled(); - int result = super.read(b); - if (monitor != null) { - if (!started) { - started = true; - monitor.setTaskSize(taskSize); - monitor.started(); - } - if (bytesRead == -1) { - if ( !monitor.isFinished() ) monitor.finished(); - } - else { - bytesRead += result; - checkCancelled(); - reportTransmitSpeed(); - } - } - return result; - } - - public int read(byte[] b, int off, int len) throws IOException { - checkCancelled(); - int result = super.read(b, off, len); - if (monitor != null) { - if (!started) { - started = true; - monitor.setTaskSize( taskSize ); - monitor.started(); - } - if (bytesRead == -1) { - if ( !monitor.isFinished() ) { - monitor.finished(); - } else { - // Something fishy is happening. See /home/jbf/project/autoplot/bugs/20141103 - } - } - else { - bytesRead += result; - checkCancelled(); - reportTransmitSpeed(); - } - } - return result; - } - - private void checkCancelled() throws IOException { - if (monitor != null && monitor.isCancelled()) { - close(); - throw new InterruptedIOException("Operation cancelled"); - } - } - - public void close() throws IOException { - super.close(); - deathTimeMilli= System.currentTimeMillis(); - if (monitor != null) { - if ( !monitor.isFinished() ) monitor.finished(); // it should be finished already from the read command. - started= false; - } - } - - /** - * disable/enable setting of progress position, true by default. Transfer - * rate will still be reported. This is introduced in case another agent - * (the das2Stream reader, in particular) can set the progress position - * more accurately. - */ - public void setEnableProgressPosition( boolean value ) { - this.enableProgressPosition= value; - } - - /** - * Utility field used by bound properties. - */ - private java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); - - /** - * Adds a PropertyChangeListener to the listener list. - * @param l The listener to add. - */ - public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { - propertyChangeSupport.addPropertyChangeListener(l); - } - - /** - * Removes a PropertyChangeListener from the listener list. - * @param l The listener to remove. - */ - public void removePropertyChangeListener(java.beans.PropertyChangeListener l) { - propertyChangeSupport.removePropertyChangeListener(l); - } - - /** - * Getter for property taskSize. - * @return Value of property taskSize. - */ - public long getStreamLength() { - return this.streamLength; - } - - /** - * Setter for property taskSize. - * @param taskSize New value of property taskSize. - */ - public void setStreamLength(long taskSize) { - long oldTaskSize = this.streamLength; - this.streamLength = taskSize; - this.taskSize= taskSize==-1 ? taskSize : streamLength/1000; - propertyChangeSupport.firePropertyChange ("streamLength", oldTaskSize, taskSize ); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/DasProgressMonitorReadableByteChannel.java b/dasCoreUtil/src/org/das2/util/DasProgressMonitorReadableByteChannel.java deleted file mode 100644 index 2bef3bc23..000000000 --- a/dasCoreUtil/src/org/das2/util/DasProgressMonitorReadableByteChannel.java +++ /dev/null @@ -1,172 +0,0 @@ -/* File: DasProgressMonitorInputStream.java - * Copyright (C) 2002-2003 The University of Iowa - * - * Created on September 23, 2003, 5:00 PM - * by Edward West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package org.das2.util; - -import java.io.IOException; -import java.io.InterruptedIOException; -import java.nio.ByteBuffer; -import java.nio.channels.ReadableByteChannel; -import java.text.DecimalFormat; -import org.das2.util.monitor.ProgressMonitor; - -/** - * - * @author Edward West - */ -public class DasProgressMonitorReadableByteChannel implements ReadableByteChannel { - - private ProgressMonitor monitor; - private boolean started = false; - private int bytesRead = 0; - long birthTimeMilli; - long deathTimeMilli; - DecimalFormat transferRateFormat; - boolean enableProgressPosition = true; - private long streamLength = 1000000; // this is usually close because of server side averaging. - private long taskSize = streamLength / 1000; - // we're wrapping this channel - ReadableByteChannel in; - - /** Creates a new instance of DasProgressMonitorInputStream */ - public DasProgressMonitorReadableByteChannel(ReadableByteChannel in, ProgressMonitor monitor) { - this.monitor = monitor; - this.birthTimeMilli = System.currentTimeMillis(); - this.deathTimeMilli = -1; - this.in = in; - } - - private void reportTransmitSpeed() { - if (transferRateFormat == null) { - transferRateFormat = new DecimalFormat(); - transferRateFormat.setMaximumFractionDigits(2); - transferRateFormat.setMinimumFractionDigits(2); - } - monitor.setProgressMessage("(" + transferRateFormat.format(calcTransmitSpeed() / 1024) + "kB/s)"); - if (enableProgressPosition) { - monitor.setTaskProgress(bytesRead / 1000); - } - } - - private double calcTransmitSpeed() { - // return speed in bytes/second. - long totalBytesRead = bytesRead; - long timeElapsed; - if (deathTimeMilli > -1) { - timeElapsed = deathTimeMilli - birthTimeMilli; - } else { - timeElapsed = System.currentTimeMillis() - birthTimeMilli; - } - if (timeElapsed == 0) { - return Double.POSITIVE_INFINITY; - } - return 1000. * totalBytesRead / timeElapsed; - } - - private void checkCancelled() throws IOException { - if (monitor != null && monitor.isCancelled()) { - close(); - throw new InterruptedIOException("Operation cancelled"); - } - } - - public void close() throws IOException { - in.close(); - deathTimeMilli = System.currentTimeMillis(); - if (monitor != null) { - monitor.finished(); - started = false; - } - } - - /** - * disable/enable setting of progress position, true by default. Transfer - * rate will still be reported. This is introduced in case another agent - * (the das2Stream reader, in particular) can set the progress position - * more accurately. - */ - public void setEnableProgressPosition(boolean value) { - this.enableProgressPosition = value; - } - /** - * Utility field used by bound properties. - */ - private java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); - - /** - * Adds a PropertyChangeListener to the listener list. - * @param l The listener to add. - */ - public void addPropertyChangeListener(java.beans.PropertyChangeListener l) { - propertyChangeSupport.addPropertyChangeListener(l); - } - - /** - * Removes a PropertyChangeListener from the listener list. - * @param l The listener to remove. - */ - public void removePropertyChangeListener(java.beans.PropertyChangeListener l) { - propertyChangeSupport.removePropertyChangeListener(l); - } - - /** - * Getter for property taskSize. - * @return Value of property taskSize. - */ - public long getStreamLength() { - return this.streamLength; - } - - /** - * Setter for property taskSize. - * @param taskSize New value of property taskSize. - */ - public void setStreamLength(long taskSize) { - long oldTaskSize = this.streamLength; - this.streamLength = taskSize; - this.taskSize = taskSize == -1 ? taskSize : streamLength / 1000; - propertyChangeSupport.firePropertyChange("streamLength", oldTaskSize, taskSize); - } - - public int read(ByteBuffer dst) throws IOException { - int result = in.read(dst); - - if (!started) { - started = true; - monitor.setTaskSize(taskSize); - monitor.started(); - } - if (bytesRead == -1) { - monitor.finished(); - } else { - bytesRead += result; - checkCancelled(); - reportTransmitSpeed(); - } - - return result; - } - - public boolean isOpen() { - return in.isOpen(); - } -} diff --git a/dasCoreUtil/src/org/das2/util/DebugPropertyChangeSupport.java b/dasCoreUtil/src/org/das2/util/DebugPropertyChangeSupport.java deleted file mode 100644 index b0227895d..000000000 --- a/dasCoreUtil/src/org/das2/util/DebugPropertyChangeSupport.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.util; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeListenerProxy; -import java.beans.PropertyChangeSupport; -import java.util.Arrays; - -/** - * - * @author jbf - */ -public class DebugPropertyChangeSupport extends PropertyChangeSupport { - - public DebugPropertyChangeSupport( Object bean ) { - super(bean); - } - - @Override - public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { - //TODO: danger--remove this from production code. - if ( Arrays.asList(getPropertyChangeListeners()).contains( listener ) ) { - return; - } - super.addPropertyChangeListener(listener); - } - - - @Override - public String toString() { - PropertyChangeListener[] listeners= getPropertyChangeListeners(); - StringBuffer result= new StringBuffer(super.toString()); - for ( int i=0; i palette = new ArrayList(); - - private Palette() { - StringBuilder dp = new StringBuilder(); - dp.append(toHexString(Color.WHITE)).append(","); - dp.append(toHexString(Color.RED.brighter())).append(","); - dp.append(toHexString(Color.GREEN.brighter())).append(","); - dp.append(toHexString(Color.BLUE.brighter())).append(","); - dp.append(toHexString(Color.GRAY)).append(","); - dp.append(toHexString(Color.RED)).append(","); - dp.append(toHexString(Color.GREEN)).append(","); - dp.append(toHexString(Color.BLUE)).append(","); - dp.append(toHexString(Color.BLACK)).append(","); - dp.append(toHexString(Color.RED.darker())).append(","); - dp.append(toHexString(Color.GREEN.darker())).append(","); - dp.append(toHexString(Color.BLUE.darker())).append(","); - - String ps = Preferences.userNodeForPackage(this.getClass()).get("palette", dp.toString()); - setPalette(ps); - - setBounds(0, 0, 2 * POFFX + 4 * PSIZE, 2 * POFFY + 3 * PSIZE); - setMinimumSize(new Dimension(2 * POFFX + 4 * PSIZE, 2 * POFFY + 3 * PSIZE)); - setMaximumSize(getMinimumSize()); - setPreferredSize(getMinimumSize()); - setSize(getMinimumSize()); - setBorder(new EtchedBorder()); - - addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - int index = ((e.getY() - POFFY) / PSIZE) * 4 + (e.getX() - POFFX) / PSIZE; - if (index < palette.size()) { - Color c = palette.get(index); - getColorSelectionModel().setSelectedColor(c); - setProposedColor(c); - } - } - }); - } - - private void setPalette(String s) { - String[] ss = s.split(","); - for (int i = 0; i < 12; i++) { - palette.add(Color.decode(ss[i])); - } - } - - private int PSIZE = 20; - private int POFFX = 3; - private int POFFY = 3; - - @Override - protected void paintComponent(Graphics g) { - - for (int j = 0; j < 3; j++) { - for (int i = 0; i < 4; i++) { - if (palette.size() > j * 4 + i) { - g.setColor(palette.get(j * 4 + i)); - Rectangle r = new Rectangle(POFFX + i * PSIZE, POFFY + j * PSIZE, PSIZE - 1, PSIZE - 1); - g.fillRect(r.x, r.y, r.width, r.height); - } - } - } - super.paintComponent(g); //To change body of generated methods, choose Tools | Templates. - } - - public void addToPalette(Color c) { - palette.add(0, c); - while (palette.size() > 12) { - palette.remove(12); - } - StringBuilder dp = new StringBuilder(); - for (Color color : palette) { - dp.append(toHexString(color)).append(","); - } - Preferences.userNodeForPackage(this.getClass()).put("palette", dp.toString()); - } - - } - - /** - * return an icon block with the color and size. - * - * @param iconColor the color - * @param w the width in pixels - * @param h the height in pixels - * @return an icon. - */ - public static Icon colorIcon(Color iconColor, int w, int h) { - BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - Graphics g = image.getGraphics(); - if (iconColor.getAlpha() != 255) { // draw checkerboard to indicate transparency - for (int j = 0; j < 16 / 4; j++) { - for (int i = 0; i < 16 / 4; i++) { - g.setColor((i - j) % 2 == 0 ? Color.GRAY : Color.WHITE); - g.fillRect(0 + i * 4, 0 + j * 4, 4, 4); - } - } - } - g.setColor(iconColor); - g.fillRect(0, 0, w, h); - return new ImageIcon(image); - } - - public static String toHexString(Color c) { - return "0x" + Integer.toHexString(c.getRGB()).substring(2).toUpperCase(); - } - - public static void main(String[] args) { - JColorChooser custom = new JColorChooser(); - custom.addChooserPanel(new DesktopColorChooserPanel()); - if (JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(null, custom)) { - System.err.println("c: " + custom.getColor()); - } - } -} diff --git a/dasCoreUtil/src/org/das2/util/Entities.java b/dasCoreUtil/src/org/das2/util/Entities.java deleted file mode 100755 index 6901956c7..000000000 --- a/dasCoreUtil/src/org/das2/util/Entities.java +++ /dev/null @@ -1,368 +0,0 @@ -/** - * From Lucene Search Engine. - * code found at http://www.koders.com, decodeEntities() added. - * - */ -package org.das2.util; - -import java.util.HashMap; - -public class Entities { - - static final HashMap decoder = new HashMap(300); - static final String[] encoder = new String[0x100]; - - /** - * utility method for decoding entities like &rho; into unicode. - * Malformed entities (like B1; instead of α) are formatted as "???" - * @param str string e.g. "ρ degrees" - * @return string with unicode characters for entities. - */ - public static String decodeEntities(String str) { - int i0=0, i; - - int MAX_ENTITY_LEN=10; - StringBuilder result= new StringBuilder(); - while ( true ) { - i= str.indexOf("&",i0); - - if ( i==-1 ) { - result.append( str.substring(i0) ); - return result.toString(); - } else { - int i1= str.indexOf(";",i); - if ( i1!=-1 && i1-i exclude ) throws IllegalArgumentException { - if (!root.exists()) { - return true; - } - if (!root.canRead()) { - throw new IllegalArgumentException("cannot read folder " + root ); - } - File[] children = root.listFiles(); // root is known to exist. - boolean success = true; - boolean noExclude= true; - for (int i = 0; i < children.length; i++) { - if ( exclude!=null && exclude.contains(children[i].getName()) ) { - noExclude= false; - continue; - } - if (children[i].isDirectory()) { - success = success && deleteFileTree(children[i],exclude); - } else { - success = success && ( !children[i].exists() || children[i].delete() ); // in case file is deleted by another process, check exists again. - if (!success) { - throw new IllegalArgumentException("unable to delete file " + children[i]); - } - } - } - if ( noExclude ) { - success = success && (!root.exists() || root.delete()); - } - if (!success) { - throw new IllegalArgumentException("unable to delete folder " + root); - } - return success; - } - - /** - * deletes all files with the given name, and root, just as "find . -name name -exec rm {} \;" would. - * TODO: check links. For example deleteWithinFileTree( root, ".listing" ) - * @throws IllegalArgumentException if it is unable to delete a file - * @return true if the operation was successful. - */ - public static boolean deleteWithinFileTree(File root,String name) throws IllegalArgumentException { - if (!root.exists()) { - return true; - } - if ( !root.canRead() ) { - System.err.println("unable to read folder: "+root ); - return true; - } - File[] children = root.listFiles(); - boolean success = true; - for (int i = 0; i < children.length; i++) { - if (children[i].isDirectory()) { - success = success && deleteWithinFileTree(children[i],name); - } else { - if ( children[i].getName().equals(name) ) { - success = success && ( !children[i].exists() || children[i].delete() ); - if (!success) { - throw new IllegalArgumentException("unable to delete file " + children[i]); - } - } - } - } - return success; - } - - /** - * find a files with the given name within the given root, just as "find . -name name -print \;" would. - * TODO: check links. For example, find( "/usr/share/fonts/truetype", "FreeMono.ttf" ) - * @param root the root to start - * @param name name to look for. - * @throws IllegalArgumentException if the root does not exist. - * @return the File found, or null if it does not exist. - */ - public static File find(File root,String name) throws IllegalArgumentException { - if (!root.exists()) { - throw new IllegalArgumentException("File does not exist:"+root); - } - if ( !root.isDirectory() ) { - throw new IllegalArgumentException("root should be a directory: "+root); - } - if ( !root.canRead() ) { - throw new IllegalArgumentException("unable to read root: "+root); - } - File[] children = root.listFiles(); // root is known to exist - for (File children1 : children) { - if (children1.isDirectory()) { - File f = find(children1, name); - if ( f!=null ) return f; - } else { - if (children1.getName().equals(name)) { - return children1; - } - } - } - return null; - } - - /** - * find a files with the given name within one of the given roots. - * TODO: check links. For example, find( "/usr/share/fonts/truetype", "FreeMono.ttf" ) - * This allows root folders that do not exist. - * @param roots array of roots to start search. - * @param name name to look for. - * @return the File found, or null if it does not exist. - */ - public static File find(File[] roots,String name) { - for ( File root: roots ) { - if ( root.exists() ) { - File r= find( root, name ); - if ( r!=null ) { - return r; - } - } - } - return null; - } - - /** - * find all files under the root matching the spec. - * @param root the root of the search (e.g. /fonts/) - * @param name the pattern to match - * @param matches list that will accept the matches, or null if one should be created. - * @return the list. - */ - public static List listRecursively( File root, Pattern name, List matches ) { - if (!root.exists()) { - throw new IllegalArgumentException("File does not exist:"+root); - } - if (!root.isDirectory()) { - throw new IllegalArgumentException("root is not a folder:"+root); - } - if (!root.canRead()) return Collections.emptyList(); - if ( matches==null ) matches= new ArrayList(); - File[] children = root.listFiles(); // root is known to exist. - for (File children1 : children) { - if (children1.isDirectory()) { - listRecursively(children1, name, matches); - } else { - if (name.matcher(children1.getName()).matches()) { - matches.add(children1); - } - } - } - return matches; - } - - /** - * Return an array of files where the regex is found at the end. A check is performed to see if the root is case-insensitive. - * @param root the root of the search (e.g. /fonts/) - * @param glob the glob to match (e.g. *.ttf) - * @return list of files. - */ - public static File[] listRecursively( File root, String glob ) { - String regex= Glob.getRegex( glob ); - boolean b= new File(root,"xxx").equals(new File(root,"XXX")); - if ( b ) regex= "(?i)"+regex; - Pattern name= Pattern.compile( ".*" + regex ); - List result= listRecursively( root, name, null ); - return result.toArray( new File[result.size()] ); - } - - /** - * copies the file or folder from src to dst. - * @param src the source file or folder. - * @param dst the location for the new file or folder. - * @throws FileNotFoundException - * @throws IOException - */ - public static void fileCopy( File src, File dst ) throws FileNotFoundException, IOException { - if ( !src.exists() ) throw new IllegalArgumentException("src does not exist."); - if ( !src.canRead() ) throw new IllegalArgumentException("src cannot be read."); - if ( src.isDirectory() && ( !dst.exists() || dst.isDirectory() ) ) { - if ( !dst.exists() ) { - if ( !dst.mkdirs() ) throw new IOException("unable to mkdir " + dst); - } - File dst1= new File( dst, src.getName() ); - if ( !dst1.exists() && !dst1.mkdir() ) throw new IOException("unable to mkdir " + dst1); - dst= dst1; - File[] files= src.listFiles(); // src is known to exist. - for ( File f:files ) { - if ( f.isDirectory() ) { - dst1= new File( dst, f.getName() ); - if ( !dst1.exists() && !dst1.mkdir() ) throw new IOException("unable to mkdir " + dst1); - } else { - dst1= dst; - } - fileCopy( f, dst1 ); - } - return; - } else if ( dst.isDirectory() ) { - dst= new File( dst, src.getName() ); - } - FileChannel ic = new FileInputStream(src).getChannel(); - FileChannel oc = new FileOutputStream(dst).getChannel(); - try { - ic.transferTo(0, ic.size(), oc); - } finally { - ic.close(); - oc.close(); - } - } - - /** - * return the first four bytes of the file as a string. Some magic - * numbers we care about: - *
  • '\x89HDF' HDF (and new NetCDF) files - * @param src - * @return a four byte string - * @throws java.io.FileNotFoundException - * @throws IllegalArgumentException if the file is less than four bytes. - */ - public static String getMagic(File src) throws FileNotFoundException, IOException { - byte[] four= new byte[4]; - FileChannel ic = new FileInputStream(src).getChannel(); - ByteBuffer buf= ByteBuffer.wrap(four); - int bytesRead=0; - try { - while ( bytesRead<4 ) { - int bytes= ic.read( buf ); - if ( bytes==-1 ) { - if ( bytesRead==0 ) { - throw new IllegalArgumentException("File is empty: "+src); - } else { - throw new IllegalArgumentException("File has less than four bytes: "+src); - } - } - bytesRead+= bytes; - } - } finally { - ic.close(); - } - return new String( four ); - } - - -} diff --git a/dasCoreUtil/src/org/das2/util/GrannyTextRenderer.java b/dasCoreUtil/src/org/das2/util/GrannyTextRenderer.java deleted file mode 100644 index 937d78ce1..000000000 --- a/dasCoreUtil/src/org/das2/util/GrannyTextRenderer.java +++ /dev/null @@ -1,541 +0,0 @@ -/* File: GrannyTextRenderer.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import java.awt.Component; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Utility class for rendering "Granny" strings, which use the codes - * identified by Grandle and Nystrom in their 1980 paper to provide - * rich formatting such as new lines and superscripts. - * These are strings like "E=mc!e2" where the !e indicates the pen should be - * moved to the exponent position before drawing. This supports sequences - * including:
    - * !A  shift up one half line
    - * !B  shift down one half line  (e.g.  !A3!n-!B4!n is 3/4).
    - * !C  newline 
    - * !D  subscript 0.62 of old font size.
    - * !U  superscript of 0.62 of old font size.
    - * !E  superscript 0.44 of old font size.
    - * !I  subscript 0.44 of old font size.
    - * !N  return to the original font size.
    - * !R  restore position to last saved position
    - * !S  save the current position.
    - * !K  reduce the font size. (Not in IDL's set.)
    - * !!  the exclamation point (!)
    - *   
    - * For Greek and math symbols, Unicode characters should be - * used like so: &#9742; (☎ phone symbol), or symbols like &Omega; and &omega; - * - * - * @author Edward West - */ -public class GrannyTextRenderer { - - public static final float LEFT_ALIGNMENT = 0.0f; - public static final float CENTER_ALIGNMENT = 0.5f; - public static final float RIGHT_ALIGNMENT = 1.0f; - - private Rectangle bounds=null; - private ArrayList lineBounds; - private String str; - private String[] tokens; - private float alignment = LEFT_ALIGNMENT; - - private static final Logger logger= LoggerManager.getLogger("das2.graph"); - - public GrannyTextRenderer( ) { - //setAlignment(CENTER_ALIGNMENT); - } - - /** - * returns the bounds of the current string. The lower-left corner of - * the first character will be roughly (0,0), to be compatible with - * FontMetrics.getStringBounds(). - * - * @return a Rectangle indicating the text boundaries. - * @throws IllegalArgumentException if the string has not been set. - */ - public Rectangle getBounds() { - if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); - maybeInitBounds(); - return new Rectangle(bounds); // defensive copy - } - - private void maybeInitBounds() { - if (bounds == null) { - if ( lineBounds.size()>0 ) { - bounds = new Rectangle((Rectangle)lineBounds.get(0)); - for (int i = 1; i < lineBounds.size(); i++) { - bounds.add((Rectangle)lineBounds.get(i)); - } - } else { - bounds = new Rectangle( 0,-12,12,12 ); - } - } - } - - /** - * returns the width of the bounding box, in pixels. - * @return the width of the bounding box, in pixels. - * @throws IllegalArgumentException if the string has not been set. - */ - public double getWidth() { - if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); - maybeInitBounds(); - return bounds.getWidth(); - } - - /** - * returns the width in pixels of the first line. - * @return the width in pixels of the first line. - * @throws IllegalArgumentException if the string has not been set. - * - */ - public double getLineOneWidth() { - if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); - return getLineWidth(1); - } - - /** - * returns the calculated width each line. - * @param lineNumber the index of the line, starting with 1. - * @return the width of the bounding box, in pixels. - * @throws IndexOutOfBoundsException if no such line exists. - */ - private double getLineWidth(int lineNumber) { - if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); - return ((Rectangle)lineBounds.get(lineNumber - 1)).getWidth(); - } - - /** - * returns the hieght of the calculated bounding box. - * @return the height of the bounding box, in pixels. - * @throws IllegalArgumentException if the string has not been set. - */ - public double getHeight() { - if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); - maybeInitBounds(); - return bounds.getHeight(); - } - - /** - * return the amount that the bounding box will go above the baseline. - * This is also the height of the first line. - * @return the amount that the bounding box will go above the baseline. - * @throws IllegalArgumentException if the string has not been set. - */ - public double getAscent() { - if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); - return -1*((Rectangle)lineBounds.get(0)).getY(); - } - - /** - * return the amount that the bounding box will go below the baseline. - * @return the amount that the bounding box will go below the baseline. - * @throws IllegalArgumentException if the string has not been set. - */ - public double getDescent() { - if ( lineBounds==null ) throw new IllegalArgumentException("string is not set"); - maybeInitBounds(); - return bounds.getHeight() + bounds.getY(); - } - - /** - * reset the current string for the GTR to draw, calculating the boundaries - * of the string. For greek and math symbols, unicode characters should be - * used. (See www.unicode.org). See the documentation for this class - * for a description of symbols. - * @deprecated use setString( Graphics g, String str ) instead. - * @param c the component which will provide the graphics. - * @param str the granny string, such as "E=mc!e2" - */ - public void setString( Component c, String str ) { - bounds = null; - lineBounds = new ArrayList(); - this.str = Entities.decodeEntities(str); - this.tokens = buildTokenArray(this.str); - this.draw( c.getGraphics(), c.getFont(), 0f, 0f, false ); - } - - /** - * reset the current string for the GTR to draw, calculating the boundaries - * of the string. For greek and math symbols, unicode characters should be - * used. (See www.unicode.org). - * - * @param g the graphics context which will supply the FontMetrics. - * @param str the granny string, such as "E=mc!e2" - */ - public void setString( Graphics g, String str) { - bounds = null; - lineBounds = new ArrayList(); - this.str = Entities.decodeEntities(str); - this.tokens = buildTokenArray(this.str); - this.draw( g, g.getFont(), 0f, 0f, false ); - } - - /** - * reset the current string for the GTR to draw, calculating the boundaries - * of the string. For greek and math symbols, unicode characters should be - * used. (See www.unicode.org). - * - * @param font the font. This should be consistent - * with the Font used when drawing. - * @param label the granny string, such as "E=mc!e2" - */ - public void setString( Font font, String label) { - bounds = null; - lineBounds = new ArrayList(); - this.str = Entities.decodeEntities(label); - this.tokens = buildTokenArray(this.str); - this.draw( null, font, 0f, 0f, false ); - } - - /** - * returns the current alignment, by default LEFT_ALIGNMENT. - * @return the current alignment. - */ - public float getAlignment() { - return alignment; - } - - /** - * set the alignment for rendering, one of LEFT_ALIGNMENT CENTER_ALIGNMENT or RIGHT_ALIGNMENT. - * @param a the alignment, one of LEFT_ALIGNMENT CENTER_ALIGNMENT or RIGHT_ALIGNMENT. - */ - public void setAlignment( float a) { - if (a != LEFT_ALIGNMENT && a != CENTER_ALIGNMENT && a != RIGHT_ALIGNMENT) { - throw new IllegalArgumentException("alignment should 0., 0.5, or 1.0"); - } - alignment = a; - } - - /** - * draw the current string. Note the first line will be above iy, and following lines will - * be below iy. This is to be consistent with Graphics2D.drawString. - * - * @param ig Graphic object to use to render the text. - * @param ix The x position of the first character of text. - * @param iy The y position of the baseline of the first line of text. - */ - public void draw( Graphics ig, float ix, float iy ) { - this.draw( ig, ig.getFont(), ix, iy, true); - } - - - /** - * Draws the given string and/or computes its bounds. - * - * This method is intended to be called by both {@link #drawString(Graphics,String,float,float)} - * and {@link #getBounds(String,float,float,Component)}. - * - * All added string rendering capabilities should be handled here so that any changes - * will be incorporated into both the rendering algorithm and the bounds finding - * algorithm at the same time. - * - * @param ig Graphic object to use to render the text. This can be null if - * draw is false. - * @param ix The x position of the first character of text. - * @param iy The y position of the baseline of the first line of text. - * @param c The Component that the String will be rendered in. - * This can be null if draw is true - * @param draw A boolean flag indicating whether or not the drawing code should be executed. - * @throws NullPointerException if ig is null AND draw is true. - * @throws NullPointerException if c is null AND draw is false. - */ - private void draw(Graphics ig, Font baseFont, float ix, float iy, boolean draw ) { - Graphics2D g = null; - Rectangle boundsl = null; - - if (draw) { - g = (Graphics2D)ig.create(); -// RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, -// RenderingHints.VALUE_ANTIALIAS_ON); -// hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); -// g.setRenderingHints(hints); - } - - final int NONE = 0; - final int SUB_U = 1; - final int SUB_D = 2; - final int SUB_L = 3; - final int EXP = 4; - final int IND = 5; - - final int LOWCAPS= 10; // not in IDL's set - final int SUB_A = 11; - final int SUB_B = 12; - - final class TextPosition { - public TextPosition(int sub, int ei, float x, float y) { - this.sub = sub; this.ei = ei; this.x = x; this.y = y; } - public TextPosition(TextPosition p) { - copy(p); } - public void copy(TextPosition p) { - sub = p.sub; ei = p.ei; x = p.x; y = p.y; } - public int sub; - public int ei; - public float x; - public float y; - } - - if ( ig==null ) { - ig= getHeadlessGraphicsContext(); - } - - if ( baseFont==null ) { - baseFont= Font.decode("sans-10"); - } - - int lineNum=1; - - TextPosition current = new TextPosition(NONE, NONE, ix, iy); - if (draw) { - if (alignment == CENTER_ALIGNMENT) { - current.x += (getWidth() - getLineOneWidth()) / 2.0; - } else if (alignment == RIGHT_ALIGNMENT) { - current.x += (getWidth() - getLineOneWidth()); - } - } - - if (!draw) { - boundsl= new Rectangle((int)ix,(int)iy,0,0); - } - - Stack saveStack = new Stack(); - - for (String strl : tokens) { - if ( !strl.equals("!!") && strl.charAt(0) == '!') { - if ( strl.length()==1 ) break; - switch (strl.charAt(1)) { - case 'A': - case 'a': - current.sub= SUB_A; - current.ei = NONE; - break; - case 'B': - case 'b': - current.sub= SUB_B; - current.ei = NONE; - break; - case 'C': - case 'c': - lineNum++; - current.sub = NONE; - current.ei = NONE; - current.x = ix; - current.y += baseFont.getSize2D(); - if (draw) { - g.setFont(baseFont); - if (alignment == CENTER_ALIGNMENT) { - current.x += (getWidth() - getLineWidth(lineNum)) / 2.0; - } else if (alignment == RIGHT_ALIGNMENT) { - current.x += (getWidth() - getLineWidth(lineNum)); - } - } - saveStack.clear(); - if (!draw) { - lineBounds.add(boundsl); - boundsl = new Rectangle((int)current.x, (int)current.y, 0, 0); - } - break; - case 'U': - case 'u': - current.sub = SUB_U; - current.ei = NONE; - break; - case 'D': - case 'd': - current.sub = SUB_D; - current.ei = NONE; - break; - case 'L': - case 'l': - current.sub = SUB_L; - current.ei = NONE; - break; - case 'K': - case 'k': - current.ei = LOWCAPS; - break; - case 'E': - case 'e': - current.ei = EXP; - break; - case 'I': - case 'i': - current.ei = IND; - break; - case 'S': - case 's': - saveStack.push(new TextPosition(current)); - break; - case 'R': - case 'r': - if ( !saveStack.empty() ) { - if (saveStack.peek() == null) return; - current.copy((TextPosition)saveStack.pop()); - } else { - logger.log(Level.WARNING, "saveStack was empty: missing !s from: {0}", this.str); - } - break; - case 'N': - case 'n': - current.sub = NONE; - current.ei = NONE; - break; - case '!': - break; - default:break; - } - } else { - Font font = baseFont; - float size = baseFont.getSize2D(); - float y = current.y; - switch (current.sub) { - case SUB_U: - font = baseFont.deriveFont(size * 0.62f); - y = y - 0.38f * size; - size = size * 0.62f; - break; - case SUB_D: - font = baseFont.deriveFont(size * 0.62f); - y = y + 0.31f * size; - size = size * 0.62f; - break; - case SUB_L: - font = baseFont.deriveFont(size * 0.62f); - y = y + 0.62f * size; - size = size * 0.62f; - break; - case SUB_A: - y= current.y - size/2; - break; - case SUB_B: - y= current.y + size/2; - break; - - default:break; - } - switch (current.ei) { - case EXP: - font = font.deriveFont(size * 0.44f); - y = y - 0.56f * size; - break; - case IND: - font = font.deriveFont(size * 0.44f); - y = y + 0.22f * size; - break; - case LOWCAPS: - font= font.deriveFont(size * 0.80f ); - break; - default:break; - } - if ( strl.equals("!!") ) strl= "!"; - if (draw) { - g.setFont(font); - g.drawString(strl, current.x, y); - current.x += g.getFontMetrics(font).stringWidth(strl); - //bounds.translate((int)ix,(int)iy); - //g.draw(bounds); //useful for debugging - //g.drawLine((int)ix,(int)iy,(int)ix+4,(int)iy); - } else { - FontMetrics fm= ig.getFontMetrics(font); - boundsl.add(current.x, y+fm.getDescent()); - boundsl.add(current.x+fm.stringWidth(strl),y-fm.getAscent() ); // removed -5.0 pixels - current.x += ig.getFontMetrics(font).stringWidth(strl); - } - } - } // for (String strl : tokens) { - if (!draw) { - lineBounds.add(boundsl); - } - if (draw) { - g.dispose(); - } - } - - private static String[] buildTokenArray(String str) { - java.util.List vector = new ArrayList(); - int begin; - int end = 0; - while(end < str.length()) { - begin = end; - if (str.charAt(begin) == '!') { - end = begin + 2; - if ( end>=str.length() ) end= str.length(); - } else { - end = str.indexOf('!', begin); - if (end == -1) end = str.length(); - } - vector.add(str.substring(begin, end)); - } - - String[] list= vector.toArray( new String[vector.size()] ); - - return list; - } - - @Override - public String toString() { - maybeInitBounds(); - StringBuilder buffer = new StringBuilder(getClass().getName()); - buffer.append(": ").append(str).append(", "); - buffer.append("bounds: ").append(bounds).append(", ").append("lineBounds:").append(lineBounds).append(", "); - return buffer.toString(); - } - -// -// /** -// * useful for debugging. -// */ -// private void drawBounds(Graphics g, int ix, int iy) { -// g.setColor(Color.BLUE); -// g.drawRect(bounds.x + ix, bounds.y + iy, bounds.width, bounds.height); -// g.setColor(Color.GREEN); -// for (java.util.Iterator i = lineBounds.iterator(); i.hasNext();) { -// Rectangle rc = (Rectangle)i.next(); -// g.drawRect(rc.x + ix, rc.y + iy, rc.width, rc.height); -// } -// } - - private static Graphics headlessGraphics=null; - private static synchronized Graphics getHeadlessGraphicsContext() { - if ( headlessGraphics==null ) { - headlessGraphics= new BufferedImage(10,10,BufferedImage.TYPE_INT_ARGB).getGraphics(); - } - return headlessGraphics; - } - -} diff --git a/dasCoreUtil/src/org/das2/util/IDLParser.java b/dasCoreUtil/src/org/das2/util/IDLParser.java deleted file mode 100755 index 9b35f3644..000000000 --- a/dasCoreUtil/src/org/das2/util/IDLParser.java +++ /dev/null @@ -1,280 +0,0 @@ -/* File: IDLParser.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import java.util.StringTokenizer; -import java.util.Vector; -import java.util.logging.Logger; -/** - * This was created originally to evaluate expressions found in das2server dsdf files that would - * describe column locations, which would be expressions like "10^(findgen(3)/3.3)" - * @author jbf - */ -public class IDLParser { - - public static final int EXPR=1; - public static final int EXPR_LIST=2; - public static final int FACTOR=3; - public static final int TERM=4; - public static final int NUMBER=5; - public static final int NOPARSER=6; - - /** Creates a new instance of idlParser */ - public IDLParser() { - } - - public String[] IDLTokenizer(String s) { - String delimiters=" \t[,]()^*/+-"; - StringTokenizer st = new StringTokenizer(s, delimiters, true); - int countTokens=st.countTokens(); - String[] tokens=new String[countTokens]; - for (int i=0; i 0) - { - String[] temp = new String[tokens.length-nullcount]; - int tIndex = 0; - for (int i = 0; i < tokens.length; i++) - { - if (tokens[i] == null) continue; - temp[tIndex] = tokens[i]; - tIndex++; - } - tokens = temp; - } - return tokens; - } - - public double parseIDLScalar(String s) { - String[] tokens= IDLTokenizer(s); - IDLValue expr= parseIDLArrayTokens(tokens,EXPR); - if (expr == null) return Double.NaN; - else return expr.toScalar(); - } - public double[] parseIDLArray(String s) { - String[] tokens= IDLTokenizer(s); - IDLValue expr= parseIDLArrayTokens(tokens,EXPR); - if (expr == null) return null; - else return expr.toArray(); - } - - private IDLValue parseIDLExprList(String[] tokens) { - int ileft= 0; - int nleft= 0; - int iright=0; - int itok; - - Vector exprs= new Vector(); - - ileft=1; - for (itok=1; itok - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import java.util.Arrays; - -public class IDLValue { - - static int SCALAR = 1; - static int ARRAY = 2; - - protected double[] aValue; - - protected double sValue; - - protected int type; - - IDLValue() { - } - - public IDLValue(double a) { - sValue= a; - type= SCALAR; - } - - /** - * create an IDLValue representing this array. The data is copied - * as the object is constructed. - * @param a - */ - public IDLValue(double[] a) { - aValue= Arrays.copyOf(a,a.length); - type= ARRAY; - } - - public IDLValue IDLmultiply(IDLValue b) { - IDLValue result= new IDLValue(); - if ((type==SCALAR) && (b.type==SCALAR)) { - result.type=SCALAR; - result.sValue=sValue*b.sValue; - } else if ((type==ARRAY) && (b.type==SCALAR)) { - result.type=ARRAY; - result.aValue= new double[aValue.length]; - for (int i=0; i readers = ImageIO.getImageReaders(iis); - - if (readers.hasNext()) { - - // pick the first available ImageReader - ImageReader reader = readers.next(); - - // attach source to the reader - reader.setInput(iis, true); - - // read metadata of first image - IIOMetadata metadata = reader.getImageMetadata(0); - try { - IIOMetadataNode n= (IIOMetadataNode)metadata.getAsTree("javax_imageio_png_1.0"); - NodeList nl= n.getElementsByTagName("tEXtEntry"); - for ( int i=0; i= 0 ? value : value + 256); - } - - private static int fourBytesToInt(byte b[], int offset) - { - int value; - - value = byteToUnsigned(b[offset++]); - value |= (byteToUnsigned(b[offset++]) << 8); - value |= (byteToUnsigned(b[offset++]) << 16); - value |= (byteToUnsigned(b[offset++]) << 24); - - return(value); - } - - private static void intToFourBytes(int iValue, byte b[], int offset) - { - b[offset++] = (byte)((iValue) & 0xff); - b[offset++] = (byte)((iValue >>> 8 ) & 0xff); - b[offset++] = (byte)((iValue >>> 16) & 0xff); - b[offset++] = (byte)((iValue >>> 24) & 0xff); - } - - private static void PERM_OP(int a, int b, int n, int m, int results[]) - { - int t; - - t = ((a >>> n) ^ b) & m; - a ^= t << n; - b ^= t; - - results[0] = a; - results[1] = b; - } - - private static int HPERM_OP(int a, int n, int m) - { - int t; - - t = ((a << (16 - n)) ^ a) & m; - a = a ^ t ^ (t >>> (16 - n)); - - return(a); - } - - private static int [] des_set_key(byte key[]) - { - int schedule[] = new int[ITERATIONS * 2]; - - int c = fourBytesToInt(key, 0); - int d = fourBytesToInt(key, 4); - - int results[] = new int[2]; - - PERM_OP(d, c, 4, 0x0f0f0f0f, results); - d = results[0]; c = results[1]; - - c = HPERM_OP(c, -2, 0xcccc0000); - d = HPERM_OP(d, -2, 0xcccc0000); - - PERM_OP(d, c, 1, 0x55555555, results); - d = results[0]; c = results[1]; - - PERM_OP(c, d, 8, 0x00ff00ff, results); - c = results[0]; d = results[1]; - - PERM_OP(d, c, 1, 0x55555555, results); - d = results[0]; c = results[1]; - - d = (((d & 0x000000ff) << 16) | (d & 0x0000ff00) | - ((d & 0x00ff0000) >>> 16) | ((c & 0xf0000000) >>> 4)); - c &= 0x0fffffff; - - int s, t; - int j = 0; - - for(int i = 0; i < ITERATIONS; i ++) - { - if(shifts2[i]) - { - c = (c >>> 2) | (c << 26); - d = (d >>> 2) | (d << 26); - } - else - { - c = (c >>> 1) | (c << 27); - d = (d >>> 1) | (d << 27); - } - - c &= 0x0fffffff; - d &= 0x0fffffff; - - s = skb[0][ (c ) & 0x3f ]| - skb[1][((c >>> 6) & 0x03) | ((c >>> 7) & 0x3c)]| - skb[2][((c >>> 13) & 0x0f) | ((c >>> 14) & 0x30)]| - skb[3][((c >>> 20) & 0x01) | ((c >>> 21) & 0x06) | - ((c >>> 22) & 0x38)]; - - t = skb[4][ (d ) & 0x3f ]| - skb[5][((d >>> 7) & 0x03) | ((d >>> 8) & 0x3c)]| - skb[6][ (d >>>15) & 0x3f ]| - skb[7][((d >>>21) & 0x0f) | ((d >>> 22) & 0x30)]; - - schedule[j++] = ((t << 16) | (s & 0x0000ffff)) & 0xffffffff; - s = ((s >>> 16) | (t & 0xffff0000)); - - s = (s << 4) | (s >>> 28); - schedule[j++] = s & 0xffffffff; - } - return(schedule); - } - - private static int D_ENCRYPT - ( - int L, int R, int S, int E0, int E1, int s[] - ) - { - int t, u, v; - - v = R ^ (R >>> 16); - u = v & E0; - v = v & E1; - u = (u ^ (u << 16)) ^ R ^ s[S]; - t = (v ^ (v << 16)) ^ R ^ s[S + 1]; - t = (t >>> 4) | (t << 28); - - L ^= SPtrans[1][(t ) & 0x3f] | - SPtrans[3][(t >>> 8) & 0x3f] | - SPtrans[5][(t >>> 16) & 0x3f] | - SPtrans[7][(t >>> 24) & 0x3f] | - SPtrans[0][(u ) & 0x3f] | - SPtrans[2][(u >>> 8) & 0x3f] | - SPtrans[4][(u >>> 16) & 0x3f] | - SPtrans[6][(u >>> 24) & 0x3f]; - - return(L); - } - - private static int [] body(int schedule[], int Eswap0, int Eswap1) - { - int left = 0; - int right = 0; - int t = 0; - - for(int j = 0; j < 25; j ++) - { - for(int i = 0; i < ITERATIONS * 2; i += 4) - { - left = D_ENCRYPT(left, right, i, Eswap0, Eswap1, schedule); - right = D_ENCRYPT(right, left, i + 2, Eswap0, Eswap1, schedule); - } - t = left; - left = right; - right = t; - } - - t = right; - - right = (left >>> 1) | (left << 31); - left = (t >>> 1) | (t << 31); - - left &= 0xffffffff; - right &= 0xffffffff; - - int results[] = new int[2]; - - PERM_OP(right, left, 1, 0x55555555, results); - right = results[0]; left = results[1]; - - PERM_OP(left, right, 8, 0x00ff00ff, results); - left = results[0]; right = results[1]; - - PERM_OP(right, left, 2, 0x33333333, results); - right = results[0]; left = results[1]; - - PERM_OP(left, right, 16, 0x0000ffff, results); - left = results[0]; right = results[1]; - - PERM_OP(right, left, 4, 0x0f0f0f0f, results); - right = results[0]; left = results[1]; - - int out[] = new int[2]; - - out[0] = left; out[1] = right; - - return(out); - } - - public static String crypt(String salt, String original) - { - StringBuilder sb= new StringBuilder(salt); - while(sb.length() < 2) - sb.append('A'); - salt= sb.toString(); - - StringBuilder buffer = new StringBuilder(" "); - - char charZero = salt.charAt(0); - char charOne = salt.charAt(1); - - buffer.setCharAt(0, charZero); - buffer.setCharAt(1, charOne); - - int Eswap0 = con_salt[(int)charZero]; - int Eswap1 = con_salt[(int)charOne] << 4; - - byte key[] = new byte[8]; - - for(int i = 0; i < key.length; i ++) - key[i] = (byte)0; - - for(int i = 0; i < key.length && i < original.length(); i ++) - { - int iChar = (int)original.charAt(i); - - key[i] = (byte)(iChar << 1); - } - - int schedule[] = des_set_key(key); - int out[] = body(schedule, Eswap0, Eswap1); - - byte b[] = new byte[9]; - - intToFourBytes(out[0], b, 0); - intToFourBytes(out[1], b, 4); - b[8] = 0; - - for(int i = 2, y = 0, u = 0x80; i < 13; i ++) - { - for(int j = 0, c = 0; j < 6; j ++) - { - c <<= 1; - - if(((int)b[y] & u) != 0) - c |= 1; - - u >>>= 1; - - if(u == 0) - { - y++; - u = 0x80; - } - buffer.setCharAt(i, (char)cov_2char[c]); - } - } - return(buffer.toString()); - } - - public static void main(String args[]) - { - if(args.length >= 2) - { - System.err.println( String.format( "[%s] [%s] => [%s]", args[0], args[1], JCrypt.crypt(args[0], args[1]) ) ); - } else { - System.err.println("java crypt "); - } - } -} diff --git a/dasCoreUtil/src/org/das2/util/LatexToGranny.java b/dasCoreUtil/src/org/das2/util/LatexToGranny.java deleted file mode 100644 index f37eb4207..000000000 --- a/dasCoreUtil/src/org/das2/util/LatexToGranny.java +++ /dev/null @@ -1,127 +0,0 @@ - -package org.das2.util; - -import java.util.StringTokenizer; - -/** - * Lightweight converter for LaTeX expressions to Granny strings, for the - * MMS mission. This handles strings like:
      - *
    • cm^{-3} - *
    • nA/m^{2} - *
    • \rho^2 + 2\Gamma_{ij} - *
    • \sqrt{a + b} (This is not handled by the IDL library either.) - *
    - * The IDL project that minimally specifies is: TeXtoIDL, at http://physics.mnstate.edu/craig/textoidl/ - * @author jbf - */ -public class LatexToGranny { - private static final String STATE_OPEN="open"; - private static final String STATE_CAROT="carot"; - private static final String STATE_UNDERSCORE="underscore"; - private static final String STATE_EXP="exp"; - private static final String STATE_SUBSCRIPT="subscript"; - private static final String STATE_BACKSLASH="backslash"; - - /** - * for the latex encoded string, return the granny string that - * implements. - * @param latex LaTeX encoded string. E.g. \rho^2 + 2\Gamma_{ij} - * @return the granny string. E.g. ρ!U2!n + 2Γ!Dij!N - */ - public static String latexToGranny( String latex ) { - Object state= STATE_OPEN; - StringTokenizer st= new StringTokenizer( latex, "^_\\{}+ ", true ); - - StringBuilder build= new StringBuilder(); - - String t; - - while ( st.hasMoreTokens() ) { - t= st.nextToken(); - if ( t.equals("^") ) { - state= STATE_CAROT; - } else if ( t.equals("_") ) { - state= STATE_UNDERSCORE; - } else if ( t.equals("\\") ) { - state= STATE_BACKSLASH; - } else if ( t.equals("{") ) { - if ( state==STATE_CAROT ) { - build.append("!U"); - state= STATE_EXP; - } else if ( state==STATE_UNDERSCORE ) { - build.append("!D"); - state= STATE_EXP; - } else { - build.append("{"); - state= STATE_OPEN; - } - } else if ( t.equals("}") ) { - if ( state==STATE_EXP ) { - build.append("!N"); - state= STATE_OPEN; - } else if ( state==STATE_SUBSCRIPT ) { - build.append("!N"); - state= STATE_OPEN; - } else { - build.append("}"); - state= STATE_OPEN; - } - } else { - if ( state==STATE_BACKSLASH ) { - build.append("&").append( lookupEntity(t) ).append(";"); - state= STATE_OPEN; - } else if ( state==STATE_CAROT ) { - build.append( "!U" ); - build.append( t ); - build.append( "!N" ); - state= STATE_OPEN; - } else { - build.append( t ); - } - } - } - - return build.toString(); - } - - /** - * detect if the string is LaTeX. This currently looks for ^{ or _{, - * but is expected to be changed in the future. - * @param s the string - * @return true if the string appears to be LaTeX. - */ - public static boolean isLatex( String s ) { - if ( s.contains("^{") || s.contains("_{") ) { - return true; - } else { - return false; - } - } - - /** - * look up the entity for the given latex symbol. E.g.: rho->rho - * This is expected to be changed in the future. - * @return the entity - */ - private static String lookupEntity( String ent ) { - return ent; - //if ( ent.equals("sqrt") ) { - // return "radic"; - //} else { - // return ent; - //} - } - - public static void main( String[] args ) { - String[] tests= { "\\rho^2 + 2\\Gamma_{ij} ", - "cm^{-3}", - "nA/m^{2}", - "\\rho^2 + 2\\Gamma_{ij} ", - "A^{B+C}", - "\\sqrt{a + b}" }; - for ( String t: tests ) { - System.err.println( t ); - System.err.println( latexToGranny( t ) ); - } - } -} diff --git a/dasCoreUtil/src/org/das2/util/LoggerManager.java b/dasCoreUtil/src/org/das2/util/LoggerManager.java deleted file mode 100644 index 5eeeb4b31..000000000 --- a/dasCoreUtil/src/org/das2/util/LoggerManager.java +++ /dev/null @@ -1,487 +0,0 @@ - -package org.das2.util; - -import java.awt.AWTEvent; -import java.awt.Component; -import java.awt.Container; -import java.awt.Dialog; -import java.awt.EventQueue; -import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.FocusEvent; -import java.awt.event.ItemEvent; -import java.beans.PropertyChangeEvent; -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogManager; -import java.util.logging.Logger; -import javax.swing.AbstractButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JMenu; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JTabbedPane; -import javax.swing.JTextField; -import javax.swing.JViewport; -import javax.swing.SwingUtilities; -import javax.swing.border.TitledBorder; -import javax.swing.event.ChangeEvent; - -/** - * Central place that keeps track of loggers. Note that both org.das.datum - * and org.das2.datum have this same class, which is there to avoid coupling between the - * packages. - * @author jbf - */ -public final class LoggerManager { - - private static final Set loggers= new HashSet(); - private static final Map log= new HashMap(); - private static final Set extraHandlers= new HashSet(); - - /** - * return the requested logger, but add it to the list of known loggers. - * @param id - * @return - */ - public synchronized static Logger getLogger( String id ) { - Logger result= log.get(id); - if ( result!=null ) { - return result; - } else { - result= Logger.getLogger(id); - log.put( id, result ); - for ( Handler h: extraHandlers ) { - result.addHandler(h); - } - } - loggers.add(id); - return Logger.getLogger(id); - } - - /** - * return the list of known loggers. - * @return - */ - public static Set getLoggers() { - return loggers; - } - - /** - * Add the handler to all loggers created by this manager, and all - * future. Only call this once! I would think that adding a handler to - * the root would essentially add the handler to all loggers, but it doesn't - * seem to. - * - * @param handler e.g. GraphicalLogHandler - */ - public static void addHandlerToAll( Handler handler ) { - for ( String l: loggers ) { - log.get(l).addHandler(handler); - } - extraHandlers.add(handler); - } - - private static boolean disableTimers = true; - - public static boolean isEnableTimers() { - return !disableTimers; - } - - /** - * if enableTimers is true, then resetTimer and markTime have - * an effect. Each thread can have a timer to measure the execution - * time for a process. - * @param enableTimers true to enable timers - */ - public static void setEnableTimers(boolean enableTimers) { - disableTimers = !enableTimers; - } - - // clean up code that times things by keeping track of timer... - private static final Map timers= new WeakHashMap(); - - /** - * reset the timer. The lifecycle is like so: - * LoggerManager.resetTimer("big task"); - * LoggerManager.markTime("done loading"); - * LoggerManager.markTime("calculated data"); - * LoggerManager.clearTimer(); - * Note the timers are stored with weak references to the threads, so - * clearTimer needn't be called. - */ - public static void resetTimer() { - if ( disableTimers ) return; - resetTimer(null); - } - - /** - * reset the timer for this thread. The lifecycle is like so: - * LoggerManager.resetTimer("big task"); - * LoggerManager.markTime("done loading"); - * LoggerManager.markTime("calculated data"); - * LoggerManager.clearTimer(); - * Note the timers are stored with weak references to the threads, so - * clearTimer needn't be called. - * @param task - */ - public static void resetTimer( String task ) { - if ( disableTimers ) return; - if ( task==null ) { - task= Thread.currentThread().getName(); - } else { - if ( EventQueue.isDispatchThread() ) { - task= task + " (GUI)"; - } - } - System.err.println( String.format( "== %s ==", task ) ); - timers.put( Thread.currentThread(), System.currentTimeMillis() ); - } - - /** - * mark the time using the thread name. - */ - public static void markTime() { - if ( disableTimers ) return; - markTime(null); - } - - /** - * mark the time that this occurred. - * @param message message to accompany - */ - public static void markTime( String message ) { - if ( disableTimers ) return; - Long t0= timers.get( Thread.currentThread() ); - if ( t0!=null ) { - if ( message==null ) message= Thread.currentThread().getName(); - System.err.println( String.format( "%05d: %s", System.currentTimeMillis()-t0, message ) ); - } - } - - /** - * explicitly remove this timer. @see resetTimer. - */ - public static void clearTimer() { - if ( disableTimers ) return; - timers.remove( Thread.currentThread() ); - } - - private static Container findReferenceComponent( Container c ) { - Container child=null; - while ( c!=null ) { - child= c; - c= c.getParent(); - if ( c instanceof JTabbedPane ) { - return child; - } else if ( c instanceof JPanel && ( ((JPanel)c).getBorder() instanceof TitledBorder ) ) { - return c; - } else if ( c instanceof JDialog ) { - return c; - } else if ( c!=null && c.getClass().getName().startsWith("org") ) { - return c; - } - } - return child; - } - - /** - * return a human-consumable label for the component. - * @param c - * @return - */ - private static String labelFor( Component c ) { - if ( c instanceof JPopupMenu ) { - return ((JPopupMenu)c).getLabel(); - } else { - if ( c.getParent() instanceof JTabbedPane ) { - JTabbedPane tp= (JTabbedPane)c.getParent(); - int itab= tp.indexOfComponent(c); - return tp.getTitleAt(itab); - } else if ( c instanceof JPanel && ( ((JPanel)c).getBorder() instanceof TitledBorder )) { - TitledBorder tb= (TitledBorder)((JPanel)c).getBorder(); - String title= tb.getTitle(); - if ( title.endsWith(" [?]") ) { - title= title.substring(0,title.length()-4); - } - return title; - } else { - return c.getName(); - } - - } - } - - private static int lastEvent= 0; - - // keep track of responsiveness of GUI event handling. - // This is the start of the event, and clients should call logExitGuiEvent to log time. - private static long lastEventTime=0; - - private static void logGuiEvent( Object source, String thisRef ) { - if ( !EventQueue.isDispatchThread() ) { - return; - } - lastEventTime= System.currentTimeMillis(); - - AWTEvent evt= EventQueue.getCurrentEvent(); - if ( evt!=null ) { - int thisEvent= evt.hashCode(); - if ( thisEvent==lastEvent ) { // avoid secondary messages. - return; - } - lastEvent= thisEvent; - } else { - //System.err.println("wasn't expecting this..."); // this happens when plotting "http://jfaden.net/~jbf/autoplot/bugs/sf/1140/pngwalk/product.vap?timeRange=1996-04-04" - } - - String ssrc= source.toString(); - if ( ssrc.length()>10 ) { - int i=ssrc.indexOf("["); - if ( i>-1 ) ssrc= ssrc.substring(0,i); - if ( source instanceof JComponent ) { - Container c= (JComponent)source; - Container cc= findReferenceComponent(c); - StringBuilder src; - if ( cc instanceof JPanel && ( ((JPanel)cc).getBorder() instanceof TitledBorder ) ) { - String title= labelFor(c); - src= new StringBuilder( " of \""+title + "\""); - } else { - src= new StringBuilder( ); - } - - Container w= cc.getParent(); //because findReferenceComponent might be a tab - if ( !( w instanceof JTabbedPane ) ) { - w= findReferenceComponent(c.getParent()); - if ( w!=null ) { - if ( w.getParent() instanceof JViewport ) { - w= w.getParent(); - } - if ( w.getParent() instanceof JScrollPane ) { - w= w.getParent(); - } - if ( w.getParent() instanceof JTabbedPane ) { - JTabbedPane tp= (JTabbedPane)w.getParent(); - int itab= tp.indexOfComponent(w); - String n= tp.getTitleAt(itab); - src.append(" of \"").append(n).append("\""); - } - } - } - if ( src.length()==0 && cc instanceof JPopupMenu ) { - String t= ((JPopupMenu)cc).getLabel(); - if ( t!=null && t.length()==0 ) t= cc.getName(); - if ( t==null ) { - Component inv= ((JPopupMenu)cc).getInvoker(); - while ( inv instanceof JMenu ) { - t= ((JMenu)inv).getText(); - src.append(" of menu \"").append(t).append( "\""); - Component inv1= inv.getParent(); - if ( inv1 instanceof JPopupMenu ) { - inv= ((JPopupMenu)inv1).getInvoker(); - } else { - inv= null; - } - } - if ( inv!=null ) { - src.append(" of \"").append(labelFor(inv)).append( "\""); - } - } else { - src.append(" of menu \"").append(t).append( "\""); - } - } else if ( c instanceof JComboBox ) { - src.append(c.getName()); - } else if ( c instanceof AbstractButton ) { - String text= ((AbstractButton)c).getText(); - if ( text==null || text.length()==0 ) { - text= c.getName(); - } - src.append("\"").append(text).append("\""); - } else if ( c instanceof JTextField ) { - String text= ((JTextField)c).getText(); - if ( text==null || text.length()==0 ) { - text= c.getName(); - } - src.append("\"").append(text).append("\""); - } else if ( c instanceof JTextField ) { - String text= ((JTextField)c).getText(); - if ( text==null || text.length()==0 ) { - text= c.getName(); - } - src.append("\"").append(text).append("\""); - } - - Window h= SwingUtilities.getWindowAncestor(c); - String htitle=null; - if ( h instanceof Dialog ) { - src.append(" of \"").append( ((Dialog)h).getTitle()).append("\""); - } else if ( h instanceof JFrame ) { - src.append(" of \"").append( ((JFrame)h).getTitle()).append("\""); - } - ssrc= src.toString(); - - } - } - if ( thisRef.length()>30 ) { // pngwalk tool uses action commands to handle argument passing. - getLogger("gui").log( Level.FINE, "{1}", new Object[]{ ssrc }); - } else { - getLogger("gui").log( Level.FINE, "\"{0}\" from {1}", new Object[]{ thisRef, ssrc }); - } - - } - - /** - * provide easy way to log all GUI events. - * @param e - */ - public static void logGuiEvent( ActionEvent e ) { - if ( e==null ) { - getLogger("gui").log( Level.FINEST, "null ActionEvent"); - return; - } - if ( !( getLogger("gui").isLoggable(Level.FINE ) ) ) { - return; - } - if ( e.getSource() instanceof JCheckBox ) { - JCheckBox cb= (JCheckBox)e.getSource(); - logGuiEvent( e.getSource(), ( cb.isSelected() ? "select " : "deselect " ) + e.getActionCommand() ); - } else if ( e.getSource() instanceof JComboBox ) { - JComboBox cb= (JComboBox)e.getSource(); - logGuiEvent( e.getSource(), cb.getEditor().getItem().toString() ); - } else { - logGuiEvent( e.getSource(), e.getActionCommand() ); - } - } - - /** - * call this at the end of the GUI event to measure time to respond. - * @param e the focus event. - */ - public static void logExitGuiEvent( ActionEvent e ) { - long t1= System.currentTimeMillis(); - getLogger("gui").log( Level.FINE, "handled \"{0}\" in (ms): {1}", new Object[]{ "actionEvent", t1-lastEventTime }); - } - - public static void logGuiEvent( ChangeEvent e ) { - if ( !( getLogger("gui").isLoggable(Level.FINE ) ) ) { - return; - } - logGuiEvent( e.getSource(), "changeEvent" ); - } - - /** - * call this at the end of the GUI event to measure time to respond. - * @param e the focus event. - */ - public static void logExitGuiEvent( ChangeEvent e ) { - long t1= System.currentTimeMillis(); - getLogger("gui").log( Level.FINE, "handled \"{0}\" in (ms): {1}", new Object[]{ "changeEvent", t1-lastEventTime }); - } - - public static void logGuiEvent( ItemEvent e ) { - if ( !( getLogger("gui").isLoggable(Level.FINE ) ) ) { - return; - } - logGuiEvent( e.getSource(), "itemEvent" ); - } - - /** - * call this at the end of the GUI event to measure time to respond. - * @param e the focus event. - */ - public static void logExitGuiEvent( ItemEvent e ) { - long t1= System.currentTimeMillis(); - getLogger("gui").log( Level.FINE, "handled \"{0}\" in (ms): {1}", new Object[]{ "itemEvent", t1-lastEventTime }); - } - - public static void logGuiEvent( FocusEvent e ) { - if ( !( getLogger("gui").isLoggable(Level.FINE ) ) ) { - return; - } - logGuiEvent( e.getSource(), "focusEvent" ); - } - - /** - * call this at the end of the GUI event to measure time to respond. - * @param e the focus event. - */ - public static void logExitGuiEvent( FocusEvent e ) { - long t1= System.currentTimeMillis(); - getLogger("gui").log( Level.FINE, "handled \"{0}\" in (ms): {1}", new Object[]{ "focusEvent", t1-lastEventTime }); - } - - /** - * log property change events. (I realized I spend a lot of time debugging walking - * through the property change fire event code, and I should just add a - * log message to all propertyChange codes.) - * @param e - */ - public static void logPropertyChangeEvent( PropertyChangeEvent e ) { - getLogger("gui").log(Level.FINE, "PropertyChange {0}={1}", new Object[]{e.getPropertyName(), e.getNewValue()}); - } - - /** - * log property change events. (I realized I spend a lot of time debugging walking - * through the property change fire event code, and I should just add a - * log message to all propertyChange codes.) - * @param e - * @param source comment on the source - */ - public static void logPropertyChangeEvent( PropertyChangeEvent e, String source ) { - getLogger("gui").log(Level.FINE, "PropertyChange {0}={1} {2}", new Object[]{e.getPropertyName(), e.getNewValue(), source}); - } - - - public static void main( String[] args ) { - Logger l= LoggerManager.getLogger("test"); - Exception e= new java.lang.Exception("this is the problem") ; - l.log( Level.WARNING, null, e ); // BUG 1119 DEMONSTRATION PURPOSES - l.log( Level.WARNING, e.getMessage(), e ); - l.log( Level.WARNING, "Exception: {0}", e ); - l.log( Level.INFO, "hello there..." ); - } - - /** - * A slightly more transparent logging configuration would provide feedback - * about what configuration file it's loading. This will echo - * when the configuration file would be. - * - * The idea is when you are completely frustrated with not getting the logger - * to behave, you can add: - * org.das2.util.LoggerManager.readConfiguration() - * to your code. - * - */ - public static void readConfiguration() { - String configfile= System.getProperty("java.util.logging.config.file"); - if ( configfile==null ) { - System.err.println("no config file, set java property java.util.logging.config.file like so:"); - System.err.println("-Djava.util.logging.config.file=/tmp/logging.properties"); - } else { - File ff= new File( configfile ); - if ( ff.isAbsolute() ) { - System.err.println("loading logging configuration from "+ff); - } else { - ff= ff.getAbsoluteFile(); - System.err.println("loading logging configuration from "+ff); - } - } - try { - LogManager.getLogManager().readConfiguration(); - } catch ( IOException ex ) { - ex.printStackTrace(); - } - } -} diff --git a/dasCoreUtil/src/org/das2/util/NBConsoleFormatter.java b/dasCoreUtil/src/org/das2/util/NBConsoleFormatter.java deleted file mode 100644 index 026eb6d61..000000000 --- a/dasCoreUtil/src/org/das2/util/NBConsoleFormatter.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * NBConsoleFormatter.java - * - * Created on April 7, 2005, 12:13 PM - */ - -package org.das2.util; - -import java.text.MessageFormat; -import java.util.logging.Formatter; -import java.util.logging.Level; -import java.util.logging.LogRecord; - -/** - * Formats log messages to the stdout, so that Netbeans will automatically - * make links from the stack traces printed to stderr. - * @author Jeremy - */ -public class NBConsoleFormatter extends Formatter { - boolean coalesce=true; - String lastMessage=null; - int coalesceHits=0; - - public String format( LogRecord rec ) { - //if ( rec.getLevel()==Level.WARNING &&rec.getMessage()==null ) { - //System.err.println("Here"); - //} - if ( coalesce && lastMessage!=null && lastMessage.equals(rec.getMessage()) ) { - coalesceHits++; - return ""; - } else { - StackTraceElement[] st= new Throwable().getStackTrace(); - //String msg= Message.fo - int len= Math.min( 9, st.length ); - String msg= MessageFormat.format( String.valueOf( rec.getMessage() ), rec.getParameters() ); - String result= rec.getLoggerName()+" ["+Thread.currentThread().getName()+"]\n"+rec.getLevel().getLocalizedName()+": "+msg; - if ( len>2 ) { - result= result - +( "\n\tat "+st[len-2] ) - +( "\n\tat "+st[len-1]+"\n" ); - } - if ( coalesceHits>0 ) { - result= "(Last message repeats "+(coalesceHits+1)+" times)\n"+result; - } - coalesceHits= 0; - lastMessage= rec.getMessage(); - return result; - } - } - - public NBConsoleFormatter() { - } - -} diff --git a/dasCoreUtil/src/org/das2/util/OsUtil.java b/dasCoreUtil/src/org/das2/util/OsUtil.java deleted file mode 100644 index 91a17685c..000000000 --- a/dasCoreUtil/src/org/das2/util/OsUtil.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.das2.util; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.lang.management.ManagementFactory; - -/** - * Utility methods for operating system functions. - * @see org.das2.util.filesystem.FileSystemUtil - * @author jbf - */ -public class OsUtil { - - /** - * return the processID (pid), or the fallback if the pid cannot be found. - * @param fallback the string (null is okay) to return when the pid cannot be found. - * @return the process id or the fallback provided by the caller. - */ - public static String getProcessId(final String fallback) { - // Note: may fail in some JVM implementations - // therefore fallback has to be provided - - // something like '@', at least in SUN / Oracle JVMs - final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); - final int index = jvmName.indexOf('@'); - - if (index < 1) { - // part before '@' empty (index = 0) / '@' not found (index = -1) - return fallback; - } - - try { - return Long.toString(Long.parseLong(jvmName.substring(0, index))); - } catch (NumberFormatException e) { - // ignore - } - return fallback; - } - - /** - * Unconditionally close an Reader. - *

    - * Equivalent to {@link Reader#close()}, except any exceptions will be ignored. - * This is typically used in finally blocks. - * - * From apache commons. http://grepcode.com/file_/repo1.maven.org/maven2/commons-io/commons-io/1.2/org/apache/commons/io/IOUtils.java/?v=source - * - * @param input the Reader to close, may be null or already closed - */ - public static void closeQuietly(InputStream input) { - try { - if (input != null) { - input.close(); - } - } catch (IOException ioe) { - // ignore - } - } - /** - * Compare the contents of two Streams to determine if they are equal or - * not. - *

    - * This method buffers the input internally using - * BufferedInputStream if they are not already buffered. - * - * @param input1 the first stream - * @param input2 the second stream - * @return true if the content of the streams are equal or they both don't - * exist, false otherwise - * @throws NullPointerException if either input is null - * @throws IOException if an I/O error occurs - * - * From Apache Commons http://grepcode.com/file_/repo1.maven.org/maven2/commons-io/commons-io/1.2/org/apache/commons/io/IOUtils.java/?v=source - */ - public static boolean contentEquals(InputStream input1, InputStream input2) - throws IOException { - if (!(input1 instanceof BufferedInputStream)) { - input1 = new BufferedInputStream(input1); - } - if (!(input2 instanceof BufferedInputStream)) { - input2 = new BufferedInputStream(input2); - } - - int ch = input1.read(); - while (-1 != ch) { - int ch2 = input2.read(); - if (ch != ch2) { - return false; - } - ch = input1.read(); - } - - int ch2 = input2.read(); - return (ch2 == -1); - } - - /** - * Compare the contents of two files to determine if they are equal or not. - *

    - * This method checks to see if the two files are different lengths - * or if they point to the same file, before resorting to byte-by-byte - * comparison of the contents. - *

    - * Code origin: Avalon - * - * @param file1 the first file - * @param file2 the second file - * @return true if the content of the files are equal or they both don't - * exist, false otherwise - * @throws IOException in case of an I/O error - * - * From Apache Commons http://grepcode.com/file/repo1.maven.org/maven2/commons-io/commons-io/1.2/org/apache/commons/io/FileUtils.java - */ - public static boolean contentEquals(File file1, File file2) throws IOException { - boolean file1Exists = file1.exists(); - if (file1Exists != file2.exists()) { - return false; - } - - if (!file1Exists) { - // two not existing files are equal - return true; - } - - if (file1.isDirectory() || file2.isDirectory()) { - // don't want to compare directory contents - throw new IOException("Can't compare directories, only files"); - } - - if (file1.length() != file2.length()) { - // lengths differ, cannot be equal - return false; - } - - if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) { - // same file - return true; - } - - InputStream input1 = null; - InputStream input2 = null; - try { - input1 = new FileInputStream(file1); - input2 = new FileInputStream(file2); - return contentEquals(input1, input2); - - } finally { - closeQuietly(input1); - closeQuietly(input2); - } - } - -} - diff --git a/dasCoreUtil/src/org/das2/util/Splash.java b/dasCoreUtil/src/org/das2/util/Splash.java deleted file mode 100644 index e00018a38..000000000 --- a/dasCoreUtil/src/org/das2/util/Splash.java +++ /dev/null @@ -1,131 +0,0 @@ -/* File: Splash.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import java.util.logging.*; -import javax.swing.*; -import java.awt.*; -import java.net.URL; - -/** - * - * @author jbf - */ -public class Splash extends JWindow { - - private static Splash instance=null; - - private Handler handler; - private JLabel messageLabel; - - public static String getVersion() { - //DName: das_20030505_01_beta D - String cvsTagName= "$Name$"; - String version; - if (cvsTagName.length()<=9) { - version="untagged_version"; - } else { - version= cvsTagName.substring(6,cvsTagName.length()-2); - } - return version; - } - - public Handler getLogHandler() { - if ( handler==null ) { - handler= createhandler(); - } - return handler; - } - - private Handler createhandler() { - Handler result= new Handler() { - public void publish( LogRecord logRecord ) { - System.out.println( logRecord.getMessage() ); - messageLabel.setText(logRecord.getMessage() ); - } - public void flush() {} - public void close() {} - }; - return result; - } - - private static ImageIcon getSplashImage() { - URL url= Splash.class.getResource("/images/dasSplash.gif"); - if ( url==null ) return null; - return new ImageIcon(url); - } - - public static Splash getInstance() { - if ( instance==null ) { - instance= new Splash(); - } - return instance; - } - - public static void showSplash() { - getInstance(); - instance.setVisible(true); - } - - public static void hideSplash() { - getInstance(); - instance.setVisible(false); - } - - /** Creates a new instance of Splash */ - public Splash() { - super(); - JPanel panel= new JPanel(new BorderLayout()); - panel.add(new JLabel(getSplashImage()),BorderLayout.CENTER); - - Box bottomPanel= Box.createHorizontalBox(); - - messageLabel= new JLabel(""); - messageLabel.setMinimumSize( new Dimension( 200, 10 ) ); - bottomPanel.add( messageLabel ); - bottomPanel.add( Box.createHorizontalGlue() ); - bottomPanel.add( new JLabel("version "+getVersion()+" ",JLabel.RIGHT) ); - - panel.add( bottomPanel, BorderLayout.SOUTH ); - this.setContentPane(panel); - this.pack(); - //this.setLocation(300,300); - this.setLocationRelativeTo(null); - } - - public static void main( String[] args ) { - System.out.println("This is das2 version "+getVersion()); - Splash.showSplash(); - Logger.getLogger("").addHandler( Splash.getInstance().getLogHandler() ); - try { - for ( int i=0; i<6; i++ ) { - Thread.sleep(500); - Logger.getLogger("").log(Level.INFO, "i={0}", i); - //Splash.getInstance().messageLabel.setText( "ii-="+i ); - } - } catch ( java.lang.InterruptedException e ) {} - Splash.hideSplash(); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/StringTools.java b/dasCoreUtil/src/org/das2/util/StringTools.java deleted file mode 100644 index 9469c99b8..000000000 --- a/dasCoreUtil/src/org/das2/util/StringTools.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.util; - -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * - * @author jbf - */ -public class StringTools { - - /** - * splits the string, guarding the space within protect. - * @param delim the delimiter we split on. (similar to s.split(delim,-2)) - * @param protect character that blocks off delimiter, such as quote. - * @return - */ - public static String[] guardedSplit( String str, String delim, char protect ) { - byte[] copy= str.getBytes( Charset.forName("US-ASCII") ); - if ( Pattern.compile(str).matcher( String.valueOf(delim) ).matches() ) { - throw new IllegalArgumentException("the delimiter cannot match the protect character"); - } - byte hide='_'; - if ( Pattern.compile(str).matcher( String.valueOf(hide) ).matches() ) { - throw new IllegalArgumentException("the delimiter cannot match _"); - } - - boolean inside= false; - boolean escape= false; // \" - for ( int i=0; i result= new ArrayList(); - - Pattern spl= Pattern.compile(delim); - Matcher m= spl.matcher(scopy); - int i=0; - while ( m.find() ) { - int i0= i; - int i1= m.start(); - result.add( str.substring(i0,i1) ); - i= m.end(); - } - result.add( str.substring(i) ); - - return result.toArray( new String[result.size()] ); - - } - - public static void main( String[] args ) { - String s= "a b c \"d \\\" e\" f"; - System.err.println( guardedSplit( s, " ", '"' ) ); - } -} diff --git a/dasCoreUtil/src/org/das2/util/ThrowRuntimeExceptionHandler.java b/dasCoreUtil/src/org/das2/util/ThrowRuntimeExceptionHandler.java deleted file mode 100644 index a186ad236..000000000 --- a/dasCoreUtil/src/org/das2/util/ThrowRuntimeExceptionHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * NullExceptionHandler.java - * - * Created on November 16, 2006, 12:59 PM - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -package org.das2.util; - -/** - * ExceptionHandler that throws a RuntimeException caused by the Exception. - * This is useful for server-side applications that need to handle the - * exception externally. - * - * @author jbf - */ -public class ThrowRuntimeExceptionHandler implements ExceptionHandler { - - /** Creates a new instance of NullExceptionHandler */ - public ThrowRuntimeExceptionHandler() { - } - - public void handle(Throwable t) { - t.printStackTrace(); - throw new RuntimeException(t); - } - - public void handleUncaught(Throwable t) { - t.printStackTrace(); - throw new RuntimeException(t); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/TickleTimer.java b/dasCoreUtil/src/org/das2/util/TickleTimer.java deleted file mode 100644 index 34aaf0d29..000000000 --- a/dasCoreUtil/src/org/das2/util/TickleTimer.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * TickleTimer.java - * - * Created on July 28, 2006, 9:23 PM - * - */ - -package org.das2.util; - -import java.beans.PropertyChangeListener; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * TickleTimer is a timer that fires once it's been left alone for - * a while. The idea is the keyboard can be pecked away and - * the change event will not be fired until the keyboard is idle. - * - * This is a copy of the TickleTimer used in Autoplot. - * - * java.util.Timer performs very similar functionality. - * @author Jeremy Faden - */ -public class TickleTimer { - long tickleTime; - long delay; - boolean running; - boolean firing=false; // true when the listener is being invoked. - List messages; - - static final Logger log= org.das2.util.LoggerManager.getLogger("das2.util"); - - /** - * @param delay time in milliseconds to wait until firing off the change. - * If delay is =<0, then events will be fired off immediately. - * @param PropertyChangeListener provides the callback when the timer - * runs out. The listener is added as one of the bean's property - * change listeners. - */ - public TickleTimer( long delay, PropertyChangeListener listener ) { - this.tickleTime= System.currentTimeMillis(); - this.delay= delay; - addPropertyChangeListener( listener ); - this.running= false; - messages= new ArrayList(); - } - - private void startTimer() { - running= true; - if ( delay<=0 ) { - newRunnable().run(); - } else { - new Thread( newRunnable(), "tickleTimerThread" ).start(); - messages.clear(); - } - } - - private Runnable newRunnable() { - return new Runnable() { - public void run() { - long d= System.currentTimeMillis() - tickleTime; - while ( d < delay ) { - try { - log.log(Level.FINER, "tickleTimer sleep {0}", (delay - d)); - Thread.sleep( delay-d ); - } catch (InterruptedException ex) { - throw new RuntimeException(ex); - } - d= System.currentTimeMillis() - tickleTime; - } - log.log(Level.FINER, "tickleTimer fire after {0}", d ); - firing= true; - running= false; //sometimes listeners need to retickle the timer... - propertyChangeSupport.firePropertyChange("running",true,false); - firing= false; - messages= new ArrayList(); - } - }; - } - - public synchronized void tickle(){ - tickle(null); - } - - public synchronized void tickle( String message ) { - if ( firing ) { - log.log(Level.FINE, "tickle while firing, which may be result in bugs"); - } - tickleTime= System.currentTimeMillis(); - if ( !running ) startTimer(); - if ( message!=null ) messages.add(message); - } - - private java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); - - public final void addPropertyChangeListener(java.beans.PropertyChangeListener l) { - propertyChangeSupport.addPropertyChangeListener(l); - } - - public final void removePropertyChangeListener(java.beans.PropertyChangeListener l) { - propertyChangeSupport.removePropertyChangeListener(l); - } - - public boolean isRunning() { - return this.running; - } - - public void setRunning(boolean running) { - boolean oldRunning = this.running; - this.running = running; - propertyChangeSupport.firePropertyChange ("running", Boolean.valueOf(oldRunning), Boolean.valueOf(running)); - } - - public List getMessages() { - return Collections.unmodifiableList(messages); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/TimerConsoleFormatter.java b/dasCoreUtil/src/org/das2/util/TimerConsoleFormatter.java deleted file mode 100644 index 1e638a57f..000000000 --- a/dasCoreUtil/src/org/das2/util/TimerConsoleFormatter.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * NBConsoleFormatter.java - * - * Created on April 7, 2005, 12:13 PM - */ -package org.das2.util; - -import java.text.DecimalFormat; -import java.util.logging.Formatter; -import java.util.logging.LogRecord; - -/** - * - * @author Jeremy - */ -public class TimerConsoleFormatter extends Formatter { - - long t0= System.currentTimeMillis(); - DecimalFormat nf; - String lastLoggerName; - String resetMessage; - - public String format(LogRecord rec) { - - final String message = rec.getMessage(); - //if ( lastLoggerName==null || lastLoggerName!=rec.getLoggerName() ) { - if (message != null && resetMessage!=null && message.contains(resetMessage)) { - lastLoggerName = rec.getLoggerName(); - t0 = System.currentTimeMillis(); - } - - String spaces = " "; - String source = "???"; - - StackTraceElement[] st = new Throwable().getStackTrace(); - if (st.length > 8) { - int idx= 5; - while ( idx<8 && st[idx].getClassName().contains("java.util.logging.Logger") ) idx++; - source = String.valueOf(st[idx].getClassName()); - if (source.startsWith("org.das2")) { - source = source.substring("org.das2".length()); - if (source.length() < spaces.length()) { - source = spaces.substring(source.length()) + source; - } - } - } - - long t = System.currentTimeMillis() - t0; - - String threadId= Thread.currentThread().getName(); - threadId = fixedColumn(threadId, 20); - - return nf.format(t) + ":" + fixedColumn(rec.getLoggerName(),20) + ": " + source + ": " + threadId+":" + rec.getLevel().getLocalizedName() + ": " + String.valueOf(message) + "\n"; - } - - public void setResetMessage( String msg ) { - this.resetMessage= msg; - } - - public TimerConsoleFormatter() { - t0 = System.currentTimeMillis(); - nf = new DecimalFormat("00000"); - } - - String spaces= " "; - private String fixedColumn(String threadId, int sp) { - try { - if (threadId.length() > sp) threadId = threadId.substring(threadId.length()-sp, threadId.length()); - if (threadId.length() < sp) threadId = spaces.substring(0, sp - threadId.length()) + threadId; - return threadId; - } catch ( StringIndexOutOfBoundsException ex ) { - return threadId; - } - } -} diff --git a/dasCoreUtil/src/org/das2/util/TimingConsoleFormatter.java b/dasCoreUtil/src/org/das2/util/TimingConsoleFormatter.java deleted file mode 100644 index 46025d877..000000000 --- a/dasCoreUtil/src/org/das2/util/TimingConsoleFormatter.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.das2.util; - -import java.text.MessageFormat; -import java.util.logging.Formatter; -import java.util.logging.LogRecord; - -/** - * Print the elapsed time and logger name with each message. - * @author jbf - */ -public class TimingConsoleFormatter extends Formatter { - - long t0= System.currentTimeMillis(); - - @Override - public String format( LogRecord rec ) { - long dt= System.currentTimeMillis() - t0; - Object[] parms= rec.getParameters(); - - // the following copied from Autoplot's org.virbo.autoplot.scriptconsole.LogConsole - String recMsg; - String rm1= rec.getMessage(); - if ( rm1!=null ) { - if ( rm1.length()>1 ) { - char c= rm1.charAt(0); - if ( c=='E' ) { - if ( rm1.equals("ENTRY {0}") ) { - recMsg= "ENTRY "+ rec.getSourceClassName() + "." +rec.getSourceMethodName() + " {0}"; - } else if ( rm1.equals("ENTRY") ) { - recMsg= "ENTRY "+ rec.getSourceClassName() + "." +rec.getSourceMethodName(); - } else { - recMsg= rm1; - } - } else if ( c=='R' ) { - if ( rm1.equals("RETURN {0}") ) { - recMsg= "RETURN "+ rec.getSourceClassName() + "." +rec.getSourceMethodName() + " {0}"; - } else if ( rm1.equals("RETURN") ) { - recMsg= "RETURN "+ rec.getSourceClassName() + "." +rec.getSourceMethodName(); - } else { - recMsg= rm1; - } - } else { - recMsg = rm1; - } - } else { - recMsg = rec.getMessage(); - } - } else { - recMsg= null; - } - - if ( parms!=null && parms.length>0 ) { - try { - recMsg = MessageFormat.format( recMsg, parms ); - } catch ( NullPointerException ex ) { - recMsg= String.valueOf( rec.getMessage() ); //TODO: fix this log message! bug https://sourceforge.net/p/autoplot/bugs/1194/ - } - } - if ( ( recMsg==null || recMsg.length()==0 ) && rec.getThrown()!=null ) { - recMsg= rec.getThrown().toString(); - //TODO: consider if "debug" property should be set instead. Also it would be nice to digest this for jython errors. - rec.getThrown().printStackTrace(); - // This is interesting--I've wondered where the single-line-message items have been coming from... - } else { - // no message. breakpoint here for debugging. - int i=0; - } - return String.format("%9.3f %s: %s\n", dt/1000., rec.getLoggerName(), recMsg ); - } - - public TimingConsoleFormatter() { - } - -} diff --git a/dasCoreUtil/src/org/das2/util/URLBuddy.java b/dasCoreUtil/src/org/das2/util/URLBuddy.java deleted file mode 100644 index 2d16ca293..000000000 --- a/dasCoreUtil/src/org/das2/util/URLBuddy.java +++ /dev/null @@ -1,140 +0,0 @@ -/* File: URLBuddy.java - * Copyright (C) 2002-2003 The University of Iowa - * - * Created on April 7, 2004, 11:34 AM - * by Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util; - -import java.util.*; -import java.util.regex.*; - -/** - * - * @author eew - */ -public class URLBuddy { - - private static final String ALPHA = "[A-Za-z]"; - private static final String DIGIT = "[0-9]"; - private static final String HEX = "[" + DIGIT + "A-F]"; - - public static final Pattern VALID_QUERY_NAME = Pattern.compile( - ALPHA + "[" + ALPHA + DIGIT + "-_:.]*" - ); - public static final Pattern VALID_QUERY_VALUE = Pattern.compile( - "(?:[" + ALPHA + DIGIT + "\\.\\-\\*\\_\\+]|\\%(" + HEX + HEX + "))*" - ); - - private URLBuddy() { - } - - public static String encodeUTF8(String str) { - try { - return java.net.URLEncoder.encode(str, "UTF-8"); - } - catch (java.io.UnsupportedEncodingException uee) { - //All JVM's are required to support UTF-8 - throw new RuntimeException(uee); - } - } - - public static String decodeUTF8(String str) { - try { - return java.net.URLDecoder.decode(str, "UTF-8"); - } - catch (java.io.UnsupportedEncodingException uee) { - //All JVM's are required to support UTF-8 - throw new RuntimeException(uee); - } - } - - - /** Returns an unmodifiable map representing the query string passed in. - * each key is a name from the string and each value is the url encoded - * value for the key. - *@param str an URLEncoded query string - */ - public static Map parseQueryString(String str) { - HashMap map = new HashMap(); - String[] tokens = str.split("\\&"); - for (int i = 0; i < tokens.length; i++) { - int eqIndex = tokens[i].indexOf('='); - if (eqIndex == -1) { - throwUnexpectedToken(tokens[i], str, "name/value pair"); - } - String name = tokens[i].substring(0, eqIndex); - String value = tokens[i].substring(eqIndex + 1); - if (!validName(name)) { - throwUnexpectedToken(name, str, "valid name"); - } - if (!validValue(value)) { - throwUnexpectedToken(name, str, "url encoded value"); - } - value = decodeUTF8(value); - map.put(name, value); - } - return Collections.unmodifiableMap(map); - } - - private static void throwUnexpectedToken(String token, String input, String expecting) { - int index = input.indexOf(token); - StringBuilder messageBuffer = new StringBuilder(); - messageBuffer.append("Error parsing query string: Expecting "); - messageBuffer.append(expecting).append(", found '"); - messageBuffer.append(token).append("'\n"); - messageBuffer.append("Input: ").append(input).append('\n'); - messageBuffer.append(" "); - for (int i = 0; i < index; i++) { - messageBuffer.append('.'); - } - messageBuffer.append('^'); - throw new IllegalArgumentException(messageBuffer.toString()); - } - - public static String formatQueryString(Map m) { - StringBuilder query = new StringBuilder(); - for (Iterator i = m.entrySet().iterator(); i.hasNext();) { - Map.Entry entry = (Map.Entry)i.next(); - String name = (String)entry.getKey(); - String value = (String)entry.getValue(); - if (!validName(name)) { - throw new IllegalArgumentException("'" + name + "' is not a valid query name."); - } - value = encodeUTF8(value); - query.append(name).append('=').append(value).append('&'); - } - if (query.charAt(query.length() - 1) == '&') { - query.deleteCharAt(query.length() - 1); - } - return query.toString(); - } - - private static boolean validName(String name) { - Matcher m = VALID_QUERY_NAME.matcher(name); - return m.matches(); - } - - private static boolean validValue(String value) { - Matcher m = VALID_QUERY_VALUE.matcher(value); - return m.matches(); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/awt/LoggingEventQueue.java b/dasCoreUtil/src/org/das2/util/awt/LoggingEventQueue.java deleted file mode 100644 index 04aa47894..000000000 --- a/dasCoreUtil/src/org/das2/util/awt/LoggingEventQueue.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * LoggingEventQueue.java - * - * Created on October 31, 2005, 1:02 PM - * - * - */ - -package org.das2.util.awt; - -import java.awt.AWTEvent; -import java.awt.EventQueue; -import java.awt.event.InvocationEvent; -import java.util.LinkedList; -import java.util.Queue; -import java.util.logging.Logger; - -/** - * Tool for debugging event queue stuff. This can be used to log the event - * queue, or insert breakpoints, etc. - * Toolkit.getDefaultToolkit().getSystemEventQueue().push(new LoggingEventQueue()); - * - * @author Jeremy - * - */ -public class LoggingEventQueue extends EventQueue { - - final static Logger logger= Logger.getLogger( "das2.gui" ); - - private LoggingEventQueue() { - - } - - @Override - public void postEvent(java.awt.AWTEvent theEvent) { - if (theEvent instanceof InvocationEvent) { - // logger.info("XXX: "+theEvent); - } else { - // logger.info(""+theEvent); - } - super.postEvent(theEvent); - } - - private static LoggingEventQueue instance; - public static LoggingEventQueue getInstance() { - if ( instance==null ) { - instance= new LoggingEventQueue(); - } - return instance; - } - - public static synchronized void dumpPendingEvents() { - System.err.println("---------------------------------------------------------------"); - Queue queue= new LinkedList(); - AWTEvent evt; - while ( instance.peekEvent()!=null ) { - try { - evt= instance.getNextEvent(); - } catch (InterruptedException ex) { - throw new RuntimeException(ex); - } - System.err.println("[ "+evt); - queue.add(evt); - } - System.err.println("-----e--n--d-----------------------------------------------------"); - while ( queue.size() > 0 ) { - instance.postEvent((AWTEvent)queue.remove()); - } - } - -} diff --git a/dasCoreUtil/src/org/das2/util/awt/PdfGraphicsOutput.java b/dasCoreUtil/src/org/das2/util/awt/PdfGraphicsOutput.java deleted file mode 100644 index 160a311b9..000000000 --- a/dasCoreUtil/src/org/das2/util/awt/PdfGraphicsOutput.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * PDFGraphicsOutput.java - * - * Created on January 28, 2005, 5:27 PM - */ - -package org.das2.util.awt; - -import com.itextpdf.awt.FontMapper; -import com.itextpdf.awt.PdfGraphics2D; -import com.itextpdf.text.Rectangle; -import com.itextpdf.text.pdf.PdfContentByte; -import com.itextpdf.text.pdf.PdfWriter; -import com.itextpdf.text.Document; -import com.itextpdf.text.DocumentException; -import com.itextpdf.text.pdf.BaseFont; -import java.awt.Font; -import java.awt.FontFormatException; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.das2.util.FileUtil; -import org.das2.util.LoggerManager; - -/** - * support writing to PDF. - * @author eew - */ -public class PdfGraphicsOutput implements GraphicsOutput { - - private static final Logger logger= LoggerManager.getLogger("das2.graphics.pdf"); - - private float width; - private float height; - private OutputStream out; - private Document doc; - private PdfWriter writer; - private PdfContentByte cb; - private Graphics2D graphics; - - public static final String READING_FONTS="PleaseWait"; - public static final Object STATE_IDLE="idle"; - public static final Object STATE_READING="reading"; - public static final Object STATE_READY="ready"; - - private static Map fontToTtfMap; - private static Object state= STATE_IDLE; - - private static final Object lockObject= new Object(); - - /* these were introduced to make findbugs happy, but it also clarifies code. On Macs and Linux, we also look in the user's home */ - private static final String MAC_FONT_HOME= "/Library/Fonts/"; - private static final String WINDOWS_FONT_HOME_1= "C:/Windows/Fonts"; - private static final String WINDOWS_FONT_HOME_2= "D:/Windows/Fonts"; - private static final String LINUX_FONT_HOME= "/usr/share/fonts/"; - private static final String SOLARIS_FONT_HOME= "/usr/X/lib/X11/fonts/TrueType/"; - - public synchronized static Map resetFontToTtfMap() { - fontToTtfMap= null; - return getFontToTtfMap(); - } - - /** - * Establish a name to .ttf file mapping. On my development system with 233 fonts, this takes less than 400ms. - */ - private synchronized static Map getFontToTtfMap() { - - String osName= System.getProperty( "os.name" ); - String userhome= System.getProperty("user.home"); - - // Identify the search path for ttf fonts, which must have the extension .ttf. See http://www.fontation.com/feature/ which confirms this code. - File[] dirs; - if ( osName.startsWith("Mac") ) { - dirs= new File[] { new File( userhome + "/Library/Fonts/" ), new File( MAC_FONT_HOME ) }; - } else if ( osName.startsWith("Linux") ) { - dirs= new File[] { new File( userhome, ".fonts" ), new File( LINUX_FONT_HOME ) }; // note Ubuntu is /usr/share/fonts/truetype, but this will work. - } else if ( osName.startsWith("Windows") ) { - dirs= new File[] { new File( WINDOWS_FONT_HOME_1), new File( WINDOWS_FONT_HOME_2 ) }; // - } else if ( osName.startsWith("SunOS") ) { - dirs= new File[] { new File( userhome, "fonts" ), new File( SOLARIS_FONT_HOME ) }; - } else { - logger.warning("unknown os.name, no fonts will be embedded"); - dirs= new File[] { }; - } - - if ( fontToTtfMap==null ) { - state= STATE_READING; - - logger.log( Level.FINE, "indexing fonts..." ); - long t0= System.currentTimeMillis(); - HashMap fontToTtfMap1= new HashMap(); - for ( File dir: dirs ) { - if ( !dir.exists() ) { - continue; - } - File[] ttfFonts= FileUtil.listRecursively( dir, "*.ttf" ); - for ( File f: ttfFonts ) { - FileInputStream in = null; - try { - com.itextpdf.text.pdf.BaseFont.createFont( f.toString(), BaseFont.IDENTITY_H, BaseFont.EMBEDDED ); // check to see if iText is going to fuss about licensing. - in = new FileInputStream(f); - Font font= Font.createFont(Font.TRUETYPE_FONT, in ); - logger.log( Level.FINEST, "adding {0} -> {1}", new Object[]{font.getFontName(), f}); - fontToTtfMap1.put( font.getFontName(), f ); - } catch ( DocumentException ex ) { - logger.log( Level.SEVERE, ex.getMessage(), ex ); - } catch (FontFormatException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } catch (IOException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } finally { - try { - if ( in!=null ) in.close(); - } catch (IOException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - } - } - } - fontToTtfMap= fontToTtfMap1; - state= STATE_READY; - logger.log( Level.FINE, "{0}fonts indexed in {1} millis", new Object[] { fontToTtfMap.size(), ( System.currentTimeMillis() - t0) } ); - } - return fontToTtfMap; - } - - /** - * kludge to support call from AWT. If the font map is not yet - * loaded, return READING_FONTS and start the lookup on a new thread. - * @param font - * @return READING_FONTS or the name (or null). - */ - public static String ttfFromNameInteractive( final java.awt.Font font ) { - synchronized ( lockObject ) { - if ( state==STATE_READY ) { - return ttfFromName(font); - } - if ( fontToTtfMap==null ) { - if ( state==STATE_IDLE ) { - state= STATE_READING; - Runnable run= new Runnable() { - @Override - public void run() { - String x= ttfFromName( font ); - logger.log(Level.FINEST, "ttfFromName()->{0}", x); - } - }; - new Thread( run ).start(); - } - return READING_FONTS; - } else { - return ttfFromName(font); - } - } - } - - /** - * dump the keys out to a file. - * @param f a file target. - */ - public static void dumpMapToFile( File f ) { - Map map= getFontToTtfMap(); - List keys= new ArrayList( map.keySet() ); - Collections.sort(keys); - - PrintWriter out=null; - try { - out= new PrintWriter( f ); - for ( String k: keys ) { - out.printf( "\"%s\" \"%s\"\n", k, map.get(k) ); - } - } catch (FileNotFoundException ex) { - throw new IllegalArgumentException(ex); - } finally { - if ( out!=null ) out.close(); - } - } - - /** - * return the name of the .ttf file for the platform, or null. - * @param font - * @return the name of the .ttf file, or null. - */ - public static String ttfFromName( java.awt.Font font ) { - Map map= getFontToTtfMap(); - File f= map.get(font.getFontName()); - if ( f==null ) { - String s= font.getFamily(); - if ( ( font.getStyle() & Font.BOLD )==Font.BOLD ) s= s + " Bold"; - if ( ( font.getStyle() & Font.ITALIC )==Font.ITALIC ) s= s + " Italic"; - f= map.get( s ); - if ( f==null ) { - return null; - } else { - return f.toString(); - } - } else { - return f.toString(); - } - } - - FontMapper fontMapper = new FontMapper() { - @Override - public BaseFont awtToPdf(java.awt.Font font) { - try { - String ffile= ttfFromName(font); - if ( ffile==null ) { - logger.log(Level.WARNING, "couldn''t find ttf font file for {0}", font.getName()); - return BaseFont.createFont(); - } else { - return BaseFont.createFont( - ffile, - BaseFont.IDENTITY_H, BaseFont.EMBEDDED); - } - } catch (DocumentException e) { - logger.log( Level.WARNING, e.getMessage(), e ); - } catch (IOException e) { - logger.log( Level.WARNING, e.getMessage(), e ); - } - return null; - } - - @Override - public java.awt.Font pdfToAwt(BaseFont font, int size) { - return null; - } - }; - - /** Creates a new instance of PDFGraphicsOutput */ - public PdfGraphicsOutput() {} - - @Override - public Graphics2D getGraphics2D() { - if (graphics != null) { - return graphics; - } - if ( graphicsShapes ) { - graphics = new PdfGraphics2D(cb, width, height, true); - } else { - graphics = new PdfGraphics2D(cb, width, height, fontMapper); - } - - return graphics; - } - - @Override - public void finish() throws IOException { - graphics.dispose(); - cb.restoreState(); - doc.close(); - } - - @Override - public Graphics getGraphics() { - return getGraphics2D(); - } - - @Override - public void setOutputStream(OutputStream out) { - this.out = out; - } - - private boolean graphicsShapes= true; - - /** - * If true, then fonts are written out as lines and will match the screen. - * If false, then labels are editable. - * @param graphicsShapes - */ - public void setGraphicsShapes( boolean graphicsShapes ) { - this.graphicsShapes= graphicsShapes; - } - - /** - * set the size in points. - * @param width - * @param height - */ - @Override - public void setSize( int width, int height ) { - this.width = (float)width; - this.height = (float)height; - } - @Override - public void start() { - try { - Rectangle rect = new Rectangle(width, height); - doc = new Document(rect, 0f, 0f, 0f, 0f); - doc.addCreator("das2.org"); - doc.addCreationDate(); - writer = PdfWriter.getInstance(doc, out); - doc.open(); - cb = writer.getDirectContent(); - cb.saveState(); - } catch (DocumentException de) { - throw new RuntimeException(de); - } - } - - public static void main( String[] args ) { - Map map= getFontToTtfMap(); - System.err.println( map.size() ); - } - -} \ No newline at end of file diff --git a/dasCoreUtil/src/org/das2/util/filesystem/AppletHttpProtocol.java b/dasCoreUtil/src/org/das2/util/filesystem/AppletHttpProtocol.java deleted file mode 100755 index 0c53b55af..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/AppletHttpProtocol.java +++ /dev/null @@ -1,77 +0,0 @@ - -package org.das2.util.filesystem; - -import org.das2.util.DasProgressMonitorInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; - -/** - * uses HTTP, and doesn't download resources to cache - * @author jbf - */ -public class AppletHttpProtocol implements WebProtocol { - - @Override - public InputStream getInputStream(WebFileObject fo, org.das2.util.monitor.ProgressMonitor mon) throws IOException { - HttpURLConnection connect = (HttpURLConnection) fo.wfs.getURL(fo.pathname).openConnection(); - connect.connect(); - int len = connect.getContentLength(); - FileSystem.loggerUrl.log(Level.FINE, "getInputStream {0}", new Object[] { connect.getURL() } ); - DasProgressMonitorInputStream in = new DasProgressMonitorInputStream(connect.getInputStream(), mon); - if (len != -1) - in.setStreamLength(len); - return in; - } - - @Override - public Map getMetadata(WebFileObject fo) throws IOException { - String realName = fo.pathname; - boolean exists; - - URL ur = new URL(fo.wfs.getRootURL(), realName); - - FileSystem.loggerUrl.log(Level.FINE, "openConnection {0}", new Object[] { ur } ); - HttpURLConnection connect = (HttpURLConnection) ur.openConnection(); - connect.setRequestMethod("HEAD"); - HttpURLConnection.setFollowRedirects(false); - connect.connect(); - HttpURLConnection.setFollowRedirects(true); - // check for rename, which means we'll do another request - if (connect.getResponseCode() == 303) { - String surl = connect.getHeaderField("Location"); - if (surl.startsWith(fo.wfs.root.toString())) { - realName = surl.substring(fo.wfs.root.toString().length()); - } - connect.disconnect(); - ur = new URL(fo.wfs.getRootURL(), realName); - FileSystem.loggerUrl.log(Level.FINE, "openConnection {0}", new Object[] { ur } ); - connect = (HttpURLConnection) ur.openConnection(); - connect.setRequestMethod("HEAD"); - connect.connect(); - } - exists = connect.getResponseCode() != 404; - - Map result = new HashMap(); - - Map> fields = connect.getHeaderFields(); - for (Entry> e : fields.entrySet()) { - String key= e.getKey(); - List value = e.getValue(); - result.put(key, value.get(0)); - } - - result.put( META_EXIST, String.valueOf(exists) ); - - connect.disconnect(); - - return result; - - } -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/DefaultHttpProtocol.java b/dasCoreUtil/src/org/das2/util/filesystem/DefaultHttpProtocol.java deleted file mode 100755 index 59440e39b..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/DefaultHttpProtocol.java +++ /dev/null @@ -1,99 +0,0 @@ - -package org.das2.util.filesystem; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import org.das2.util.monitor.ProgressMonitor; - -/** - * This http protocol uses the local cache. - * - * @author jbf - */ -public class DefaultHttpProtocol implements WebProtocol { - - @Override - public InputStream getInputStream(WebFileObject fo, ProgressMonitor mon) throws IOException { - if (fo.isFolder) { - throw new IllegalArgumentException("is a folder"); - } - File ff= fo.getFile(mon); - return new FileInputStream(ff); - } - - @Override - public Map getMetadata(WebFileObject fo) throws IOException { - if (!fo.wfs.getRootURL().getProtocol().equals("ftp")) { - String realName = fo.pathname; - boolean exists; - - URL ur = new URL(fo.wfs.getRootURL(), realName); - HttpURLConnection connect = (HttpURLConnection) ur.openConnection(); - connect.setRequestMethod("HEAD"); - HttpURLConnection.setFollowRedirects(false); - - if ( fo.wfs instanceof HttpFileSystem ) { - HttpFileSystem hfs= ((HttpFileSystem)fo.wfs); - if ( hfs.getCookie()!=null ) { - connect.setRequestProperty("Cookie", hfs.getCookie()); - } - } - connect.connect(); - HttpURLConnection.setFollowRedirects(true); - // check for rename, which means we'll do another request - if (connect.getResponseCode() == 303) { - String surl = connect.getHeaderField("Location"); - if (surl.startsWith(fo.wfs.root.toString())) { - realName = surl.substring(fo.wfs.root.toString().length()); - } - connect.disconnect(); - ur = new URL(fo.wfs.getRootURL(), realName); - connect = (HttpURLConnection) ur.openConnection(); - connect.setRequestMethod("HEAD"); - connect.connect(); - } - exists = connect.getResponseCode() != 404; - - Map result = new HashMap(); - - Map> fields = connect.getHeaderFields(); - for (Entry> e : fields.entrySet()) { - String key= e.getKey(); - List value = e.getValue(); - result.put(key, value.get(0)); - } - - result.put(META_EXIST, String.valueOf(exists)); - - connect.disconnect(); - - return result; - - } else { - - Map result = new HashMap(); - - URL url= new URL( fo.wfs.getRootURL(), fo.pathname ); - URLConnection urlc = url.openConnection(); - try { - urlc.connect(); - urlc.getInputStream().close(); - result.put( META_EXIST, "true" ); - - } catch ( IOException ex ) { - result.put( META_EXIST, "false" ); - } - return result; - - } - } -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/ExpensiveOpCache.java b/dasCoreUtil/src/org/das2/util/filesystem/ExpensiveOpCache.java deleted file mode 100644 index 23e85e60e..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/ExpensiveOpCache.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.util.filesystem; - -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.das2.util.LoggerManager; - -/** - * This simply reduces invocations of an expensive operation by - * caching the result for a given number of milliseconds. This was - * introduced to remove the number of HEAD requests to the server for - * a given file. - * @author jbf - */ -public class ExpensiveOpCache { - - private static final Logger logger= LoggerManager.getLogger("das2.filesystem.opcache"); - - public static interface Op { - Object doOp( String key ) throws Exception; - } - - /** - * Cache the result of op for no more than limitMs milliseconds. - * @param op - * @param limitMs - */ - public ExpensiveOpCache( Op op, int limitMs ) { - this.limitMs= limitMs; - this.op= op; - } - - Op op; - int limitMs; - Map results= new HashMap(); - Map times= new HashMap(); - - /** - * Do the operation if it has not been done, or if it has not been - * done recently. - * @param key - * @throws Exception, see the given op. - * @return the result of the operation - */ - public Object doOp( String key ) throws Exception { - Long t; - Object result; - synchronized (this ) { - t = times.get(key); - result= results.get(key); - } - long t0= System.currentTimeMillis(); - long dt= t!=null ? t0 - t : 99999; - if ( t==null ) { - logger.log(Level.FINE, "no cache entry for: {0}", new Object[]{ key }); - result= op.doOp(key); - synchronized ( this ) { - times.put( key, t0 ); - results.put( key, result ); - } - } else if ( dt > limitMs ) { - logger.log(Level.FINE, "stale ({0}s) cache entry {1}: {2}", new Object[]{ dt/1000, key, result.toString()}); - result= op.doOp(key); - synchronized ( this ) { - times.put( key, t0 ); - results.put( key, result ); - } - } else { - logger.log(Level.FINE, "using cached value for {0}: {1}", new Object[]{key, result.toString()}); - } - return result; - } - - public synchronized void reset() { - times.clear(); - results.clear(); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/FSTreeModel.java b/dasCoreUtil/src/org/das2/util/filesystem/FSTreeModel.java deleted file mode 100644 index d7dd65521..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/FSTreeModel.java +++ /dev/null @@ -1,262 +0,0 @@ - -package org.das2.util.filesystem; - -import java.awt.Dimension; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.ConsoleHandler; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JOptionPane; -import javax.swing.JScrollPane; -import javax.swing.JTree; -import javax.swing.SwingUtilities; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.MutableTreeNode; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; -import org.das2.util.LoggerManager; - -/** - * present a FileSystem as a TreeModel to display in JTrees. - * @author jbf - */ -public class FSTreeModel extends DefaultTreeModel { - - private static final Logger logger= LoggerManager.getLogger( "das2.filesystem.fstree"); - public static final String PENDING_NOTE = " PENDING"; - - FileSystem fs; - List listCachePath= new ArrayList(); - Map listCachePendingFolders= new HashMap(); - Map listCache= new HashMap(); - - public FSTreeModel(FileSystem fs) { - super( new FSTreeNode( "/",fs.getRootURI().toString()) ); - this.fs = fs; - } - - private static class FSTreeNode extends DefaultMutableTreeNode { - String path; - String label; - boolean pending; - FSTreeNode( String fileSystemPath, String label ) { - this.path= fileSystemPath; - this.label= label; - this.pending= false; - } - @Override - public String toString() { - return this.label + ( pending ? PENDING_NOTE : "" ); - } - public String getFileSystemPath() { - return this.path; - } - - public boolean isPending() { - return pending; - } - public void setPending( boolean pending ) { - this.pending= pending; - } - } - - @Override - public boolean isLeaf(Object node) { - boolean isFolder= ((FSTreeNode)node).path.endsWith("/"); - logger.log(Level.FINEST, "isLeaf({0}) -> {1}", new Object[] { node, !isFolder } ); - return !isFolder; - } - - @Override - public int getChildCount(Object parent) { - int count= getChildren(parent).length; - logger.log(Level.FINER, "getChildCount({0}) -> {1}", new Object[] { parent, count } ) ; - return count; - } - - @Override - public Object getChild(Object parent, int index) { - Object[] kids= getChildren(parent); - logger.log(Level.FINEST, "getChild({0},{1}) -> {2}", new Object[]{parent, index,kids[index] } ); - return kids[index]; - } - - boolean stopTest= false; - - private void listingImmediately( final Object listCachePendingFolder ) { - logger.log(Level.FINE, "listingImmediatey({0})", new Object[]{ listCachePendingFolder } ); - try { - final String folder= folderForNode(listCachePendingFolder); - - long t0= System.currentTimeMillis(); - logger.log(Level.FINE, "listImmediately {0}", folder); - final String[] folderKids= fs.listDirectory(folder); - logger.fine( String.format( "done in %5.2f sec: listImmediately %s", (System.currentTimeMillis()-t0)/1000.0, folder ) ); - final DefaultMutableTreeNode[] listCache1 = new DefaultMutableTreeNode[folderKids.length]; - final int[] nodes= new int[folderKids.length]; - for ( int i=0; i array[{1}]", new Object[]{ listCachePath.get(listCachePath.size()-1), nodes.length } ); - - for (MutableTreeNode rm1 : rm) { - FSTreeModel.this.removeNodeFromParent(rm1); - } - for ( int i=0; i {1}", new Object[]{parent, result}); - return result; - - } else { - listCache.put( key, new DefaultMutableTreeNode[] { } ); - listCachePendingFolders.put( key, theFolder ); - - boolean async= true; - if ( async ) { - if ( theFolder.equals("/") ) { - listCachePath.clear(); - listCachePath.add( new TreePath( fs ) ); - } else { - listCachePath.add( new TreePath( parent ) ); - } - - startListing(parent); - result= listCache.get( key ); - logger.log( Level.FINEST, "getChildren({0}) -> {1}", new Object[]{parent, result}); - return result; - } else { - - listingImmediately(parent); - result= listCache.get( key ); - logger.log( Level.FINEST, "getChildren({0}) -> {1}", new Object[]{parent, result}); - return result; - } - - } - } - } - - - @Override - public int getIndexOfChild(Object parent, Object child) { - Object[] cc= getChildren(parent); - int result= -1; - for ( int i=0; i {2}", new Object[]{parent, child, result }); - return result; - } - - public static void main( String[] args ) throws FileNotFoundException, UnknownHostException, FileSystem.FileSystemOfflineException { - logger.setLevel(Level.FINE); - for ( Handler h: logger.getHandlers() ) { - logger.removeHandler(h); - } - Handler h= new ConsoleHandler(); - h.setLevel(Level.FINE); - logger.addHandler(h ); - //FileSystem fs= FileSystem.create("file:///home/jbf/tmp/"); - //FileSystem fs= FileSystem.create("http://autoplot.org/data/vap/"); - //FileSystem fs= FileSystem.create("http://emfisis.physics.uiowa.edu/pub/jyds/"); - FileSystem fs= FileSystem.create("http://sarahandjeremy.net/~jbf/"); - TreeModel tm= new FSTreeModel(fs) ; - JTree mytree= new JTree( tm ); - mytree.setMinimumSize( new Dimension(400,600) ); - mytree.setPreferredSize( new Dimension(400,600) ); - JOptionPane.showMessageDialog( null, new JScrollPane(mytree), "Test FSTREE", JOptionPane.INFORMATION_MESSAGE ); - - } -} - diff --git a/dasCoreUtil/src/org/das2/util/filesystem/FTPFileSystem.java b/dasCoreUtil/src/org/das2/util/filesystem/FTPFileSystem.java deleted file mode 100644 index 8df98ba74..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/FTPFileSystem.java +++ /dev/null @@ -1,176 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * FTPFileSystem.java - * - * Created on August 17, 2005, 3:33 PM - * - */ - -package org.das2.util.filesystem; - -import org.das2.util.monitor.ProgressMonitor; -import org.das2.util.monitor.NullProgressMonitor; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URI; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; - -/** - * This original FtpFileSystem implementation is based on Java's FTP client. Note, Autoplot uses a third-party library because - * of limitations of the Java implementation. See DataSource project of Autoplot. TODO: like what? - * - * @author Jeremy - */ -public class FTPFileSystem extends WebFileSystem { - FTPFileSystem( URI root ) { - super( root, localRoot(root) ); - } - - @Override - public boolean isDirectory(String filename) { - return filename.endsWith("/"); - } - - private String[] parseLsl( String dir, File listing ) throws IOException { - InputStream in= new FileInputStream( listing ); - - BufferedReader reader=null; - List result= new ArrayList( 20 ); - - try { - reader= new BufferedReader( new InputStreamReader( in ) ); - - String aline= reader.readLine(); - - boolean done= aline==null; - - String types="d-"; - - long bytesRead= 0; - long totalSize; - long sumSize=0; - - while ( ! done ) { - - bytesRead= bytesRead+ aline.length() + 1; - - aline= aline.trim(); - - if ( aline.length() == 0 ) { - done=true; - } else { - - char type= aline.charAt(0); - if ( type == 't' ) { - if ( aline.indexOf( "total" ) == 0 ) { - //totalSize= Long.parseLong( aline.substring( 5 ).trim() ); - } - } - - if ( types.indexOf(type)!=-1 ) { - int i= aline.lastIndexOf( ' ' ); - String name= aline.substring( i+1 ); - //long size= Long.parseLong( aline.substring( 31, 31+12 ) ); // tested on: linux - boolean isFolder= type=='d'; - - result.add( name + ( isFolder ? "/" : "" ) ); - - //sumSize= sumSize + size; - - } - - aline= reader.readLine(); - done= aline==null; - - } // while - - } - } finally { - if ( reader!=null ) reader.close(); - } - return (String[])result.toArray(new String[result.size()]); - } - - @Override - public String[] listDirectory(String directory) { - directory= toCanonicalFolderName( directory ); - - try { - File f= new File( localRoot, directory ); - try { - FileSystemUtil.maybeMkdirs(f); - } catch ( IOException ex ) { - throw new IllegalArgumentException("unable to mkdirs "+f ); - } - File listing= new File( localRoot, directory + ".listing" ); - if ( !listing.canRead() ) { - File partFile= listing; - downloadFile( directory, listing, getPartFile(listing), new NullProgressMonitor() ); - } - listing.deleteOnExit(); - return parseLsl( directory, listing ); - } catch ( IOException e ) { - throw new RuntimeException(e); - } - } - - @Override - protected void downloadFile(String filename, java.io.File targetFile, File partFile, ProgressMonitor monitor ) throws java.io.IOException { - FileOutputStream out=null; - InputStream is= null; - try { - filename= toCanonicalFilename( filename ); - URL url= new URL( root + filename.substring(1) ); - - URLConnection urlc = url.openConnection(); - - int i= urlc.getContentLength(); - monitor.setTaskSize( i ); - out= new FileOutputStream( partFile ); - loggerUrl.log(Level.FINE, "getInputStream {0}", new Object[] { urlc.getURL() } ); - is = urlc.getInputStream(); // To download - monitor.started(); - copyStream(is, out, monitor ); - monitor.finished(); - out.close(); - is.close(); - if ( ! partFile.renameTo( targetFile ) ) { - throw new IllegalArgumentException("unable to rename "+partFile+" to "+targetFile ); - } - } catch ( IOException e ) { - if ( out!=null ) out.close(); - if ( is!=null ) is.close(); - if ( partFile.exists() && ! partFile.delete() ) { - throw new IllegalArgumentException("unable to delete "+partFile ); - } - throw e; - } - - } - -} \ No newline at end of file diff --git a/dasCoreUtil/src/org/das2/util/filesystem/FileObject.java b/dasCoreUtil/src/org/das2/util/filesystem/FileObject.java deleted file mode 100755 index 0c77d3700..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/FileObject.java +++ /dev/null @@ -1,215 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * WebFile.java - * - * Created on May 14, 2004, 10:06 AM - */ - -package org.das2.util.filesystem; - -import org.das2.util.monitor.ProgressMonitor; -import org.das2.util.monitor.NullProgressMonitor; -import java.io.*; -import java.nio.channels.*; - -/** - *

    Class for describing and accessing files in file systems. This - * is similar to java.io.File, except that it can describe files on remote - * file systems like an ftp site.

    - * - *

    Note: this is modelled after the NetBeans fileSystem FileObject, with the - * thought that we might use it later.

    - * - * @author Jeremy - */ -public abstract class FileObject { - - /** - * returns true if the file can be read by the client. - * @return true if the file can be read (see getInputStream) - */ - public abstract boolean canRead(); - - /** - * returns objects within a folder. - * @return an array of all FileObjects with the folder. - * @throws java.io.IOException - */ - public abstract FileObject[] getChildren() throws IOException; - - /** - * opens an inputStream, perhaps transferring the file to a - * cache first. - * @param monitor for monitoring the download. The monitor won't be used when the access - * is immediate, for example with local FileObjects. - * @throws FileNotFoundException if the file doesn't exist. - * @throws IOException - * @return an InputStream - */ - public abstract InputStream getInputStream( ProgressMonitor monitor ) throws FileNotFoundException, IOException; - - /** - * opens an inputStream, perhaps transferring the file to a - * cache first. Note no monitor is used but this may block at sub-interactive - * time until the file is downloaded. - * - * @throws FileNotFoundException if the file doesn't exist. - * @throws IOException - * @return an InputStream - */ - public InputStream getInputStream() throws FileNotFoundException, IOException { - return getInputStream( new NullProgressMonitor() ); - } - - /** - * opens a Channel, perhaps transferring the file to a local cache first. monitor - * is used to monitor the download. - * @param monitor for monitoring the download. The monitor won't be used when the access - * is immediate, for example with local FileObjects. - * @throws FileNotFoundException if the file doesn't exist. - * @throws IOException - * @return a java.nio.channels.Channel for fast IO reads. - */ - public abstract ReadableByteChannel getChannel( ProgressMonitor monitor ) throws FileNotFoundException, IOException; - - /** - * opens a Channel, but without a monitor. Note this may block at sub-interactive time - * if the FileObject needs to be downloaded before access. - * @throws FileNotFoundException if the file doesn't exist. - * @throws IOException - * @return a java.nio.channels.Channel for fast IO reads. - */ - public ReadableByteChannel getChannel() throws FileNotFoundException, IOException { - return getChannel( new NullProgressMonitor() ); - } - - /** - * gets a File object that can be opened by the client. This may download a remote file, so - * a progress monitor can be used to monitor the download. - * @return a reference to a File that can be opened. - * @param monitor for monitoring the download. The monitor won't be used when the access - * is immediate, for example with local FileObjects. - * @throws java.io.FileNotFoundException if the file doesn't exist. - * @throws IOException if the file cannot be made local - * @throws NullPointerException if the monitor is null. - */ - public abstract File getFile( ProgressMonitor monitor ) throws FileNotFoundException, IOException; - - /** - * gets a File object that can be opened by the client. Note this may block at sub-interactive time - * if the remote file needs to be downloaded before access. - * @return a reference to a File that can be opened. - * @throws java.io.FileNotFoundException if the file doesn't exist. - */ - public File getFile() throws FileNotFoundException, IOException { - return getFile( new NullProgressMonitor() ); - } - - /** - * returns the parent FileObject (a folder). - * If the fileObject is root, then null should be returned. - * @return the parent folder of this object. - */ - public abstract FileObject getParent(); - - /** - * returns the size of the file. - * @return the size in bytes of the file, and -1 if the size is unknown. - */ - public abstract long getSize(); - - /** - * returns true if the file is a data file that to be used - * reading or writing data. (And not a folder.) - * @return true if the file is a data file - */ - public abstract boolean isData(); - - /** - * indicates the type of FileObject - * @return true if the object is a folder (directory). - */ - public abstract boolean isFolder(); - - /** - * true is the file is read-only. - * @return true if the file is read-only - */ - public abstract boolean isReadOnly(); - - /** - * returns true if this is the root of the filesystem it came from. - * @return true if this is the root of the filesystem it came from. - */ - public abstract boolean isRoot(); - - /** - * returns true if the file is locally available, meaning clients can - * call getFile() and the readable File reference will be available in - * interactive time. Note that isLocal does not imply exists(). Also, - * This may result in side effects such as a website hit. - * @return true if the file is locally available and fresh - */ - public abstract boolean isLocal(); - - /** - * returns true if the file exists. This may have the side effect of - * downloading the file. - * @return true if the file exists - */ - public abstract boolean exists(); - - /** - * returns the canonical name of the file within the filesystem. For example, - * in the local filesystem /mnt/data/steven/, /mnt/data/steven/jan/01.dat - * would be /jan/01.dat. - * - * For example, /a/b/c.dat. - * @return the name of the file within the FileSystem. - */ - public abstract String getNameExt(); - - /** - * returns the Date when the file was last modified. or new Date(0L) if the date is - * not available. - * - * @return the last modified Date, or new Date(0) if it is not available. - */ - public abstract java.util.Date lastModified(); - - - /** - * returns extra capabilities, such as writing to the filesystem. - * @param T - * @return - */ - public T getCapability( Class clazz ) { - return null; - } - - /** - * remove the local file in the cache, if any. This can/will be overriden by other - * filesystems. - * @return true if the action was handled. (Local files are not deleted.) - */ - public boolean removeLocalFile() { - return true; - } - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/FileSystem.java b/dasCoreUtil/src/org/das2/util/filesystem/FileSystem.java deleted file mode 100755 index be7ce2aaf..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/FileSystem.java +++ /dev/null @@ -1,773 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * FileSystem.java - * - * Created on May 14, 2004, 12:43 PM - */ - -package org.das2.util.filesystem; - -import java.io.*; -import java.net.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.das2.util.DefaultExceptionHandler; -import org.das2.util.ExceptionHandler; -import org.das2.util.FileUtil; -import org.das2.util.ThrowRuntimeExceptionHandler; -import org.das2.util.monitor.NullProgressMonitor; -import org.das2.util.monitor.ProgressMonitor; - -/** - * Filesystems provide an abstraction layer so that clients can access - * any heirarchy of files in a implementation-independent way. For example, - * remote filesystems accessible via http are accessible through the same - * interface as a local filesystem. - * - * @author Jeremy - */ - - -public abstract class FileSystem { - - URI root; - protected static final Logger logger= org.das2.util.LoggerManager.getLogger( "das2.filesystem" ); - - /** - * this logger is for opening connections to remote sites. - */ - protected static final Logger loggerUrl= org.das2.util.LoggerManager.getLogger( "das2.url" ); - - public static class FileSystemOfflineException extends IOException { - public FileSystemOfflineException() { - super(); - } - public FileSystemOfflineException( String message ) { - super( message ); - } - public FileSystemOfflineException( IOException e ) { - super( e.getMessage() ); - initCause(e); - } - public FileSystemOfflineException( IOException e, URI root ) { - super( e.getMessage() + ": "+root ); - initCause(e); - } - } - - private static final Map instances= Collections.synchronizedMap( new HashMap() ); - - /** - * non-null means filesystem is bring created and we should wait. - */ - private static final Map blocks= Collections.synchronizedMap( new HashMap() ); - - /** - * - * @param root - * @throws java.net.UnknownHostException - * @throws java.io.FileNotFoundException - * @return the FileSystem - * @throws org.das2.util.filesystem.FileSystem.FileSystemOfflineException - * @throws IllegalArgumentException if the url cannot be converted to a URI. - * @deprecated use create( URI root ) instead. - */ - public static FileSystem create(URL root) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - try { - return create( root.toURI(), new NullProgressMonitor() ); - } catch (URISyntaxException ex) { - throw new IllegalArgumentException(ex); - } - } - - /** - * convenient method that converts string like "http://das2.org/" into a URI. - * @param s string representation of URI, like "http://das2.org/" or "file:///tmp/" - * @return FileSystem object. - * @throws org.das2.util.filesystem.FileSystem.FileSystemOfflineException - * @throws UnknownHostException - * @throws FileNotFoundException - */ - public static FileSystem create( String s ) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - return create( s, new NullProgressMonitor() ); - } - - /** - * convenient method that converts string like "http://das2.org/" into a URI. - * See http://stackoverflow.com/questions/573184/java-convert-string-to-valid-uri-object , about halfway down for Feb 21 '09 answer. - * @param s string representation of URI, like "http://das2.org/" or "file:///tmp/" - * @param mon monitor progress. For most FS types this is instantaneous, but for zip this can take sub-interactive time. - * @return FileSystem object. - * @throws org.das2.util.filesystem.FileSystem.FileSystemOfflineException - * @throws UnknownHostException - * @throws FileNotFoundException - */ - public static FileSystem create( String s, ProgressMonitor mon ) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - String[] parts = s.split(":",2); - if ( parts.length==1 ) { - throw new IllegalArgumentException( "name must start with scheme like 'file:', no colon found"); - } - try { - return create( new URI( parts[0], parts[1], null ), mon ); - } catch (URISyntaxException ex) { - throw new IllegalArgumentException( ex ); - } - } - - /** - * - * @param root - * @param mon - * @return - * @throws java.net.UnknownHostException - * @throws java.io.FileNotFoundException - * @deprecated use create( URI root, ProgressMonitor mon ) instead. - * @throws org.das2.util.filesystem.FileSystem.FileSystemOfflineException - * @throws IllegalArgumentException if the url cannot be converted to a URI. - * @throws IllegalArgumentException if the local root does not exist. - */ - public static FileSystem create( URL root, ProgressMonitor mon ) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - try { - return create(root.toURI(), mon); - } catch (URISyntaxException ex) { - throw new IllegalArgumentException(ex); - } - } - - /** - * creates a FileSystem, removing and recreating it if it was in the cache. - * @param root - * @return - * @throws org.das2.util.filesystem.FileSystem.FileSystemOfflineException - * @throws UnknownHostException - * @throws FileNotFoundException if the remote folder is not found. - */ - public static FileSystem recreate( URI root ) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - return recreate( root, new NullProgressMonitor() ); - } - - /** - * Creates a FileSystem by parsing the URI and creating the correct FS type. - * Presently, file, http, and ftp are supported. If the URI contains a folder - * ending in .zip and a FileSystemFactory is registered as handling .zip, then - * The zip file will be transferred and the zip file mounted. - * - * @param root - * @return - * @throws org.das2.util.filesystem.FileSystem.FileSystemOfflineException - * @throws java.net.UnknownHostException - * @throws java.io.FileNotFoundException - * @throws IllegalArgumentException if the URI must be converted to a URL, but cannot. - * @throws IllegalArgumentException if the local root does not exist. - */ - public static FileSystem create(URI root) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - return create(root, new NullProgressMonitor()); - } - - /** - * creates a FileSystem, removing and recreating it if it was in the cache. - * @param root - * @param mon - * @return - * @throws org.das2.util.filesystem.FileSystem.FileSystemOfflineException - * @throws UnknownHostException - * @throws FileNotFoundException if the file is not found on the remote host. - */ - public static FileSystem recreate( URI root, ProgressMonitor mon ) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - //TODO: there may be a need to synchronize here - FileSystem result= instances.get(root); - if ( result!=null ) { - return instances.remove(root); - } - return create( root, mon ); - } - - /** - * allow factories to peek, so they can see if there is a parent that is offline. - * @param root - * @return - */ - public static FileSystem peek( URI root ) { - root = toCanonicalFolderName(root); - FileSystem result = instances.get(root); - return result; - } - - /** - * remove all the cached FileSystem instances. - */ - public synchronized static void reset() { - instances.clear(); - blocks.clear(); - KeyChain.getDefault().clearAll(); - if ( !FileUtil.deleteWithinFileTree( settings().getLocalCacheDir(), ".listing" ) ) { - logger.log(Level.WARNING, "delete all .listing files within tree {0} failed.", settings().getLocalCacheDir()); - } - } - - /** - * remove all the cached FileSystem instances. - * @param fs the filesystem - */ - public synchronized static void reset( FileSystem fs ) { - instances.remove(fs.getRootURI()); - blocks.remove(fs.getRootURI()); - KeyChain.getDefault().clearUserPassword(fs.getRootURI()); - if ( !FileUtil.deleteWithinFileTree( fs.getLocalRoot(), ".listing" ) ) { - logger.log(Level.WARNING, "delete all .listing files within tree {0} failed.", settings().getLocalCacheDir()); - } - } - - - /** - * Creates a FileSystem by parsing the URI and creating the correct FS type. - * Presently, file, http, and ftp are supported. If the URI contains a folder - * ending in .zip and a FileSystemFactory is registered as handling .zip, then - * The zip file will be transferred and the zip file mounted. - * - * @param root the URI, like URI("http://das2.org/") or URI("file:///tmp/") - * @param mon monitor progress. For most FS types this is instantaneous, but for zip this can take sub-interactive time. - * @return the FileSystem implementation - * @throws org.das2.util.filesystem.FileSystem.FileSystemOfflineException - * @throws java.net.UnknownHostException - * @throws java.io.FileNotFoundException - * @throws IllegalArgumentException if the URI must be converted to a URL, but cannot. - * @throws IllegalArgumentException if the local root does not exist. - */ - public static FileSystem create( URI root, ProgressMonitor mon ) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - logger.log(Level.FINER, "request for filesystem {0}", root); - - FileSystem result; - - if ( !root.toString().endsWith("/") ) { - try { - root= new URI( root.toString()+"/" ); - } catch ( URISyntaxException ex ) { - } - } - - result= instances.get(root); - if ( result!=null ) { - return result; - } - - String waitObject; - boolean ishouldwait= false; - synchronized (blocks) { - if ( blocks.containsKey(root) ) { - waitObject= blocks.get(root); - ishouldwait= true; - } else { - waitObject= String.valueOf( Long.valueOf( System.currentTimeMillis() ) ); // just to be sure it's unique. - blocks.put( root, waitObject ); - logger.log(Level.FINE, "created waitObject {0} {1}", new Object[]{waitObject, root}); - } - } - - if ( ishouldwait ) { // wait until the other thread is done. If the other thread doesn't put the result in instances, then there's a problem... - try { - synchronized ( waitObject ) { - logger.log(Level.FINE, "waiting for {0} {1}", new Object[]{waitObject, root}); - waitObject.wait(); //TODO: I witnessed a bug where this was stuck and there were no objects in blocks. - logger.log(Level.FINE, "done waiting for {0}", root); - } - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - - result= instances.get(root); - - if ( result!=null ) { - logger.log( Level.FINE,"using existing filesystem {0}", root ); - return result; - } else { - // assume the other thread told them what was going on. - throw new FileSystemOfflineException("other thread failed to create filesystem."); - } - - } - - FileSystemFactory factory; - if ( root.getPath()!=null && ( root.getPath().contains(".zip") || root.getPath().contains(".ZIP") ) && registry.containsKey("zip") ) { - try { - String surl= FileSystemUtil.fromUri(root); - int i= surl.indexOf(".zip"); - if ( i==-1 ) i= surl.indexOf(".ZIP"); - String[] ss= FileSystem.splitUrl( surl.substring(0,i+4) ); - URI parent = new URI(ss[2]); //getparent - String zipname = ss[3].substring(ss[2].length()); - String subdir = surl.substring(i+4); - FileSystem remote = FileSystem.create(parent); - mon.setProgressMessage("loading zip file"); - File localZipFile = remote.getFileObject(zipname).getFile(mon); - factory = (FileSystemFactory) registry.get("zip"); - FileSystem zipfs = factory.createFileSystem(localZipFile.toURI()); - if ( subdir.equals("") || subdir.equals("/") ) { - result= zipfs; - } else { - result= new SubFileSystem(zipfs, subdir); - } - } catch (UnknownHostException ex ) { - throw ex; - } catch ( FileNotFoundException ex ) { - throw ex; - } catch (URISyntaxException ex) { - //this shouldn't happen - throw new RuntimeException(ex); - } catch (IOException ex) { - throw new FileSystemOfflineException(ex); - } finally { - logger.log( Level.FINE,"created zip new filesystem {0}", root ); - if ( result!=null ) instances.put(root, result); - blocks.remove(root); - } - } else { - factory= (FileSystemFactory) registry.get(root.getScheme()); - } - - if ( factory==null ) { - logger.log(Level.FINE, "releasing {0}", waitObject); - synchronized( waitObject ) { - waitObject.notifyAll(); //TODO: the other threads are going to think it's offline. - } - logger.log(Level.SEVERE, "unsupported protocol: {0}", root); - throw new IllegalArgumentException( "unsupported protocol: "+root ); - - } else { - try { - if ( result==null ) { // if we didn't create it in the zip file part - - result = factory.createFileSystem(root); - - } - - } finally { - logger.log( Level.FINE,"created new filesystem {0}", root ); - if ( result!=null ) instances.put(root, result); - blocks.remove(root); - - logger.log(Level.FINE, "releasing {0}", waitObject); // need to do this in the finally block in case there was an Exception. - synchronized( waitObject ) { - waitObject.notifyAll(); - } - } - - } - - if ( settings.isOffline() && result instanceof WebFileSystem ) { - logger.log( Level.FINE,"filesystem is now offline because of settings {0}", root ); - ((WebFileSystem)result).setOffline(true); - } - - return result; - } - - public static FileSystemSettings settings() { - return settings; - } - - private static FileSystemSettings settings= new FileSystemSettings(); - - private static final HashMap registry; - static { - registry= new HashMap(); - registry.put("file",new LocalFileSystemFactory() ); - registry.put("http",new HttpFileSystemFactory() ); - registry.put("https",new HttpFileSystemFactory() ); - registry.put("ftp",new FtpFileSystemFactory() ); - } - - public static void registerFileSystemFactory( String proto, FileSystemFactory factory ) { - registry.put( proto, factory ); - } - - protected FileSystem( URI root ) { - if ( !root.toString().endsWith("/" ) ) { - String s= root.toString(); - try { - root = new URI(s + "/"); - } catch (URISyntaxException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - throw new RuntimeException(ex); // shouldn't happen - } - } - this.root= root; - } - - public URI getRootURI() { - return root; - } - - private static String getRegexFromGlob( String glob ) { - final String regex= glob.replaceAll("\\.","\\\\.").replaceAll("\\*","\\.\\*").replaceAll("\\?","\\."); - return regex; - } - - - /** - * returns the canonical name /a/b/c.dat of a string that - * contains backslashes and might not have the leading / - * and trailing slashes. Also, double slashes (//) are - * removed. Note this is the name of the FileObject - * within the FileSystem. Folder names will not necessarily - * be suffixed with '/' - * @param filename name - * @return name with \ converted to /, etc. - */ - protected static String toCanonicalFilename( String filename ) { - filename= filename.replaceAll( "\\\\", "/" ); - if ( filename.length()==0 || filename.charAt(0)!='/' ) { - filename= "/"+filename; - } - filename= filename.replaceAll( "//", "/" ); - return filename; - } - - protected static String toCanonicalFolderName( String name ) { - name= toCanonicalFilename( name ); - if ( !name.endsWith("/") ) name= name + "/"; - return name; - } - - protected static URI toCanonicalFolderName( URI name ) { - try { - String sname= name.toString(); - if ( !sname.endsWith("/") ) sname= sname + "/"; - return new URI(sname); - } catch ( URISyntaxException ex ) { - throw new RuntimeException(ex); - } - } - - /** - * return the FileObject that corresponds to the name. - * @param filename the file name within the filesystem - * @return the FileObject - */ - abstract public FileObject getFileObject( String filename ); - - abstract public boolean isDirectory( String filename ) throws IOException; - - /** - * returns a list of the names of the files in a directory. Names ending - * in "/" are themselves directories, and the "/" is not part of the name. - * This is optional, and a directory may or may not be tagged with the trailing - * slash. - * @param directory the directory name within the filesystem. - * @return list of files and folders within the filesystem. - * @throws java.io.IOException - */ - abstract public String[] listDirectory( String directory ) throws IOException; - - /** - * returns a list of the names of the files in a directory. Names ending - * in "/" are themselves directories, and the "/" is not part of the name. - * This is optional, and a directory may or may not be tagged with the trailing - * slash. (REALLY? This needs to be fixed...) - * - * @param directory the directory name within the filesystem. - * @param monitor a progress monitor for the task. - * @return list of files and folders within the filesystem. - * @throws java.io.IOException - */ - public String[] listDirectory( String directory, ProgressMonitor monitor ) throws IOException { - monitor.started(); - monitor.setProgressMessage( "listing "+directory ); - try { - String[] result= listDirectory( directory ); - return result; - } finally { - monitor.finished(); - } - } - - /** - * returns a list of the names of the files in a directory that match regex. - * Trailing slashes on directory names are not part of the name and need - * not be part of the regex. - * Note regex is a regular expression (.*\.dat), not a glob (*.dat) - * @param directory the directory - * @param regex regular expression - * @return - * @throws IOException - */ - abstract public String[] listDirectory( String directory, String regex ) throws IOException; - - /** - * returns a list of the names of the files in a directory that match regex. - * Trailing slashes on directory names are not part of the name and need - * not be part of the regex. - * Note regex is a regular expression (.*\.dat), not a glob (*.dat) - * - * @param directory - * @param regex regular expression that must be matched, or null. - * @param monitor progress monitor for the task. - * @return - * @throws IOException - */ - public String[] listDirectory( String directory, String regex, ProgressMonitor monitor ) throws IOException { - monitor.started(); - monitor.setProgressMessage( "listing "+directory ); - try { - String[] result= listDirectory( directory, regex ); - return result; - } finally { - monitor.finished(); - } - } - - /** - * do a deep listing of directories, resolving wildcards along the way. Note this - * can be quite expensive, so be careful when levels are too deep. - * @param directory - * @param regex regular expression (.*\.dat) (not a glob like *.dat). - * @return - * @throws IOException - */ - public String[] listDirectoryDeep( String directory, String regex ) throws IOException { - List result= new ArrayList(); - int i= regex.indexOf( "/" ); - logger.fine( String.format( "listDirectoryDeep(%s,%s)\n",directory,regex) ); - if ( i==-1 ) { - return listDirectory( directory, regex ); - } else if ( i==0 ) { - return listDirectory( directory, regex.substring(1) ); - } else { - String[] ss= listDirectory( directory, regex.substring(0,i) ); - if ( ss.length==1 && ss[0].length()==(i+1) && ss[0].substring(0,i).equals(regex.substring(0,i) ) ) { - String dir= ss[0]; - String[] ss1= listDirectoryDeep( directory + dir, regex.substring(dir.length()) ); - for ( int j=0; j des ) { - String[] result= new String[des.size()]; - Collection ddes= des.values(); - int i=0; - for ( DirectoryEntry ent : ddes ) { - result[i]= ent.name + ( ent.type=='d' ? "/" : "" ); - i=i+1; - } - return result; - } - - /** - * return a copy of all cached filesystems - * @return - */ - public static FileSystem[] peekInstances() { - int s= instances.size(); - return instances.values().toArray( new FileSystem[s] ); - } -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/FileSystemFactory.java b/dasCoreUtil/src/org/das2/util/filesystem/FileSystemFactory.java deleted file mode 100644 index 69e4db23c..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/FileSystemFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * FileSystemFactory.java - * - * Created on November 15, 2007, 9:26 AM - */ - -package org.das2.util.filesystem; - -import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; -import java.net.URI; -import java.net.UnknownHostException; -import java.io.FileNotFoundException; - -/** - * creates a new instance of a type of filesystem - * @author jbf - */ -public interface FileSystemFactory { - FileSystem createFileSystem( URI root ) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException; -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/FileSystemSettings.java b/dasCoreUtil/src/org/das2/util/filesystem/FileSystemSettings.java deleted file mode 100755 index 33383c460..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/FileSystemSettings.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package org.das2.util.filesystem; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.io.File; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * controls for file systems. - * @author jbf - */ -public class FileSystemSettings { - - private static final Logger logger= org.das2.util.LoggerManager.getLogger("org.das2.util.filesystem"); - - /** - * check the security manager to see if all permissions are allowed, - * True indicates is not an applet running in a sandbox. - * - * copy of DasAppliction.hasAllPermission - * @return true if all permissions are allowed - */ - public static boolean hasAllPermission() { - try { - if ( restrictPermission==true ) return false; - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission(new java.security.AllPermission()); - } - return true; - } catch ( SecurityException ex ) { - return false; - } - } - - private static boolean restrictPermission= false; - - /** - * true means don't attempt to gain access to applet-restricted functions. - * @param v true means don't attempt to gain access to applet-restricted functions. - * @see org.das2.DasApplication#setRestrictPermission - */ - public static void setRestrictPermission( boolean v ) { - restrictPermission= v; - } - - /** - * this should only be called by FileSystem. Use FileSystem.settings(). - */ - protected FileSystemSettings() { - File local; - if ( !hasAllPermission() ) { - local= new File("applet_mode"); // this should not be opened. - } else { - if (System.getProperty("user.name").equals("Web")) { - local = new File("/tmp"); - } else { - local = new File(System.getProperty("user.home")); - } - local = new File(local, ".das2/fsCache/wfs/"); - } - localCacheDir = local; - } - - /** - * return the connection timeout in milliseconds. - * @return - */ - public int getConnectTimeoutMs() { - return 5000; - } - - // NOTE WebFileSystem contains some settings as well!! - - public enum Persistence { - /** - * No persistence. No files are cached locally. - */ - NONE, - /** - * Within a session, files are cached locally. This is the default. - */ - SESSION, - /** - * Files persist until a new version is available on the remote cache. - */ - EXPIRES, - /** - * Files persist indefinitely, and the server is only contacted when a file - * is not available locally. - */ - ALWAYS - } - - protected File localCacheDir = null; - /** - * setting for the location of where the local cache is kept. - */ - public static final String PROP_LOCALCACHEDIR = "localCacheDir"; - - /** - * setting for the location of where the local cache is kept. - * @return File that is the local cache directory. - */ - public File getLocalCacheDir() { - return localCacheDir; - } - - public void setLocalCacheDir(File localCacheDir) { - File oldLocalCacheDir = this.localCacheDir; - this.localCacheDir = localCacheDir; - logger.log( Level.FINE, "setLocalCacheDir({0})", localCacheDir); - propertyChangeSupport.firePropertyChange(PROP_LOCALCACHEDIR, oldLocalCacheDir, localCacheDir); - } - protected Persistence persistence = Persistence.SESSION; - /** - * setting for how long files should be kept and using in the cache. - */ - public static final String PROP_PERSISTENCE = "persistence"; - - /** - * get the setting for how long files should be kept and using in the cache, - * e.g. Persistence.SESSION means during the session. - * - * @return the setting - */ - public Persistence getPersistence() { - return persistence; - } - - public void setPersistence(Persistence persistence) { - Persistence oldPersistence = this.persistence; - this.persistence = persistence; - propertyChangeSupport.firePropertyChange(PROP_PERSISTENCE, oldPersistence, persistence); - } - protected boolean allowOffline = true; - /** - * allow use of persistent, cached files when the file system is not accessible. - * FileSystem implementations will throw FileNotFound exception when remote - * resources are not available, and FileSystemOfflineExceptions are not - * thrown. - */ - public static final String PROP_ALLOWOFFLINE = "allowOffline"; - - public boolean isAllowOffline() { - return allowOffline; - } - - public void setAllowOffline(boolean allowOffline) { - boolean oldAllowOffline = allowOffline; - this.allowOffline = allowOffline; - propertyChangeSupport.firePropertyChange(PROP_ALLOWOFFLINE, oldAllowOffline, allowOffline); - } - - public static final String PROP_OFFLINE= "offline"; - - private boolean offline= false; - - public boolean isOffline() { - return offline; - } - - /** - * If true, then force the filesystems to be offline. If false, then use each filesystem's status. - * FileSystem.reset() should be called after this. - * @param offline - */ - public void setOffline( boolean offline ) { - boolean v= this.offline; - this.offline= offline; - propertyChangeSupport.firePropertyChange( PROP_OFFLINE, v, offline); - } - - /** - * the longest amount of time we'll wait for an external process to make progress downloading. - */ - protected static final long allowableExternalIdleMs= 60000; - - private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); - - public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { - propertyChangeSupport.addPropertyChangeListener(listener); - } - - public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { - propertyChangeSupport.removePropertyChangeListener(listener); - } - - public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { - propertyChangeSupport.removePropertyChangeListener(propertyName, listener); - } - - public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { - propertyChangeSupport.addPropertyChangeListener(propertyName, listener); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/FileSystemUtil.java b/dasCoreUtil/src/org/das2/util/filesystem/FileSystemUtil.java deleted file mode 100644 index 545f7fbf1..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/FileSystemUtil.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.util.filesystem; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.ByteBuffer; -import java.nio.channels.Channel; -import java.nio.channels.Channels; -import java.nio.channels.FileChannel; -import java.nio.channels.ReadableByteChannel; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.GZIPInputStream; -import org.das2.util.LoggerManager; - -/** - * - * @author jbf - */ -public class FileSystemUtil { - - private final static Logger logger= LoggerManager.getLogger( "das2.filesystem" ); - /** - * Dump the contents of the InputStream into a file. If the inputStream comes - * from a file, then java.nio is used to transfer the data quickly. - * @param in - * @param f - * @throws java.io.FileNotFoundException - * @throws java.io.IOException - */ - public static void dumpToFile( InputStream in, File f ) throws FileNotFoundException, IOException { - - ReadableByteChannel ic = Channels.newChannel(in); - FileChannel oc=null; - try { - oc= new FileOutputStream(f).getChannel(); - if ( ic instanceof FileChannel ) { - FileChannel fic= (FileChannel)ic; - fic.transferTo(0, fic.size(), oc); - } else { - ByteBuffer buf= ByteBuffer.allocateDirect( 16*1024 ); - while ( ic.read(buf) >= 0 || buf.position() != 0 ) { - buf.flip(); - oc.write(buf); - buf.compact(); - } - } - } finally { - closeResources( oc, ic ); - } - } - - /** - * encapsulate the logic that cleanly closes both channels. - * @param chout channel that needs closing - * @param chin channel that needs closing. - * @throws java.io.IOException - */ - private static void closeResources( Channel chout, Channel chin ) throws IOException { - if ( chout!=null && chout.isOpen() ) { - try { - chout.close(); - } finally { - } - } - if ( chin!=null && chin.isOpen() ) { - try { - chin.close(); - } finally { - } - } - } - - /** - * un-gzip the file. This is similar to the unix gunzip command. - * @param fz zipped input file - * @param file unzipped destination file - * @throws java.io.IOException - */ - public static void unzip( File fz, File file) throws IOException { - GZIPInputStream in= null; - OutputStream out= null; - try { - in = new GZIPInputStream(new FileInputStream(fz)); - out = new FileOutputStream(file); - - byte[] buf = new byte[1024]; //TODO: use FileChannel - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - } finally { - try { - if ( in!=null ) in.close(); - } finally { - if ( out!=null ) out.close(); - } - } - } - - /** - * return null if the URI is not cacheable, or the URI of the parent if it is. - * - * For example, - *
    -     * {@code
    -     * URI uri= new URL("http://autoplot.org/data/demos2011.xml").toURI();
    -     * URI parentUri= FileSystemUtil.isCacheable( uri );
    -     * if ( parentUri ) {
    -     *     FileSystem fd= FileSystem.create(parentUri);
    -     *     FileObject fo= fd.getFileObject( ruri.relativize(parentUri).toString() );
    -     *     in= fo.getInputStream();
    -     * }
    -     * }
    -     * 
    - * @param ruri - * @return the URI of the parent, or null. - */ - public static URI isCacheable(URI ruri) { - if ( ruri.getQuery()==null && ruri.getPath().length()>1 && !ruri.getPath().endsWith("/") ) { - String s= ruri.toString(); - int i= s.lastIndexOf("/"); - String folder= s.substring(0,i); - try { - //TODO: actually list the parent to make sure it contains the child. - return new URL(folder).toURI(); - } catch (URISyntaxException ex) { - logger.log( Level.SEVERE, "couldn't create URI from parent URL", ex); - return null; - } catch (MalformedURLException ex) { - logger.log( Level.SEVERE, "url caused malformed URL exception when creating parent URL: ", ex); - return null; - } - } else { - return null; - } - } - - /** - * return the parent of the URI, or null if this is not possible. - * @param ruri - * @return - */ - public static URI getParentUri( URI ruri ) { - if ( ruri.getQuery()==null && ruri.getPath().length()>1 ) { - String s= ruri.toString(); - int i= s.length(); - if ( s.charAt(i-1)=='/') { - i=i-1; - } - i= s.lastIndexOf("/",i-1); - String folder= s.substring(0,i); - try { - return new URL(folder).toURI(); - } catch (URISyntaxException ex) { - logger.log( Level.SEVERE, "couldn't create URI from parent URL", ex); - return null; - } catch (MalformedURLException ex) { - logger.log( Level.SEVERE, "url caused malformed URL exception when creating parent URL: ", ex); - return null; - } - } else { - return null; - } - } - - /** - * create the file folder if it does not exist. Throw an IOException if it failed. - * @param file - * @throws IOException - */ - public static void maybeMkdirs( File file ) throws IOException { - if ( file.exists() ) return; - if ( !file.mkdirs() ) { - if ( file.exists() ) { - // somebody else made the file. - } else { - logger.log( Level.SEVERE, "Unable to mkdirs {0}", file); // print it in case the IOException is misinterpretted. - throw new IOException( "Unable to mkdirs "+file ); - } - } - } - - /** - * convert " " to "%20", etc, by looking for and encoding illegal characters. - * We can't just aggressively convert... - * @param surl - * @return - */ - private static String uriEncode(String surl) { - - //surl = surl.replaceAll("#", "%23" ); - surl = surl.replaceAll("%", "%25" ); // see above - surl = surl.replaceAll(" ", "%20" ); - //surl = surl.replaceAll("&", "%26" ); - //surl = surl.replaceAll("\\+", "%2B" ); - //surl = surl.replaceAll("/", "%2F" ); - //surl = surl.replaceAll(":", "%3A" ); - //surl = surl.replaceAll(";", "%3B" ); - surl = surl.replaceAll("<", "%3C"); - surl = surl.replaceAll(">", "%3E"); - //surl = surl.replaceAll("\\?", "%3F" ); - surl = surl.replaceAll("\\[", "%5B"); // Windows appends these in temporary downloadf rte_1495358356 - surl = surl.replaceAll("\\]", "%5D"); - - return surl; - } - - /** - * convert "%20" to " ", etc, by using URLDecoder and catching the UnsupportedEncodingException that will never occur. - * @param s - * @return - */ - private static String uriDecode(String s) { - String surl= s; -// if ( surl.contains("+") && !surl.contains("%20") ) { // legacy -// surl = surl.replaceAll("+", " " ); -// } - surl = surl.replaceAll("%20", " " ); - //surl = surl.replaceAll("%23", "#" ); - //surl = surl.replaceAll("%26", "&" ); - //surl = surl.replaceAll("%2B", "+" ); - //surl = surl.replaceAll("%2F", "/" ); - //surl = surl.replaceAll("%3A", ":" ); - //surl = surl.replaceAll("%3B", ";" ); - surl = surl.replaceAll("%3C", "<" ); - surl = surl.replaceAll("%3E", ">" ); - //surl = surl.replaceAll("%3F", "?" ); - surl = surl.replaceAll("%5B", "\\[" ); // Windows appends these in temporary downloadf rte_1495358356 - surl = surl.replaceAll("%5D", "\\]" ); - surl = surl.replaceAll("%25", "%" ); - - return surl; - } - - /** - * canonical method for converting URI to human-readable string, containing - * spaces and other illegal characters. Note pluses in the query part - * are interpreted as spaces. - * This was borrowed from Autoplot's URISplit code. - * @param uri URI like URI("file:/home/jbf/ct/autoplot/bugs/1830227625/%5BajbTest%5D/") - * @return string representation of a path like file:/home/jbf/ct/autoplot/bugs/1830227625/[ajbTest]/ - */ - public static String fromUri( URI uri ) { - String surl= uri.toASCIIString(); - int i= surl.indexOf("?"); - String query= i==-1 ? "" : surl.substring(i); - if ( i!=-1 ) { - return uriDecode(surl.substring(0,i)) + query; - } else { - return uriDecode(surl); - } - - } - - /** - * encode the string as a URI. The following characters are encoded: - * " " % < > [ ] - * @param s string representation of a path like file:/home/jbf/ct/autoplot/bugs/1830227625/[ajbTest]/ - * @return URI like URI("file:/home/jbf/ct/autoplot/bugs/1830227625/%5BajbTest%5D/") - */ - public static URI toUri( String s ) { - try { - return new URI( uriEncode(s) ); - } catch (URISyntaxException ex) { - throw new RuntimeException(ex); - } - } - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/FtpFileSystemFactory.java b/dasCoreUtil/src/org/das2/util/filesystem/FtpFileSystemFactory.java deleted file mode 100644 index ed4f013ae..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/FtpFileSystemFactory.java +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * FtpFileSystemFactory.java - * - * Created on November 15, 2007, 9:31 AM - */ - -package org.das2.util.filesystem; - -import java.net.URI; - -/** - * - * @author jbf - */ -public class FtpFileSystemFactory implements FileSystemFactory { - - /** Creates a new instance of FtpFileSystemFactory */ - public FtpFileSystemFactory() { - } - - @Override - public FileSystem createFileSystem(URI root) throws FileSystem.FileSystemOfflineException { - return new FTPFileSystem( root ); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/Glob.java b/dasCoreUtil/src/org/das2/util/filesystem/Glob.java deleted file mode 100644 index 24dec9ca5..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/Glob.java +++ /dev/null @@ -1,183 +0,0 @@ -/* File: Glob.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util.filesystem; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; -import java.util.regex.Pattern; -import javax.swing.filechooser.FileFilter; - -/** - * known bug: *.java matches ".java". The unix glob behavior is to - * require that a leading . must be explicitly matched. - * @author jbf - */ -public class Glob { - - /** - * - * @param regex - * @return - */ - private static String getParentDirectory( String regex ) { - String[] s= regex.split( "/" ); - StringBuilder dirRegex; - if ( s.length>1 ) { - dirRegex= new StringBuilder(s[0]); - for ( int i=1; i .*\.dat - */ - public static Pattern getPattern( String glob ) { - final String regex= getRegex( glob ); - final Pattern absPattern= Pattern.compile(regex); - return absPattern; - } - - /** - * converts a glob into a regex. - * @param glob, like 'foo*.dat' - * @return the regular expression that implements, like 'foo.*\.dat' - */ - public static String getRegex( String glob ) { - StringTokenizer tk= new StringTokenizer(glob,"*?.+\\", true); - StringBuilder result= new StringBuilder(); - while ( tk.hasMoreElements() ) { - String nt= tk.nextToken(); - if ( nt.equals("*") ) { - result.append(".*"); - } else if ( nt.equals("?") ) { - result.append("."); - } else if ( nt.equals(".") ) { - result.append("\\."); - } else if ( nt.equals("+") ) { - result.append("\\+"); - } else if ( nt.equals("\\") ) { - result.append("\\\\"); - } else { - result.append(nt); - } - } - return result.toString(); - //return glob.replaceAll("\\.","\\\\.").replaceAll("\\*","\\.\\*").replaceAll("\\?","\\."); - } - - /** - * unglob the glob into an array of the matching FileObjects. - * See sftp://papco.org/home/jbf/ct/autoplot/script/demos/filesystem/unGlobDemo.jy - * @param fs the filesystem - * @param glob the glob, such as '/*.gif' - * @return an array of FileObjects that match the glob. - * @throws java.io.IOException - */ - public static FileObject[] unGlob( FileSystem fs, String glob ) throws IOException { - return unGlob( fs, glob, false ); - } - - /** - * unglob the glob into an array of the matching FileObjects. - * @param fs the filesystem - * @param glob the glob, which must start with /. - * @param directoriesOnly - * @return - * @throws IOException - */ - private static FileObject[] unGlob( FileSystem fs, String glob, final boolean directoriesOnly ) throws IOException { - if ( File.separatorChar=='\\' ) glob= glob.replaceAll( "\\\\", "/" ); - String parentGlob= getParentDirectory( glob ); - FileObject[] files; - if ( parentGlob!=null ) { - if ( isRoot( parentGlob ) ) { - FileObject rootFile= fs.getFileObject(parentGlob); - if ( rootFile.exists() ) { - files= new FileObject[] { rootFile }; - } else { - throw new IllegalArgumentException("root does not exist: "+glob); - } - } else { - files= unGlob( fs, parentGlob, true ); - } - } else { - throw new IllegalArgumentException("absolute files only"); - } - - final String regex= getRegex( glob ); - final Pattern absPattern= Pattern.compile(regex); - List list= new ArrayList(); - for (FileObject file1 : files) { - FileObject[] files1 = ((FileObject) file1).getChildren(); - for (FileObject file : files1) { - String s= file.getNameExt(); - if ( absPattern.matcher(s).matches() && ( !directoriesOnly || file.isFolder() ) ) { - list.add( file ); - } - } - } - return (FileObject[])list.toArray(new FileObject[list.size()]); - } - - public static FileFilter getGlobFileFilter( final String glob ) { - final Pattern pattern= getPattern(glob); - FileFilter f; - return new FileFilter() { - @Override - public boolean accept(File pathname) { - if ( pathname.toString()==null ) return false; - return pattern.matcher( pathname.getName() ).matches( ); - } - @Override - public String getDescription() { - return glob; - } - }; - } - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/HtmlUtil.java b/dasCoreUtil/src/org/das2/util/filesystem/HtmlUtil.java deleted file mode 100755 index 2c56a791a..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/HtmlUtil.java +++ /dev/null @@ -1,213 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * HtmlUtil.java - * - * Created on May 14, 2004, 9:06 AM - */ - -package org.das2.util.filesystem; - -import java.util.logging.Level; -import org.das2.util.monitor.CancelledOperationException; -import org.das2.util.Base64; -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.das2.util.LoggerManager; - -/** - * - * @author Jeremy - */ -public class HtmlUtil { - - private final static Logger logger= LoggerManager.getLogger( "das2.filesystem" ); - /** - * this logger is for opening connections to remote sites. - */ - protected static final Logger loggerUrl= org.das2.util.LoggerManager.getLogger( "das2.url" ); - - public static boolean isDirectory( URL url ) { - String file= url.getFile(); - return file.charAt(file.length()-1) != '/'; - } - - /** - * nice clients consume both the stderr and stdout coming from websites. - * This reads everything off of the stream and closes it. - * http://docs.oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html suggests that you "do not abandon connection" - * @param err the input stream - * @throws IOException - */ - public static void consumeStream( InputStream err ) throws IOException { - byte[] buf= new byte[2048]; - try { - if ( err!=null ) { - int ret = 0; - while ((ret = err.read(buf)) > 0) { - // empty out the error stream. - } - } - } finally { - if ( err!=null ) err.close(); - } - } - - /** - * Get the listing of the web directory, returning links that are "under" the given URL. - * Note this does not handle off-line modes where we need to log into - * a website first, as is often the case for a hotel. - * - * This was refactored to support caching of listings by simply writing the content to disk. - * - * @param url - * @param urlStream - * @return list of URIs referred to in the page. - * @throws IOException - * @throws CancelledOperationException - */ - public static URL[] getDirectoryListing( URL url, InputStream urlStream ) throws IOException, CancelledOperationException { - return getDirectoryListing( url, urlStream, true ); - } - - /** - * Get the listing of the web directory, returning links that are "under" the given URL. - * Note this does not handle off-line modes where we need to log into - * a website first, as is often the case for a hotel. - * - * This was refactored to support caching of listings by simply writing the content to disk. - * - * @param url the address. - * @param urlStream stream containing the URL content. - * @param childCheck only return links to URLs "under" the url. - * @return list of URIs referred to in the page. - * @throws IOException - * @throws CancelledOperationException - */ - public static URL[] getDirectoryListing( URL url, InputStream urlStream, boolean childCheck ) throws IOException, CancelledOperationException { - // search the input stream for links - // first, read in the entire URL - - long t0= System.currentTimeMillis(); - byte b[] = new byte[10000]; - int numRead = urlStream.read(b); - StringBuilder contentBuffer = new StringBuilder( 10000 ); - - if ( numRead!=-1 ) contentBuffer.append( new String( b, 0, numRead ) ); - while (numRead != -1) { - FileSystem.logger.finest("download listing"); - numRead = urlStream.read(b); - if (numRead != -1) { - String newContent = new String(b, 0, numRead); - contentBuffer.append( newContent ); - } - } - urlStream.close(); - - logger.log(Level.FINER, "read listing data in {0} millis", (System.currentTimeMillis() - t0)); - String content= contentBuffer.toString(); - - String hrefRegex= "(?i)href\\s*=\\s*([\"'])(.+?)\\1"; - Pattern hrefPattern= Pattern.compile( hrefRegex ); - - Matcher matcher= hrefPattern.matcher( content ); - - ArrayList urlList= new ArrayList(); - - String surl= url.toString(); - - while ( matcher.find() ) { - FileSystem.logger.finest("parse listing"); - String strLink= matcher.group(2); - URL urlLink; - - try { - urlLink = new URL(url, strLink); - strLink = urlLink.toString(); - } catch (MalformedURLException e) { - logger.log(Level.SEVERE, "bad URL: {0} {1}", new Object[]{url, strLink}); - continue; - } - - if ( childCheck ) { - if ( strLink.startsWith(surl) && strLink.length() > surl.length() && null==urlLink.getQuery() ) { - String file= strLink.substring( surl.length() ); - if ( !file.startsWith("../") ) { - urlList.add( urlLink ); - } - } - } else { - urlList.add( urlLink ); - } - } - - return (URL[]) urlList.toArray( new URL[urlList.size()] ); - - } - - /** - * Get the listing of the web directory, returning links that are "under" the given URL. - * Note this does not handle off-line modes where we need to log into - * a website first, as is often the case for a hotel. - * TODO: check for 302 redirect that Holiday Inn used to get credentials page - * @param url - * @return list of URIs referred to in the page. - * @throws IOException - * @throws CancelledOperationException - */ - public static URL[] getDirectoryListing( URL url ) throws IOException, CancelledOperationException { - - long t0= System.currentTimeMillis(); - - FileSystem.logger.log(Level.FINER, "listing {0}", url); - - String file= url.getFile(); - if ( file.charAt(file.length()-1)!='/' ) { - url= new URL( url.toString()+'/' ); - } - - String userInfo= KeyChain.getDefault().getUserInfo(url); - - //long t0= System.currentTimeMillis(); - URLConnection urlConnection = url.openConnection(); - - urlConnection.setAllowUserInteraction(false); - urlConnection.setConnectTimeout(FileSystem.settings().getConnectTimeoutMs() ); - - //int contentLength=10000; - - logger.log(Level.FINER, "connected in {0} millis", (System.currentTimeMillis() - t0)); - if ( userInfo != null) { - String encode = Base64.encodeBytes( userInfo.getBytes()); - urlConnection.setRequestProperty("Authorization", "Basic " + encode); - } - - //HERE is where we should support offline use. - InputStream urlStream; - - loggerUrl.log(Level.FINE, "getInputStream {0}", new Object[] { urlConnection.getURL() } ); - urlStream= urlConnection.getInputStream(); - - return getDirectoryListing( url, urlStream ); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/HttpFileSystem.java b/dasCoreUtil/src/org/das2/util/filesystem/HttpFileSystem.java deleted file mode 100755 index 948b6caf6..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/HttpFileSystem.java +++ /dev/null @@ -1,914 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * WebFileSystem.java - * - * Created on May 13, 2004, 1:22 PM - * - * A WebFileSystem allows web files to be opened just as if they were - * local files, since it manages the transfer of the file to a local - * file system. - */ -package org.das2.util.filesystem; - -import org.das2.util.monitor.CancelledOperationException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InterruptedIOException; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLConnection; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import org.das2.util.Base64; -import org.das2.util.monitor.ProgressMonitor; -import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; -import java.util.concurrent.locks.Lock; -import java.util.logging.Logger; -import java.util.regex.Pattern; -import java.util.zip.GZIPInputStream; -import javax.swing.SwingUtilities; -import org.das2.util.OsUtil; -import static org.das2.util.filesystem.FileSystem.toCanonicalFilename; -import org.das2.util.monitor.NullProgressMonitor; - -/** - * Make a web folder accessible. This assumes listings are provided in html form by requesting links ending in a slash. - * For example, http://autoplot.org/data/pngwalk/. Links to resources "outside" of the filesystem are not considered part of - * the filesystem. Again, this assumes listings are HTML content, and I suspect this will be changing (xml+client-side-xslt)... - * - * @author Jeremy - */ -public class HttpFileSystem extends WebFileSystem { - - protected static final Logger logger= org.das2.util.LoggerManager.getLogger( "das2.filesystem.http" ); - - /** - * provide some caching for directory entries. - */ - private final Map listingEntries= new HashMap(); - private final Map listingEntryFreshness= new HashMap(); - - /** Creates a new instance of WebFileSystem */ - private HttpFileSystem(URI root, File localRoot) { - super(root, localRoot); - } - - private String cookie=null; - - /** - * return the cookie needed. - * @return - */ - protected String getCookie() { - return this.cookie; - } - - public static HttpFileSystem createHttpFileSystem(URI rooturi) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - try { - - String auth= rooturi.getAuthority(); - if ( auth==null ) { - throw new MalformedURLException("URL does not contain authority, check for ///"); - } - String[] ss= auth.split("@"); - - URL root; - - if ( ss.length>3 ) { - throw new IllegalArgumentException("user info section can contain at most two at (@) symbols"); - } else if ( ss.length==3 ) {//bugfix 3299977. UMich server uses email:password@umich. Java doesn't like this. - // the user didn't escape the at (@) in the email. escape it here. - StringBuilder userInfo= new StringBuilder( ss[0] ); - for ( int i=1;i<2;i++ ) userInfo.append("%40").append(ss[i]); - auth= ss[2]; - try { - URI rooturi2= new URI( rooturi.getScheme() + "://" + userInfo.toString()+"@"+auth + rooturi.getPath() ); - rooturi= rooturi2; - } catch ( URISyntaxException ex ) { - throw new IllegalArgumentException("unable to handle: "+rooturi); - } - } - - root= rooturi.toURL(); - - logger.log(Level.FINER, "See https://www.draw.io/#G0B1Ywc5_Vexx1d3ctdGZxZDNkM3M" ); - logger.log(Level.FINER, "URL Reference: {0}", root); - - boolean doCheck= true; - URI parentURI= FileSystemUtil.getParentUri( rooturi ); - if ( parentURI!=null ) { - HttpFileSystem parent= (HttpFileSystem) peek( parentURI ); - if ( parent!=null && parent.isOffline() ) { - logger.finer("parent is offline, do not check..."); - doCheck= false; - } - } - - - boolean offline = true; - String offlineMessage= ""; - int offlineResponseCode= 0; - - String cookie= null; - - while ( doCheck && !FileSystem.settings().isOffline() ) { - - // verify URL is valid and accessible - HttpURLConnection urlc = (HttpURLConnection) root.openConnection(); - - urlc.setConnectTimeout( FileSystem.settings().getConnectTimeoutMs() ); - - //urlc.setRequestMethod("HEAD"); // Causes problems with the LANL firewall. - - String userInfo; // null means that userInfo has not been attempted. - - try { - logger.log(Level.FINER, "Check keychain: ", root); - userInfo = KeyChain.getDefault().getUserInfo(root); - } catch (CancelledOperationException ex) { - logger.log( Level.FINER, "user cancelled credentials for {0}", rooturi); - break; - } - - if ( userInfo != null) { - String encode = Base64.encodeBytes( userInfo.getBytes()); - urlc.setRequestProperty("Authorization", "Basic " + encode); - } - - cookie= KeyChain.getDefault().getCookie(root); - if ( cookie!=null ) { - urlc.setRequestProperty("Cookie",cookie); - } - - try { - logger.log( Level.FINER, "Verify Credentials {0}", urlc ); - if ( userInfo!=null && !userInfo.contains(":") ) { - logger.log( Level.INFO, "urlc={0}", urlc ); - logger.log( Level.INFO, "userInfo does not appear to contain password: {0}", userInfo ); - } else { - logger.log( Level.FINER, "userInfo.length={0}", ( userInfo==null ? -1 : userInfo.length() )); - } - urlc.connect(); - logger.log( Level.FINER, "made connection, now consume rest of stream: {0}", urlc ); - HtmlUtil.consumeStream( urlc.getInputStream() ); - logger.log( Level.FINER, "done consuming and initial connection is complete: {0}" ); - offline= false; - doCheck= false; - logger.finer( "Verify Credentials exits with okay"); - - } catch ( IOException ex ) { - - logger.finer("Error with credentials"); - int code= 0; - String msg; - try { - code= urlc.getResponseCode(); - msg= urlc.getResponseMessage(); - } catch ( IOException ex2 ) { - // do nothing in this case, just try to get a response code. - logger.log(Level.SEVERE,ex2.getMessage(),ex2); - msg= ex2.getMessage(); - } - - HtmlUtil.consumeStream( urlc.getErrorStream() ); - - if ( code==404 ) { - logger.log( Level.SEVERE, String.format( "%d: folder not found: %s\n%s", code, root, msg ), ex ); - throw (FileNotFoundException)ex; - - } else if ( code!=401 ) { - // Note this may still be code 403. We still enter the same branch for now, because the user might be on a network that isn't permitted now. - logger.log( Level.SEVERE, String.format( "%d: failed to connect to %s\n%s", code, root, msg ), ex ); - if ( FileSystem.settings().isAllowOffline() ) { - logger.info("remote filesystem is offline, allowing access to local cache."); - break; - } else { - throw new FileSystemOfflineException("" + code + ": " + msg ); - } - } - - if ( "true".equals( System.getProperty("java.awt.headless") ) && userInfo!=null ) { - logger.finer( "Headless mode means we have to give up"); - if ( FileSystem.settings().isAllowOffline() ) { - logger.info("remote filesystem is offline, allowing access to local cache."); - break; - } else { - throw new FileSystemOfflineException("" + code + ": " + msg ); - } - } - - offlineMessage= msg; - offlineResponseCode= code; - } - - if (urlc.getResponseCode() != HttpURLConnection.HTTP_OK && urlc.getResponseCode() != HttpURLConnection.HTTP_FORBIDDEN) { - if ( urlc.getResponseCode()==HttpURLConnection.HTTP_UNAUTHORIZED ) { - // might be nice to modify URL so that credentials are used. - KeyChain.getDefault().clearUserPassword(root); - if ( userInfo==null ) { - String port= root.getPort()==-1 ? "" : ( ":" +root.getPort() ); - URL rootAuth= new URL( root.getProtocol() + "://" + "user@" + root.getHost() + port + root.getFile() ); - try { - URI rootAuthUri= rootAuth.toURI(); - rooturi= rootAuthUri; - root= rooturi.toURL(); - - } catch ( URISyntaxException ex ) { - throw new RuntimeException(ex); - } - } - } else { - offline= false; - } - } else { - offline= false; - } - - } - - File local; - - if (FileSystemSettings.hasAllPermission()) { - local = localRoot(rooturi); - logger.log(Level.FINER, "initializing httpfs {0} at {1}", new Object[]{root, local}); - } else { - local = null; - logger.log(Level.FINER, "initializing httpfs {0} in applet mode", root); - } - HttpFileSystem result = new HttpFileSystem(rooturi, local); - - if ( offline ) { - logger.log( Level.WARNING, "filesystem is offline: {0}", rooturi ); - } - - result.offline = offline; - result.offlineMessage= offlineMessage; - result.offlineResponseCode= offlineResponseCode; - result.cookie= cookie; - - return result; - - } catch (FileSystemOfflineException e) { - throw e; - } catch (FileNotFoundException e) { - throw e; - } catch (UnknownHostException e) { - throw e; - } catch (IOException e) { - throw new FileSystemOfflineException(e,rooturi); - } - - } - - /** - * It looks like an external process (another das2 app) is downloading the resource. Wait for the other - * process to download the file. If the file is idle for more than the allowable external idle millisecond limit - * (FileSystemSettings.allowableExternalIdleMs), then return false. - * @param f the file - * @param partFile the part file we're watching - * @param monitor - * @return true if the other app appears to have loaded the resource, false otherwise. - */ - private boolean waitDownloadExternal( File f, File partFile, ProgressMonitor monitor ) { - monitor.setProgressMessage("waiting for other process load"); - while ( partFile.exists() && ( System.currentTimeMillis() - partFile.lastModified() ) < FileSystemSettings.allowableExternalIdleMs ) { - try { - Thread.sleep(300); - logger.log(Level.FINEST, "waiting for external process to download {0}", partFile); - monitor.setTaskProgress(partFile.length()); - if ( monitor.isCancelled() ) { - return false; - } - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - } - if ( partFile.exists() ) { - logger.finer("timeout waiting for partFile to be deleted"); - return false; - } else { - if ( f.exists() ) { - logger.finer("successfully waited for external download to complete"); - return true; - } else { - logger.finer("part file removed but complete file is not found"); - return false; - } - } - } - - /** - * - * @param filename filename within the filesystem. - * @param f the target filename where the file is to be download. - * @param partFile use this file to stage the download - * @param monitor monitor the progress. - * @throws IOException - */ - @Override - protected void downloadFile(String filename, File f, File partFile, ProgressMonitor monitor) throws IOException { - - Lock lock = getDownloadLock(filename, f, monitor); - - if (lock == null) { - return; - } - - filename = toCanonicalFilename(filename); - - logger.log(Level.FINER, "downloadFile {0}, using temp file {1}", new Object[] { filename, partFile } ); - - try { - URL remoteURL = new URL(root.toString() + filename.substring(1) ); - - loggerUrl.log(Level.FINE, "openConnection {0}", new Object[] { remoteURL } ); - URLConnection urlc = remoteURL.openConnection(); - urlc.setConnectTimeout( FileSystem.settings().getConnectTimeoutMs() ); - - //bug http://sourceforge.net/p/autoplot/bugs/1393/ shows where this is necessary. - urlc.setUseCaches(false); - - String userInfo; - try { - userInfo = KeyChain.getDefault().getUserInfo(root); - } catch (CancelledOperationException ex) { - throw new IOException("user cancelled at credentials entry"); - } - if ( userInfo != null) { - String encode = Base64.encodeBytes(userInfo.getBytes()); - urlc.setRequestProperty("Authorization", "Basic " + encode); - } - - if ( cookie!=null ) { - urlc.addRequestProperty("Cookie", cookie ); - } - - InputStream in; - try { - loggerUrl.log(Level.FINE, "getInputStream {0}", new Object[] { urlc.getURL() } ); - in= urlc.getInputStream(); - - } catch ( FileNotFoundException ex ) { - remoteURL= new URL(root.toString() + filename.substring(1) + ".gz" ); - - loggerUrl.log(Level.FINE, "openConnection {0}", new Object[] { remoteURL } ); - urlc = remoteURL.openConnection(); - urlc.setConnectTimeout( FileSystem.settings().getConnectTimeoutMs() ); - - if ( userInfo != null) { - String encode = Base64.encodeBytes(userInfo.getBytes()); - urlc.setRequestProperty("Authorization", "Basic " + encode); - } - try { - loggerUrl.log(Level.FINE, "getInputStream {0}", new Object[] { urlc.getURL() } ); - in= new GZIPInputStream( urlc.getInputStream() ); - } catch ( FileNotFoundException ex2 ) { - throw ex; - } - - } - - HttpURLConnection hurlc = (HttpURLConnection) urlc; - if (hurlc.getResponseCode() == 404) { - logger.log(Level.INFO, "{0} URL: {1}", new Object[]{hurlc.getResponseCode(), remoteURL}); - throw new FileNotFoundException("not found: " + remoteURL); - } else if (hurlc.getResponseCode() != 200) { - logger.log(Level.INFO, "{0} URL: {1}", new Object[]{hurlc.getResponseCode(), remoteURL}); - throw new IOException( hurlc.getResponseCode()+": "+ hurlc.getResponseMessage() + "\n"+remoteURL ); - } - - Date d; - List sd= urlc.getHeaderFields().get("Last-Modified"); - if ( sd!=null && sd.size()>0 ) { - d= new Date( sd.get(sd.size()-1) ); - } else { - d= new Date(); - } - - monitor.setTaskSize(urlc.getContentLength()); - - if (!f.getParentFile().exists()) { - logger.log(Level.FINER, "make dirs {0}", f.getParentFile()); - FileSystemUtil.maybeMkdirs( f.getParentFile() ); - } - if (partFile.exists()) { - logger.log(Level.FINER, "partFile exists {0}", partFile); - long ageMillis= System.currentTimeMillis() - partFile.lastModified(); // TODO: this is where OS-level locking would be nice... - if ( ageMillis
    {@code
    -     *   EXIST->Boolean
    -     *   REAL_NAME->String
    -     *}
    - * others are just HTTP header fields like (see wget --server-response https://raw.githubusercontent.com/autoplot/jyds/master/dd.jyds): - *
    {@code
    -     *   Content-Length   the length in bytes of the resource.
    -     *   Cache-Control    max-age=300
    -     *   Date: Fri, 18 Jul 2014 12:07:18 GMT  time stamp.
    -     *   ETag: "750d4f66c58a0ac7fef2784253bf6954d4d38a85"
    -     *   Accept-Ranges    accepts requests for part of a file.
    -     *}
    - * @param f the name within the filesystem - * @return the metadata, such as the Date and ETag. - * @throws java.io.IOException - * @throws org.das2.util.monitor.CancelledOperationException - */ - protected Map getHeadMeta(String f) throws IOException, CancelledOperationException { - String realName = f; - boolean exists; - try { - URL ur = new URL(this.root.toURL(), f); - HttpURLConnection connect = (HttpURLConnection) ur.openConnection(); - String userInfo= KeyChain.getDefault().getUserInfo(ur); - if ( userInfo != null) { - String encode = Base64.encodeBytes(userInfo.getBytes()); - connect.setRequestProperty("Authorization", "Basic " + encode); - } - connect.setRequestMethod("HEAD"); - HttpURLConnection.setFollowRedirects(false); - connect.connect(); - HttpURLConnection.setFollowRedirects(true); - // check for rename, which means we'll do another request - if (connect.getResponseCode() == 303) { - String surl = connect.getHeaderField("Location"); - if (surl.startsWith(root.toString())) { - realName = surl.substring(root.toString().length()); - } - connect.disconnect(); - ur = new URL(this.root.toURL(), realName); - connect = (HttpURLConnection) ur.openConnection(); - connect.setRequestMethod("HEAD"); - connect.connect(); - } - exists = connect.getResponseCode() != 404; - - Map result = new HashMap(); - result.putAll(connect.getHeaderFields()); - result.put( "EXIST", exists ); - connect.disconnect(); - - return result; - - } catch (MalformedURLException ex) { - throw new IllegalArgumentException(ex); - } - - } - - /** dumb method looks for / in parent directory's listing. Since we have - * to list the parent, then IOException can be thrown. - * - * @return true if the name appears to be a directory (folder). - * @throws java.io.IOException - */ - @Override - public boolean isDirectory(String filename) throws IOException { - - if (localRoot == null) { - return filename.endsWith("/"); - } - - File f = new File(localRoot, filename); - if (f.exists()) { - return f.isDirectory(); - } else { - if (filename.endsWith("/")) { - return true; - } else { - File parentFile = f.getParentFile(); - String parent = getLocalName(parentFile); - if (!parent.endsWith("/")) { - parent = parent + "/"; - } - String[] list = listDirectory(parent); - String lookFor; - if (filename.startsWith("/")) { - lookFor = filename.substring(1) + "/"; - } else { - lookFor = filename + "/"; - } - for (String list1 : list) { - if (list1.equals(lookFor)) { - return true; - } - } - return false; - } - } - } - - /** - * always hide these file types. - * @return Arrays.asList( new String[] { ".css", ... } ). - */ - private List hideExtensions() { - return Arrays.asList( new String[] { ".css", ".php", ".jnlp", ".part" } ); - } - - /** - * list the directory, using the cached entry from listDirectoryFromMemory, or - * by HtmlUtil.getDirectoryListing. If there is a ro_cache, then add extra entries from here as well. - * Note the following extentions are hidden: .css, .php, .jnlp, .part. - * @param directory name within the filesystem - * @return names within the directory - * @throws IOException - */ - @Override - public String[] listDirectory(String directory) throws IOException { - - DirectoryEntry[] cached= listDirectoryFromMemory( directory ); - if ( cached!=null ) { - return FileSystem.getListing( cached ); - } - - if ( protocol!=null && protocol instanceof AppletHttpProtocol ) { // support applets. This could check for local write access, but DefaultHttpProtocol shows a problem here too. - InputStream in=null; - URL[] list; - try { - in= protocol.getInputStream( new WebFileObject(this,directory,new Date() ), new NullProgressMonitor() ); - list= HtmlUtil.getDirectoryListing( getURL(directory), in ); - } catch ( CancelledOperationException ex ) { - throw new IllegalArgumentException(ex); //TODO: this should probably be IOException(ex). See use 20 lines below as well. - } finally { - if ( in!=null ) in.close(); - } - - String[] result; - result = new String[list.length]; - int n = directory.length(); - for (int i = 0; i < list.length; i++) { - URL url = list[i]; - result[i] = getLocalName(url).substring(n); - } - return result; - } - - directory = toCanonicalFolderName(directory); - - Map result; - if ( isListingCached(directory) ) { - logger.log(Level.FINER, "using cached listing for {0}", directory); - - File listing= listingFile(directory); - - URL[] list; - FileInputStream fin=null; - try { - fin= new FileInputStream(listing); - list = HtmlUtil.getDirectoryListing(getURL(directory), fin ); - } catch (CancelledOperationException ex) { - throw new IllegalArgumentException(ex); // shouldn't happen since it's local - } finally { - if ( fin!=null ) fin.close(); - } - - result = new LinkedHashMap(list.length); - int n = directory.length(); - for (URL url : list) { - DirectoryEntry de1= new DirectoryEntry(); - de1.modified= Long.MAX_VALUE; // HTTP is somewhat expensive to get dates and sizes, so put in Long.MAX_VALUE to indicate need to load. - de1.name= getLocalName(url).substring(n); - de1.type= 'f'; //TODO: directories mis-marked? - de1.size= Long.MAX_VALUE; - result.put(de1.name,de1); - } - - result= addRoCacheEntries( directory, result ); - - cacheListing( directory, result.values().toArray( new DirectoryEntry[result.size()] ) ); - - return FileSystem.getListing( result ); - } - - boolean successOrCancel= false; - - if ( this.isOffline() ) { - File f= new File(localRoot, directory).getCanonicalFile(); - logger.log(Level.FINER, "this filesystem is offline, using local listing: {0}", f); - - if ( !f.exists() ) throw new FileSystemOfflineException("unable to list "+f+" when offline"); - File[] listing = f.listFiles(); - - List result1= new ArrayList(); - for (File f1 : listing) { - if ( f1.getName().endsWith(".listing") ) continue; - if ( f1.isDirectory() ) { - result1.add( f1.getName() + "/" ); - } else { - result1.add( f1.getName() ); - } - } - result= addRoCacheEntries( directory, new LinkedHashMap() ); - for ( DirectoryEntry f1: result.values() ) { - if ( f1.type=='d' ) { - result1.add( f1.name + "/" ); - } else { - result1.add( f1.name ); - } - } - return result1.toArray( new String[result1.size()] ); - } - - - while ( !successOrCancel ) { - logger.log(Level.FINER, "list {0}", directory); - URL[] list; - try { - File listing= listingFile( directory ); - - downloadFile( directory, listing, getPartFile(listing), new NullProgressMonitor() ); - - FileInputStream fin=null; - try { - fin= new FileInputStream(listing); - list = HtmlUtil.getDirectoryListing( getURL(directory), fin ); - } finally { - if ( fin!=null ) fin.close(); - } - - //remove .css stuff - ArrayList newlist= new ArrayList(); - List hideExtensions= hideExtensions(); - for ( URL s: list ) { - boolean hide= false; - for ( String e : hideExtensions ) { - if ( s.getFile().endsWith(e) ) hide= true; - } - if ( !hide ) newlist.add(s); - } - list= (URL[]) newlist.toArray( new URL[newlist.size()] ); - - result = new LinkedHashMap(); - int n = directory.length(); - for (URL url : list) { - DirectoryEntry de1= new DirectoryEntry(); - de1.modified= Long.MAX_VALUE; - de1.name= getLocalName(url).substring(n); - de1.type= 'f'; - de1.size= Long.MAX_VALUE; - result.put(de1.name,de1); - } - - result= addRoCacheEntries( directory, result ); - cacheListing( directory, result.values().toArray( new DirectoryEntry[result.size()] ) ); - - return FileSystem.getListing(result); - - } catch (CancelledOperationException ex) { - throw new IOException( "user cancelled at credentials" ); // JAVA6 - } catch ( IOException ex ) { - if ( isOffline() ) { - logger.info("** using local listing because remote is not available"); - logger.info("or some other error occurred. **"); - File localFile= new File( localRoot, directory ); - return localFile.list(); - } else { - throw ex; - } - } - - } - return( new String[] { "should not get here" } ); // we should not be able to reach this point - - } - -// public String[] listDirectoryOld(String directory) throws IOException { -// directory = HttpFileSystem.toCanonicalFilename(directory); -// if (!isDirectory(directory)) { -// throw new IllegalArgumentException("is not a directory: " + directory); -// } -// -// if (!directory.endsWith("/")) { -// directory = directory + "/"; -// } -// synchronized (listings) { -// if ( isListingCached(directory) ) { //TODO: there are no timestamps to invalidate listings!!! How is it I haven't run across this before...https://sourceforge.net/tracker/index.php?func=detail&aid=3395693&group_id=199733&atid=970682 -// logger.log( Level.FINE, "use cached listing for {0}", directory ); -// String[] result= (String[]) listings.get(directory); -// String[] resultc= new String[result.length]; -// System.arraycopy( result, 0, resultc, 0, result.length ); -// return resultc; -// -// } else { -// logger.log(Level.FINE, "list {0}", directory); -// URL[] list; -// try { -// list = HtmlUtil.getDirectoryListing(getURL(directory)); -// } catch (CancelledOperationException ex) { -// throw new IOException( "user cancelled at credentials" ); // JAVA6 -// } catch ( IOException ex ) { -// if ( isOffline() ) { -// System.err.println("** using local listing because remote is not available"); -// System.err.println("or some other error occurred. **"); -// File localFile= new File( localRoot, directory ); -// return localFile.list(); -// } else { -// throw ex; -// } -// } -// String[] result = new String[list.length]; -// int n = directory.length(); -// for (int i = 0; i < list.length; i++) { -// URL url = list[i]; -// result[i] = getLocalName(url).substring(n); -// } -// listings.put(directory, result); -// listingFreshness.put( directory, System.currentTimeMillis()+LISTING_TIMEOUT_MS ); -// return result; -// } -// } -// } - - @Override - public String[] listDirectory(String directory, String regex) throws IOException { - - if ( SwingUtilities.isEventDispatchThread() ) { - //logger.warning("listDirectory called on event thread!"); - } - - logger.log(Level.FINER, "listDirectory({0},{1})", new Object[]{directory, regex}); - - if ( regex.endsWith("/") ) regex= regex.substring(0,regex.length()-1); - - directory = toCanonicalFilename(directory); - if (!isDirectory(directory)) { - throw new IllegalArgumentException("is not a directory: " + directory); - } - - String[] listing = listDirectory(directory); - Pattern pattern = Pattern.compile(regex); - ArrayList result = new ArrayList(); - for (String s : listing) { - String c= s; - if ( s.charAt(s.length()-1)=='/' ) c= s.substring(0,s.length()-1); - if (pattern.matcher(c).matches()) { - result.add(s); - } - } - return (String[]) result.toArray(new String[result.size()]); - - } - - - /** - * HTTP listings are really done by querying the single file, so support this by issuing a head request - * @param filename filename within the system - * @param force if true, then guarantee a listing and throw an IOException if it cannot be done. - * @return the DirectoryEntry showing size and date. - * @throws IOException - */ - @Override - public DirectoryEntry maybeUpdateDirectoryEntry(String filename, boolean force) throws IOException { - Long fresh= listingEntryFreshness.get(filename); - if ( fresh!=null ) { - if ( new Date().getTime()-fresh < HttpFileSystem.HTTP_CHECK_TIMESTAMP_LIMIT_MS ) { - return listingEntries.get(filename); - } else { - synchronized ( this ) { - listingEntryFreshness.remove(filename); - listingEntries.remove(filename); - } - } - } - try { - Map meta= getHeadMeta(filename); - DirectoryEntry de= new DirectoryEntry(); - List odate= (List)meta.get("Date"); - List osize= (List)meta.get("Content-Length"); - de.type= filename.endsWith("/") ? 'd' : 'f'; - if ( odate!=null && osize!=null ) { - de.modified= new Date((String)odate.get(0)).getTime(); - de.size= Long.parseLong((String)osize.get(0)); - synchronized ( this ) { - listingEntries.put(filename,de); - listingEntryFreshness.put(filename,new Date().getTime()); - } - return de; - } else { - return super.maybeUpdateDirectoryEntry(filename, force); - } - } catch (CancelledOperationException ex) { - return super.maybeUpdateDirectoryEntry(filename, force); - } - } - - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/HttpFileSystemFactory.java b/dasCoreUtil/src/org/das2/util/filesystem/HttpFileSystemFactory.java deleted file mode 100644 index 4373773cc..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/HttpFileSystemFactory.java +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * HttpFileSystemFactory.java - * - * Created on November 15, 2007, 9:28 AM - * - */ -package org.das2.util.filesystem; - -import java.io.FileNotFoundException; -import java.net.URI; -import java.net.UnknownHostException; -import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; - -/** - * Creates a FileSystem for reading files via HTTP and HTTPS. - * @author jbf - */ -public class HttpFileSystemFactory implements FileSystemFactory { - - public HttpFileSystemFactory() { - } - - @Override - public FileSystem createFileSystem(URI root) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - HttpFileSystem hfs = HttpFileSystem.createHttpFileSystem(root); - if (!FileSystemSettings.hasAllPermission()) hfs.setAppletMode(true); - return hfs; - } -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/KeyChain.java b/dasCoreUtil/src/org/das2/util/filesystem/KeyChain.java deleted file mode 100644 index 375b3cafe..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/KeyChain.java +++ /dev/null @@ -1,604 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.util.filesystem; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.net.HttpURLConnection; -import org.das2.util.monitor.CancelledOperationException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLEncoder; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.AbstractAction; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPasswordField; -import javax.swing.JSeparator; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.SwingConstants; -import org.das2.util.Base64; - -/** - * class that contains the credentials for websites. This is first - * introduced so that ftp://papco:@mrfrench.lanl.gov/ and subdirectories - * would just ask for credentials once. Also, this allows all the sensitive - * information to be stored in one class. - * - * @author jbf - */ -public class KeyChain { - - private static final Logger logger= org.das2.util.LoggerManager.getLogger("das2.filesystem.keychain"); - - private static KeyChain instance; - - /** - * get the single instance of the class. - * @return the single instance of the class. - */ - public static synchronized KeyChain getDefault() { - if ( instance==null ) { - instance= new KeyChain(); - instance.loadInitial(); - } - return instance; - } - - /** - * If the keys file is found, then pre-load these credentials. - * The keys file is in new File( FileSystem.settings().getLocalCacheDir(), "keychain.txt" ); - */ - private void loadInitial() { - File keysFile= new File( FileSystem.settings().getLocalCacheDir(), "keychain.txt" ); - if ( keysFile.exists() ) { - logger.log( Level.FINE, "loading keys from {0}", keysFile); - BufferedReader r=null; - try { - r= new BufferedReader( new FileReader(keysFile) ); - String line= r.readLine(); - while ( line!=null ) { - int i= line.indexOf("#"); - if ( i>-1 ) line= line.substring(0,i); - line= line.trim(); - if ( line.length()>0 ) { - String[] ss= line.split("\\s+"); - if ( ss.length!=2 ) { - logger.log( Level.WARNING, "skipping line because wrong number of fields: {0}", line); - } else { - String hash= ss[0].trim(); - if ( hash.endsWith("/") ) { - hash= hash.substring(0,hash.length()-1); - } - String storedUserInfo= ss[1].trim(); - //TODO: shouldn't "http://ectsoc@www.rbsp-ect.lanl.gov" match "http://www.rbsp-ect.lanl.gov ectsoc:..." - keys.put( hash, storedUserInfo ); - } - } - line= r.readLine(); - } - } catch ( IOException ex ) { - logger.log( Level.SEVERE, "while loading keychain.txt file "+keysFile, ex ); - } finally { - if ( r!=null ) { - try { - r.close(); - logger.log(Level.FINE, "loaded keys from keychain file {0}", keysFile); - } catch (IOException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - } - } - - } - } - - /** - * append to the keys file. - * @param url - * @param key - * @throws IOException - * @see #writeKeysFile(java.lang.String, java.lang.String) - */ - private void appendKeysFile( String url, String key ) throws IOException { - File keysFile= new File( FileSystem.settings().getLocalCacheDir(), "keychain.txt" ); - PrintWriter w= null; - try { - if ( keysFile.exists() ) { - if ( keysFile.canWrite() ) { - w= new PrintWriter( new FileWriter(keysFile,true) ); - } else { - throw new IOException( "Unable to append to file: "+ keysFile ); - } - } else { - try { - w= new PrintWriter( new FileWriter(keysFile) ); - if ( !keysFile.setReadable(false) ) logger.warning("setReadable failure"); - if ( !keysFile.setReadable(false,false) ) logger.warning("setReadable failure"); - if ( !keysFile.setReadable(true,true) ) logger.warning("setReadable failure"); - if ( !keysFile.setWritable(false) ) logger.warning("setWritable failure"); - if ( !keysFile.setWritable(false,false) ) logger.warning("setWritable failure"); - if ( !keysFile.setWritable(true,true) ) logger.warning("setWritable failure"); - - } catch ( IOException ex ) { - throw new IOException( "Unable to create file: "+ keysFile ); - } - } - w.append( url ).append("\t").append(key).append("\n"); - - } finally { - if ( w!=null ) w.close(); - } - - - - } - - - /** - * dump the loaded keys into the file new File( FileSystem.settings().getLocalCacheDir(), "keychain.txt" ) - */ - public void writeKeysFile() { - try { - writeKeysFile(false); - } catch ( IOException ex ) { - throw new IllegalArgumentException(ex); - } - } - - /** - * format the keys file. Since Java 5 didn't have a way to restrict - * access to the file, this would simply display the keychain file contents - * and have the operator write the keychain file to disk. The required - * Java7 is able to restict access to the file properly. - * @param toFile the file should be created. - * @throws IOException - * @see #appendKeysFile(java.lang.String, java.lang.String) - */ - public void writeKeysFile( boolean toFile ) throws IOException { - File keysFile= new File( FileSystem.settings().getLocalCacheDir(), "keychain.txt" ); - - PrintWriter w=null; - final ByteArrayOutputStream out= new ByteArrayOutputStream(); - - try { - w= new PrintWriter( out ); - w.println("# keys file produced on "+ new java.util.Date() ); - w.println("# "+keysFile ); - for ( Entry key : keys.entrySet() ) { - w.println( key.getKey() + "\t" + key.getValue() ); - } - } finally { - if ( w!=null ) w.close(); - } - - if ( toFile ) { - FileOutputStream fout=null; - try { - fout= new FileOutputStream(keysFile); - fout.write( out.toByteArray() ); - } finally { - if ( fout!=null ) fout.close(); - } - if ( !keysFile.setReadable(false) ) logger.warning("setReadable failure"); - if ( !keysFile.setReadable(false,false) ) logger.warning("setReadable failure"); - if ( !keysFile.setReadable(true,true) ) logger.warning("setReadable failure"); - if ( !keysFile.setWritable(false) ) logger.warning("setWritable failure"); - if ( !keysFile.setWritable(false,false) ) logger.warning("setWritable failure"); - if ( !keysFile.setWritable(true,true) ) logger.warning("setWritable failure"); - } - - JButton button= new JButton( new AbstractAction( "Show Passwords") { - @Override - public void actionPerformed(ActionEvent e) { - JTextArea ta= new JTextArea(); - ta.setText( new String( out.toByteArray() ) ); - JOptionPane.showMessageDialog(parent, ta ); - } - }); - - String s= toFile ? "The keychain file has been created:" : "You must create a protected file"; - JPanel p= new JPanel(new BorderLayout()); - p.add( new JLabel("******************************
    " - + s + "
    " - +keysFile +"
    " - +"that contains all passwords.
    " - +"Click the button below to show content, which contains passwords.
    " - +"******************************" ) ); - p.add( button, BorderLayout.SOUTH ); - JOptionPane.showMessageDialog( parent, p ); - - } - - /** - * map from URL, without trailing slash, to key. - */ - private final Map keys= new HashMap(); - - /** - * map from URL, without trailing slash, to cookie. - */ - private final Map cookies= new HashMap(); - - /** - * parent component for password dialog. - */ - private Component parent=null; - - /** - * get the user credentials, maybe throwing CancelledOperationException if the - * user hits cancel. - * @param uri - * @return - * @throws CancelledOperationException - */ - public String getUserInfo( URI uri ) throws CancelledOperationException { - try { - return getUserInfo(uri.toURL()); - } catch (MalformedURLException ex) { - throw new RuntimeException(ex); - } - } - - /** - * return the user info (username:password) associated with this URL. - * @param url the URL - * @return null or the user info. - * @throws CancelledOperationException - */ - public String getUserInfo( URL url ) throws CancelledOperationException { - String userInfo= url.getUserInfo(); - if ( userInfo==null ) return null; - return getUserInfo( url, userInfo ); - } - - /** - * return the user info but base-64 encoded. This is put in so that - * a future version of the software can cache these as well. This is - * intended to be inserted like so: - * - * connection= theUrl.getConnection(); - * String encode= KeyChain.getDefault().getUserInfoBase64Encoded( theUrl ); - * if ( encode!=null ) connection.setRequestProperty("Authorization", "Basic " + encode); - * - * @param url the URL which may contain user info. - * @return the base-64 encoded credentials. - * @throws CancelledOperationException - */ - public String getUserInfoBase64Encoded( URL url ) throws CancelledOperationException { - String userInfo= getUserInfo(url); - if ( userInfo!=null ) { - return Base64.encodeBytes( userInfo.getBytes()); - } else { - return null; - } - } - - public void setParentGUI( Component c ) { - this.parent= c; - } - - /** - * return null or the WWW-Authenticate string. - * @param url - * @return - */ - public String getWWWAuthenticate( URL url ) { - try { - URLConnection c= url.openConnection(); - c.connect(); - String s= c.getHeaderField("WWW-Authenticate"); - if ( s==null ) { - logger.fine("WWW-Authenticate is not provided."); - return null; - } else { - int i= s.indexOf("\""); - if ( i>-1 ) { - s= s.substring(i); - } - return s; - } - //BufferedReader in= new BufferedReader( new InputStreamReader( c.getInputStream() ) ); - //String line= ""; - //while ( line!=null ) line= in.readLine(); // eat the rest of the stream, because this is important. - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - return null; - } - - /** - * get the user credentials, maybe throwing CancelledOperationException if the - * user hits cancel. If the password is "pass" or "password" then don't use - * it, prompt for it instead. - * - * The userInfo passed in can contain just "user" or the user account to log in with, then - * maybe a colon and "pass" or the password. So examples include:
      - *
    • null, where null is returned and credentials are presumed to - *
    • user, where both the username and password are needed. - *
    • user:pass where both are needed - *
    • joe:pass there the user joe is presumed and pass is needed - *
    • joe:JoPass1 where both the user and password are already specified, and this is returned. - *
    - * - * Note a %40 in the username is converted to @. - * @param url - * @param userInfo that is available separately. (Java doesn't like user@usersHost:password@server) - * @return the userinfo, like "us3r:passw0rd" - * @throws CancelledOperationException - */ - public String getUserInfo( URL url, String userInfo ) throws CancelledOperationException { - - if ( userInfo==null ) return null; - - String userName=null; - String[] ss= userInfo.split(":",-2); - if ( !ss[0].equals("user") ) { - userName= ss[0]; - if ( userName.contains("%40") ) { - userName= userName.replaceAll( "%40", "@" ); - StringBuilder userInfob= new StringBuilder(userName); - for ( int i=1; i0 ) { - String s= ( userName!=null ? userName+"@" : "" ) + url.getHost() + url.getFile(); - panel.add( new JLabel( "Enter Login details to access
    "+n+" on
    "+s )); - } else { - panel.add( new JLabel( url.getHost() ) ); - } - JSeparator sep= new JSeparator( SwingConstants.HORIZONTAL ); - sep.setPreferredSize( new Dimension(0,16) ); - panel.add( sep ); - panel.add( new JLabel("Username:") ); - JTextField userTf= new JTextField(); - if ( !ss[0].equals("user") ) userTf.setText(userName); - panel.add( userTf ); - panel.add( new JLabel("Password:") ); - JPasswordField passTf= new JPasswordField(); - if ( ss.length>1 && !( ss[1].equals("pass")||ss[1].equals("password")) ) passTf.setText(ss[1]); - panel.add( passTf ); - - JCheckBox storeKeychain= new JCheckBox("store password in keychain.txt file"); - storeKeychain.setToolTipText("passwords can be stored in keychain.txt files in your cache, but beware of security implications and confusion this can cause."); - - if ( System.getProperty("user.name").equals("jbf") ) { - panel.add( storeKeychain ); - } - - //int r= JOptionPane.showConfirmDialog( null, panel, "Authentication Required", JOptionPane.OK_CANCEL_OPTION ); - int r= JOptionPane.showConfirmDialog( parent, panel, proto + " Authentication Required", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null); - if ( JOptionPane.OK_OPTION==r ) { - char[] pass= passTf.getPassword(); - storedUserInfo= userTf.getText() + ":" + new String(pass); - keys.put( hash, storedUserInfo ); - if ( storeKeychain.isSelected() ) { - try { - appendKeysFile( hash, storedUserInfo ); - } catch (IOException ex) { - logger.log( Level.WARNING, null, ex ); - } - } - return storedUserInfo; - } else if ( JOptionPane.CANCEL_OPTION==r ) { - throw new CancelledOperationException(); - } - } else { - if ( "true".equals( System.getProperty("java.awt.headless") ) ) { - System.err.println("** java.awt.headless=true: HEADLESS MODE means needed credentials cannot be queried"); - logger.log( Level.WARNING, "** java.awt.headless=true: HEADLESS MODE means needed credentials cannot be queried" ); - } - return userInfo; - } - } - - return userInfo; - } - - /** - * clear all passwords. - */ - public void clearAll() { - logger.fine("clear all cached passwords in the keychain, and reload all keychain.txt files."); - keys.clear(); - loadInitial(); - } - - public void clearUserPassword(URI uri) { - try { - clearUserPassword(uri.toURL()); - } catch (MalformedURLException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - } - /** - * remove the password from the list of known passwords. This was introduced - * because we needed to clear a bad password in FTP. - * @param url - */ - public void clearUserPassword(URL url) { - - String userInfo= url.getUserInfo(); - if ( userInfo==null ) return; - - String userName=null; - String[] ss= userInfo.split(":",-2); - if ( !ss[0].equals("user") ) { - userName= ss[0]; - if ( userName.contains("%40") ) { - userName= userName.replaceAll( "%40", "@" ); - } - } - - String hash= url.getProtocol() + "://" + ( userName!=null ? userName+"@" : "" ) + url.getHost(); - - String storedUserInfo= keys.get(hash); - if ( storedUserInfo!=null ) { - keys.remove(hash); - } - } - - /** - * plug the username and password into the URI. - * @param root - */ - public URI resolveUserInfo(URI root) throws CancelledOperationException { - try { - String userInfo = getUserInfo(root); - URI newuri = new URI(root.getScheme(), userInfo, root.getHost(), root.getPort(), root.getPath(), root.getQuery(), root.getFragment()); - return newuri; - } catch (URISyntaxException ex) { - throw new IllegalArgumentException(ex); - } - } - - public String hideUserInfo( URI root ) { - String userInfo= root.getUserInfo(); - int i= userInfo.indexOf(":"); - if ( i>-1 ) { - userInfo= userInfo.substring(0,i) + ":*****"; - } - URI uri; - try { - uri = new URI(root.getScheme(), userInfo, root.getHost(), root.getPort(), root.getPath(), root.getQuery(), root.getFragment()); - return uri.toString(); // suspect https://sourceforge.net/tracker/?func=detail&aid=3055130&group_id=199733&atid=970682 - } catch (URISyntaxException ex) { - throw new RuntimeException(ex); - } - } - - public static void main( String[]args ) throws MalformedURLException, CancelledOperationException { - KeyChain.getDefault().getUserInfo( new URL( "http://junomwg@www-pw.physics.uiowa.edu/juno/mwg/" ) ); - KeyChain.getDefault().getUserInfo( new URL( "ftp://jbf@localhost/" ) ); - } - - /** - * Add a cookie for the URL. This was added as a work-around to provide - * access to the MMS data server at LASP. - * @param url - * @param cookie - */ - public void addCookie( String url, String cookie ) { - if ( url.endsWith("/") ) { - url= url.substring(0,url.length()-1); - } - if ( !url.equals("https://lasp.colorado.edu/mms/sdc/about/browse") ) { - System.err.println("Warning: This is only works for https://lasp.colorado.edu/mms/sdc/about/browse"); - } - cookies.put( url, cookie ); - } - - /** - * Some servers want cookies to handle the authentication. This checks for - * "https://lasp.colorado.edu/mms/sdc/about/browse/" and handles logins for - * this server. - * @param url - * @return null or the cookie to include in the request header. - */ - protected String getCookie(URL url) { - String cookie= null; - if ( url.toString().contains("https://lasp.colorado.edu/mms/sdc/about/browse/") ) { - String hash= "https://lasp.colorado.edu/mms/sdc/about/browse"; - cookie= cookies.get(hash); - if ( cookie==null ) { - try { - - System.err.println( "See http://stackoverflow.com/questions/9619030/resolving-javax-net-ssl-sslhandshakeexception-sun-security-validator-validatore"); - System.err.println( "jsse.enableSNIExtension="+ System.getProperty("jsse.enableSNIExtension") ); - - URL urlr= new URL( "https://lasp-login.colorado.edu/idp/Authn/UserPassword" ); - HttpURLConnection conn= (HttpURLConnection) urlr.openConnection(); - - conn.setDoOutput(true); - conn.connect(); - - HtmlUtil.consumeStream(conn.getErrorStream()); - HtmlUtil.consumeStream(conn.getInputStream()); - - String cookie0= conn.getHeaderField("Set-Cookie"); - conn.disconnect(); - - String user= getUserInfo( new URL("https://lasp.colorado.edu/mms/sdc/about/browse"), "user:" ); - - conn= (HttpURLConnection) urlr.openConnection(); - conn.setDoOutput(true); - conn.setRequestMethod("POST"); - conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded" ); - int i= user.indexOf(":"); - String username= user.substring(0,i); - String password= user.substring(i+1); - String encodedData = "j_username="+URLEncoder.encode(username,"US-ASCII")+"&j_password="+URLEncoder.encode(password,"US-ASCII"); - conn.setRequestProperty( "Referer", "https://lasp-login.colorado.edu/Authn/UserPassword" ); - conn.setRequestProperty( "Content-Length", String.valueOf(encodedData.length())); - conn.setRequestProperty( "Cookie", cookie0 ); - conn.connect(); - OutputStream os = conn.getOutputStream(); - os.write(encodedData.getBytes("US-ASCII")); - os.close(); - String cookie1= conn.getHeaderField("Set-Cookie"); - os.close(); - cookie= cookie1; - } catch (MalformedURLException ex) { - Logger.getLogger(KeyChain.class.getName()).log(Level.SEVERE, null, ex); - } catch (CancelledOperationException ex) { - Logger.getLogger(KeyChain.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - Logger.getLogger(KeyChain.class.getName()).log(Level.SEVERE, null, ex); - } - } - //cookie= ""; // see email to jeremy-faden@uiowa.edu at 2015-09-01T12:50 CDT for cookie - //cookie= "_shibsession_64656661756c7468747470733a2f2f646d7a2d73686962322e6c6173702e636f6c6f7261646f2e6564752f73686962626f6c657468=_76cd22bfba4f8da6a96910259901710b"; - } - return cookie; - } - -} - diff --git a/dasCoreUtil/src/org/das2/util/filesystem/LocalFileObject.java b/dasCoreUtil/src/org/das2/util/filesystem/LocalFileObject.java deleted file mode 100755 index 0a4fe6c74..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/LocalFileObject.java +++ /dev/null @@ -1,161 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * LocalFileObject.java - * - * Created on May 25, 2004, 6:01 PM - */ - -package org.das2.util.filesystem; - -import java.util.logging.Level; -import java.util.logging.Logger; -import org.das2.util.monitor.ProgressMonitor; -import java.io.*; -import java.util.zip.GZIPInputStream; -import org.das2.util.monitor.NullProgressMonitor; - -/** - * - * @author Jeremy - */ -public class LocalFileObject extends FileObject { - - File localFile; - File localGzFile; - File localRoot; - LocalFileSystem lfs; - - protected LocalFileObject( LocalFileSystem lfs, File localRoot, String filename ) { - this.lfs= lfs; - this.localFile= new File( localRoot, filename ); - this.localGzFile= new File( localRoot, filename+".gz" ); - this.localRoot= localRoot; - } - - public boolean canRead() { - return localFile.canRead(); - } - - public FileObject[] getChildren() { - File[] files= localFile.listFiles(); - LocalFileObject[] result= new LocalFileObject[files.length]; - for ( int i=0; i T getCapability(Class clazz) { - if ( clazz==WriteCapability.class ) { - return (T) new WriteCapability() { - public OutputStream getOutputStream() throws FileNotFoundException { - return new FileOutputStream(localFile); - } - public boolean canWrite() { - return localFile.canWrite(); - } - public boolean delete() { - return localFile.delete(); - } - }; - } else { - return super.getCapability(clazz); - } - } - - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/LocalFileSystem.java b/dasCoreUtil/src/org/das2/util/filesystem/LocalFileSystem.java deleted file mode 100755 index 51ae04e15..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/LocalFileSystem.java +++ /dev/null @@ -1,145 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * FileWebFileSystem.java - * - * Created on May 14, 2004, 1:02 PM - */ - -package org.das2.util.filesystem; - -import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.regex.*; - -/** - * - * @author Jeremy - */ -public class LocalFileSystem extends FileSystem { - - File localRoot; - - /** - * Note the String used to create the URL should have either one or three slashes: - * file:/home/jbf or file:///home/jbf - * but not file://home/jbf - * Also, on Windows, /c:/documents and settings/jbf/ is okay. - */ - protected LocalFileSystem(URI root) throws FileSystemOfflineException { - super( root ); - if ( !("file".equals(root.getScheme()) ) ) { - throw new IllegalArgumentException("protocol not file: "+root); - } - String surl= root.getPath(); - - if ( !surl.endsWith("/") ) surl+="/"; - if ( surl.startsWith("file://") && !surl.startsWith("file:///") ) { - throw new URIException("Local file URLs should start with file:/ or file:///, but not file:// "+surl); - } - String[] split= FileSystem.splitUrl( surl ); - localRoot=new File( split[2].substring(split[0].length()) ); -// try { // simulate slow web site -// Thread.sleep(5000); -// } catch (InterruptedException ex) { -// logger.log(Level.SEVERE, null, ex); -// } - if ( !localRoot.exists() ) { - File[] roots= File.listRoots(); - if ( Arrays.asList(roots).contains(localRoot) ) { - throw new FileSystemOfflineException(); - } else { - throw new IllegalArgumentException( "local root does not exist: "+localRoot ); - } - } - boolean b= new File("xxx").equals(new File("XXX")); - properties.put( PROP_CASE_INSENSITIVE, Boolean.valueOf( b ) ); - } - - public boolean isDirectory(String filename) { - return new File( localRoot, filename ).isDirectory(); - } - - String getLocalName( File file ) { - if ( !file.toString().startsWith(localRoot.toString() ) ) { - throw new IllegalArgumentException( "file \""+file+"\"is not of this web file system" ); - } - String filename= file.toString().substring(localRoot.toString().length() ); - filename= filename.replaceAll( "\\\\", "/" ); - return filename; - } - - public String[] listDirectory(String directory) { - File f= new File( localRoot, directory ); - if ( !f.canRead() || ( f.getParentFile()!=null && f.isHidden() ) ) { - throw new IllegalArgumentException("cannot read directory " +f ); - } - File[] files= f.listFiles(); - if ( files==null ) { // On Windows, I was getting null with c:\Users\sklemuk\Documents. - return new String[0]; - } - List result= new ArrayList(); - for ( int i=0; i utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * LocalFileSystemFactory.java - * - * Created on November 15, 2007, 9:30 AM - */ - -package org.das2.util.filesystem; - -import java.net.URI; - -/** - * - * @author jbf - */ -public class LocalFileSystemFactory implements FileSystemFactory { - - /** Creates a new instance of LocalFileSystemFactory */ - public LocalFileSystemFactory() { - } - - public FileSystem createFileSystem(URI root) throws FileSystem.FileSystemOfflineException { - return new LocalFileSystem(root); - } - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/SubFileSystem.java b/dasCoreUtil/src/org/das2/util/filesystem/SubFileSystem.java deleted file mode 100644 index 13743e754..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/SubFileSystem.java +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright (C) 2003-2015 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * SubFileSystem.java - * - * Created on January 16, 2007, 2:19 PM - * - * - */ - -package org.das2.util.filesystem; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URISyntaxException; - -/** - * present part of a filesystem as a filesystem. - * @author Jeremy - */ -public class SubFileSystem extends FileSystem { - FileSystem parent; - String dir; - - private static String trimFront( String dir ) { - int i=0; - while ( i -1) { - if (monitor.isCancelled()) { - throw new InterruptedIOException(); - } - monitor.setTaskProgress(totalBytesRead); - out.write(buffer, 0, bytesRead); - bytesRead = is.read(buffer, 0, 2048); - totalBytesRead += bytesRead; - logger.finest("transferring data"); - } - } - - /** - * Transfers the file from the remote store to a local copy f. This should only be - * used within the class and subclasses, clients should use getFileObject( String ).getFile(). - * - * @param filename the name of the file, relative to the filesystem. - * @param f the file to where the file is downloaded. - * @param partfile the temporary file during download. - */ - protected void downloadFile(String filename, File f, File partfile, ProgressMonitor monitor) throws IOException { - // This shouldn't be called for local files, but just in case... - if (isLocal()) { - return; - } - - Lock lock = getDownloadLock(filename, f, monitor); - - if (lock == null) { - return; //Another thread downloaded the file - } - - try { - if ( filename.startsWith(fsuri.getPath()) ) { - logger.log( Level.INFO, "something is funny, we have the path twice:{0} {1}", new Object[]{filename, fsuri}); - } - filename = fsuri.getPath() + filename; - org.apache.commons.vfs.FileObject vfsob = vfsSystem.resolveFile(filename); - - if(!vfsob.exists()) { - throw new FileNotFoundException("attempt to download non-existent file: "+vfsob); - } - - long size = vfsob.getContent().getSize(); - monitor.setTaskSize(size); - - // If necessary, create destination folder - if (!f.getParentFile().exists()) { - logger.log(Level.FINE, "Creating destination directory {0}", f.getParentFile()); - FileSystemUtil.maybeMkdirs( f.getParentFile() ); - } - - if (partfile.exists()) { - logger.fine("Deleting existing partfile."); - if ( ! partfile.delete() ) { - throw new IllegalArgumentException("unable to delete "+partfile ); - } - - } - - // create partfile - if (partfile.createNewFile()) { - InputStream is = vfsob.getContent().getInputStream(); - FileOutputStream os = new FileOutputStream(partfile); - - monitor.setLabel("Downloading file..."); - monitor.started(); - try { - copyStream(is, os, monitor); - is.close(); - os.close(); - if ( ! partfile.renameTo(f) ) { - throw new IllegalArgumentException("unable to rename file "+partfile + " to "+f ); - } - } catch (IOException e) { - // clean up and pass the exception on - is.close(); - os.close(); - if ( partfile.exists() && ! partfile.delete() ) { - throw new IOException("unable to delete file "+partfile ); - } - throw (e); - } - } else { - // failed to create partfile - throw new IOException("Error creating local file " + f); - } - } finally { - // Ensure that the download lock is released no matter what - lock.unlock(); - monitor.finished(); - } - } - - @Override - protected void finalize() throws Throwable { - // ensure that any open VFS filesystem gets closed so threads terminate - try { - close(); - } finally { - super.finalize(); - } - } - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/VFSFileSystemFactory.java b/dasCoreUtil/src/org/das2/util/filesystem/VFSFileSystemFactory.java deleted file mode 100755 index 9139a02f0..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/VFSFileSystemFactory.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.das2.util.filesystem; - -import java.net.URI; -import java.net.UnknownHostException; -import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; - -/** - * - * @author ed - */ -public class VFSFileSystemFactory implements FileSystemFactory { - - public VFSFileSystemFactory() { - } - - public FileSystem createFileSystem(URI root) throws FileSystemOfflineException, UnknownHostException { - VFSFileSystem vfs = VFSFileSystem.createVFSFileSystem(root); - return vfs; - } -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/WebFileObject.java b/dasCoreUtil/src/org/das2/util/filesystem/WebFileObject.java deleted file mode 100644 index e93a4883e..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/WebFileObject.java +++ /dev/null @@ -1,572 +0,0 @@ -/* Copyright (C) 2003-2015 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * WebFile.java - * - * Created on May 14, 2004, 10:06 AM - */ -package org.das2.util.filesystem; - -//import java.awt.EventQueue; -import java.util.logging.Level; -import org.das2.util.monitor.ProgressMonitor; -import org.das2.util.monitor.NullProgressMonitor; -import java.io.*; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.channels.Channels; -import java.util.*; -import java.util.logging.Logger; -import org.das2.util.Base64; -import org.das2.util.filesystem.FileSystem.DirectoryEntry; -import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; -import static org.das2.util.filesystem.FileSystem.loggerUrl; -import org.das2.util.monitor.CancelledOperationException; - -/** - * - * @author Jeremy - * - * This is a general-purpose FileObject representing items where latency requires a little - * caching of metadata. This is used for both HTTP and FTP implementations. - * - */ -public class WebFileObject extends FileObject { - - private static final Logger logger= org.das2.util.LoggerManager.getLogger("das2.filesystem.wfs"); - - final WebFileSystem wfs; - String pathname; - File localFile; - boolean isRoot; - boolean isFolder; - Map metadata; - long metaFresh= 0; // freshness of the metadata. - Date modifiedDate; // more accessible version of the metadata - long size=-1; // more accessible version of the metadata - - /** - * true if we know if it's a folder or not. - */ - boolean isFolderResolved = false; - public int METADATA_FRESH_TIMEOUT_MS = 10000; - - @Override - public boolean canRead() { - return true; - } - - /** - * load the metadata for the file object, presently only for HTTP doing a HEAD request. - * This is nasty and needs to be rewritten... - * For thread safety, metadata should only be read by other threads, and only if it exists. - * @throws IOException - */ - protected synchronized void maybeLoadMetadata() throws IOException { - if ( metadata==null ) { - if ( this.wfs.offline ) { - if ( FileSystem.settings().isOffline() ) { //bug https://sourceforge.net/tracker/?func=detail&aid=3578171&group_id=199733&atid=970682 - metadata= new HashMap(); - metadata.put( WebProtocol.META_EXIST, isLocal() ? "true" : "false" ); - } else { - if ( wfs.protocol!=null ) { - metadata= wfs.protocol.getMetadata( this ); - } - } - } else { - if ( wfs.protocol!=null ) { - metadata= wfs.protocol.getMetadata( this ); - } - } - metaFresh= System.currentTimeMillis(); - } - } - - @Override - public FileObject[] getChildren() throws IOException { - if (!isFolder) { - throw new IllegalArgumentException(toString() + "is not a folder"); - } - String[] list = wfs.listDirectory(pathname); - FileObject[] result = new FileObject[list.length]; - for (int i = 0; i < result.length; i++) { - result[i] = new WebFileObject(wfs, list[i], new Date(System.currentTimeMillis())); - } - return result; - } - - @Override - public InputStream getInputStream(ProgressMonitor monitor) throws FileNotFoundException, IOException { - if ( wfs.protocol !=null && !this.wfs.offline ) { - logger.log(Level.FINE, "get inputstream from {0}", wfs.protocol); - return wfs.protocol.getInputStream(this, monitor); - } - if (isFolder) { - throw new IllegalArgumentException("is a folder"); - } - - if ( this.modifiedDate.getTime()==0 ) { - lastModified(); // trigger load of the modifiedDate - } - if ( !localFile.exists() || ( this.modifiedDate.getTime()-localFile.lastModified() > 10 ) && !this.wfs.isOffline() ) { //TODO: test me! - File partFile = new File(localFile.toString() + ".part"); - wfs.downloadFile(pathname, localFile, partFile, monitor); - } - logger.log( Level.FINE, "read local file {0}", localFile); - return new FileInputStream(localFile); - } - - /** - * - * @return a WebFileObject referencing the parent directory. - */ - @Override - public FileObject getParent() { - return new WebFileObject(wfs, wfs.getLocalName(localFile.getParentFile()), new Date(System.currentTimeMillis())); - } - - @Override - public boolean isData() { - return !this.isFolder; - } - - @Override - public boolean isFolder() { - if ( this.isFolderResolved ) { - return this.isFolder; - } else { - //TODO: make HttpFileObject that does HEAD requests to properly answer these questions. See HttpFileSystem.getHeadMeta() - throw new RuntimeException("IOException in constructor prevented us from resolving"); - } - } - - @Override - public boolean isReadOnly() { - return true; - } - - @Override - public boolean isRoot() { - return this.isRoot; - } - - @Override - public java.util.Date lastModified() { - if ( System.currentTimeMillis() - metaFresh > METADATA_FRESH_TIMEOUT_MS ) { - metadata= null; - modifiedDate= new Date( Long.MAX_VALUE ); - } - if ( modifiedDate.getTime()==Long.MAX_VALUE ) { - try { - maybeLoadMetadata(); - } catch ( IOException ex ) { - logger.log(Level.FINE, "unable to load metadata: {0}", ex); - modifiedDate= new Date( localFile.lastModified() ); - } - if ( metadata!=null && metadata.containsKey("Last-Modified") ) { - long date= Date.parse( metadata.get("Last-Modified") ); - modifiedDate= new Date( date ); - } else { - logger.fine("metadata doesn't contain Last-Modified, using localFile" ); - modifiedDate= new Date( localFile.lastModified() ); - } - } - return new Date(modifiedDate.getTime()); - } - - /** - * return the fileObject size in bytes. This may contact the server to get the size, and this - * caches the size. - * @return - */ - @Override - public long getSize() { - if (isFolder) { - throw new IllegalArgumentException("is a folder"); - } - if ( this.size==-1 ) { - try { - maybeLoadMetadata(); - } catch ( IOException ex ) { - logger.log(Level.FINE, "unable to load metadata: {0}", ex); - size= localFile.length(); - } - if ( metadata.containsKey("Content-Length") ) { - size= Long.parseLong(metadata.get("Content-Length") ); - } else { - logger.fine("remote length is not known"); - size= localFile.length(); - } - } - return size; - } - - /** - * allow subclasses, such as FtpBeanFileSystem, to delay loading of the date. - * @param d - */ - protected void setLastModified( Date d ) { - if ( this.modifiedDate.getTime()==0 || this.modifiedDate.getTime()==Long.MAX_VALUE ) { - this.modifiedDate= d; - } else { - if ( !d.equals(modifiedDate) ) { - throw new IllegalArgumentException("valid date cannot be modified"); - } - } - } - - /** - * allow classes to delay loading of the size. - * @param size the size in bytes of the file. - * @throws IllegalArgumentException if the size is reset to a different value from a valid value. - */ - protected void setSize( long size ) { - if ( this.size==-1 ) { - this.size= size; - } else { - if ( size!=this.size ) { - throw new IllegalArgumentException("valid size cannot be modified"); - } - } - } - - /** - * returns the File that corresponds to the remote file. This may or may - * not exist, depending on whether it's been downloaded yet. - * @return the file reference within the cache. - */ - protected File getLocalFile() { - return this.localFile; - } - - @Override - public boolean removeLocalFile() { - if ( this.localFile==null ) { - logger.fine("failed to removeLocalFile, it is null. Applet mode" ); - return true; - } - if ( !this.localFile.exists() ) { - logger.fine("localfile does not exist." ); - return true; - } - if ( this.localFile.canWrite() ) { - if ( !this.localFile.delete() ) { - logger.log(Level.FINE, "failed to removeLocalFile: {0}", this.localFile); - return false; - } else { - logger.log(Level.FINER, "local file was removed: {0}", localFile); - return true; - } - } else { - logger.log(Level.FINE, "user does not have access to delete the local file: {0}", this.localFile); - return true; - } - } - - @Override - public boolean exists() { - if ( wfs.getReadOnlyCache()!=null ) { - File f= wfs.getReadOnlyCache(); - File ff= new File( f, this.pathname ); - if ( ff.exists() ) return true; - } - if ( localFile!=null && localFile.exists()) { // applet support - return true; - } else { - try { - if ( wfs.protocol!=null ) { - maybeLoadMetadata(); - return "true".equals( metadata.get( WebProtocol.META_EXIST ) ); - } else { - // TODO: use HTTP HEAD, etc - logger.fine("This implementation of WebFileObject.exists() is not optimal"); - File partFile = new File(localFile.toString() + ".part"); - wfs.downloadFile(pathname, localFile, partFile, new NullProgressMonitor()); - return localFile.exists(); - } - } catch (FileNotFoundException e) { - return false; - } catch (IOException e) { - // I'm going to assume that it's because the file was not found. 404's from pw's server end up here - return false; - } - } - } - - protected WebFileObject( WebFileSystem wfs, String pathname, Date modifiedDate ) { - - this.modifiedDate = modifiedDate; - - this.wfs = wfs; - this.pathname = pathname; - this.isFolderResolved = false; - - if ( ! wfs.isAppletMode() ) { - this.localFile = new File(wfs.getLocalRoot(), pathname); // TODO: perhaps this should be ro_cache.txt... - - if ( FileSystem.settings().getPersistence()==FileSystemSettings.Persistence.SESSION ) this.localFile.deleteOnExit(); - - try { - if (!localFile.canRead()) { - if ( !( pathname.endsWith(".zip") || pathname.endsWith(".ZIP") ) && wfs.isDirectory(pathname) ) { // klugde, see https://sourceforge.net/tracker/index.php?func=detail&aid=3049303&group_id=199733&atid=970682 - FileSystemUtil.maybeMkdirs(localFile); - this.isFolder = true; - if ("".equals(pathname)) { - this.isRoot = true; - } - } else { - this.isFolder = false; - } - } else { - this.isFolder = localFile.isDirectory(); - } - this.isFolderResolved= true; - } catch (IOException ex) { - logger.log(Level.SEVERE,"unable construct web file object",ex); - this.isFolderResolved = false; - } - } - } - - @Override - public String toString() { - return "[" + wfs + "]" + getNameExt(); - } - - @Override - public String getNameExt() { - return pathname; - } - - /** - * return a Channel for the resource. If the resource can be made locally available, a FileChannel is returned. - * @param monitor - * @return - * @throws java.io.FileNotFoundException - * @throws java.io.IOException - */ - @Override - public java.nio.channels.ReadableByteChannel getChannel(ProgressMonitor monitor) throws FileNotFoundException, IOException { - InputStream in= getInputStream(monitor); - return Channels.newChannel(in); - } - - /** - * return the file for the WebFileObject. - * @param monitor - * @return - * @throws FileNotFoundException - * @throws IOException - */ - @Override - public File getFile(ProgressMonitor monitor) throws FileNotFoundException, IOException { - - if ( wfs.isAppletMode() ) throw new SecurityException("getFile cannot be used with applets."); - - //bugfix http://sourceforge.net/tracker/?func=detail&aid=3155917&group_id=199733&atid=970682: - // calling EventQueue.isDispatchThread() starts the event thread, causing problems when called from RSI's IDL. -// if ( false ) { -// if ( EventQueue.isDispatchThread() ) { -// logger.log(Level.SEVERE, "download on event thread! {0}", this.getNameExt()); -// } -// } - - boolean download = false; - - if ( monitor==null ) throw new NullPointerException("monitor may not be null"); - Date remoteDate; - long remoteLength=0; - - //check readonly cache for file. - if ( this.wfs.getReadOnlyCache()!=null ) { - File cacheFile= new File( this.wfs.getReadOnlyCache(), this.getNameExt() ); - if ( cacheFile.exists() ) { - logger.log(Level.FINE, "using file from ro_cache: {0}", this.getNameExt()); - return cacheFile; - } - } - - if ( isLocal() ) { // isLocal does a careful check of timestamps, and minds the limits on access. - remoteDate = new Date(localFile.lastModified()); - remoteLength= localFile.length(); - - } else if (wfs instanceof HttpFileSystem && !wfs.isOffline() ) { - URL url = wfs.getURL(this.getNameExt()); - loggerUrl.log( Level.FINE, "HEAD to get timestamp: {0}",url); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("HEAD"); - - String userInfo= null; - - try { - userInfo = KeyChain.getDefault().getUserInfo( url ); - } catch (CancelledOperationException ex) { - throw new FileSystemOfflineException("user cancelled credentials"); - } - - if ( userInfo != null) { - String encode = Base64.encodeBytes( userInfo.getBytes()); - connection.setRequestProperty("Authorization", "Basic " + encode); - } - - String cookie= ((HttpFileSystem)wfs).getCookie(); - if ( cookie!=null ) { - connection.setRequestProperty("Cookie", cookie ); - } - - try { - connection.connect(); - remoteDate = new Date(connection.getLastModified()); // here bug 1393 w/webstart https://sourceforge.net/p/autoplot/bugs/1393/ - logger.log(Level.FINE, "HEAD request reports connection.getLastModified()={0}", remoteDate); - int contentLength= connection.getContentLength(); - if ( contentLength>-1 ) remoteLength= contentLength; - - } catch ( IOException ex ) { - if ( !((HttpFileSystem)wfs).isOffline() ) { - throw ex; - } else { - remoteDate= new Date(0); - } - } - } else { - if ( this.lastModified().getTime()==0 || this.lastModified().getTime()==Long.MAX_VALUE ) { - DirectoryEntry result= wfs.maybeUpdateDirectoryEntry( this.getNameExt(), true ); // trigger load of the modifiedDate - if ( result==null ) { - logger.fine("file does not exist on remote filesystem"); - } else { - result= wfs.maybeUpdateDirectoryEntry( this.getNameExt(), true ); - remoteDate= new Date( result.modified ); - remoteLength= result.size; - this.setLastModified( remoteDate ); - this.setSize( remoteLength ); - } - if ( !( wfs instanceof HttpFileSystem ) ) { - download= true; - } //FTP filesystem timetags are very course. - } - remoteDate = this.lastModified(); - remoteLength= this.getSize(); - } - - if (localFile.exists()) { - Date localFileLastModified = new Date(localFile.lastModified()); - if (remoteDate.after(localFileLastModified) || remoteLength!=localFile.length() ) { - logger.log(Level.FINE, "remote file length is different or is newer than local copy of {0}, download.", this.getNameExt()); - download = true; - } - } else { - download = true; - } - - //check readonly cache for file. - if ( download && this.wfs.getReadOnlyCache()!=null ) { - File cacheFile= new File( this.wfs.getReadOnlyCache(), this.getNameExt() ); - if ( cacheFile.exists() ) { - logger.log(Level.FINE, "using file from ro_cache: {0}", this.getNameExt()); - return cacheFile; - } - } - - if (download) { - try { - logger.log(Level.FINE, "downloading file {0}", getNameExt()); - if (!localFile.getParentFile().exists()) { - FileSystemUtil.maybeMkdirs( localFile.getParentFile() ); - } - File partFile = wfs.getPartFile( localFile ); - wfs.downloadFile(pathname, localFile, partFile, monitor.getSubtaskMonitor("download file")); - - if ( !localFile.setLastModified(remoteDate.getTime()) ) { - logger.log(Level.FINE, "unable to modify date of {0}", localFile); - } - - logger.log(Level.FINE, "downloaded local file has date {0}", new Date(localFile.lastModified())); - - } catch (FileNotFoundException e) { - // TODO: do something with part file. - throw e; - } catch (IOException ex ) { - if ( ex.getMessage()!=null && ex.getMessage().contains("Forbidden") ) { - throw ex; - } - if ( this.wfs instanceof HttpFileSystem && !(ex instanceof InterruptedIOException ) ) { //TODO: when would we use this--it needs to be more precise. - if ( this.wfs.isOffline() ) { - logger.log(Level.SEVERE,"unable getFile",ex); - throw new FileSystem.FileSystemOfflineException("not found in local cache: "+getNameExt() ); - } - } - throw ex; - } finally { - monitor.finished(); - } - } - - return localFile; - - } - - /** - * returns true is the file is locally available, meaning clients can - * call getFile() and the readable File reference will be available in - * interactive time. For FileObjects from HttpFileSystem, a HEAD request - * is made to ensure that the local file is as new as the website one (when offline=false). - * @return true if the file is local and can be used without web access. - */ - @Override - public boolean isLocal() { - if ( wfs.isAppletMode() ) return false; - - boolean download; - - if ( this.wfs.getReadOnlyCache()!=null ) { - File cacheFile= new File( this.wfs.getReadOnlyCache(), this.getNameExt() ); - if ( cacheFile.exists() ) { - logger.log(Level.FINE, "file exists in ro_cache, so trivially local: {0}", this.getNameExt()); - return true; - } - } - - if (localFile.exists()) { - if ( !wfs.isOffline() ) { - try { - synchronized ( wfs ) { - DirectoryEntry remoteDate= (DirectoryEntry) wfs.accessCache.doOp( this.getNameExt() ); - long localFileLastModified = localFile.lastModified(); - setLastModified( new Date(remoteDate.modified) ); - setSize( remoteDate.size ); - if ( remoteDate.modified > localFileLastModified ) { - logger.log(Level.FINE, "remote file is newer than local copy of {0}, download.", this.getNameExt()); - download = true; - } else download = remoteDate.size!= localFile.length(); - } - } catch ( Exception ex ) { - logger.log( Level.WARNING, ex.getMessage(), ex ); - return false; - } - } else { - return true; - } - - } else { - download = true; - } - - return !download; - - } -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/WebFileSystem.java b/dasCoreUtil/src/org/das2/util/filesystem/WebFileSystem.java deleted file mode 100644 index dd38e1a9b..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/WebFileSystem.java +++ /dev/null @@ -1,873 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * WebFileSystem.java - * - * Created on May 13, 2004, 1:22 PM - * - * A WebFileSystem allows web files to be opened just as if they were - * local files, since it manages the transfer of the file to a local - * file system. - */ -package org.das2.util.filesystem; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InterruptedIOException; -import java.io.OutputStream; -import java.lang.management.ManagementFactory; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import org.das2.util.monitor.ProgressMonitor; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.logging.Logger; -import java.util.regex.Pattern; -import org.das2.util.FileUtil; -import org.das2.util.monitor.CancelledOperationException; - -/** - * Base class for HTTP and FTP-based filesystems. A local cache is kept of - * the files. - * - * @author Jeremy - */ -public abstract class WebFileSystem extends FileSystem { - - protected static final Logger logger= org.das2.util.LoggerManager.getLogger( "das2.filesystem.wfs" ); - - /** - * we keep a cached listing in on disk. This is backed by the website. - */ - public static final int LISTING_TIMEOUT_MS = 10000; - - /** - * we keep a cached listing in memory for performance. This is backed by the .listing file. - */ - public static final int MEMORY_LISTING_TIMEOUT_MS= 4000; - - /** - * timestamp checks will occur no more often than this. - */ - public static final int HTTP_CHECK_TIMESTAMP_LIMIT_MS = 4000; - - public static File getDownloadDirectory() { - File local = FileSystem.settings().getLocalCacheDir(); - return local; - } - - /** - * get access times via ExpensiveOpCache, which limits the number of HEAD requests to the server. - */ - ExpensiveOpCache accessCache; - - protected final File localRoot; - /** - * if true, then don't download to local cache. Instead, provide inputStream - * and getFile throws exception. - */ - private boolean applet; - /** - * plug-in template for implementation. if non-null, use this. - */ - protected WebProtocol protocol; - - /** - * true means only local files are used from the cache. - */ - protected boolean offline = false; - - /** - * the response message explaining why the filesystem is offline. - */ - protected String offlineMessage= ""; - - /** - * if true, then the remote filesystem is not accessible, but local cache - * copies may be accessed. See FileSystemSettings.allowOffline - */ - public static final String PROP_OFFLINE = "offline"; - - public boolean isOffline() { - return offline; - } - - /** - * @param offline - */ - public void setOffline(boolean offline) { - boolean oldOffline = offline; - this.offline = offline; - //FileSystem.settings().setOffline(true); some may be online, some offline. - propertyChangeSupport.firePropertyChange(PROP_OFFLINE, oldOffline, offline); - } - - /** - * return the reason (if any provided) why the filesystem is offline, - * @return the message for the response code - */ - public String getOfflineMessage() { - return offlineMessage; - } - - protected int offlineResponseCode= 0; - - /** - * if non-zero, the response code (e.g. 403) why the filesystem is offline. - * @return the response code. - */ - public int getOfflineResponseCode() { - return offlineResponseCode; - } - - /** - * alternate location to check for file before downloading. - */ - public static final String PROP_READ_ONLY_CACHE= "readOnlyCache"; - - private File readOnlyCache= null; - - public final void setReadOnlyCache( File f ) { - File oldValue= this.readOnlyCache; - this.readOnlyCache= f; - propertyChangeSupport.firePropertyChange(PROP_READ_ONLY_CACHE, oldValue, readOnlyCache ); - } - - public final File getReadOnlyCache( ) { - return this.readOnlyCache; - } - - - private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); - - public void addPropertyChangeListener(PropertyChangeListener listener) { - propertyChangeSupport.addPropertyChangeListener(listener); - } - - public void removePropertyChangeListener(PropertyChangeListener listener) { - propertyChangeSupport.removePropertyChangeListener(listener); - } - - /** - * return the name of the folder containing a local copy of the cache. - * @param localRoot the root which will typically be a subfolder of - * FileSystem.settings().getLocalCacheDir(); - * @return null or ... - */ - - private static File lookForROCache( File start ) { - - File localRoot= start; - File stopFile= FileSystem.settings().getLocalCacheDir(); - File result= null; - - if ( !localRoot.toString().startsWith(stopFile.toString()) ) { - throw new IllegalArgumentException("localRoot filename must start with start filename"); - } - - while ( !( localRoot.equals(stopFile) ) ) { - File f= new File( localRoot, "ro_cache.txt" ); - if ( f.exists() ) { - BufferedReader read = null; - try { - read = new BufferedReader(new FileReader(f)); - String s = read.readLine(); - while (s != null) { - int i= s.indexOf("#"); - if ( i>-1 ) s= s.substring(0,i); - if ( s.trim().length()>0 ) { - if ( s.startsWith("http:") || s.startsWith("https:") || s.startsWith("ftp:") ) { - throw new IllegalArgumentException("ro_cache should contain the name of a local folder"); - } - String sf= s.trim(); - result= new File(sf); - break; - } - s = read.readLine(); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } finally { - try { - if ( read!=null ) read.close(); - } catch (IOException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - } - } - break; - } else { - localRoot= localRoot.getParentFile(); - } - } - if ( result==null ) { - return result; - } else { - String tail= start.getAbsolutePath().substring(localRoot.getAbsolutePath().length()); - if ( tail.length()>0 ) { - return new File( result, tail ); - } else { - return result; - } - } - } - - /** - * Allow the local RO Cache to contain files that are not yet in the remote filesystem, to support the case - * where a data provider tests locally available products before mirroring them out to the public website. - * @param directory - * @param remoteList - * @return - */ - protected Map addRoCacheEntries( String directory, Map remoteList ) { - File f= this.getReadOnlyCache(); - if ( f!=null ) { - String[] ss= new File( f, directory ).list(); - if ( ss==null ) return remoteList; - List add= new ArrayList(); - for ( String s: ss ) { - File f1= new File( f, directory+s ); - if ( f1.isDirectory() ) { - s= s+"/"; //TODO: verify windows. - } - if ( !remoteList.containsKey(s) ) { - - DirectoryEntry de1= new DirectoryEntry(); - de1.modified= f1.lastModified(); - de1.name= s; - de1.type= f1.isDirectory() ? 'd': 'f'; - de1.size= f1.length(); - add.add( de1 ); - } - } - for ( DirectoryEntry de1: add ) { - remoteList.put( de1.name, de1 ); - } - } - return remoteList; - } - - /** Creates a new instance of WebFileSystem - * @param root the remote URI. - * @param localRoot local directory used to store local copies of the data. - */ - protected WebFileSystem(URI root, File localRoot) { - super(root); - this.localRoot = localRoot; - if (localRoot == null) { - if ( root.getScheme().equals("http") - || root.getScheme().equals("https" ) ) { - this.protocol = new AppletHttpProtocol(); - } - } else { - if (root.getScheme().equals("http") - || root.getScheme().equals("https" ) ) { - this.protocol = new DefaultHttpProtocol(); - } - File f= lookForROCache( localRoot ); - if ( f!=null ) { - setReadOnlyCache( f ); - } - } - - ExpensiveOpCache.Op accessTime; - - if ( root.getScheme().equals("http") || root.getScheme().equals("https") ) { - accessTime= new LastAccessTime(); - } else { - accessTime= new ListingsLastAccessTime(); - } - this.accessCache= new ExpensiveOpCache( accessTime, HTTP_CHECK_TIMESTAMP_LIMIT_MS ); - - } - - private class LastAccessTime implements ExpensiveOpCache.Op { - - @Override - public Object doOp(String key) throws IOException { - URL url = getURL(key); - loggerUrl.log( Level.FINE, "HEAD to get timestamp: {0}",url); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - try { - String encode= KeyChain.getDefault().getUserInfoBase64Encoded(url); - if ( encode!=null ) { - connection.setRequestProperty("Authorization", "Basic " + encode); - } - } catch (CancelledOperationException ex) { - logger.log(Level.INFO,"user cancelled auth dialog"); - // this is what we would do before. - } - connection.setRequestMethod("HEAD"); - connection.connect(); - DirectoryEntry result= new DirectoryEntry(); - result.modified= connection.getLastModified(); - result.name= key; - result.size= connection.getContentLength(); - connection.disconnect(); - logger.log(Level.FINER, "done HEAD request to get timestamp ({0})", url); - return result; - } - } - - /** - * assume local files contain the last access time. This uses the .listings file and - * assumes that listDirectory will be keeping things up-to-date. - */ - private class ListingsLastAccessTime implements ExpensiveOpCache.Op { - @Override - public Object doOp(String key) throws IOException { - File localFile= new File( getLocalRoot(), key ); - String name= localFile.getName(); // remove the path information - String parent= WebFileSystem.this.getLocalName( localFile.getParentFile() ); - parent= parent + '/'; - String[] ss= listDirectory( parent ); // fill the cache - logger.log( Level.FINE, "ss.length={0}", ss.length ); - DirectoryEntry[] des= listDirectoryFromMemory(parent); - if ( des==null ) return new Date(0); - for (DirectoryEntry de : des) { - if (de.name.equals(name)) { - return de; - } - } - return FileSystem.NULL; - } - } - - /** - * return the local root for the URI. - * @param root the URI such as http://das2.org/data/ - * @return /home/jbf/autoplot_data/fscache/http/das2.org/data/ - */ - public static File localRoot(URI root) { - - File local = FileSystem.settings().getLocalCacheDir(); - - logger.log( Level.FINE, "WFS localRoot={0}", local); - - String s = root.getScheme() + "/" + root.getHost() + "/" + root.getPath(); //TODO: check getPath - - local = new File(local, s); - try { - FileSystemUtil.maybeMkdirs(local); - } catch (IOException ex) { - throw new IllegalArgumentException( ex ); - } - - return local; - } - - /** - * Keep track of active downloads. This handles, for example, the case - * where the same file is requested several times by different threads. - */ - private final Map downloads = new HashMap(); - - /** - * Wait while another thread is downloading the file. - * @param monitor this thread's monitor. - * @param mon the monitor of the thread doing the download. - * @param filename - * @throws java.lang.RuntimeException - */ - private void waitForDownload( ProgressMonitor monitor, final String filename ) { - - monitor.setProgressMessage("waiting for file to download"); - - ProgressMonitor downloadMonitor = (ProgressMonitor) downloads.get(filename); - - monitor.started(); - - while (downloadMonitor != null) { - - // in case downloadMonitor switched from indeterminate to determinate - monitor.setTaskSize( downloadMonitor.getTaskSize() ); - - // this monitor can tell the downloading monitor to cancel. - if (monitor.isCancelled()) { - downloadMonitor.cancel(); - } - - if ( !monitor.isCancelled() ) { //TODO: syncronized block or something - // echo what the download monitor is reporting. - monitor.setTaskProgress(downloadMonitor.getTaskProgress()); - } - - try { - downloads.wait(100); // wait 100ms, then proceed to support progress information - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - downloadMonitor = (ProgressMonitor) downloads.get(filename); - - if ( downloadMonitor!=null && downloadMonitor.isFinished() ) { - logger.warning("watched downloadMonitor is finished but is not being unlocked"); - monitor.setProgressMessage("file is downloaded, just a moment"); - } - } - - monitor.finished(); - } - - /** - * ID string for the process. - */ - protected static final String id= String.format( "%014d_%s", System.currentTimeMillis(), ManagementFactory.getRuntimeMXBean().getName() ); - - /** - * return a name where the download is to be staged. This will - * be unique for any process. We make the following assumptions: - *
      - *
    • multiple Java processes will be using and modifying the file database. - *
    • java processes may be on different machines - *
    • an ID conflict may occur should two processes have the same UID, hostname, and are started at the same clock time millisecond. - *
    - * See https://sourceforge.net/p/autoplot/bugs/1301/ - * @param localFile - * @return the temporary filename to use. - */ - public final File getPartFile( File localFile ) { - return new File( localFile.toString()+ "." + id + ".part" ); - } - - /** - * Request lock to download file. If this thread gets the lock, then it - * should download the file and call mutatorLock.unlock() when the - * download is complete. If another thread is downloading the file, this - * will block until the download is complete, and null will be returned to - * indicate that the file has already been downloaded. This must start the - * monitor when it gets the lock. - * - * @param filename the filename with in the filesystem. - * @param f the File which will be the local copy. - * @param monitor a monitor for the download. If a MutatorLock is returned, then - * the monitor is not touched, but other threads may use it to keep track - * of the download progress. - * @throws FileNotFoundException if the file wasn't found after another thread loaded the file. - * @return Lock or null if another thread loaded the resource. The client should call lock.unlock() when the download is complete - */ - protected Lock getDownloadLock(final String filename, File f, ProgressMonitor monitor) throws IOException { - logger.log(Level.FINER, "{0} wants download lock for {1} wfs impl {2}", new Object[]{Thread.currentThread().getName(), filename, this.hashCode()}); - synchronized (downloads) { - ProgressMonitor mon = (ProgressMonitor) downloads.get(filename); - if (mon != null) { // the webfilesystem is already loading this file, so wait. - logger.log(Level.FINER, "another thread is downloading {0}, waiting...", filename); - waitForDownload( monitor, filename ); //TODO: this seems strange, that we would have this in a synchronized block. - if (f.exists()) { - return null; - } else { - if ( monitor.isCancelled() ) { - throw new InterruptedIOException("request was cancelled"); - } else { - throw new FileNotFoundException("expected to find " + f); - } - } - } else { - logger.log(Level.FINER, "this thread will download {0}.", filename); - downloads.put(filename, monitor); - monitor.started(); // this is necessary for the other monitors - return new LocalReentrantLock(filename); - } - } - } - - private class LocalReentrantLock extends ReentrantLock { - String filename; - private LocalReentrantLock( String filename ) { - this.filename= filename; - } - @Override - public void lock() { - } - - @Override - public void unlock() { - synchronized (downloads) { - downloads.remove(filename); - downloads.notifyAll(); - } - } - } - - /** - * Transfers the file from the remote store to a local copy f. This should only be - * used within the class and subclasses, clients should use getFileObject( String ).getFile(). - * Subclasses implementing this should download data to partfile, then rename partfile to - * f after the download is complete. - * - * Note, this is non-trivial, since several threads and even several processes may be using - * the same area at once. See HttpFileSystem's implementation of this before attempting to - * implement the function. - * - * @param filename the name of the file, relative to the filesystem. - * @param f the file to where the file is downloaded. - * @param partfile the temporary file during download. - * @param monitor progress monitor - * @throws java.io.IOException - */ - protected abstract void downloadFile(String filename, File f, File partfile, ProgressMonitor monitor) throws IOException; - - /** Get the root of the local file cache - * @return the root of the local file cache - * @deprecated use getLocalRoot().getAbsolutePath() - */ - public String getLocalRootAbsPath() { - return this.localRoot.getAbsolutePath(); - } - - @Override - public File getLocalRoot() { - return this.localRoot; - } - - public synchronized void resetListingCache() { - if ( !FileUtil.deleteWithinFileTree(localRoot,".listing") ) { // yikes. This should probably not be in a synchronized block... - throw new IllegalArgumentException("unable to delete all .listing files"); - } - listings.clear(); - listingFreshness.clear(); - } - - /** - * From FTPBeanFileSystem. - * @param directory - */ - public synchronized void resetListCache( String directory ) { - directory = toCanonicalFolderName(directory); - - File f= new File(localRoot, directory + ".listing"); - if ( f.exists() && ! f.delete() ) { - throw new IllegalArgumentException("unable to delete .listing file: "+f); - } - listings.remove( directory ); - listingFreshness.remove( directory ); - } - - /** - * return the File for the cached listing, even if it does not exist. - * @param directory - * @return - */ - protected File listingFile( String directory ) { - File f= new File(localRoot, directory); - try { - FileSystemUtil.maybeMkdirs( f ); - } catch ( IOException ex ) { - throw new IllegalArgumentException("unable to mkdir "+f,ex); - } - File listing = new File(localRoot, directory + ".listing"); - return listing; - } - - private final Map listings= new HashMap(); - private final Map listingFreshness= new HashMap(); - - /** - * return true if the listing file (.listing) is available in the - * file system cache, and is still fresh. LISTING_TIMEOUT_MS controls - * the freshness, where files older than LISTING_TIMEOUT_MS milliseconds - * will not be used. Note the timestamp on the file comes from the server - * providing the listing, so the age may be negative when clocks are not - * synchronized. - * @param directory - * @return true if the listing is cached. - */ - public synchronized boolean isListingCached( String directory ) { - File f= new File(localRoot, directory); - if ( !f.exists() ) return false; - File listing = listingFile( directory ); - if ( listing.exists() ) { - long ageMs= ( System.currentTimeMillis() - listing.lastModified() ); - if ( ageMs0 ) { - listDirectory(path.substring(0,i+1)); - des= listDirectoryFromMemory(path.substring(0,i+1)); - } - if ( force && des==null ) { - throw new IOException("unable to get listing: " + this.getRootURL() + path.substring(1,i+1) ); - } - DirectoryEntry result= null; - if ( des!=null ) { - String fname= path.substring(i+1); - for ( i=0; i3 ) { - throw new IllegalArgumentException("user info section can contain at most two at (@) symbols"); - } else if ( ss.length==3 ) {//bugfix 3299977. UMich server uses email:password@umich. Java doesn't like this. - // the user didn't escape the at (@) in the email. escape it here. - StringBuilder userInfo_= new StringBuilder( ss[0] ); - for ( int i=1;i<2;i++ ) userInfo_.append("%40").append(ss[i]); - auth= ss[2]; - try { - URI rooturi2= new URI( root.getScheme() + "://" + userInfo_.toString()+"@"+auth + root.getPath() ); - return rooturi2.toURL(); - } catch ( URISyntaxException ex2 ) { - throw new RuntimeException(ex2); - } catch ( MalformedURLException ex2 ) { - throw new RuntimeException(ex2); - } - } else { - throw new RuntimeException(ex); - } - - } - } - /** - * return the name of the File within the FileSystem, where File is a local - * file within the local copy of the filesystem. - * @param file - * @return the name within the filesystem - */ - public String getLocalName(File file) { - if (!file.toString().startsWith(localRoot.toString())) { - throw new IllegalArgumentException("file \"" + file + "\"is not of this web file system"); - } - String filename = file.toString().substring(localRoot.toString().length()); - filename = filename.replaceAll("\\\\", "/"); - return filename; - } - - public String getLocalName(URL url) { - if (!url.toString().startsWith(root.toString())) { - throw new IllegalArgumentException("url \"" + url + "\"is not of this web file system"); - } - String filename = FileSystem.toCanonicalFilename(url.toString().substring(root.toString().length())); - return filename; - } - - /** - * Return the handle for this file. This is not the file itself, and - * accessing this object does not necessarily download the resource. This will be a - * container for file metadata, in addition to providing access to the data - * file itself. - * @param filename the name of the file within the filesystem. - * @return a FileObject for the file - */ - @Override - public FileObject getFileObject(String filename) { - return new WebFileObject( this, filename, new Date( Long.MAX_VALUE ) ); // note result.modified may be Long.MAX_VALUE, indicating need to load. - } - - /** - * reduce the number of hits to a server by caching last access times for local files. - * Note subclasses of this must call markAccess to indicate the file is accessed. - * - * For example, we will not do a head request to check for an update more than once per minute. - * - * @param filename the filename within the filesystem. - * @return the last time the file was accessed via a HEAD request. - */ - protected synchronized long getLastAccessed( String filename ) { - try { - DirectoryEntry result = (DirectoryEntry) accessCache.doOp( filename ); - return result.modified; - } catch (Exception ex) { - logger.log(Level.SEVERE, "returning 1970-01-01", ex); - return 0; - } - } - - /** - * copies data from in to out, sending the number of bytesTransferred to the monitor. - * NOTE: monitor.finished is not called, breaking monitor rules. - * @param is the input stream - * @param out the output stream - * @param monitor monitor for the task. Note this violates the monitor policy, and only calls setTaskProgress. Use with care! - * @throws java.io.IOException - */ - protected void copyStream(InputStream is, OutputStream out, ProgressMonitor monitor) throws IOException { - byte[] buffer = new byte[2048]; - int bytesRead = is.read(buffer, 0, 2048); - long totalBytesRead = bytesRead; - while (bytesRead > -1) { - if (monitor.isCancelled()) { - throw new InterruptedIOException(); - } - monitor.setTaskProgress(totalBytesRead); - out.write(buffer, 0, bytesRead); - bytesRead = is.read(buffer, 0, 2048); - totalBytesRead += bytesRead; - logger.finest("transferring data"); - } - } - - /** - * nice clients consume both the stderr and stdout coming from websites. - * http://docs.oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html suggests that you "do not abandon connection" - * @param err - * @throws IOException - * @deprecated see HtmlUtil.consumeStream. - * @see HtmlUtil#consumeStream(java.io.InputStream) - */ - public static void consumeStream( InputStream err ) throws IOException { - HtmlUtil.consumeStream(err); - } - - @Override - public String toString() { - return "wfs " + root; - } - - public boolean isAppletMode() { - return applet; - } - - public void setAppletMode(boolean applet) { - this.applet = applet; - } -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/WebProtocol.java b/dasCoreUtil/src/org/das2/util/filesystem/WebProtocol.java deleted file mode 100755 index 12e6b8b6c..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/WebProtocol.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.util.filesystem; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; - -/** - * template for web-based protocols to implement FileSystems - * @author jbf - */ -public interface WebProtocol { - public static final String META_EXIST="exist"; - - public InputStream getInputStream( WebFileObject fo, org.das2.util.monitor.ProgressMonitor mon ) throws IOException; - public Map getMetadata( WebFileObject fo ) throws IOException; - -} diff --git a/dasCoreUtil/src/org/das2/util/filesystem/WriteCapability.java b/dasCoreUtil/src/org/das2/util/filesystem/WriteCapability.java deleted file mode 100644 index d925079f9..000000000 --- a/dasCoreUtil/src/org/das2/util/filesystem/WriteCapability.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.util.filesystem; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * - * @author jbf - */ -public interface WriteCapability { - /** - * Get the output stream. - * @return - * @throws IOException - */ - public OutputStream getOutputStream( ) throws IOException; - - /** - * Test to see if we can write to this file. - * @return - * @throws IOException - */ - public boolean canWrite() throws IOException; - - /** - * delete the file - * @return - * @throws IOException - */ - public boolean delete() throws IOException; -} diff --git a/dasCoreUtil/src/org/das2/util/monitor/AbstractProgressMonitor.java b/dasCoreUtil/src/org/das2/util/monitor/AbstractProgressMonitor.java deleted file mode 100644 index 4fabb27a1..000000000 --- a/dasCoreUtil/src/org/das2/util/monitor/AbstractProgressMonitor.java +++ /dev/null @@ -1,171 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * NullProgressMonitor.java - * - * Created on October 23, 2007, 10:11 AM - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -package org.das2.util.monitor; - -import java.util.logging.Logger; -import org.das2.util.LoggerManager; - -/** - * This is a progress monitor to use when we don't care about the progress, - * this doesn't provide a view of the progress to the client. - * - * Further, this can act as a base class for other monitor types. - * - * @author jbf - */ -public class AbstractProgressMonitor implements ProgressMonitor { - - private static final Logger logger = LoggerManager.getLogger("das2.system"); - - public AbstractProgressMonitor() { - } - - private long taskSize=-1 ; - - private int cancelCheck= 0; - - @Override - public void setTaskSize(long taskSize) { - this.taskSize= taskSize; - } - - @Override - public long getTaskSize( ) { - return taskSize; - } - - private String progressMessage; - - @Override - public void setProgressMessage( String message ) { - this.progressMessage= message; - } - - /** - * provide access to the last progress message setting. - * @return the last progress message setting. - */ - public String getProgressMessage() { - return this.progressMessage; - } - - private long position=0; - - @Override - public void setTaskProgress(long position) throws IllegalArgumentException { - this.position= position; - } - - @Override - public long getTaskProgress() { - return position; - } - - private boolean started= false; - - @Override - public void started() { - this.started= false; - } - - @Override - public boolean isStarted() { - return started; - } - - private boolean finished= false; - - @Override - public void finished() { - if ( finished ) { - logger.warning("monitor finished was called twice!"); - } else { - logger.fine("enter monitor finished"); - } - finished= true; - } - - @Override - public boolean isFinished() { - return finished; - } - - private boolean cancelled= false; - - @Override - public void cancel() { - cancelled= true; - } - - @Override - public boolean isCancelled() { - cancelCheck++; - return cancelled; - } - - @Deprecated - public void setAdditionalInfo(String s) { }; - - private String label; - - @Override - public void setLabel( String s ) { - this.label= s; - } - - @Override - public String getLabel() { - return label; - } - - /** - * return a human-readable representation of the monitor, which is - * currently position + "of" + taskSize. - * @return return a human-readable representation of the monitor - */ - @Override - public String toString() { - return "" + this.position + " of "+ this.taskSize; - } - - @Override - public ProgressMonitor getSubtaskMonitor(int start, int end, String label) { - if ( label!=null ) setProgressMessage(label); - return SubTaskMonitor.create( this, start, end ); - } - - @Override - public ProgressMonitor getSubtaskMonitor(String label) { - if ( label!=null ) setProgressMessage(label); - return SubTaskMonitor.create( this, true ); - } - - @Override - public boolean canBeCancelled() { - return cancelCheck>0; - } -} diff --git a/dasCoreUtil/src/org/das2/util/monitor/AlertNullProgressMonitor.java b/dasCoreUtil/src/org/das2/util/monitor/AlertNullProgressMonitor.java deleted file mode 100644 index 90e94df78..000000000 --- a/dasCoreUtil/src/org/das2/util/monitor/AlertNullProgressMonitor.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package org.das2.util.monitor; - -/** - * Like the NullProgressMonitor, but print to stderr when the task is - * taking a non-trivial amount of time. The NullProgressMonitor is used - * when the developer thinks that a task is trivial, so this should be used - * to verify. After one second, this will start dumping messages to - * stderr. - * @author jbf - */ -public class AlertNullProgressMonitor extends NullProgressMonitor { - - /** - * the birth time for the monitor. - */ - long t0= System.currentTimeMillis(); - - /** - * the time the lastAlert was issued. - */ - long lastAlert= 0; - - /** - * create a monitor. - */ - public AlertNullProgressMonitor() { - } - - /** - * create a monitor with the label. - * @param label the label - */ - public AlertNullProgressMonitor(String label) { - this(); - this.setLabel(label); - } - - @Override - public void setTaskProgress(long position) throws IllegalArgumentException { - super.setTaskProgress(position); //To change body of generated methods, choose Tools | Templates. - long t= System.currentTimeMillis(); - if ( ( t-t0 > 1000 ) && ( t-lastAlert > 500 ) ) { - System.err.println( String.format( "%s: %d of %d... (trivial task is taking longer than expected)",this.getLabel(),this.getTaskProgress(),this.getTaskSize()) ); - if ( this.getLabel()==null ) { - StackTraceElement[] sts= new Exception("getStackTrace").getStackTrace(); - System.err.println( sts[1] ); - } - lastAlert= t; - } - } - - -} diff --git a/dasCoreUtil/src/org/das2/util/monitor/CancelledOperationException.java b/dasCoreUtil/src/org/das2/util/monitor/CancelledOperationException.java deleted file mode 100644 index 9e48670f3..000000000 --- a/dasCoreUtil/src/org/das2/util/monitor/CancelledOperationException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package org.das2.util.monitor; - -/** - * FileSystem cancel exception. Note this is used where we once used - * org.das2.CancelledOperationException, and we separated this to decouple - * org.das2.util. - * - * @author jbf - */ -public class CancelledOperationException extends Exception { - /** Creates a new instance of CancelledOperationException */ - public CancelledOperationException() { - super(); - } - - public CancelledOperationException(String message) { - } - - public CancelledOperationException(Throwable cause) { - super(cause); - } - - public CancelledOperationException(String message, Throwable cause) { - super(message,cause); - } -} diff --git a/dasCoreUtil/src/org/das2/util/monitor/NullProgressMonitor.java b/dasCoreUtil/src/org/das2/util/monitor/NullProgressMonitor.java deleted file mode 100644 index 09c46545d..000000000 --- a/dasCoreUtil/src/org/das2/util/monitor/NullProgressMonitor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * NullProgressMonitor.java - * - * Created on October 23, 2007, 10:11 AM - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -package org.das2.util.monitor; - -/** - * This is a progress monitor to use when we don't care about the progress. - * - * Further, this can act as a base class for other monitor types. - * - * @author jbf - */ -public class NullProgressMonitor extends AbstractProgressMonitor { - - public NullProgressMonitor() { - } - -} diff --git a/dasCoreUtil/src/org/das2/util/monitor/ProgressMonitor.java b/dasCoreUtil/src/org/das2/util/monitor/ProgressMonitor.java deleted file mode 100755 index 3e3133cf2..000000000 --- a/dasCoreUtil/src/org/das2/util/monitor/ProgressMonitor.java +++ /dev/null @@ -1,207 +0,0 @@ -/* File: ProgressMonitor.java - * Copyright (C) 2002-2003 The University of Iowa - * Created by: Jeremy Faden - * Jessica Swanner - * Edward E. West - * - * This file is part of the das2 library. - * - * das2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package org.das2.util.monitor; - -/** ProgressMonitor defines a set of methods that are useful for - * keeping track of the progress of an operation. This interface also allows - * the operation being tracked to be notified if the user wishes to cancel the - * operation. Code using this interface to track progress should call - * {@link #isCancelled()} prior to calling {@link #setTaskProgress(long)}. - * Implementations of this interface should throw an - * IllegalArgumentException when setTaskProgress(int) - * is called after the operation has been cancelled. - *

    - * Code using the ProgressMonitor should call {@link #started()} - * before setTaskProgress(long) is called for the first time. - * setTaskProgress() should not be called after - * cancel() or finished() has been called. Therefore, - * monitored processes should check isCancelled() before setTaskProgress(long) - * is called. An - * implementation may throw an IllegalArgumentException if - * setTaskProgress(int) is called before started() or - * after finished() is called. Note if isCancelled is not called - * by the process, then the cancel button will become disabled. - * - *

    TODO: consider allowing the client to specify that an unchecked exception is - * allowed, and he can then catch the exception. - * 1. The caller knows that the service provider's work can be ignored, and this would work. - * 2. The service provider knows that the service must be cleaned up with a try/finally block. - * Should CancelledOperationException be a an unchecked exception? - * See https://bugs-pw.physics.uiowa.edu/mantis/view.php?id=457 - *

    - * - *

    A client codes receiving a monitor must do one of two things. - * It should either call setTaskSize(long), started(), setTaskProgress(long) zero or more times, then - * finished(); or it should do nothing with the monitor, possibly passing the - * monitor to a subprocess. This is to ensure that it's easy to see that - * the monitor lifecycle is properly performed.

    - * - * @author jbf - */ -public interface ProgressMonitor { - - public final static long SIZE_INDETERMINATE= -1; - - /** Sets the maximum value for the task progress of this - * ProgressMonitor. - * @param taskSize maximum value for the task progress. A taskSize of -1 indicates the taskSize is indeterminate. - */ - void setTaskSize(long taskSize); - - /** Notifies the ProgressMonitor of a change in the progress - * of the task. - * @param position the current task position - * @throws IllegalArgumentException if {@link #isCancelled()} returns true or, - * possibly if started() has not been called or - * finished() has been called. - */ - void setTaskProgress(long position) throws IllegalArgumentException; - - /** - * Provides additional feedback as to what's going on in the process. - * This message should be set by the service provider, not the client, - * and refer to the implementation of the task. e.g. "Reading file myData.dat" - * @param message the message describing the state of progress. - */ - void setProgressMessage( String message ); - - /** - * Returns the current progress of the monitored task. - * @return the current progress of the monitored task. - */ - long getTaskProgress(); - - /** - * Set a concise string that describes the task being performed. Monitors - * don't necessarily need to display this label, and this request may be - * ignored. It is only provided so a process can describe the task that - * is going on. This is usually set by the client of the process to indicate - * what service we are waiting for. e.g. "Loading Data" - * @param label the label describing the task. - */ - public void setLabel( String label ); - - /** - * Return the label string displayed. This is primarily to aid in debugging, - * and this method need not return the string set by setLabel. - * @return the label. - */ - public String getLabel(); - - /** - * Return the size of the task. The units are arbitrary - * @return the size of the task. - */ - long getTaskSize(); - - /** Notifies the ProgressMonitor that the task - * being monitored has started. If the ProgressMonitor - * is in a cancelled state when this method is called, that - * ProgressMonitor should be 'uncancelled'. - */ - void started(); - - /** Notifies the ProgressMonitor that the task - * being monitored has finished. This must only be called once, and note that isFinished() must return - * the state of this monitor. - */ - void finished(); - - /** - * Notifies the ProgressMonitor that the task - * being monitored should be canceled. After this method is - * called, implementations should return true on - * any subsequent calls to {@link #isCancelled()} and should - * throw an IllegalStateException on any subsequent calls to - * {@link #setTaskProgress(long)}. - */ - void cancel(); - - /** - * Returns true if the operation being tracked - * should be cancelled. For example, the human operator has pressed - * the cancel button indicating that the process should be stopped. Note - * that if the process is not checking the cancel status, the cancel button - * should be disabled. - * - * @return true if the operation being tracked - * should be cancelled. - */ - boolean isCancelled(); - - /** - * return true if the process appears to support cancel. Many - * processes use a monitor to provide status feedback, but do not check - * if the human operator has pressed cancel. - * @return - */ - boolean canBeCancelled(); - - /** additional information to be displayed alongside the progress. That - * might be of interest. - * "85 of 100 (50KB/s)" - * @param s the message, such as (50KB/s) - * @deprecated setProgressMessage should be used by the service provider - * to indicate how the process is being implemented. - */ - public void setAdditionalInfo(String s); - - /** - * true if the process has indicated that it has started. - * @return true if the process has indicated that it has started. - */ - boolean isStarted(); - - /** - * true if the process has indicated that it is finished - * @return true if the process has indicated that it is finished - */ - boolean isFinished(); - - /** - * return a monitor to use for a subtask. This is provided mostly as - * a convenience. setTaskProgress calls to the subtask monitor are mapped to - * this monitor. A label can also be specified for the subtask to improve - * user experience. - * - * If the parent process is not supporting cancel, then subprocesses cannot support cancel. - * - * @param start start position on this monitor. - * @param end end position on this monitor (exclusive). - * @param label a label for the subtask, often this is handled as progress message; or null. - * @return a new progress monitor. (generally type SubTaskMonitor) - */ - public ProgressMonitor getSubtaskMonitor( int start, int end, String label); - - /** - * get the subtask monitor when the current task length is indeterminate. This avoids clients having to - * put in dummy numbers that will cause problems in the future. - * - * The subtask can set the taskSize and taskProgress and these should be conveyed to the parent. - * - * @param label a label for the subtask, often this is handled as progress message; or null. - * @return a new progress monitor. (generally type SubTaskMonitor) - */ - public ProgressMonitor getSubtaskMonitor( String label ); -} diff --git a/dasCoreUtil/src/org/das2/util/monitor/SubTaskMonitor.java b/dasCoreUtil/src/org/das2/util/monitor/SubTaskMonitor.java deleted file mode 100644 index 3b5193058..000000000 --- a/dasCoreUtil/src/org/das2/util/monitor/SubTaskMonitor.java +++ /dev/null @@ -1,256 +0,0 @@ -/* Copyright (C) 2003-2008 The University of Iowa - * - * This file is part of the Das2 utilities library. - * - * Das2 utilities are free software: you can redistribute and/or modify them - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Das2 utilities are distributed in the hope that they 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 - * as well as the GNU General Public License along with Das2 utilities. If - * not, see . - * - * SubTaskMonitor.java - * - * Created on August 18, 2005, 4:01 PM - */ - -package org.das2.util.monitor; - -import java.util.logging.Logger; -import org.das2.util.LoggerManager; - - -/** - * creates a ProgressMonitor that maps its progress to a parent's progress. - * For example, if a process takes a progress monitor, but is implemented in - * two steps that each take a progress monitor, then two subtask monitors can - * be created to monitor each step, and the client who passed in the monitor - * will see the whole deal as one process. - * - * Note we would often abuse monitors, handing them off to several processes - * that would each take the monitor through its lifecycle. Now we require the - * lifecycle be performed once, so that machines can use the monitors to monitor - * processes. In this case, SubTaskMonitors should be used, and getSubtaskMonitor - * was added. Note too that this class should not be called directly, because - * getSubtaskMonitor allows the source class to return a monitor of its own design. - * - * @see ProgressMonitor#getSubtaskMonitor(int, int, java.lang.String) - * @see ProgressMonitor#getSubtaskMonitor(java.lang.String) - * @author Jeremy - */ -public class SubTaskMonitor implements ProgressMonitor { - - private static final Logger logger = LoggerManager.getLogger("das2.system"); - - ProgressMonitor parent; - long min, max, progress, size; - String label; - - /** - * echo progress messages up to parent to mimic old behavior. - */ - boolean doEchoToParent= false; - - /** - * true if we should check the parent cancel status. - */ - boolean cancelCheck; - - /** - * create the subtask monitor - * @param parent parent monitor - * @param min minium - * @param max maximum - * @param cancelChecked true if we should check the parent's cancel status - */ - private SubTaskMonitor( ProgressMonitor parent, long min, long max, boolean cancelChecked ) { - this.parent= parent; - this.min= min; - this.max= max; - this.size= -1; - this.cancelCheck= cancelChecked; - } - - /** - * create the subtask monitor. - * @param parent parent monitor - * @param min minium - * @param max maximum - * @return SubTaskMonitor - */ - public static SubTaskMonitor create( ProgressMonitor parent, long min, long max ) { - return new SubTaskMonitor( parent, min, max, false ); - } - - /** - * create the subtask monitor. - * @param parent parent monitor - * @param min minium - * @param max maximum - * @param cancelChecked true if we should check the parent's cancel status - * @return SubTaskMonitor - */ - public static SubTaskMonitor create( ProgressMonitor parent, long min, long max, boolean cancelChecked ) { - return new SubTaskMonitor( parent, min, max, cancelChecked ); - } - - /** - * mode for when parent is indeterminate - * @param parent - * @param cancelChecked - * @return - */ - public static SubTaskMonitor create( ProgressMonitor parent, boolean cancelChecked ) { - SubTaskMonitor result= new SubTaskMonitor( parent, -1, -1, cancelChecked ); - result.doEchoToParent= true; // See sftp://klunk.physics.uiowa.edu:/home/jbf/project/autoplot/script/bugs/1251_subtask_monitor/demo2.jy - return result; - } - - @Override - public void cancel() { - if ( parent.canBeCancelled() ) { - parent.cancel(); - } - } - - private boolean finished= false; - - @Override - public void finished() { - if ( finished ) { - logger.warning("monitor finished was called twice!"); - } else { - logger.fine("enter monitor finished"); - } - this.finished= true; // only to support the bean property - } - - @Override - public boolean isFinished() { - return this.finished; - } - - @Override - public long getTaskProgress() { - return progress; - } - - @Override - public boolean isCancelled() { - return parent.isCancelled(); - } - - @Deprecated - @Override - public void setAdditionalInfo(String s) { - // ignore - } - - @Override - public void setTaskProgress(long position) throws IllegalArgumentException { - this.progress= position; - if ( cancelCheck ) { - parent.isCancelled(); // so there is one setTaskProgress for each isCancelled - } - if ( max==min && min==-1 ) { - // parent is indeterminate - } else { - if ( size==-1 ) { - parent.setTaskProgress( min ); - } else { - parent.setTaskProgress( min + ( max - min ) * position / size ); - } - } - } - - @Override - public void setTaskSize(long taskSize) { - this.size= taskSize; - if ( max==min && min==-1 && doEchoToParent ) { - min= 0; - max= taskSize; - parent.setTaskSize(taskSize); - } - } - - @Override - public long getTaskSize() { - return this.size; - } - - boolean started= false; - - @Override - public void started() { - this.started= true; - if ( parent.isStarted()==false ) parent.started(); - } - - @Override - public boolean isStarted() { - return started; - } - - @Override - public void setLabel(String label) { - this.label= label; - if ( this.doEchoToParent ) { - parent.setLabel(label); - } - } - - @Override - public String getLabel() { - return label; - } - - @Override - public String toString() { - if (label == null) { - return parent.toString(); - } else { - return parent.toString() + ">" + label; - } - } - - /** - * these messages are lost, unless doEchoToParent is set. - * @param message - */ - @Override - public void setProgressMessage(String message) { - if( this.doEchoToParent ) { - parent.setProgressMessage(message); - } - //parent.setProgressMessage(message); - } - - @Override - public ProgressMonitor getSubtaskMonitor(int start, int end, String label) { - //setProgressMessage(label); - if ( this.min==-1 && this.max==-1 ) { - return SubTaskMonitor.create( this, cancelCheck ); - } else { - return SubTaskMonitor.create( this, start, end, cancelCheck ); - } - } - - @Override - public ProgressMonitor getSubtaskMonitor(String label) { - return SubTaskMonitor.create( this, cancelCheck ); - } - - - @Override - public boolean canBeCancelled() { - return cancelCheck; - } - -} diff --git a/dasCoreUtil/src/org/das2/util/monitor/UncheckedCancelledOperationException.java b/dasCoreUtil/src/org/das2/util/monitor/UncheckedCancelledOperationException.java deleted file mode 100644 index 407edd270..000000000 --- a/dasCoreUtil/src/org/das2/util/monitor/UncheckedCancelledOperationException.java +++ /dev/null @@ -1,25 +0,0 @@ - -package org.das2.util.monitor; - -/** - * Experiment with unchecked vs checked exceptions. This allows - * calling codes to check for this specific exception. Note that this - * is a subclass of RuntimeException, not CancelledOperationException. - * @author faden@cottagesystems.com - */ -public class UncheckedCancelledOperationException extends RuntimeException { - public UncheckedCancelledOperationException() { - super(); - } - - public UncheckedCancelledOperationException(String message) { - } - - public UncheckedCancelledOperationException(Throwable cause) { - super(cause); - } - - public UncheckedCancelledOperationException(String message, Throwable cause) { - super(message,cause); - } -} diff --git a/dasCoreUtil/src/org/das2/util/monitor/package.html b/dasCoreUtil/src/org/das2/util/monitor/package.html deleted file mode 100644 index a026525b4..000000000 --- a/dasCoreUtil/src/org/das2/util/monitor/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -

    ProgressMonitors are used to provide feedback to the human operator - for long processes. -

    - - diff --git a/dasCoreUtil/src/test/filesystem/TestFileSystemLockup.java b/dasCoreUtil/src/test/filesystem/TestFileSystemLockup.java deleted file mode 100644 index 94d75ca31..000000000 --- a/dasCoreUtil/src/test/filesystem/TestFileSystemLockup.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package test.filesystem; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.UnknownHostException; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.das2.util.filesystem.FileSystem; -import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; - -/** - * - * @author jbf - */ -public class TestFileSystemLockup { - - public static void main( String[] args ) { - final long t0= System.currentTimeMillis(); - - System.err.println("The old filesystem would only allow one FS to be created at a time, possibly locking out others."); - System.err.println("hard-coding delays in create simulates the problems seen."); - System.err.println("hard-coding delays in getFile shows things are okay if done later."); - - for ( int i=0; i<7; i++ ) { - final int fi= i; - Runnable run= new Runnable() { - public void run() { - try { - FileSystem fs; - if ( fi>2 ) { - fs= FileSystem.create("file:/home/jbf/temp/fstest/fs3"); - } else { - fs= FileSystem.create("file:/home/jbf/temp/fstest/fs" + fi); - } - long t1= System.currentTimeMillis(); - File ff= fs.getFileObject("afile").getFile(); - long ts0= System.currentTimeMillis() - t0; - long ts1= System.currentTimeMillis() - t1; - System.err.printf( "%6.3f %6.3f %s %d\n", ts0/1000., ts1/1000., ff, fs.hashCode() ); - } catch (FileNotFoundException ex) { - Logger.getLogger(TestFileSystemLockup.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); - } catch (FileSystemOfflineException ex) { - Logger.getLogger(TestFileSystemLockup.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); - } catch (UnknownHostException ex) { - Logger.getLogger(TestFileSystemLockup.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); - } catch (IOException ex) { - Logger.getLogger(TestFileSystemLockup.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); - } - } - }; - new Thread(run).start(); - - } - } -} diff --git a/dasCoreUtil/src/test/filesystem/TestUniq.java b/dasCoreUtil/src/test/filesystem/TestUniq.java deleted file mode 100644 index fb83f6d4d..000000000 --- a/dasCoreUtil/src/test/filesystem/TestUniq.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ - -package test.filesystem; - -import java.io.FileNotFoundException; -import java.net.UnknownHostException; -import org.das2.util.filesystem.FileSystem; -import org.das2.util.filesystem.FileSystem.FileSystemOfflineException; - -/** - * This should demo that the same object is returned. - * @author jbf - */ -public class TestUniq { - public static void main( String[] args ) throws FileSystemOfflineException, UnknownHostException, FileNotFoundException { - FileSystem fs1= FileSystem.create("http://sarahandjeremy.net/~jbf/1wire/data/2012"); - FileSystem fs2= FileSystem.create("http://sarahandjeremy.net/~jbf/1wire/data/2012/"); - - System.err.println( fs1==fs2 ); - - } -}