001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2020 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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 014 * implied. See the License for the specific language governing 015 * permissions and limitations under the License. 016 */ 017package org.microbean.settings.converter; 018 019import java.util.AbstractMap.SimpleImmutableEntry; 020import java.util.Arrays; 021import java.util.Map.Entry; 022import java.util.Set; 023 024import java.util.regex.Pattern; 025 026import javax.enterprise.inject.Vetoed; 027 028import org.microbean.settings.Converter; 029import org.microbean.settings.Value; 030 031@Vetoed 032public class EntryConverter<K, V> implements Converter<Entry<K, V>> { 033 034 private static final long serialVersionUID = 1L; 035 036 private static final Pattern backslashEqualsPattern = Pattern.compile("\\\\="); 037 038 private static final Pattern splitPattern = Pattern.compile("(?<!\\\\)="); 039 040 private final Converter<? extends K> keyConverter; 041 042 private final Converter<? extends V> valueConverter; 043 044 public EntryConverter(final Converter<? extends K> keyConverter, 045 final Converter<? extends V> valueConverter) { 046 super(); 047 this.keyConverter = keyConverter; 048 this.valueConverter = valueConverter; 049 } 050 051 @Override 052 public Entry<K, V> convert(final Value value) { 053 final Entry<K, V> returnValue; 054 if (value == null) { 055 returnValue = null; 056 } else { 057 final String stringValue = value.get(); 058 if (stringValue == null) { 059 returnValue = null; 060 } else { 061 final String[] parts = split(stringValue); 062 assert parts != null; 063 final String keyString; 064 if (parts.length > 0) { 065 keyString = parts[0]; 066 } else { 067 keyString = null; 068 } 069 final String valueString; 070 if (parts.length > 1) { 071 assert parts.length == 2 : "Unexpected parts: " + Arrays.asList(parts); 072 valueString = parts[1]; 073 } else { 074 valueString = null; 075 } 076 returnValue = new SimpleImmutableEntry<>(this.keyConverter.convert(new Value(value, keyString)), 077 this.valueConverter.convert(new Value(value, valueString))); 078 } 079 } 080 return returnValue; 081 } 082 083 private static final String[] split(final String text) { 084 final String[] returnValue; 085 if (text == null) { 086 returnValue = new String[0]; 087 } else { 088 returnValue = splitPattern.split(text, 2); 089 assert returnValue != null; 090 for (int i = 0; i < returnValue.length; i++) { 091 // TODO: maybe just do this replacement on the key, not the 092 // value? That would eventually allow maps of maps. 093 returnValue[i] = backslashEqualsPattern.matcher(returnValue[i]).replaceAll("="); 094 } 095 } 096 return returnValue; 097 } 098 099 100}