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.wicket.model;
018
019import org.apache.wicket.Component;
020import org.apache.wicket.core.util.lang.PropertyResolver;
021
022/**
023 * A model that references a property by name on the current model of the component it is bound to.
024 * This enables direct usage of inherited models such as compound property models.
025 * 
026 * @author Jonathan Locke
027 * 
028 * @param <T>
029 *            The Model object
030 */
031public class ComponentPropertyModel<T> implements IComponentAssignedModel<T>
032{
033        private static final long serialVersionUID = 1L;
034
035        /** Name of property to read */
036        private final String propertyName;
037
038        /**
039         * Constructor
040         * 
041         * @param propertyName
042         *            The name of the property to reference
043         */
044        public ComponentPropertyModel(final String propertyName)
045        {
046                this.propertyName = propertyName;
047        }
048
049        @Override
050        public T getObject()
051        {
052                throw new IllegalStateException("Wrapper should have been called");
053        }
054
055        @Override
056        public final void setObject(T object)
057        {
058                IComponentAssignedModel.super.setObject(object);
059        }
060
061        @Override
062        public IWrapModel<T> wrapOnAssignment(final Component component)
063        {
064                return new AssignmentWrapper<T>(component, propertyName);
065        }
066
067        /**
068         * Wrapper used when assigning a ComponentPropertyModel to a component.
069         * 
070         * @param <P>
071         *            The Model Object
072         */
073        private class AssignmentWrapper<P> implements IWrapModel<P>
074        {
075                private static final long serialVersionUID = 1L;
076
077                private final Component component;
078
079                private final String propertyName;
080
081                AssignmentWrapper(final Component component, final String propertyName)
082                {
083                        this.component = component;
084                        this.propertyName = propertyName;
085                }
086
087                @Override
088                public IModel<T> getWrappedModel()
089                {
090                        return ComponentPropertyModel.this;
091                }
092
093                protected String propertyExpression()
094                {
095                        return propertyName;
096                }
097
098                @SuppressWarnings("unchecked")
099                @Override
100                public P getObject()
101                {
102                        return (P)PropertyResolver.getValue(propertyName, component.getParent()
103                                .getInnermostModel()
104                                .getObject());
105                }
106
107                @Override
108                public void detach()
109                {
110                        ComponentPropertyModel.this.detach();
111                }
112        }
113}