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}