001package ball.util; 002/*- 003 * ########################################################################## 004 * Utilities 005 * $Id: PropertiesImpl.java 6199 2020-06-15 21:18:10Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/util/PropertiesImpl.java $ 007 * %% 008 * Copyright (C) 2008 - 2020 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import java.io.File; 024import java.io.FileInputStream; 025import java.io.IOException; 026import java.io.InputStream; 027import java.io.InputStreamReader; 028import java.io.OutputStream; 029import java.io.OutputStreamWriter; 030import java.lang.reflect.Method; 031import java.nio.charset.Charset; 032import java.util.Properties; 033 034import static ball.util.Converter.convertTo; 035import static java.nio.charset.StandardCharsets.UTF_8; 036 037/** 038 * {@link Properties} implementation that overrides 039 * {@link #load(InputStream)} and {@link #store(OutputStream,String)} 040 * methods to specify the {@link Charset} (as {@code UTF-8}). 041 * 042 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 043 * @version $Revision: 6199 $ 044 */ 045public class PropertiesImpl extends Properties { 046 private static final long serialVersionUID = -5034894719756448226L; 047 048 /** 049 * UTF-8 050 */ 051 protected static final Charset CHARSET = UTF_8; 052 053 /** 054 * See {@link Properties#Properties()}. 055 */ 056 public PropertiesImpl() { this(null); } 057 058 /** 059 * See {@link Properties#Properties(Properties)}. 060 * 061 * @param defaults A {@link Properties} that contains default 062 * values for any keys not found in this 063 * {@link Properties}. 064 */ 065 public PropertiesImpl(Properties defaults) { super(defaults); } 066 067 /** 068 * See {@link #PropertiesImpl(Properties)}. 069 * 070 * @param defaults The default {@link Properties}. 071 * @param file The {@link File} to load 072 * (may be {@code null}). 073 * 074 * @throws IOException If {@code file} is not null and cannot be 075 * read. 076 */ 077 public PropertiesImpl(Properties defaults, File file) throws IOException { 078 this(defaults); 079 080 if (file != null) { 081 try (InputStream in = new FileInputStream(file)) { 082 load(in); 083 } 084 } 085 } 086 087 /** 088 * See {@link #PropertiesImpl(Properties)}. 089 * 090 * @param defaults The default {@link Properties}. 091 * @param resource The name of the {@code resource} to load 092 * (may be {@code null}). 093 * 094 * @throws IOException If {@code resource} is not null and cannot be 095 * read. 096 */ 097 public PropertiesImpl(Properties defaults, 098 String resource) throws IOException { 099 this(defaults); 100 101 if (resource != null) { 102 try (InputStream in = getClass().getResourceAsStream(resource)) { 103 load(in); 104 } 105 } 106 } 107 108 /** 109 * Method to configure an {@link Object} properties with values in 110 * {@link.this} {@link PropertiesImpl}. (An {@link Object} "setter" 111 * does not have to return {@code void} to be invoked.) 112 * 113 * @param object The {@link Object} to configure. 114 * 115 * @return The argument {@link Object}. 116 * 117 * @throws Exception If any problem is encountered. 118 */ 119 public Object configure(Object object) throws Exception { 120 return configure(this, object); 121 } 122 123 @Override 124 public void load(InputStream in) throws IOException { load(this, in); } 125 126 @Override 127 public void store(OutputStream out, String comment) throws IOException { 128 store(this, out, comment); 129 } 130 131 protected static void load(Properties properties, 132 InputStream in) throws IOException { 133 properties.load(new InputStreamReader(in, CHARSET)); 134 } 135 136 protected static void store(Properties properties, 137 OutputStream out, 138 String comment) throws IOException { 139 OutputStreamWriter writer = new OutputStreamWriter(out, CHARSET); 140 141 properties.store(writer, comment); 142 writer.flush(); 143 } 144 145 /** 146 * See {@link #configure(Object)}. 147 * 148 * @param properties The {@link Properties} with the 149 * configuration parameters.. 150 * @param object The {@link Object} to configure. 151 * 152 * @return The argument {@link Object}. 153 * 154 * @throws Exception If any problem is encountered. 155 */ 156 public static Object configure(Properties properties, 157 Object object) throws Exception { 158 for (String key : properties.stringPropertyNames()) { 159 Object value = properties.get(key); 160 Method method = 161 getSetMethod(object, 162 key, (value != null) ? value.getClass() : null); 163 164 if (method != null) { 165 value = convertTo(value, method.getParameterTypes()[0]); 166 method.invoke(object, value); 167 } 168 } 169 170 return object; 171 } 172 173 private static Method getSetMethod(Object object, 174 String property, Class<?> parameter) { 175 Method method = null; 176 String name = 177 "set" 178 + property.substring(0, 1).toUpperCase() 179 + property.substring(1); 180 181 if (method == null) { 182 if (parameter != null) { 183 try { 184 method = object.getClass().getMethod(name, parameter); 185 } catch (Exception exception) { 186 } 187 } 188 } 189 190 if (method == null) { 191 for (Method m : object.getClass().getMethods()) { 192 if (m.getName().equals(name) 193 && (! m.isVarArgs()) 194 && m.getParameterTypes().length == 1) { 195 method = m; 196 break; 197 } 198 } 199 } 200 201 return method; 202 } 203}