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 The Apache MINA Project (dev@mina.apache.org)
34   * @version $Revision: 601229 $, $Date: 2007-12-05 08:13:18 +0100 (mer, 05 déc 2007) $
35   */
36  public class MapEditor extends AbstractPropertyEditor {
37      static final Pattern ELEMENT = Pattern.compile(
38              "([,\\s]+)|" + // Entry delimiter
39              "(\\s*=\\s*)|" + // Key-Value delimiter
40              "(?<=\")((?:\\\\\"|\\\\'|\\\\\\\\|\\\\ |[^\"])*)(?=\")|" +
41              "(?<=')((?:\\\\\"|\\\\'|\\\\\\\\|\\\\ |[^'])*)(?=')|" +
42              "((?:[^\\\\\\s'\",]|\\\\ |\\\\\"|\\\\')+)");
43      
44      private final Class<?> keyType;
45      private final Class<?> valueType;
46      
47      public MapEditor(Class<?> keyType, Class<?> valueType) {
48          if (keyType == null) {
49              throw new NullPointerException("keyType");
50          }
51          if (valueType == null) {
52              throw new NullPointerException("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(
65                      "No key " + PropertyEditor.class.getSimpleName() + 
66                      " found for " + keyType.getSimpleName() + '.');
67          }
68          return e;
69      }
70  
71      private PropertyEditor getValueEditor() {
72          PropertyEditor e = PropertyEditorFactory.getInstance(valueType);
73          if (e == null) {
74              throw new IllegalArgumentException(
75                      "No value " + PropertyEditor.class.getSimpleName() + 
76                      " found for " + valueType.getSimpleName() + '.');
77          }
78          return e;
79      }
80  
81      @Override
82      @SuppressWarnings("unchecked")
83      protected final String toText(Object value) {
84          StringBuilder buf = new StringBuilder();
85          for (Object o: ((Map) value).entrySet()) {
86              Map.Entry entry = (Map.Entry) o;
87              Object ekey = entry.getKey();
88              Object evalue = entry.getValue();
89              
90              PropertyEditor ekeyEditor = PropertyEditorFactory.getInstance(ekey);
91              if (ekeyEditor == null) {
92                  throw new IllegalArgumentException(
93                          "No key " + PropertyEditor.class.getSimpleName() + 
94                          " found for " + ekey.getClass().getSimpleName() + '.');
95              }
96              ekeyEditor.setValue(ekey);
97              
98              PropertyEditor evalueEditor = PropertyEditorFactory.getInstance(evalue);
99              if (evalueEditor == null) {
100                 throw new IllegalArgumentException(
101                         "No value " + PropertyEditor.class.getSimpleName() + 
102                         " found for " + evalue.getClass().getSimpleName() + '.');
103             }
104             ekeyEditor.setValue(ekey);
105             evalueEditor.setValue(evalue);
106             
107             // TODO normalize.
108             String keyString = ekeyEditor.getAsText();
109             String valueString = evalueEditor.getAsText();
110             buf.append(keyString);
111             buf.append(" = ");
112             buf.append(valueString);
113             buf.append(", ");
114         }
115         
116         // Remove the last delimiter.
117         if (buf.length() >= 2) {
118             buf.setLength(buf.length() - 2);
119         }
120         return buf.toString();
121     }
122 
123     @Override
124     protected final Object toValue(String text) throws IllegalArgumentException {
125         PropertyEditor keyEditor = getKeyEditor();
126         PropertyEditor valueEditor = getValueEditor();
127         Map<Object, Object> answer = newMap();
128         Matcher m = ELEMENT.matcher(text);
129         TokenType lastTokenType = TokenType.ENTRY_DELIM;
130         Object key = null;
131         Object value = null;
132 
133         while (m.find()) {
134             if (m.group(1) != null) {
135                 switch (lastTokenType) {
136                 case VALUE: case ENTRY_DELIM:
137                     break;
138                 default:
139                     throw new IllegalArgumentException(
140                             "Unexpected entry delimiter: " + text);
141                 }
142                 
143                 lastTokenType = TokenType.ENTRY_DELIM;
144                 continue;
145             }
146             
147             if (m.group(2) != null) {
148                 if (lastTokenType != TokenType.KEY) {
149                     throw new IllegalArgumentException(
150                             "Unexpected key-value delimiter: " + text);
151                 }
152                 
153                 lastTokenType = TokenType.KEY_VALUE_DELIM;
154                 continue;
155             }
156             
157             
158             // TODO escape here.
159             String region = m.group();
160 
161             if (m.group(3) != null || m.group(4) != null) {
162                 // Skip the last '"'.
163                 m.region(m.end() + 1, m.regionEnd());
164             }
165             
166             switch (lastTokenType) {
167             case ENTRY_DELIM:
168                 keyEditor.setAsText(region);
169                 key = keyEditor.getValue();
170                 lastTokenType = TokenType.KEY;
171                 break;
172             case KEY_VALUE_DELIM:
173                 valueEditor.setAsText(region);
174                 value = valueEditor.getValue();
175                 lastTokenType = TokenType.VALUE;
176                 answer.put(key, value);
177                 break;
178             case KEY: case VALUE:
179                 throw new IllegalArgumentException(
180                         "Unexpected key or value: " + text);
181             }
182         }
183         
184         return answer;
185     }
186     
187     protected Map<Object, Object> newMap() {
188         return new LinkedHashMap<Object, Object>();
189     }
190     
191     private static enum TokenType {
192         ENTRY_DELIM,
193         KEY_VALUE_DELIM,
194         KEY,
195         VALUE,
196     }
197 }