001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.beans.propertyeditors;
018
019import java.beans.PropertyEditorSupport;
020
021import org.springframework.lang.Nullable;
022import org.springframework.util.StringUtils;
023
024/**
025 * Editor for a {@link Character}, to populate a property
026 * of type {@code Character} or {@code char} from a String value.
027 *
028 * <p>Note that the JDK does not contain a default
029 * {@link java.beans.PropertyEditor property editor} for {@code char}!
030 * {@link org.springframework.beans.BeanWrapperImpl} will register this
031 * editor by default.
032 *
033 * <p>Also supports conversion from a Unicode character sequence; e.g.
034 * {@code u0041} ('A').
035 *
036 * @author Juergen Hoeller
037 * @author Rob Harrop
038 * @author Rick Evans
039 * @since 1.2
040 * @see Character
041 * @see org.springframework.beans.BeanWrapperImpl
042 */
043public class CharacterEditor extends PropertyEditorSupport {
044
045        /**
046         * The prefix that identifies a string as being a Unicode character sequence.
047         */
048        private static final String UNICODE_PREFIX = "\\u";
049
050        /**
051         * The length of a Unicode character sequence.
052         */
053        private static final int UNICODE_LENGTH = 6;
054
055
056        private final boolean allowEmpty;
057
058
059        /**
060         * Create a new CharacterEditor instance.
061         * <p>The "allowEmpty" parameter controls whether an empty String is to be
062         * allowed in parsing, i.e. be interpreted as the {@code null} value when
063         * {@link #setAsText(String) text is being converted}. If {@code false},
064         * an {@link IllegalArgumentException} will be thrown at that time.
065         * @param allowEmpty if empty strings are to be allowed
066         */
067        public CharacterEditor(boolean allowEmpty) {
068                this.allowEmpty = allowEmpty;
069        }
070
071
072        @Override
073        public void setAsText(@Nullable String text) throws IllegalArgumentException {
074                if (this.allowEmpty && !StringUtils.hasLength(text)) {
075                        // Treat empty String as null value.
076                        setValue(null);
077                }
078                else if (text == null) {
079                        throw new IllegalArgumentException("null String cannot be converted to char type");
080                }
081                else if (isUnicodeCharacterSequence(text)) {
082                        setAsUnicode(text);
083                }
084                else if (text.length() == 1) {
085                        setValue(Character.valueOf(text.charAt(0)));
086                }
087                else {
088                        throw new IllegalArgumentException("String [" + text + "] with length " +
089                                        text.length() + " cannot be converted to char type: neither Unicode nor single character");
090                }
091        }
092
093        @Override
094        public String getAsText() {
095                Object value = getValue();
096                return (value != null ? value.toString() : "");
097        }
098
099
100        private boolean isUnicodeCharacterSequence(String sequence) {
101                return (sequence.startsWith(UNICODE_PREFIX) && sequence.length() == UNICODE_LENGTH);
102        }
103
104        private void setAsUnicode(String text) {
105                int code = Integer.parseInt(text.substring(UNICODE_PREFIX.length()), 16);
106                setValue(Character.valueOf((char) code));
107        }
108
109}