package org.nutz.ioc.impl; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.nutz.castor.Castors; import org.nutz.lang.Files; import org.nutz.lang.Lang; import org.nutz.lang.Streams; import org.nutz.lang.Strings; import org.nutz.lang.util.Disks; import org.nutz.lang.util.FileVisitor; import org.nutz.lang.util.MultiLineProperties; import org.nutz.log.Log; import org.nutz.log.Logs; import org.nutz.resource.NutResource; import org.nutz.resource.Scans; /** * 代理Properties文件,以便直接在Ioc配置文件中使用 * * @author wendal(wendal1985@gmail.com) * @author zozoh(zozohtnt@gmail.com) * * @since 1.b.37 */ public class PropertiesProxy { private static final Log log = Logs.get(); // 是否为UTF8格式的Properties文件 private final boolean utf8; // 是否忽略无法加载的文件 private boolean ignoreResourceNotFound = false; private MultiLineProperties mp = new MultiLineProperties(); public PropertiesProxy() { this(true); } public PropertiesProxy(boolean utf8) { this.utf8 = utf8; } public PropertiesProxy(String... paths) { this(true); this.setPaths(paths); } public PropertiesProxy(InputStream in) { this(true); try { mp = new MultiLineProperties(); mp.load(new InputStreamReader(in)); } catch (IOException e) { throw Lang.wrapThrow(e); } } /** * @param r * 文本输入流 * @since 1.b.50 */ public PropertiesProxy(Reader r) { this(true); try { mp = new MultiLineProperties(); mp.load(r); } catch (IOException e) { throw Lang.wrapThrow(e); } } /** * 加载指定文件/文件夹的Properties文件,合并成一个Properties对象 * <p> * <b style=color:red>如果有重复的key,请务必注意加载的顺序!!<b/> * * @param paths * 需要加载的Properties文件路径 */ public void setPaths(String... paths) { mp = new MultiLineProperties(); try { List<NutResource> list = getResources(paths); if (utf8) for (NutResource nr : list) mp.load(nr.getReader(), false); else { Properties p = new Properties(); for (NutResource nr : list) { p.load(nr.getInputStream()); } mp.putAll(p); } } catch (IOException e) { throw Lang.wrapThrow(e); } } /** * 加载指定文件/文件夹的Properties文件 * * @param paths * 需要加载的Properties文件路径 * @return 加载到的Properties文件Resource列表 */ private List<NutResource> getResources(String... paths) { List<NutResource> list = new ArrayList<NutResource>(); for (String path : paths) { try { List<NutResource> resources = Scans.me().loadResource("^.+[.]properties$", path); list.addAll(resources); } catch (Exception e) { if (ignoreResourceNotFound) { if (log.isWarnEnabled()) { log.warn("Could not load resource from " + path + ": " + e.getMessage()); } } else { throw Lang.wrapThrow(e); } } } return list; } public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) { this.ignoreResourceNotFound = ignoreResourceNotFound; } /** * @param key * 键 * @return 是否包括这个键 * @since 1.b.50 */ public boolean has(String key) { return mp.containsKey(key); } public void put(String key, String value) { mp.put(key, value); } public PropertiesProxy set(String key, String val) { put(key, val); return this; } public String check(String key) { String val = get(key); if (null == val) throw Lang.makeThrow("Ioc.$conf expect property '%s'", key); return val; } public boolean getBoolean(String key) { return getBoolean(key, false); } public boolean getBoolean(String key, boolean dfval) { String val = get(key); if (Strings.isBlank(val)) return dfval; return Castors.me().castTo(val, Boolean.class); } public String get(String key) { return mp.get(key); } public String get(String key, String defaultValue) { return Strings.sNull(mp.get(key), defaultValue); } public String trim(String key) { return Strings.trim(get(key)); } public String trim(String key, String defaultValue) { return Strings.trim(get(key, defaultValue)); } public int getInt(String key) { return getInt(key, -1); } public int getInt(String key, int defaultValue) { try { return Integer.parseInt(get(key)); } catch (NumberFormatException e) { return defaultValue; } } public long getLong(String key) { return getLong(key, -1); } public long getLong(String key, long dfval) { try { return Long.parseLong(get(key)); } catch (NumberFormatException e) { return dfval; } } public String getTrim(String key) { return Strings.trim(get(key)); } public String getTrim(String key, String defaultValue) { return Strings.trim(get(key, defaultValue)); } public List<String> getKeys() { return mp.keys(); } public Collection<String> getValues() { return mp.values(); } public Properties toProperties() { Properties p = new Properties(); for (String key : mp.keySet()) { p.put(key, mp.get(key)); } return p; } /** * 根据自身的一个键对应的值扩展自身的属性。 * <p> * 本函数假设你可能有下面的键值: * * <pre> * ... * files: * path/to_a.properties * path/to_b.properties * #End files * </pre> * * 那么如果你调用 <code>joinByKey("files");</code> <br> * 则会将其值的两个属性文件展开,加入到自身。 * <p> * 属性文件的路径可以是磁盘全路径,或者在 CLASSPATH 里的路径 * * @param key * 键 * @return 自身 */ public PropertiesProxy joinByKey(String key) { String str = get(key); final PropertiesProxy me = this; if (!Strings.isBlank(str)) { String[] ss = Strings.splitIgnoreBlank(str, "\n"); for (String s : ss) { File f = Files.findFile(s); if (null == f) { throw Lang.makeThrow("Fail to found path '%s' in CLASSPATH or File System!", s); } // 如果是一个包,引用全部 Files if (f.isDirectory()) { Disks.visitFile(f, new FileVisitor() { public void visit(File f) { me.joinAndClose(Streams.fileInr(f)); } }, new FileFilter() { public boolean accept(File f) { if (f.isDirectory()) return !f.isHidden() && !f.getName().startsWith("."); return f.getName().endsWith(".properties"); } }); } // 否则引用单个文件 else if (f.isFile()) { this.joinAndClose(Streams.fileInr(f)); } } } return this; } /** * 将另外一个 Properties 文本加入本散列表 * * @param r * 文本输入流 * @return 自身 */ public PropertiesProxy joinAndClose(Reader r) { MultiLineProperties mp = new MultiLineProperties(); try { mp.load(r); } catch (IOException e) { throw Lang.wrapThrow(e); } finally { Streams.safeClose(r); } this.mp.putAll(mp); return this; } public Map<String, String> toMap() { return new HashMap<String, String>(mp); } }