Processing.org 3并加载Java属性文件?

时间:2017-09-26 10:50:39

标签: java processing

我不是那些熟悉Java的人,我试图在Processing.org 3项目中使用一些Java。我已经成功地在一个名为testprocjavapath的小型Processing示例项目中重建了该问题 - 我发布了一个bash脚本(称为testProcJavaLoadpath.sh),该脚本在此结束时重建示例项目文件发布,并运行一次项目。 testprocjavapath项目文件如下所示:

~/sketchbook/testprocjavapath
├── testprocjavapath.pde
├── myprops.properties
└── MyJavaClass.java

运行脚本时,我得到了这个:

$ bash testProcJavaLoadpath.sh
...
There was an exception myprops.properties: java.lang.NullPointerException : null
The properties file content is 'null';
Finished.

在Processing 3 IDE GUI中进行调试,此错误恰好出现在properties.load(in);行上:

Processing-testprocjavapath.png

...因为行InputStream in = MyJavaClass.class.getResourceAsStream(inFileName);失败了,因此in是一个空指针。

我理解的很多 - 我不明白的是:我如何在.properties处理草图文件所在的目录中加载一个{,1}}文本文件, .pde文件(即这个特定的草图文件夹)?

据我所知,Java .java实际上用于从作为getResourceAsStream()文件打包的Java应用程序加载 - 因此它可以用于从硬盘读取尚未打包的文件作为.jar个文件?

如果没有 - 我也尝试过:

.jar

...但这也不起作用(InputStream in = new FileInputStream( new File(PROPERTIES_FILENAME)); 又是in)。

那么,我可以在null文件中使用什么命令来加载.java文件?如果我最终将整个Processing应用程序打包为myprops.properties文件(不确定Processing是否可以执行此操作,还没有查找它),我是否必须更改该命令?

以下是 .jar 文件(请确保将testProcJavaLoadpath.sh更改为您的处理安装路径):

PROCBINPATH

1 个答案:

答案 0 :(得分:0)

好的,我设法到了某个地方 - 但我仍然想要一个更合格的答案。

首先,事实证明,Processing as expect期望文件被读取(如OP示例中的myprops.properties),要存储在sketch文件夹的data子文件夹中:

https://processing.org/tutorials/data/

  

与图像文件一样,这些文本文件应放在草图的“数据”目录中,以便处理草图识别它们。

到目前为止一切顺利 - 事实上,在处理.pde草图中,我们可以使用(比方说)loadStrings("myprops.properties");,并且会读取data/myprops.properties处的文件。但是,我不需要在那里读取文件 - 我需要在支持.java类中阅读它。

现在,当你运行处理补丁(来自IDE或命令行)时,处理会发生什么,处理从草图文件夹中复制源文件,位于/tmp文件夹中的临时文件夹内(至少在Linux上);这是文件结构的样子:

/tmp/testprocjavapath9179591342074530534temp/
├── MyJavaClass.class
├── source
│   ├── MyJavaClass.java
│   └── testprocjavapath.java
└── testprocjavapath.class

请注意,我们有.java个源文件和.class个“已编译”文件,但在任何地方都没有data子文件夹或myprops.properties文件!

现在,还要注意源草图文件夹中曾经是testprocjavapath.pde的内容在临时文件夹中变为testprocjavapath.java(和相应的.class);请注意testprocjavapath.java定义:

public class testprocjavapath extends PApplet {

现在,loadStrings实际上是PApplet类的一种方法;所以如果我们读一遍:

https://github.com/processing/processing/blob/master/core/src/processing/core/PApplet.java

  

dataPath(String where):....数据路径在每个平台上的处理方式不同,不应被视为写入文件的位置。也不应该假设可以读取或列出该位置。 ...库应该使用createInput()来获取InputStream或createOutput()以获取OutputStream。 sketchPath()可用于获取相对于草图的位置。同样,不要使用它来获取文件的相对位置。 ......

...我们可以看到方法dataPath,但不推荐使用它。另一方面,有一种方法sketchPath - 但是,此方法将返回正确的路径(即本示例中~/sketchbook中的草图) if 从顶级.pde文件调用!如果您尝试将.java文件中的类定义为extends PApplet,然后从那里调用sketchPath,它将只返回当前工作目录!

所以现在的解决方案是:

  • .java类接受构造函数中的输入参数,该参数将用于记录正确的草图路径:
      public MyJavaClass(String inSketchPath) {
  • 然后,在sketchPath()文件的实例化期间将.java传递给.pde类实例:
      MyJavaClass myjc = new MyJavaClass( sketchPath() );
  • 最后,使用.java类中传递的草图路径,计算.properties文件的绝对路径,然后使用new FileInputStream( new File(theFilePath) );加载它( getResourceAsStream!)

在粘贴的已更改的testProcJavaLoadpath.sh下面,具有这些修改,并且原则上可以工作 - 这是终端输出:

$ bash testProcJavaLoadpath.sh
...
Sketch first lines: 'teststr=HelloWorld';
Sketch dataFile: '~/sketchbook/testprocjavapath/data/myprops.properties';
Sketch sketchPath: '~/sketchbook/testprocjavapath';
:: mySketchPath: '~/sketchbook/testprocjavapath'
:: The URL is 'file:/tmp/testprocjavapath4709659129218148940temp/';
:: name: MyJavaClass.class
:: resourcePath: file:/tmp/testprocjavapath4709659129218148940temp/MyJavaClass.class
:: theFilePath: '~/sketchbook/testprocjavapath/data/myprops.properties'
:: properties: key 'teststr' => value 'HelloWorld'
The properties file content is 'teststr=HelloWorld
';

...但是,我想如果我想将此代码打包在.jar或可执行的应用程序/文件中,这种方法可能会失败 - 这就是为什么我仍然喜欢更合格的答案。

更改后的 testProcJavaLoadpath.sh 是:

PROCSKETCHDIR="~/sketchbook"
PROCSKETCHDIR="${PROCSKETCHDIR/#\~/$HOME}" # expand home dir ~
echo "$PROCSKETCHDIR"
PROCBINPATH="/PATH/TO/processing-3.3.6" # path/location of Processing executable `processing-java`

MYSKETCH="testprocjavapath"
MYSKETCHDIR="$PROCSKETCHDIR/$MYSKETCH"
# reconstruct folder:
rm -rfv "$MYSKETCHDIR"
mkdir -v "$MYSKETCHDIR"

# https://processing.org/tutorials/data/
# "And just as with image files, these text files should be placed in the sketch’s “data” directory in order for them to be recognized by the Processing sketch."
# processing.core.PApplet.loadStrings - https://processing.github.io/processing-javadocs/core/
# https://github.com/processing/processing/blob/master/core/src/processing/core/PApplet.java
# "dataPath(String where): The data path is handled differently on each platform, and should not be considered a location to write files. It should also not be assumed that this location can be read from or listed. ... Libraries should use createInput() to get an InputStream or createOutput() to get an OutputStream. sketchPath() can be used to get a location relative to the sketch. Again, <b>do not</b> use this to get relative locations of files."

echo "generating $MYSKETCHDIR/$MYSKETCH.pde"
cat > "$MYSKETCHDIR/$MYSKETCH.pde" <<'EOF'

void setup() {
  size(640, 360);  // Size should be the first statement
  String[] lines = loadStrings("myprops.properties"); // reads from data/myprops.properties
  System.out.format("Sketch first lines: '%s';%n", lines[0]);
  System.out.format("Sketch dataFile: '%s';%n", dataFile("myprops.properties")); // ~/sketchbook/testprocjavapath/data/myprops.properties
  System.out.format("Sketch sketchPath: '%s';%n", sketchPath()); // ~/sketchbook/testprocjavapath
  MyJavaClass myjc = new MyJavaClass( sketchPath() );
  String thefilecontents = myjc.GetPropsFileContent();
  System.out.format("The properties file content is '%s';%n", thefilecontents);
}

EOF

mkdir -v "$MYSKETCHDIR/data"
echo "generating $MYSKETCHDIR/data/myprops.properties"
cat > "$MYSKETCHDIR/data/myprops.properties" <<'EOF'
teststr=HelloWorld
EOF

echo "generating $MYSKETCHDIR/MyJavaClass.java"
cat > "$MYSKETCHDIR/MyJavaClass.java" <<'EOF'
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream; // "InputStream is by definition not seekable."
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.io.FileInputStream; // is seekable
import java.io.File;
import java.io.ByteArrayOutputStream;
import java.net.URL;

//import processing.core.*;
import java.nio.file.Path;
import java.nio.file.Paths;

public class MyJavaClass {

  private static final String PROPERTIES_FILENAME = "myprops.properties";
  public String mySketchPath;

  /**
   * add a constructor
   */
  public MyJavaClass(String inSketchPath) {
    mySketchPath = inSketchPath;
  }

  public String GetPropsFileContent() {
    //System.out.format(":: sketchPath: '%s'%n", sketchPath()); // if `MyJavaClass extends PApplet`, then sketchPath() just prints current working directory!
    System.out.format(":: mySketchPath: '%s'%n", mySketchPath);
    getLocations();
    String myret = null;
    myret = readgetFileContent(PROPERTIES_FILENAME);
    return myret;
  }

  public String readgetFileContent(String inFileName) {
    String result = null;
    Properties properties = new Properties();
    try {
      //String theFilePath = inFileName; // verbatim relative path fails
      Path inFileNameSketchPath = Paths.get(mySketchPath, "data", inFileName); // OS path join
      String theFilePath = inFileNameSketchPath.toString();
      System.out.format(":: theFilePath: '%s'%n", theFilePath);

      //InputStream in = MyJavaClass.class.getResourceAsStream(theFilePath); // no can do, is 'null', also w/ abs path
      //InputStream in = new FileInputStream( new File(theFilePath) ); // OK, but not seekable
      FileInputStream in = new FileInputStream( new File(theFilePath) );
      properties.load(in);

      // double-check loaded properties:
      for(String key : properties.stringPropertyNames()) {
        String value = properties.getProperty(key);
        System.out.format(":: properties: key '%s' => value '%s'%n", key, value);
      }

      ByteArrayOutputStream resultbaos = new ByteArrayOutputStream();
      byte[] buffer = new byte[1024];
      int length;
      in.getChannel().position(0); // do reset - seek 0 (start), to reset stream again for reading
      while ((length = in.read(buffer)) != -1) {
        resultbaos.write(buffer, 0, length);
      }
      result = resultbaos.toString();
    } catch (IOException e) {
      System.err.println("There was an error reading " + inFileName + ": " + e.getCause()
          + " : " + e.getMessage());
    } catch (Exception e) {
      System.err.println("There was an exception " + inFileName + ": " + e
          + " : " + e.getMessage());
    }
    return result;
  }

  public void getLocations() {
    URL classURL = getClass().getProtectionDomain().getCodeSource().getLocation();
    System.out.format(":: The URL is '%s';%n", classURL); // file:/tmp/testprocjavapath3056820301028631180temp/

    String s = getClass().getName();
    int i = s.lastIndexOf(".");
    if(i > -1) s = s.substring(i + 1);
    s = s + ".class";
    System.out.println(":: name: " + s);
    Object resourcePath = this.getClass().getResource(s);
    System.out.println(":: resourcePath: " + resourcePath); // file:/tmp/testprocjavapath9185318125154993853temp/MyJavaClass.class
  }
}
EOF

# run once:
"$PROCBINPATH"/processing-java --sketch="$MYSKETCHDIR" --run