001/*
002 *  Copyright 2001-2013 Stephen Colebourne
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 *      http://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 */
016package org.joda.beans.impl.direct;
017
018import java.util.NoSuchElementException;
019import java.util.Set;
020
021import org.joda.beans.Bean;
022import org.joda.beans.JodaBeanUtils;
023import org.joda.beans.Property;
024
025/**
026 * A bean implementation designed for use by the code generator.
027 * <p>
028 * This implementation uses direct access via {@link #propertyGet(String, boolean)} and
029 * {@link #propertySet(String, Object, boolean)} to avoid reflection.
030 * <p>
031 * For code generation, the bean must directly extend this class and have a
032 * no-arguments constructor.
033 * 
034 * @author Stephen Colebourne
035 */
036public abstract class DirectBean implements Bean {
037
038    @Override
039    public <R> Property<R> property(String propertyName) {
040        return metaBean().<R>metaProperty(propertyName).createProperty(this);
041    }
042
043    @Override
044    public Set<String> propertyNames() {
045        return metaBean().metaPropertyMap().keySet();
046    }
047
048    //-------------------------------------------------------------------------
049    /**
050     * Gets the value of the property.
051     * 
052     * @param propertyName  the property name, not null
053     * @param quiet  true to return null if unable to read
054     * @return the value of the property, may be null
055     * @throws NoSuchElementException if the property name is invalid
056     */
057    protected Object propertyGet(String propertyName, boolean quiet) {
058        throw new NoSuchElementException("Unknown property: " + propertyName);
059    }
060
061    /**
062     * Sets the value of the property.
063     * 
064     * @param propertyName  the property name, not null
065     * @param value  the value of the property, may be null
066     * @param quiet  true to take no action if unable to write
067     * @throws NoSuchElementException if the property name is invalid
068     */
069    protected void propertySet(String propertyName, Object value, boolean quiet) {
070        throw new NoSuchElementException("Unknown property: " + propertyName);
071    }
072
073    /**
074     * Validates the values of the properties.
075     * 
076     * @throws RuntimeException if a property is invalid
077     */
078    protected void validate() {
079    }
080
081    //-----------------------------------------------------------------------
082    @Override
083    public boolean equals(Object obj) {
084        if (obj == this) {
085            return true;
086        }
087        if (obj != null && getClass() == obj.getClass()) {
088            DirectBean other = (DirectBean) obj;
089            for (String name : propertyNames()) {
090                Object value1 = propertyGet(name, true);
091                Object value2 = other.propertyGet(name, true);
092                if (JodaBeanUtils.equal(value1, value2) == false) {
093                    return false;
094                }
095            }
096            return true;
097        }
098        return false;
099    }
100
101    @Override
102    public int hashCode() {
103        int hash = getClass().hashCode();
104        Set<String> names = propertyNames();
105        for (String name : names) {
106            Object value = propertyGet(name, true);
107            hash += JodaBeanUtils.hashCode(value);
108        }
109        return hash;
110    }
111
112    @Override
113    public String toString() {
114        Set<String> names = propertyNames();
115        StringBuilder buf = new StringBuilder((names.size()) * 32 + 32);
116        buf.append(getClass().getSimpleName());
117        buf.append('{');
118        if (names.size() > 0) {
119            for (String name : names) {
120                Object value = propertyGet(name, true);
121                buf.append(name).append('=').append(value).append(',').append(' ');
122            }
123            buf.setLength(buf.length() - 2);
124        }
125        buf.append('}');
126        return buf.toString();
127    }
128
129}