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;
018
019
020import org.apache.wicket.util.io.IClusterable;
021
022/**
023 * A key to a piece of metadata associated with a {@link Component}, {@link Session} or
024 * {@link Application} at runtime. The key contains type information that can be used to check the
025 * type of any metadata value for the key when the value is set. MetaDataKey is abstract in order to
026 * force the creation of a subtype. That subtype is used to test for identity when looking for the
027 * metadata because actual object identity would suffer from problems under serialization. So, the
028 * correct way to declare a MetaDataKey is like this:
029 * 
030 * <pre>
031 * <code>
032 * public static final MetaDataKey&lt;Role&gt; ROLE = new MetaDataKey&lt;Role&gt;() { };
033 * </code>
034 * </pre>
035 * 
036 * @author Jonathan Locke
037 * 
038 * @param <T>
039 *            The type of the object which is stored
040 * 
041 * @see Session#getMetaData(MetaDataKey)
042 * @see Component#getMetaData(MetaDataKey)
043 * @see Application#getMetaData(MetaDataKey)
044 */
045public abstract class MetaDataKey<T> implements IClusterable
046{
047        private static final long serialVersionUID = 1L;
048
049        /**
050         * Constructor.
051         */
052        public MetaDataKey()
053        {
054        }
055
056        @Override
057        public int hashCode()
058        {
059                return getClass().hashCode();
060        }
061
062        /**
063         * @see java.lang.Object#equals(java.lang.Object)
064         */
065        @Override
066        public boolean equals(Object obj)
067        {
068                return obj != null && getClass().equals(obj.getClass());
069        }
070
071        /**
072         * @param metaData
073         *            Array of metadata to search
074         * @return The entry value
075         */
076        @SuppressWarnings("unchecked")
077        public T get(MetaDataEntry<?>[] metaData)
078        {
079                if (metaData != null)
080                {
081                        for (MetaDataEntry<?> entry : metaData)
082                        {
083                                if (equals(entry.key))
084                                {
085                                        return (T)entry.object;
086                                }
087                        }
088                }
089                return null;
090        }
091
092        /**
093         * @param metaData
094         *            The array of metadata
095         * @param object
096         *            The object to set, null to remove
097         * @return Any new metadata array (if it was reallocated)
098         */
099        public MetaDataEntry<?>[] set(MetaDataEntry<?>[] metaData, final Object object)
100        {
101                boolean set = false;
102                if (metaData != null)
103                {
104                        for (int i = 0; i < metaData.length; i++)
105                        {
106                                MetaDataEntry<?> m = metaData[i];
107                                if (equals(m.key))
108                                {
109                                        if (object != null)
110                                        {
111                                                // set new value
112                                                m.object = object;
113                                        }
114                                        else
115                                        {
116                                                // remove value and shrink or null array
117                                                if (metaData.length > 1)
118                                                {
119                                                        int l = metaData.length - 1;
120                                                        MetaDataEntry<?>[] newMetaData = new MetaDataEntry[l];
121                                                        System.arraycopy(metaData, 0, newMetaData, 0, i);
122                                                        System.arraycopy(metaData, i + 1, newMetaData, i, l - i);
123                                                        metaData = newMetaData;
124                                                }
125                                                else
126                                                {
127                                                        metaData = null;
128                                                }
129                                        }
130                                        set = true;
131                                        break;
132                                }
133                        }
134                }
135                if (!set && object != null)
136                {
137                        MetaDataEntry<T> m = new MetaDataEntry<T>(this, object);
138                        if (metaData == null)
139                        {
140                                metaData = new MetaDataEntry[1];
141                                metaData[0] = m;
142                        }
143                        else
144                        {
145                                final MetaDataEntry<?>[] newMetaData = new MetaDataEntry[metaData.length + 1];
146                                System.arraycopy(metaData, 0, newMetaData, 0, metaData.length);
147                                newMetaData[metaData.length] = m;
148                                metaData = newMetaData;
149                        }
150                }
151                return metaData;
152        }
153
154        /**
155         * @see java.lang.Object#toString()
156         */
157        @Override
158        public String toString()
159        {
160                return getClass().toString();
161        }
162}