001/**
002 * Copyright 2005-2018 The Kuali Foundation
003 *
004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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.kuali.rice.krad.uif.lifecycle;
017
018import java.io.Serializable;
019import java.util.Collections;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023
024/**
025 * Holds data about a component that might be needed to handle a post request.
026 *
027 * @author Kuali Rice Team (rice.collab@kuali.org)
028 * @see ViewPostMetadata
029 */
030public class ComponentPostMetadata implements Serializable {
031
032    private static final long serialVersionUID = -6090575873840392956L;
033
034    private String id;
035    private String path;
036
037    private Map<String, String> phasePathMapping;
038    private Map<String, List<String>> refreshPathMappings;
039    private boolean isDetachedComponent;
040
041    private Map<String, Object> unmodifiableData;
042    private Map<String, Object> data;
043
044    /**
045     * Constructor taking the id for the component to store metadata for.
046     *
047     * @param id component id
048     */
049    public ComponentPostMetadata(String id) {
050        this.id = id;
051    }
052
053    /**
054     * Id for the component the post metadata is associated with.
055     *
056     * <p>The id can be used to retrieve the component post metadata from the view post metadata</p>
057     *
058     * @return component id
059     */
060    public String getId() {
061        return id;
062    }
063
064    /**
065     * @see ComponentPostMetadata#getId()
066     */
067    public void setId(String id) {
068        this.id = id;
069    }
070
071    /**
072     * Path of the component within the view structure (tree).
073     *
074     * <p>This is set during the lifecycle process and used to retrieve the component for refresh calls</p>
075     *
076     * @return path from view
077     */
078    public String getPath() {
079        return path;
080    }
081
082    /**
083     * @see ComponentPostMetadata#getPath()
084     */
085    public void setPath(String path) {
086        this.path = path;
087    }
088
089    /**
090     * Map containing the path for the component at each lifecycle phase.
091     *
092     * <p>Note this is only stored if the path of the component is different from the {@link #getPath()}
093     * at any of the phases</p>
094     *
095     * @return map where key is the phase name and the value is the component's path
096     */
097    public Map<String, String> getPhasePathMapping() {
098        return phasePathMapping;
099    }
100
101    /**
102     * @see ComponentPostMetadata#getPhasePathMapping()
103     */
104    public void setPhasePathMapping(Map<String, String> phasePathMapping) {
105        this.phasePathMapping = phasePathMapping;
106    }
107
108    /**
109     * Map of property paths whose lifecycle will be run when the component is refreshed.
110     *
111     * <p>Each map entry contains a tree of paths to process for each of the lifecycle phases. This is so parents
112     * of the component (in each phase) are picked up during the refresh process</p>
113     *
114     * @return map of refresh paths
115     */
116    public Map<String, List<String>> getRefreshPathMappings() {
117        return refreshPathMappings;
118    }
119
120    /**
121     * @see ComponentPostMetadata#getRefreshPathMappings()
122     */
123    public void setRefreshPathMappings(Map<String, List<String>> refreshPathMappings) {
124        this.refreshPathMappings = refreshPathMappings;
125    }
126
127    /**
128     * Indicates whether the component is detached from the view (not in the view's structure, but an external
129     * component, for example a dialog).
130     *
131     * <p>This is used by the component refresh process to determine whether it can get the component instance
132     * by its path from the view (in the case of the component being attached), or if it must use its id (in the
133     * case of the component being detached).</p>
134     *
135     * @return boolean true if the component is detached, false if not
136     */
137    public boolean isDetachedComponent() {
138        return isDetachedComponent;
139    }
140
141    /**
142     * @see ComponentPostMetadata#isDetachedComponent
143     */
144    public void setDetachedComponent(boolean isDetachedComponent) {
145        this.isDetachedComponent = isDetachedComponent;
146    }
147
148    /**
149     * General post data that has been stored for the component.
150     *
151     * <p>Holds the general post data for a component. Any piece of data can be added to this map and then
152     * retrieved on a post call (for example in a controller method)</p>
153     *
154     * <p>Note map returned is unmodifiable. Use {@link ComponentPostMetadata#addData(java.lang.String,
155     * java.lang.Object)}
156     * to add new data entries</p>
157     *
158     * @return unmodifiable map of data
159     */
160    public Map<String, Object> getData() {
161        if (unmodifiableData == null) {
162            if (data == null) {
163                unmodifiableData = Collections.emptyMap();
164            } else {
165                unmodifiableData = Collections.unmodifiableMap(data);
166            }
167        }
168
169        return unmodifiableData;
170    }
171
172    /**
173     * @see ComponentPostMetadata#getData()
174     */
175    public void setData(Map<String, Object> data) {
176        this.unmodifiableData = null;
177        this.data = data;
178    }
179
180    /**
181     * Adds a new data entry to the components post data.
182     *
183     * @param key key for data, which is used to retrieve the data
184     * @param value data value
185     * @see ComponentPostMetadata#getData()
186     */
187    public void addData(String key, Object value) {
188        if (this.data == null) {
189            setData(new HashMap<String, Object>());
190        }
191
192        if (this.data.get(key) != value) {
193            synchronized (this.data) {
194                this.data.put(key, value);
195            }
196        }
197    }
198
199    /**
200     * Retrieves a post data value for the component.
201     *
202     * @param key key for the data value to retrieve
203     * @return data value, or null if data does not exist
204     *
205     * @see ComponentPostMetadata#getData()
206     */
207    public Object getData(String key) {
208        if (this.data != null) {
209            return this.data.get(key);
210        }
211
212        return null;
213    }
214
215    /**
216     * Get the path of the first object in this components path.
217     *
218     * @return the top most parent object's path for this component
219     */
220    public String getRootObjectPath() {
221        if (this.getPath() == null || !this.getPath().contains(".")) {
222            return this.getPath();
223        }
224
225        return this.getPath().substring(0, this.getPath().indexOf('.'));
226    }
227}