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.camel.impl.converter;
018
019import java.beans.PropertyEditor;
020import java.beans.PropertyEditorManager;
021import java.util.HashMap;
022import java.util.Map;
023
024import org.apache.camel.Exchange;
025import org.apache.camel.TypeConverter;
026import org.apache.camel.util.LRUSoftCache;
027import org.apache.camel.util.ObjectHelper;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031/**
032 * Uses the {@link java.beans.PropertyEditor} conversion system to convert Objects to
033 * and from String values.
034 *
035 * @deprecated should be removed as it can cause side-effects when using 3rd party property editors
036 *
037 * @version 
038 */
039@Deprecated
040public class PropertyEditorTypeConverter implements TypeConverter {
041
042    private static final Logger LOG = LoggerFactory.getLogger(PropertyEditorTypeConverter.class);
043    // use a soft bound cache to avoid using too much memory in case a lot of different classes
044    // is being converted to string
045    private final Map<Class<?>, Class<?>> misses = new LRUSoftCache<Class<?>, Class<?>>(1000);
046    // we don't anticipate so many property editors so we have unbounded map
047    private final Map<Class<?>, PropertyEditor> cache = new HashMap<Class<?>, PropertyEditor>();
048
049    public void clear() {
050        cache.clear();
051        misses.clear();
052    }
053
054    @Override
055    public boolean allowNull() {
056        return false;
057    }
058
059    @Override
060    public <T> T convertTo(Class<T> type, Object value) {
061        // We can't convert null values since we can't figure out a property
062        // editor for it.
063        if (value == null) {
064            return null;
065        }
066
067        if (value.getClass() == String.class) {
068            // No conversion needed.
069            if (type == String.class) {
070                return ObjectHelper.cast(type, value);
071            }
072
073            Class<?> key = type;
074            PropertyEditor editor = lookupEditor(key);
075            if (editor != null) {
076                // we are essentially not thread safe as we use 2 calls to convert
077                editor.setAsText(value.toString());
078                return ObjectHelper.cast(type, editor.getValue());
079            }
080        } else if (type == String.class) {
081            Class<?> key = value.getClass();
082            PropertyEditor editor = lookupEditor(key);
083            if (editor != null) {
084                // we are essentially not thread safe as we use 2 calls to convert
085                editor.setValue(value);
086                return ObjectHelper.cast(type, editor.getAsText());
087            }
088        }
089
090        return null;
091    }
092
093    private PropertyEditor lookupEditor(Class<?> type) {
094        // check misses first
095        if (misses.containsKey(type)) {
096            LOG.trace("No previously found property editor for type: {}", type);
097            return null;
098        }
099
100        synchronized (cache) {
101            // not a miss then try to lookup the editor
102            PropertyEditor editor = cache.get(type);
103            if (editor == null) {
104                // findEditor is synchronized and very slow so we want to only lookup once for a given key
105                // and then we use our own local cache for faster lookup
106                editor = PropertyEditorManager.findEditor(type);
107
108                // either we found an editor, or if not then register it as a miss
109                if (editor != null) {
110                    LOG.trace("Found property editor for type: {} -> {}", type, editor);
111                    cache.put(type, editor);
112                } else {
113                    LOG.trace("Cannot find property editor for type: {}", type);
114                    misses.put(type, type);
115                }
116            }
117            return editor;
118        }
119    }
120
121    @Override
122    public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
123        return convertTo(type, value);
124    }
125
126    @Override
127    public <T> T mandatoryConvertTo(Class<T> type, Object value) {
128        return convertTo(type, value);
129    }
130
131    @Override
132    public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) {
133        return convertTo(type, value);
134    }
135
136    @Override
137    public <T> T tryConvertTo(Class<T> type, Exchange exchange, Object value) {
138        try {
139            return convertTo(type, exchange, value);
140        } catch (Exception e) {
141            return null;
142        }
143    }
144
145    @Override
146    public <T> T tryConvertTo(Class<T> type, Object value) {
147        try {
148            return convertTo(type, null, value);
149        } catch (Exception e) {
150            return null;
151        }
152    }
153}