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