View Javadoc

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
    10   *    http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   *  Unless required by applicable law or agreed to in writing,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.integration.beans;
    21
    22  import java.beans.PropertyEditor;
    23  import java.util.Collection;
    24  import java.util.LinkedHashMap;
    25  import java.util.Map;
    26  import java.util.regex.Matcher;
    27  import java.util.regex.Pattern;
    28
    29  /**
    30   * A {@link PropertyEditor} which converts a {@link String} into
    31   * a {@link Collection} and vice versa.
    32   *
    33   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    34   */
    35  public class MapEditor extends AbstractPropertyEditor {
    36      static final Pattern ELEMENT = Pattern.compile("([,\\s]+)|"
    37              + // Entry delimiter
    38              "(\\s*=\\s*)|"
    39              + // Key-Value delimiter
    40              "(?<=\")((?:\\\\\"|\\\\'|\\\\\\\\|\\\\ |[^\"])*)(?=\")|"
    41              + "(?<=')((?:\\\\\"|\\\\'|\\\\\\\\|\\\\ |[^'])*)(?=')|" + "((?:[^\\\\\\s'\",]|\\\\ |\\\\\"|\\\\')+)");
    42
    43      private final Class<?> keyType;
    44
    45      private final Class<?> valueType;
    46
    47      public MapEditor(Class<?> keyType, Class<?> valueType) {
    48          if (keyType == null) {
    49              throw new IllegalArgumentException("keyType");
    50          }
    51          if (valueType == null) {
    52              throw new IllegalArgumentException("valueType");
    53          }
    54          this.keyType = keyType;
    55          this.valueType = valueType;
    56          getKeyEditor();
    57          getValueEditor();
    58          setTrimText(false);
    59      }
    60
    61      private PropertyEditor getKeyEditor() {
    62          PropertyEditor e = PropertyEditorFactory.getInstance(keyType);
    63          if (e == null) {
    64              throw new IllegalArgumentException("No key " + PropertyEditor.class.getSimpleName() + " found for "
    65                      + keyType.getSimpleName() + '.');
    66          }
    67          return e;
    68      }
    69
    70      private PropertyEditor getValueEditor() {
    71          PropertyEditor e = PropertyEditorFactory.getInstance(valueType);
    72          if (e == null) {
    73              throw new IllegalArgumentException("No value " + PropertyEditor.class.getSimpleName() + " found for "
    74                      + valueType.getSimpleName() + '.');
    75          }
    76          return e;
    77      }
    78
    79      @Override
    80      @SuppressWarnings("unchecked")
    81      protected final String toText(Object value) {
    82          StringBuilder buf = new StringBuilder();
    83          for (Object o : ((Map) value).entrySet()) {
    84              Map.Entry entry = (Map.Entry) o;
    85              Object ekey = entry.getKey();
    86              Object evalue = entry.getValue();
    87
    88              PropertyEditor ekeyEditor = PropertyEditorFactory.getInstance(ekey);
    89              if (ekeyEditor == null) {
    90                  throw new IllegalArgumentException("No key " + PropertyEditor.class.getSimpleName() + " found for "
    91                          + ekey.getClass().getSimpleName() + '.');
    92              }
    93              ekeyEditor.setValue(ekey);
    94
    95              PropertyEditor evalueEditor = PropertyEditorFactory.getInstance(evalue);
    96              if (evalueEditor == null) {
    97                  throw new IllegalArgumentException("No value " + PropertyEditor.class.getSimpleName() + " found for "
    98                          + evalue.getClass().getSimpleName() + '.');
    99              }
    100             ekeyEditor.setValue(ekey);
    101             evalueEditor.setValue(evalue);
    102
    103             // TODO normalize.
    104             String keyString = ekeyEditor.getAsText();
    105             String valueString = evalueEditor.getAsText();
    106             buf.append(keyString);
    107             buf.append(" = ");
    108             buf.append(valueString);
    109             buf.append(", ");
    110         }
    111
    112         // Remove the last delimiter.
    113         if (buf.length() >= 2) {
    114             buf.setLength(buf.length() - 2);
    115         }
    116         return buf.toString();
    117     }
    118
    119     @Override
    120     protected final Object toValue(String text) throws IllegalArgumentException {
    121         PropertyEditor keyEditor = getKeyEditor();
    122         PropertyEditor valueEditor = getValueEditor();
    123         Map<Object, Object> answer = newMap();
    124         Matcher m = ELEMENT.matcher(text);
    125         TokenType lastTokenType = TokenType.ENTRY_DELIM;
    126         Object key = null;
    127         Object value = null;
    128
    129         while (m.find()) {
    130             if (m.group(1) != null) {
    131                 switch (lastTokenType) {
    132                 case VALUE:
    133                 case ENTRY_DELIM:
    134                     break;
    135                 default:
    136                     throw new IllegalArgumentException("Unexpected entry delimiter: " + text);
    137                 }
    138
    139                 lastTokenType = TokenType.ENTRY_DELIM;
    140                 continue;
    141             }
    142
    143             if (m.group(2) != null) {
    144                 if (lastTokenType != TokenType.KEY) {
    145                     throw new IllegalArgumentException("Unexpected key-value delimiter: " + text);
    146                 }
    147
    148                 lastTokenType = TokenType.KEY_VALUE_DELIM;
    149                 continue;
    150             }
    151
    152             // TODO escape here.
    153             String region = m.group();
    154
    155             if (m.group(3) != null || m.group(4) != null) {
    156                 // Skip the last '"'.
    157                 m.region(m.end() + 1, m.regionEnd());
    158             }
    159
    160             switch (lastTokenType) {
    161             case ENTRY_DELIM:
    162                 keyEditor.setAsText(region);
    163                 key = keyEditor.getValue();
    164                 lastTokenType = TokenType.KEY;
    165                 break;
    166             case KEY_VALUE_DELIM:
    167                 valueEditor.setAsText(region);
    168                 value = valueEditor.getValue();
    169                 lastTokenType = TokenType.VALUE;
    170                 answer.put(key, value);
    171                 break;
    172             case KEY:
    173             case VALUE:
    174                 throw new IllegalArgumentException("Unexpected key or value: " + text);
    175             }
    176         }
    177
    178         return answer;
    179     }
    180
    181     protected Map<Object, Object> newMap() {
    182         return new LinkedHashMap<Object, Object>();
    183     }
    184
    185     private static enum TokenType {
    186         ENTRY_DELIM, KEY_VALUE_DELIM, KEY, VALUE,
    187     }
    188 }