xml可以打包成jar吗_从零开始写文本编辑器(二十六):支持对目录URL遍历XML资源(续)...

53b2952ccf2c0d524133f08aec8cdeaa.png

前言

在前一篇写目录遍历XML资源时,在IDE-eclipse中运行良好。当我写完大部分XML资源,测试通过,打包成jar,运行就crash。于是就有了本篇作续篇。

从 URI is not hierarchical 异常说起

File file = new File(url.toURI());

上篇的这行代码在jar中运行会报非法参数异常。

其原因

  • 在eclipse开发时,uri被加载后映射到 classes/ 下的每一个资源文件
  • 在 jar 运行时,uri 映射的则是资源名的定位,而 jar 不是目录,它没有目录层级关系,所以无法转换成 file。

若不是从零开始写应用程序,我还真难遇到这类异常。

起源:时光倒流回前两天

我在编写string.xml资源时,当整理完230多个菜单项后,已经筋疲力竭了。于是我意识到,不可能把全部资源写到一个xml中,至少不能手工写,非常容易出错。比如:

  • 重复项
  • 拼写错误

还有一些费精力,比如:

  • 查找定位行非常困难
  • 眼花缭乱

于是我想到把它拆分成若干独立的xml,放在同一个目录下,遍历即可。

现状

为了可维护性,我仍然要编写多个独立的 xml,然后写工具程序,自动合并成一个完整的xml。

于是在开发时,仍然使用这个有缺陷的方式,但在程序打包运行时,jar中调用的是另一套API。简单讲:

  • 在开发时,用 URL class.getResource(String name);
  • 在运行时,用 InputStream class.getResourceAsStream(String name);

为了防止以后再踩坑,我把异常说明写到源头的类上。

package editor.res.xml.util;

import java.io.File;
import java.io.FilenameFilter;
import java.net.URISyntaxException;
import java.net.URL;

/**
 * <pre>
 * when IDE-eclipse run OK.
 * when java -jar editor.jar run Exception:
 * 
 * class :Directory
 * method:listXmlFile() 
 * line  :File file = new File(url.toURI());
 * </pre>
 * 
 * <pre>
 * Exception in thread "main" java.lang.IllegalArgumentException: URI is not hierarchical
 * at java.base/java.io.File.<init>(File.java:418)
 * at editor.resource.xml.declaration.Directory.listXmlFile(Directory.java:16)
 * at editor.resource.ResourceString.load(ResourceString.java:25)
 * at editor.resource.Resource.load(Resource.java:16)
 * at editor.Editor.<init>(Editor.java:19)
 * at editor.Editor.main(Editor.java:42)
 * </pre>
 */
@Deprecated
public class Directory {

    private static final String RES = "/editor/res/xml/part/";
    public static final String STRING = RES + "string";
    public static final String ACCELERATOR = RES + "accelerator";
    public static final String MENU = RES + "menu";

    private Directory(String name) {
        super();
        this.name = name;
    }

    private String name;

    public String getName() {
        return name;
    }

    public static Directory string = new Directory(STRING);
    public static Directory accelerator = new Directory(ACCELERATOR);
    public static Directory menu = new Directory(MENU);

    @Deprecated
    public File[] listXmlFile() throws URISyntaxException {
        URL url = Directory.class.getResource(name);
        /**
         * File file = new File(url.toURI());
         * 
         * <pre>
         * Exception in thread "main" java.lang.IllegalArgumentException: URI is not hierarchical
         * at java.base/java.io.File.<init>(File.java:418)
         * at editor.resource.xml.declaration.Directory.listXmlFile(Directory.java:16)
         * at editor.resource.ResourceString.load(ResourceString.java:25)
         * at editor.resource.Resource.load(Resource.java:16)
         * at editor.Editor.<init>(Editor.java:19)
         * at editor.Editor.main(Editor.java:42)
         * </pre>
         */
        if (url != null) {
            File file = new File(url.toURI());
            if (file.isDirectory()) {
                return file.listFiles(filenameFilter);
            } else {
                return null;
            }
        } else {
            System.err.printf("资源名称错误,可能是单词拼写错误: %dn", name);
            return null;
        }
    }

    private FilenameFilter filenameFilter = new FilenameFilter() {

        @Override
        public boolean accept(File dir, String name) {
            return name.endsWith(".xml");
        }

    };
}

a40fda5ac101bc65e7695a7eba88a042.png
digraph G{
rankdir=LR;
a[label="string_a.xml"];
b[label="string_ab.xml"];
c[label="string_ac.xml"];
string[label="string.xml"];
a->string[label="merge"];
b->string[label="merge"];
c->string[label="merge"];
string->jar;
}

中间还有一些去重/名称关键词检查等步骤,这里不重复讲,见上篇。

字符串和快捷键都很好处理,但菜单XML我写了两份XML处理器代码。

  1. XmlHandlerJMenuItem
  2. XmlHandlerXMenuItem

区别是前者直接解析出JMenuItem的对象实例,让程序调用。后者是转为一个中间对象,用来进行合并/去重等处理。

package editor.res.xml.part.menu;

public class XMenuItem {
    private String name;
    private String text;
    private String accelerator;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getAccelerator() {
        return accelerator;
    }

    public void setAccelerator(String accelerator) {
        this.accelerator = accelerator;
    }

    public String toXmlString() {
        if (accelerator != null) {
            return String.format("<menuItem name="%s" text="%s" accelerator="%s"></menuItem>", name, text,
                    accelerator);
        } else {
            return String.format("<menuItem name="%s" text="%s"></menuItem>", name, text);
        }
    }
}

c4f25b0c993f5cd81cb528e2423e7875.png

上图已经是多轮重构后的代码树,已经非常直观。为了扩展,回调用Object传值,曾试着用泛型<T>,结果过于耦合,放弃了。Object用类型转换,来支持以后更多的用XML定义的资源。

package editor.xml.reader.handler.listener;

public interface XmlListener {
    void onDocumentStart();

    void onDocumentFinish();

    void onItem(String name, Object object);
}

仍然只维护一个editor工程

像多个xml合并的逻辑,应该不归editor工程自身,有两种方法

  1. 新建一个子工程专门写工具链
  2. 在编译时,把工具链包/类/资源不打包进jar。

我目前2种方案都没用,直接默认全部打包,因为还没写完,先不管。

进度

资源部分已经写了一周了,还没完成,是因为资源管理模块非常重要,以后的图标甚至布局页面和对话框等,都可以写成XML资源。所以不是我写得慢,是它本身就费时。

目前菜单项还有search project run window四大菜单没有调整。

此外菜单项是动态,比如:当前上下文是java或xml时,两者的source菜单, refactor菜单的菜单页几乎完全不同。

如果支持n个文件类型,理论上菜单项要写n倍,相应的字符串也是n倍。

别急,慢慢来。

“遇到困难说明方向对了。”----佚名

以上~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值