All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dbflute.infra.dfprop.DfPropFile Maven / Gradle / Ivy

There is a newer version: 1.2.8
Show newest version
/*
 * Copyright 2014-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.dbflute.infra.dfprop;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.dbflute.exception.DfPropFileReadFailureException;
import org.dbflute.helper.dfmap.DfMapFile;
import org.dbflute.helper.dfmap.exception.DfMapDuplicateEntryException;
import org.dbflute.helper.dfmap.exception.DfMapParseFailureException;
import org.dbflute.helper.message.ExceptionMessageBuilder;
import org.dbflute.util.Srl;

/**
 * The file handling for DBFlute property (dfprop).
 * @author jflute
 * @since 0.9.6 (2009/10/28 Wednesday)
 */
public class DfPropFile {

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    protected boolean _returnsNullIfNotFound;
    protected boolean _skipLineSeparator;
    protected boolean _checkDuplicateEntry;

    // ===================================================================================
    //                                                                                 Map
    //                                                                                 ===
    // -----------------------------------------------------
    //                                                  Read
    //                                                  ----
    /**
     * Read the map string file. 
* If the type of values is various type, this method is available.
* A trimmed line that starts with '#' is treated as line comment.
* This is the most basic method here. *
     * map:{
     *     ; key1 = string-value1
     *     ; key2 = list:{element1 ; element2 }
     *     ; key3 = map:{key1 = value1 ; key2 = value2 }
     *     ; ... = ...
     * }
     * 
* And resolve property file, switched and extended file, for environment.
* It returns merged properties like this: *
     * for example:
     * 
     * [Switch Style]
     * dbflute_exampledb
     *  |-dfprop
     *  |  |-maihama
     *  |  |  |-exampleMap.dfprop  // env
     *  |  |-exampleMap.dfprop     // main
     *
     * if maihama, env
     * if default, main
     * 
     * [Inherit Style]
     * dbflute_exampledb
     *  |-dfprop
     *  |  |-maihama
     *  |  |  |-exampleMap+.dfprop // env+
     *  |  |-exampleMap.dfprop     // main
     * 
     * if maihama, main and env+
     * if default, main only
     * 
     * [All Stars]
     * dbflute_exampledb
     *  |-dfprop
     *  |  |-maihama
     *  |  |  |-exampleMap.dfprop  // env
     *  |  |  |-exampleMap+.dfprop // env+
     *  |  |-exampleMap.dfprop     // main
     *  |  |-exampleMap+.dfprop    // main+
     * 
     * if maihama, env and env+
     * if default, main and main+
     * 
* @param dfpropPath The path of DBFlute property file. (NotNull) * @param envType The environment type of DBFlute. (NullAllowed: if null, no environment file) * @return The read map. (NotNull: if not found, returns empty map) */ public Map readMap(String dfpropPath, String envType) { assertDfpropPath(dfpropPath); return doReadMap(dfpropPath, envType, new DfPropReadingMapHandler() { public Map readMap(String path) throws FileNotFoundException, IOException { return actuallyReadMap(path); } }); } protected Map actuallyReadMap(String path) throws FileNotFoundException, IOException { try { return createMapFileStructural().readMap(createInputStream(path)); } catch (DfMapDuplicateEntryException e) { throwDfPropDuplicateEntryException(path, e); return null; // unreachable } catch (DfMapParseFailureException e) { throwDfPropMapStringParseFailureException(path, e); return null; // unreachable } } protected void throwDfPropDuplicateEntryException(String path, DfMapDuplicateEntryException e) { final ExceptionMessageBuilder br = new ExceptionMessageBuilder(); br.addNotice("Duplicate entry in the map file."); br.addItem("Advice"); br.addElement("The entry keys in the map string should be unique."); br.addElement("For example:"); br.addElement(" (x):"); br.addElement(" Sea = map:{"); br.addElement(" ; ..."); br.addElement(" }"); br.addElement(" Sea = map:{"); br.addElement(" ; ..."); br.addElement(" }"); br.addElement(" (o):"); br.addElement(" Land = map:{"); br.addElement(" ; ..."); br.addElement(" }"); br.addElement(" Sea = map:{"); br.addElement(" ; ..."); br.addElement(" }"); br.addItem("DfProp Path"); br.addElement(path); final String msg = br.buildExceptionMessage(); throw new DfPropFileReadFailureException(msg, e); } protected void throwDfPropMapStringParseFailureException(String path, DfMapParseFailureException e) { final ExceptionMessageBuilder br = new ExceptionMessageBuilder(); br.addNotice("Failed to parse the map file."); br.addItem("Advice"); br.addElement("Make sure your map file format."); br.addElement("For example:"); br.addElement(" (x):"); br.addElement(" map:{"); br.addElement(" ; map:{"); br.addElement(" ..."); br.addElement(" }"); br.addElement(" (o):"); br.addElement(" map:{"); br.addElement(" ; map:{"); br.addElement(" ..."); br.addElement(" }"); br.addElement(" }"); br.addItem("DfProp Path"); br.addElement(path); final String msg = br.buildExceptionMessage(); throw new DfPropFileReadFailureException(msg, e); } /** * Read the map string file as string value.
* If the type of all values is string type, this method is available.
* A trimmed line that starts with '#' is treated as line comment. *
     * e.g.
     * map:{
     *     ; key1 = string-value1
     *     ; key2 = string-value2
     *     ; ... = ...
     * }
     * 
*

* And resolve property file for environment. * (see the {@link #readMap(String, String)}) *

* @param dfpropPath The path of DBFlute property file. (NotNull) * @param envType The environment type of DBFlute. (NullAllowed: if null, no environment file) * @return The read map whose values is string. (NotNull: if not found, returns empty map) */ public Map readMapAsStringValue(String dfpropPath, String envType) { assertDfpropPath(dfpropPath); return doReadMap(dfpropPath, envType, new DfPropReadingMapHandler() { public Map readMap(String path) throws FileNotFoundException, IOException { return actuallyReadMapAsStringValue(path); } }); } protected Map actuallyReadMapAsStringValue(String path) throws FileNotFoundException, IOException { try { return createMapFileStructural().readMap(createInputStream(path), String.class); } catch (DfMapDuplicateEntryException e) { throwDfPropDuplicateEntryException(path, e); return null; // unreachable } } /** * Read the map string file as string list value.
* If the type of all values is string list type, this method is available.
* A trimmed line that starts with '#' is treated as line comment. *
     * e.g.
     * map:{
     *     ; key1 = list:{string-element1 ; string-element2 ; ...}
     *     ; key2 = list:{string-element1 ; string-element2 ; ...}
     *     ; ... = list:{...}
     * }
     * 
*

* And resolve property file for environment. * (see the {@link #readMap(String, String)}) *

* @param dfpropPath The path of DBFlute property file. (NotNull) * @param envType The environment type of DBFlute. (NullAllowed: if null, no environment file) * @return The read map whose values is string list. (NotNull: if not found, returns empty map) */ public Map> readMapAsStringListValue(String dfpropPath, String envType) { assertDfpropPath(dfpropPath); return doReadMap(dfpropPath, envType, new DfPropReadingMapHandler>() { public Map> readMap(String path) throws IOException { return actuallyReadMapAsStringListValue(path); } }); } protected Map> actuallyReadMapAsStringListValue(String path) throws FileNotFoundException, IOException { try { final DfMapFile mapFile = createMapFileStructural(); final Map readMap = mapFile.readMap(createInputStream(path), Object.class); return convertToStringListMap(readMap); } catch (DfMapDuplicateEntryException e) { throwDfPropDuplicateEntryException(path, e); return null; // unreachable } } protected Map> convertToStringListMap(Map readMap) { final Map> resultMap = new LinkedHashMap>(); final Set> entrySet = readMap.entrySet(); for (Entry entry : entrySet) { @SuppressWarnings("unchecked") final List listValue = (List) entry.getValue(); // simple downcast for now resultMap.put(entry.getKey(), listValue); } return resultMap; } /** * Read the map string file as string map value.
* If the type of all values is string map type, this method is available.
* A trimmed line that starts with '#' is treated as line comment. *
     * e.g.
     * map:{
     *     ; key1 = map:{string-key1 = string-value1 ; string-key2 = string-value2 }
     *     ; key2 = map:{string-key1 = string-value1 ; string-key2 = string-value2 }
     *     ; ... = map:{...}
     * }
     * 
*

* And resolve property file for environment. * (see the {@link #readMap(String, String)}) *

* @param dfpropPath The path of DBFlute property file. (NotNull) * @param envType The environment type of DBFlute. (NullAllowed: if null, no environment file) * @return The read map whose values is string map. (NotNull: if not found, returns empty map) */ public Map> readMapAsStringMapValue(String dfpropPath, String envType) { assertDfpropPath(dfpropPath); return doReadMap(dfpropPath, envType, new DfPropReadingMapHandler>() { public Map> readMap(String path) throws IOException { return actuallyReadMapAsStringMapValue(path); } }); } protected Map> actuallyReadMapAsStringMapValue(String path) throws FileNotFoundException, IOException { try { final DfMapFile mapFile = createMapFileStructural(); final Map readMap = mapFile.readMap(createInputStream(path), Object.class); return convertToStringMapValue(readMap); } catch (DfMapDuplicateEntryException e) { throwDfPropDuplicateEntryException(path, e); return null; // unreachable } } protected Map> convertToStringMapValue(Map readMap) { final Map> resultMap = new LinkedHashMap>(); final Set> entrySet = readMap.entrySet(); for (Entry entry : entrySet) { @SuppressWarnings("unchecked") final Map stringMapValue = (Map) entry.getValue(); // simple downcast for now resultMap.put(entry.getKey(), stringMapValue); } return resultMap; } // =================================================================================== // List // ==== // ----------------------------------------------------- // Read // ---- /** * Read the list string file.
* If the type of values is various type, this method is available.
* A trimmed line that starts with '#' is treated as line comment.
*
     * list:{
     *     ; element1
     *     ; list:{element2-1 ; element2-2 }
     *     ; map:{key3-1 = value3-1 ; key3-2 = value3-2 }
     *     ; ... = ...
     * }
     * 
* @param dfpropPath The path of DBFlute property file. (NotNull) * @param envType The environment type of DBFlute. (NullAllowed: if null, no environment file) * @return The read list of object. (NotNull: if not found, returns empty list) */ public List readList(String dfpropPath, String envType) { assertDfpropPath(dfpropPath); return doReadList(dfpropPath, envType, new DfPropReadingListHandler() { public List readList(String path) throws FileNotFoundException, IOException { return actuallyReadList(path); } }); } protected List actuallyReadList(String path) throws FileNotFoundException, IOException { try { return createMapFileStructural().readList(createInputStream(path)); } catch (DfMapDuplicateEntryException e) { throwDfPropDuplicateEntryException(path, e); return null; // unreachable } } // =================================================================================== // String // ====== // ----------------------------------------------------- // Read // ---- /** * Read the string file.
* A trimmed line that starts with '#' is treated as line comment. * @param dfpropPath The path of DBFlute property file. (NotNull) * @param envType The environment type of DBFlute. (NullAllowed: if null, no environment file) * @return The read string. (NotNull: if not found, returns empty string) */ public String readString(String dfpropPath, String envType) { assertDfpropPath(dfpropPath); return doReadString(dfpropPath, envType, new DfPropReadingStringHandler() { public String readString(String path) throws FileNotFoundException, IOException { return actuallyReadString(path); } }); } protected String actuallyReadString(String path) throws FileNotFoundException, IOException { return createMapFilePlain().readString(createInputStream(path)); } // =================================================================================== // Reading Logic // ============= // ----------------------------------------------------- // Map // --- protected Map doReadMap(String dfpropPath, String envType, DfPropReadingMapHandler handler) { // *see the JavaDoc of readMap() for the detail Map map = null; if (envType != null) { final String envPath = deriveEnvPath(dfpropPath, envType); map = callReadingMapChecked(handler, envPath); if (map != null) { // environment driven // dfprop // |-env // | |-foo.dfprop // *base map // | |-foo+.dfprop // if exists // |-foo.dfprop // |-foo+.dfprop // no target resolveOutsidePropInheritMap(handler, envPath, map); } else { // top driven in environment // dfprop // |-env // | |-foo+.dfprop // if exists (first priority) // |-foo.dfprop // *base map // |-foo+.dfprop // if exists (second priority) map = callReadingMapChecked(handler, dfpropPath); if (map != null) { resolveOutsidePropInheritMap(handler, dfpropPath, map); resolveOutsidePropInheritMap(handler, envPath, map); } } } else { // no environment type // dfprop // |-env // | |-... // no target // |-foo.dfprop // *base map // |-foo+.dfprop // if exists map = callReadingMapChecked(handler, dfpropPath); if (map != null) { // top driven without environment resolveOutsidePropInheritMap(handler, dfpropPath, map); } } if (map == null && !_returnsNullIfNotFound) { map = new LinkedHashMap(2); // not read-only just in case } return map; } protected Map callReadingMapChecked(DfPropReadingMapHandler handler, String path) { try { return handler.readMap(path); } catch (FileNotFoundException ignored) { return null; } catch (IOException e) { throwDfPropFileReadFailureException(path, e); return null; // unreachable } } protected boolean resolveOutsidePropInheritMap(DfPropReadingMapHandler handler, String path, Map map) { if (map == null) { // no parent return false; } final String inheritPath = deriveInheritPath(path); if (inheritPath == null) { return false; } final Map inheritMap = callReadingMapChecked(handler, inheritPath); if (inheritMap == null) { return false; } map.putAll(inheritMap); return true; } // ----------------------------------------------------- // List // ---- protected List doReadList(String dfpropPath, String envType, DfPropReadingListHandler handler) { // extended list is not supported List list = null; if (envType != null) { final String envPath = deriveEnvPath(dfpropPath, envType); list = callReadingListChecked(handler, envPath); if (list == null) { list = callReadingListChecked(handler, dfpropPath); } } else { list = callReadingListChecked(handler, dfpropPath); } if (list == null && !_returnsNullIfNotFound) { list = new ArrayList(2); // not read-only just in case } return list; } protected List callReadingListChecked(DfPropReadingListHandler handler, String path) { try { return handler.readList(path); } catch (FileNotFoundException ignored) { return null; } catch (IOException e) { throwDfPropFileReadFailureException(path, e); return null; // unreachable } } // ----------------------------------------------------- // String // ------ protected String doReadString(String dfpropPath, String envType, DfPropReadingStringHandler handler) { // extended string is not supported String str = null; if (envType != null) { final String envPath = deriveEnvPath(dfpropPath, envType); str = callReadingStringChecked(handler, envPath); if (str == null) { str = callReadingStringChecked(handler, dfpropPath); } } else { str = callReadingStringChecked(handler, dfpropPath); } if (str == null && !_returnsNullIfNotFound) { str = ""; } return str; } protected String callReadingStringChecked(DfPropReadingStringHandler handler, String envPath) { try { return handler.readString(envPath); } catch (FileNotFoundException ignored) { return null; } catch (IOException e) { throwDfPropFileReadFailureException(envPath, e); return null; // unreachable } } // =================================================================================== // Derive Path // =========== protected String deriveEnvPath(String dfpropPath, String envType) { final String basePath; final String pureFileName; if (dfpropPath.contains("/")) { basePath = Srl.substringLastFront(dfpropPath, "/"); pureFileName = Srl.substringLastRear(dfpropPath, "/"); } else { basePath = "."; pureFileName = dfpropPath; } return basePath + "/" + envType + "/" + pureFileName; } protected String deriveInheritPath(String path) { final String allowedExt = getInheritAllowedExt(); if (!path.endsWith(allowedExt)) { // out of target e.g. .dataprop return null; } return path.substring(0, path.length() - allowedExt.length()) + "+" + allowedExt; } protected String getInheritAllowedExt() { return ".dfprop"; } // =================================================================================== // Assist Helper // ============= protected InputStream createInputStream(String path) throws FileNotFoundException { return new FileInputStream(path); } protected void throwDfPropFileReadFailureException(String path, IOException e) { final ExceptionMessageBuilder br = new ExceptionMessageBuilder(); br.addNotice("Failed to read the DBFlute property file."); br.addItem("Advice"); br.addElement("Make sure the map-string is correct in the file."); br.addElement("For exapmle, the number of start and end braces are the same."); br.addItem("DBFlute Property"); br.addElement(path); final String msg = br.buildExceptionMessage(); throw new DfPropFileReadFailureException(msg, e); } // =================================================================================== // Map List File // ============= protected DfMapFile createMapFilePlain() { final DfMapFile mapListFile = newMapFile(); if (_checkDuplicateEntry) { mapListFile.checkDuplicateEntry(); } return mapListFile; } protected DfMapFile createMapFileStructural() { final DfMapFile file = createMapFilePlain(); if (_skipLineSeparator) { file.skipLineSeparator(); } return file; } protected DfMapFile newMapFile() { return new DfMapFile(); } // =================================================================================== // Assert Helper // ============= protected void assertDfpropPath(String dfpropPath) { if (dfpropPath == null || dfpropPath.trim().length() == 0) { String msg = "The argument 'dfpropPath' should not be null or empty: " + dfpropPath; throw new IllegalArgumentException(msg); } } // =================================================================================== // Option // ====== public DfPropFile returnsNullIfNotFound() { _returnsNullIfNotFound = true; return this; } public DfPropFile skipLineSeparator() { _skipLineSeparator = true; return this; } public DfPropFile checkDuplicateEntry() { _checkDuplicateEntry = true; return this; } }