37 * * Redistributions in binary form must reproduce the above copyright notice, 38 * this list of conditions and the following disclaimer in the documentation 39 * and/or other materials provided with the distribution. 40 * 41 * * Neither the name of JSR-310 nor the names of its contributors 42 * may be used to endorse or promote products derived from this software 43 * without specific prior written permission. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 46 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 47 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 48 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 49 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 50 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 51 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 52 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 53 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 54 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 55 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 */ 57 package java.time.calendar; 58 59 60 import java.io.BufferedReader; 61 import java.io.File; 62 import java.io.FileInputStream; 63 import java.io.IOException; 64 import java.io.InputStream; 65 import java.io.InputStreamReader; 66 import java.nio.file.FileSystems; 67 import java.security.AccessController; 68 import java.text.ParseException; 69 import java.time.LocalDate; 70 import java.time.format.DateTimeFormatters; 71 import java.time.temporal.ChronoField; 72 import java.time.temporal.ChronoLocalDate; 73 import java.util.Arrays; 74 import java.util.function.Block; 75 76 /** 77 * A reader for Hijrah Deviation files. 78 * <p> 79 * For each Hijrah calendar a deviation file is used 80 * to modify the initial Hijrah calendar by changing the length of 81 * individual months. 82 * <p> 83 * The default location of the deviation files is 84 * {@code <java.home> + FILE_SEP + "lib"}. 85 * The deviation files are named {@code "hijrah_" + ID}. 86 * <p> 87 * The deviation file for a calendar can be overridden by defining the 88 * property {@code java.time.calendar.HijrahChrono.File.hijrah_<ID>} 89 * with the full pathname of the deviation file. 90 * <p> 91 * The deviation file is read line by line: 92 * <ul> 93 * <li>The "#" character begins a comment - 94 * all characters including and after the "#" on the line are ignored</li> 95 * <li>Valid lines contain two fields, separated by whitespace, whitespace 96 * is otherwise ignored.</li> 97 * <li>The first field is a LocalDate using the format "MMM-dd-yyyy"; 98 * The LocalDate must be converted to a HijrahDate using 99 * the {@code islamic} calendar.</li> 100 * <li>The second field is the offset, +2, +1, -1, -2 as parsed 101 * by Integer.valueOf to modify the length of the Hijrah month.</li> 102 * <li>Empty lines are ignore.</li> 103 * <li>Exceptions are throw for invalid formatted dates and offset, 104 * and other I/O errors on the file. 105 * </ul> 106 * <p>Example:</p> 107 * <pre># Deviation data for islamicc calendar 108 * Mar-23-2012 -1 116 final class HijrahDeviationReader { 117 118 /** 119 * Default prefix for name of deviation file; suffix is typeId. 120 */ 121 private static final String DEFAULT_CONFIG_FILE_PREFIX = "hijrah_"; 122 123 /** 124 * Read Hijrah_deviation.cfg file. The config file contains the deviation 125 * data with format defined in the class javadoc. 126 * 127 * @param typeId the name of the calendar 128 * @param calendarType the calendar type 129 * @return {@code true} if the file was read and each entry accepted by the 130 * Block; else {@code false} no configuration was done 131 * 132 * @throws IOException for zip/jar file handling exception. 133 * @throws ParseException if the format of the configuration file is wrong. 134 */ 135 static boolean readDeviation(String typeId, String calendarType, 136 Block<HijrahChrono.Deviation> block) throws IOException, ParseException { 137 InputStream is = getConfigFileInputStream(typeId); 138 if (is != null) { 139 try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) { 140 String line = ""; 141 int num = 0; 142 while ((line = br.readLine()) != null) { 143 num++; 144 HijrahChrono.Deviation entry = parseLine(line, num); 145 if (entry != null) { 146 block.accept(entry); 147 } 148 } 149 } 150 return true; 151 } 152 return false; 153 } 154 155 /** 156 * Parse each deviation element. 157 * 158 * @param line a line to parse 159 * @param num line number 160 * @return an Entry or null if the line is empty. 161 * @throws ParseException if line has incorrect format. 162 */ 163 private static HijrahChrono.Deviation parseLine(final String line, final int num) throws ParseException { 164 int hash = line.indexOf("#"); 165 String nocomment = (hash < 0) ? line : line.substring(0, hash); 166 String[] split = nocomment.split("\\s"); 167 if (split.length == 0 || split[0].isEmpty()) { 168 return null; // Nothing to parse 169 } 170 if (split.length != 2) { 171 throw new ParseException("Less than 2 tokens on line : " + line + Arrays.toString(split) + ", split.length: " + split.length, num); 172 } 173 174 //element [0] is a date 175 //element [1] is the offset 176 177 LocalDate isoDate = DateTimeFormatters.pattern("MMM-dd-yyyy").parse(split[0], LocalDate::from); 178 int offset = Integer.valueOf(split[1]); 179 180 // Convert date to HijrahDate using the default Islamic Calendar 181 182 ChronoLocalDate<HijrahChrono> hijrahDate = HijrahChrono.INSTANCE.date(isoDate); 183 184 int year = hijrahDate.get(ChronoField.YEAR); 185 int month = hijrahDate.get(ChronoField.MONTH_OF_YEAR); 186 return new HijrahChrono.Deviation(year, month, year, month, offset); 187 } 188 189 190 /** 191 * Return InputStream for deviation configuration file. The default location 192 * of the deviation file is: 193 * <pre> 194 * $CLASSPATH/java/time/calendar 195 * </pre> And the default file name is: 196 * <pre> 197 * hijrah_ + typeId + .cfg 198 * </pre> The default location and file name can be overridden by setting 199 * following two Java system properties. 200 * <pre> 201 * Location: java.time.calendar.HijrahDate.deviationConfigDir 202 * File name: java.time.calendar.HijrahDate.File. + typeid 203 * </pre> Regarding the file format, see readDeviationConfig() method for 204 * details. 205 * 206 * @param typeId the name of the calendar deviation data 207 * @return InputStream for file reading. 208 * @throws IOException for zip/jar file handling exception. 209 */ 210 private static InputStream getConfigFileInputStream(final String typeId) throws IOException { 211 try { 212 InputStream stream = 213 (InputStream)AccessController 214 .doPrivileged((java.security.PrivilegedExceptionAction) () -> { 215 String propFilename = "java.time.calendar.HijrahChrono.File." + typeId; 216 String filename = System.getProperty(propFilename); 217 File file = null; 218 if (filename != null) { 219 file = new File(filename); 220 } else { 221 String libDir = System.getProperty("java.home") + File.separator + "lib"; 222 try { 223 libDir = FileSystems.getDefault().getPath(libDir).toRealPath().toString(); 224 } catch(Exception e) {} 225 filename = DEFAULT_CONFIG_FILE_PREFIX + typeId + ".cfg"; 226 file = new File(libDir, filename); 227 } 228 229 if (file.exists()) { 230 try { 231 return new FileInputStream(file); 232 } catch (IOException ioe) { 233 throw ioe; 234 } 235 } else { | 37 * * Redistributions in binary form must reproduce the above copyright notice, 38 * this list of conditions and the following disclaimer in the documentation 39 * and/or other materials provided with the distribution. 40 * 41 * * Neither the name of JSR-310 nor the names of its contributors 42 * may be used to endorse or promote products derived from this software 43 * without specific prior written permission. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 46 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 47 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 48 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 49 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 50 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 51 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 52 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 53 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 54 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 55 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 */ 57 package java.time.chrono; 58 59 60 import java.io.BufferedReader; 61 import java.io.File; 62 import java.io.FileInputStream; 63 import java.io.IOException; 64 import java.io.InputStream; 65 import java.io.InputStreamReader; 66 import java.nio.file.FileSystems; 67 import java.security.AccessController; 68 import java.text.ParseException; 69 import java.time.LocalDate; 70 import java.time.format.DateTimeFormatter; 71 import java.time.temporal.ChronoField; 72 import java.util.Arrays; 73 import java.util.function.Block; 74 75 /** 76 * A reader for Hijrah Deviation files. 77 * <p> 78 * For each Hijrah calendar a deviation file is used 79 * to modify the initial Hijrah calendar by changing the length of 80 * individual months. 81 * <p> 82 * The default location of the deviation files is 83 * {@code <java.home> + FILE_SEP + "lib"}. 84 * The deviation files are named {@code "hijrah_" + ID}. 85 * <p> 86 * The deviation file for a calendar can be overridden by defining the 87 * property {@code java.time.chrono.HijrahChronology.File.hijrah_<ID>} 88 * with the full pathname of the deviation file. 89 * <p> 90 * The deviation file is read line by line: 91 * <ul> 92 * <li>The "#" character begins a comment - 93 * all characters including and after the "#" on the line are ignored</li> 94 * <li>Valid lines contain two fields, separated by whitespace, whitespace 95 * is otherwise ignored.</li> 96 * <li>The first field is a LocalDate using the format "MMM-dd-yyyy"; 97 * The LocalDate must be converted to a HijrahDate using 98 * the {@code islamic} calendar.</li> 99 * <li>The second field is the offset, +2, +1, -1, -2 as parsed 100 * by Integer.valueOf to modify the length of the Hijrah month.</li> 101 * <li>Empty lines are ignore.</li> 102 * <li>Exceptions are throw for invalid formatted dates and offset, 103 * and other I/O errors on the file. 104 * </ul> 105 * <p>Example:</p> 106 * <pre># Deviation data for islamicc calendar 107 * Mar-23-2012 -1 115 final class HijrahDeviationReader { 116 117 /** 118 * Default prefix for name of deviation file; suffix is typeId. 119 */ 120 private static final String DEFAULT_CONFIG_FILE_PREFIX = "hijrah_"; 121 122 /** 123 * Read Hijrah_deviation.cfg file. The config file contains the deviation 124 * data with format defined in the class javadoc. 125 * 126 * @param typeId the name of the calendar 127 * @param calendarType the calendar type 128 * @return {@code true} if the file was read and each entry accepted by the 129 * Block; else {@code false} no configuration was done 130 * 131 * @throws IOException for zip/jar file handling exception. 132 * @throws ParseException if the format of the configuration file is wrong. 133 */ 134 static boolean readDeviation(String typeId, String calendarType, 135 Block<HijrahChronology.Deviation> block) throws IOException, ParseException { 136 InputStream is = getConfigFileInputStream(typeId); 137 if (is != null) { 138 try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) { 139 String line = ""; 140 int num = 0; 141 while ((line = br.readLine()) != null) { 142 num++; 143 HijrahChronology.Deviation entry = parseLine(line, num); 144 if (entry != null) { 145 block.accept(entry); 146 } 147 } 148 } 149 return true; 150 } 151 return false; 152 } 153 154 /** 155 * Parse each deviation element. 156 * 157 * @param line a line to parse 158 * @param num line number 159 * @return an Entry or null if the line is empty. 160 * @throws ParseException if line has incorrect format. 161 */ 162 private static HijrahChronology.Deviation parseLine(final String line, final int num) throws ParseException { 163 int hash = line.indexOf("#"); 164 String nocomment = (hash < 0) ? line : line.substring(0, hash); 165 String[] split = nocomment.split("\\s"); 166 if (split.length == 0 || split[0].isEmpty()) { 167 return null; // Nothing to parse 168 } 169 if (split.length != 2) { 170 throw new ParseException("Less than 2 tokens on line : " + line + Arrays.toString(split) + ", split.length: " + split.length, num); 171 } 172 173 //element [0] is a date 174 //element [1] is the offset 175 176 LocalDate isoDate = DateTimeFormatter.ofPattern("MMM-dd-yyyy").parse(split[0], LocalDate::from); 177 int offset = Integer.valueOf(split[1]); 178 179 // Convert date to HijrahDate using the default Islamic Calendar 180 181 HijrahDate hijrahDate = HijrahChronology.INSTANCE.date(isoDate); 182 183 int year = hijrahDate.get(ChronoField.YEAR); 184 int month = hijrahDate.get(ChronoField.MONTH_OF_YEAR); 185 return new HijrahChronology.Deviation(year, month, year, month, offset); 186 } 187 188 189 /** 190 * Return InputStream for deviation configuration file. The default location 191 * of the deviation file is: 192 * <pre> 193 * $CLASSPATH/java/time/calendar 194 * </pre> And the default file name is: 195 * <pre> 196 * hijrah_ + typeId + .cfg 197 * </pre> The default location and file name can be overridden by setting 198 * following two Java system properties. 199 * <pre> 200 * Location: java.time.chrono.HijrahDate.deviationConfigDir 201 * File name: java.time.chrono.HijrahDate.File. + typeid 202 * </pre> Regarding the file format, see readDeviationConfig() method for 203 * details. 204 * 205 * @param typeId the name of the calendar deviation data 206 * @return InputStream for file reading. 207 * @throws IOException for zip/jar file handling exception. 208 */ 209 private static InputStream getConfigFileInputStream(final String typeId) throws IOException { 210 try { 211 InputStream stream = AccessController 212 .doPrivileged((java.security.PrivilegedExceptionAction<InputStream>) () -> { 213 String propFilename = "java.time.chrono.HijrahChronology.File." + typeId; 214 String filename = System.getProperty(propFilename); 215 File file = null; 216 if (filename != null) { 217 file = new File(filename); 218 } else { 219 String libDir = System.getProperty("java.home") + File.separator + "lib"; 220 try { 221 libDir = FileSystems.getDefault().getPath(libDir).toRealPath().toString(); 222 } catch(Exception e) {} 223 filename = DEFAULT_CONFIG_FILE_PREFIX + typeId + ".cfg"; 224 file = new File(libDir, filename); 225 } 226 227 if (file.exists()) { 228 try { 229 return new FileInputStream(file); 230 } catch (IOException ioe) { 231 throw ioe; 232 } 233 } else { |