001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.settings; 018 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020import static org.apache.juneau.commons.utils.Utils.*; 021 022import java.util.*; 023import java.util.function.*; 024 025import org.apache.juneau.commons.function.Snippet; 026 027/** 028 * A writable {@link SettingStore} implementation created from functional interfaces. 029 * 030 * <p> 031 * This class allows you to create writable setting stores from lambda expressions or method references, 032 * making it easy to wrap existing property systems (e.g., custom configuration systems) as 033 * {@link SettingStore} instances. 034 * 035 * <h5 class='section'>Return Value Semantics:</h5> 036 * <ul class='spaced-list'> 037 * <li>If the reader function returns <c>null</c>, this store returns <c>null</c> (key doesn't exist). 038 * <li>If the reader function returns a non-null value, this store returns <c>Optional.of(value)</c>. 039 * </ul> 040 * 041 * <p> 042 * Note: This store cannot distinguish between a key that doesn't exist and a key that exists with a null value, 043 * since the reader function only returns a <c>String</c>. If you need to distinguish these cases, use {@link MapStore} instead. 044 * 045 * <h5 class='section'>Example:</h5> 046 * <p class='bjava'> 047 * <jc>// Create a writable functional store</jc> 048 * FunctionalStore <jv>store</jv> = FunctionalStore.<jsf>of</jsf>( 049 * System::getProperty, <jc>// reader</jc> 050 * (k, v) -> System.setProperty(k, v), <jc>// writer</jc> 051 * k -> System.clearProperty(k), <jc>// unset</jc> 052 * () -> { <jc>// clear</jc> 053 * <jc>// Clear all properties logic</jc> 054 * } 055 * ); 056 * 057 * <jc>// Use it</jc> 058 * <jv>store</jv>.set(<js>"my.property"</js>, <js>"value"</js>); 059 * Optional<String> <jv>value</jv> = <jv>store</jv>.get(<js>"my.property"</js>); 060 * <jv>store</jv>.unset(<js>"my.property"</js>); 061 * </p> 062 */ 063public class FunctionalStore implements SettingStore { 064 065 private final Function<String, String> reader; 066 private final BiConsumer<String, String> writer; 067 private final Consumer<String> unsetter; 068 private final Snippet clearer; 069 070 /** 071 * Creates a new writable functional store. 072 * 073 * @param reader The function to read property values. Must not be <c>null</c>. 074 * @param writer The function to write property values. Must not be <c>null</c>. 075 * @param unsetter The function to remove property values. Must not be <c>null</c>. 076 * @param clearer The snippet to clear all property values. Must not be <c>null</c>. 077 */ 078 public FunctionalStore( 079 Function<String, String> reader, 080 BiConsumer<String, String> writer, 081 Consumer<String> unsetter, 082 Snippet clearer 083 ) { 084 assertArgNotNull("reader", reader); 085 assertArgNotNull("writer", writer); 086 assertArgNotNull("unsetter", unsetter); 087 assertArgNotNull("clearer", clearer); 088 this.reader = reader; 089 this.writer = writer; 090 this.unsetter = unsetter; 091 this.clearer = clearer; 092 } 093 094 /** 095 * Returns a setting by applying the reader function. 096 * 097 * <p> 098 * If the reader function returns <c>null</c>, this method returns <c>null</c> (indicating the key doesn't exist). 099 * If the reader function returns a non-null value, this method returns <c>Optional.of(value)</c>. 100 * 101 * @param name The property name. 102 * @return The property value, or <c>null</c> if the reader function returns <c>null</c>. 103 */ 104 @Override 105 public Optional<String> get(String name) { 106 var v = reader.apply(name); 107 return v == null ? null : opt(v); 108 } 109 110 /** 111 * Sets a setting by applying the writer function. 112 * 113 * @param name The property name. 114 * @param value The property value, or <c>null</c> to set an empty override. 115 */ 116 @Override 117 public void set(String name, String value) { 118 writer.accept(name, value); 119 } 120 121 /** 122 * Removes a setting by applying the unsetter function. 123 * 124 * @param name The property name to remove. 125 */ 126 @Override 127 public void unset(String name) { 128 unsetter.accept(name); 129 } 130 131 /** 132 * Clears all settings by invoking the clearer snippet. 133 * 134 * <p> 135 * If the clearer snippet throws an exception, it will be wrapped in a {@link RuntimeException}. 136 */ 137 @Override 138 public void clear() { 139 safe(()->clearer.run()); 140 } 141 142 /** 143 * Creates a writable functional store from four functions. 144 * 145 * <p> 146 * This is a convenience factory method for creating writable functional stores. 147 * 148 * <h5 class='section'>Example:</h5> 149 * <p class='bjava'> 150 * <jc>// Create from lambdas</jc> 151 * FunctionalStore <jv>store</jv> = FunctionalStore.<jsf>of</jsf>( 152 * System::getProperty, 153 * (k, v) -> System.setProperty(k, v), 154 * k -> System.clearProperty(k), 155 * () -> { <jc>// Clear all properties</jc> } 156 * ); 157 * </p> 158 * 159 * @param reader The function to read property values. Must not be <c>null</c>. 160 * @param writer The function to write property values. Must not be <c>null</c>. 161 * @param unsetter The function to remove property values. Must not be <c>null</c>. 162 * @param clearer The snippet to clear all property values. Must not be <c>null</c>. 163 * @return A new writable functional store instance. 164 */ 165 public static FunctionalStore of( 166 Function<String, String> reader, 167 BiConsumer<String, String> writer, 168 Consumer<String> unsetter, 169 Snippet clearer 170 ) { 171 return new FunctionalStore(reader, writer, unsetter, clearer); 172 } 173} 174