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.wicket.util.convert;
018
019import java.text.ParseException;
020import java.util.Locale;
021
022import javax.swing.text.MaskFormatter;
023
024import org.apache.wicket.util.lang.Args;
025
026
027/**
028 * A converter that takes a mask into account. It is specifically meant for overrides on individual
029 * components, that provide their own converter by returning it from
030 * {@code Component#getConverter(Class)}. It uses an instance of {@link MaskFormatter} to delegate
031 * the masking and unmasking to.
032 * <p>
033 * The following characters can be specified (adopted from the MaskFormatter documentation):
034 * 
035 * <table border=1 summary="Valid characters and their descriptions">
036 * <tr>
037 * <th>Character&nbsp;</th>
038 * <th>
039 * <p align="left">
040 * Description
041 * </p>
042 * </th>
043 * </tr>
044 * <tr>
045 * <td>#</td>
046 * <td>Any valid number, uses <code>Character.isDigit</code>.</td>
047 * </tr>
048 * <tr>
049 * <td>'</td>
050 * <td>Escape character, used to escape any of the special formatting characters.</td>
051 * </tr>
052 * <tr>
053 * <td>U</td>
054 * <td>Any character (<code>Character.isLetter</code>). All lowercase letters are mapped to upper
055 * case.</td>
056 * </tr>
057 * <tr>
058 * <td>L</td>
059 * <td>Any character (<code>Character.isLetter</code>). All upper case letters are mapped to lower
060 * case.</td>
061 * </tr>
062 * <tr>
063 * <td>A</td>
064 * <td>Any character or number (<code>Character.isLetter</code> or <code>Character.isDigit</code>)</td>
065 * </tr>
066 * <tr>
067 * <td>?</td>
068 * <td>Any character (<code>Character.isLetter</code>).</td>
069 * </tr>
070 * <tr>
071 * <td></td>
072 * <td>Anything.</td>
073 * </tr>
074 * <tr>
075 * <td>H</td>
076 * <td>Any hex character (0-9, a-f or A-F).</td>
077 * </tr>
078 * </table>
079 * 
080 * <p>
081 * Typically characters correspond to one char, but in certain languages this is not the case. The
082 * mask is on a per character basis, and will thus adjust to fit as many chars as are needed.
083 * </p>
084 * 
085 * @see MaskFormatter
086 * 
087 * @author Eelco Hillenius
088 * @param <C>
089 */
090public class MaskConverter<C> implements IConverter<C>
091{
092        private static final long serialVersionUID = 1L;
093
094        /** Object that knows all about masks. */
095        private final MaskFormatter maskFormatter;
096
097        /**
098         * Construct.
099         * 
100         * @param maskFormatter
101         *            The mask formatter to use for masking and unmasking values
102         */
103        public MaskConverter(final MaskFormatter maskFormatter)
104        {
105                Args.notNull(maskFormatter, "maskFormatter");
106
107                this.maskFormatter = maskFormatter;
108        }
109
110        /**
111         * Construct; converts to Strings.
112         * 
113         * @param mask
114         *            The mask to use for this converter instance
115         * @see MaskFormatter
116         */
117        public MaskConverter(final String mask)
118        {
119                this(mask, String.class);
120        }
121
122        /**
123         * Construct.
124         * 
125         * @param mask
126         *            The mask to use for this converter instance
127         * @param type
128         *            The type to convert string values to.
129         * @see MaskFormatter
130         */
131        public MaskConverter(final String mask, final Class<?> type)
132        {
133                try
134                {
135                        maskFormatter = new MaskFormatter(mask);
136                        maskFormatter.setValueClass(type);
137                        maskFormatter.setAllowsInvalid(true);
138                        maskFormatter.setValueContainsLiteralCharacters(true);
139                }
140                catch (ParseException e)
141                {
142                        throw new RuntimeException(e);
143                }
144        }
145
146        /**
147         * Converts a string to an object using {@link MaskFormatter#stringToValue(String)}.
148         */
149        @Override
150        @SuppressWarnings("unchecked")
151        public C convertToObject(final String value, final Locale locale)
152        {
153                try
154                {
155                        return (C)maskFormatter.stringToValue(value);
156                }
157                catch (ParseException e)
158                {
159                        throw new ConversionException(e);
160                }
161        }
162
163        /**
164         * Converts the value to a string using {@link MaskFormatter#valueToString(Object)}.
165         */
166        @Override
167        public String convertToString(final C value, final Locale locale)
168        {
169                try
170                {
171                        return maskFormatter.valueToString(value);
172                }
173                catch (ParseException e)
174                {
175                        throw new ConversionException(e);
176                }
177        }
178}