001/*
002 * Copyright 2002-2019 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.core.style;
018
019import java.lang.reflect.Method;
020import java.util.Collection;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024import java.util.StringJoiner;
025
026import org.springframework.lang.Nullable;
027import org.springframework.util.ClassUtils;
028import org.springframework.util.ObjectUtils;
029
030/**
031 * Converts objects to String form, generally for debugging purposes,
032 * using Spring's {@code toString} styling conventions.
033 *
034 * <p>Uses the reflective visitor pattern underneath the hood to nicely
035 * encapsulate styling algorithms for each type of styled object.
036 *
037 * @author Keith Donald
038 * @author Juergen Hoeller
039 * @since 1.2.2
040 */
041public class DefaultValueStyler implements ValueStyler {
042
043        private static final String EMPTY = "[[empty]]";
044        private static final String NULL = "[null]";
045        private static final String COLLECTION = "collection";
046        private static final String SET = "set";
047        private static final String LIST = "list";
048        private static final String MAP = "map";
049        private static final String EMPTY_MAP = MAP + EMPTY;
050        private static final String ARRAY = "array";
051
052
053        @Override
054        public String style(@Nullable Object value) {
055                if (value == null) {
056                        return NULL;
057                }
058                else if (value instanceof String) {
059                        return "\'" + value + "\'";
060                }
061                else if (value instanceof Class) {
062                        return ClassUtils.getShortName((Class<?>) value);
063                }
064                else if (value instanceof Method) {
065                        Method method = (Method) value;
066                        return method.getName() + "@" + ClassUtils.getShortName(method.getDeclaringClass());
067                }
068                else if (value instanceof Map) {
069                        return style((Map<?, ?>) value);
070                }
071                else if (value instanceof Map.Entry) {
072                        return style((Map.Entry<? ,?>) value);
073                }
074                else if (value instanceof Collection) {
075                        return style((Collection<?>) value);
076                }
077                else if (value.getClass().isArray()) {
078                        return styleArray(ObjectUtils.toObjectArray(value));
079                }
080                else {
081                        return String.valueOf(value);
082                }
083        }
084
085        private <K, V> String style(Map<K, V> value) {
086                if (value.isEmpty()) {
087                        return EMPTY_MAP;
088                }
089
090                StringJoiner result = new StringJoiner(", ", "[", "]");
091                for (Map.Entry<K, V> entry : value.entrySet()) {
092                        result.add(style(entry));
093                }
094                return MAP + result;
095        }
096
097        private String style(Map.Entry<?, ?> value) {
098                return style(value.getKey()) + " -> " + style(value.getValue());
099        }
100
101        private String style(Collection<?> value) {
102                String collectionType = getCollectionTypeString(value);
103
104                if (value.isEmpty()) {
105                        return collectionType + EMPTY;
106                }
107
108                StringJoiner result = new StringJoiner(", ", "[", "]");
109                for (Object o : value) {
110                        result.add(style(o));
111                }
112                return collectionType + result;
113        }
114
115        private String getCollectionTypeString(Collection<?> value) {
116                if (value instanceof List) {
117                        return LIST;
118                }
119                else if (value instanceof Set) {
120                        return SET;
121                }
122                else {
123                        return COLLECTION;
124                }
125        }
126
127        private String styleArray(Object[] array) {
128                if (array.length == 0) {
129                        return ARRAY + '<' + ClassUtils.getShortName(array.getClass().getComponentType()) + '>' + EMPTY;
130                }
131
132                StringJoiner result = new StringJoiner(", ", "[", "]");
133                for (Object o : array) {
134                        result.add(style(o));
135                }
136                return ARRAY + '<' + ClassUtils.getShortName(array.getClass().getComponentType()) + '>' + result;
137        }
138
139}