001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.beanutils2.converters;
020
021import java.awt.Point;
022import java.util.regex.Pattern;
023
024/**
025 * {@link org.apache.commons.beanutils2.Converter} implementation that handles conversion to and from {@link Point}.
026 *
027 * @since 2.0.0
028 */
029public class PointConverter extends AbstractConverter<Point> {
030
031    /** Pattern used to split the {@link Point#x} and {@link Point#y} coordinate. */
032    private static final Pattern POINT_SPLIT = Pattern.compile("\\s*,\\s*");
033
034    /**
035     * Construct a <strong>{@link Point}</strong> <em>Converter</em> that throws a {@code ConversionException} if an error occurs.
036     */
037    public PointConverter() {
038    }
039
040    /**
041     * Constructs a {@link org.apache.commons.beanutils2.Converter} that will return the specified default value if a conversion error occurs.
042     *
043     * @param defaultValue The default value to be returned if the value to be converted is missing or an error occurs converting the value.
044     */
045    public PointConverter(final Point defaultValue) {
046        super(defaultValue);
047    }
048
049    /**
050     * Converts the specified input object into an output object of the specified type.
051     *
052     * @param type  Data type to which this value should be converted.
053     * @param value The {@link String} property value to convert.
054     * @return A {@link Point} represented by the x and y coordinate of the input.
055     * @throws NullPointerException     If the value is null.
056     * @throws IllegalArgumentException If the configuration value is an invalid representation of a {@link Point}.
057     * @throws NumberFormatException    If a one of coordinates cannot be parsed to an {@link Integer}.
058     */
059    @Override
060    protected <T> T convertToType(final Class<T> type, final Object value) throws Throwable {
061        if (Point.class.isAssignableFrom(type)) {
062            final String stringValue = toString(value);
063
064            if (stringValue.isEmpty()) {
065                throw new IllegalArgumentException("A point cannot be empty.");
066            }
067
068            final int lastCharIndex = stringValue.length() - 1;
069
070            if (stringValue.charAt(0) != '(' || stringValue.charAt(lastCharIndex) != ')') {
071                throw new IllegalArgumentException("Point coordinates must be enclosed in brackets.");
072            }
073
074            final String coordinates = stringValue.substring(1, lastCharIndex);
075            final String[] xy = POINT_SPLIT.split(coordinates);
076
077            if (xy.length != 2) {
078                throw new IllegalArgumentException("Point must have an x coordinate, and y coordinate only, " + "expecting the following format: (40, 200)");
079            }
080
081            final int x = Integer.parseInt(xy[0]);
082            final int y = Integer.parseInt(xy[1]);
083            return type.cast(new Point(x, y));
084        }
085
086        throw conversionException(type, value);
087    }
088
089    /**
090     * Gets the default type this {@code Converter} handles.
091     *
092     * @return The default type this {@code Converter} handles.
093     * @since 2.0.0
094     */
095    @Override
096    protected Class<Point> getDefaultType() {
097        return Point.class;
098    }
099}