View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of Java-Common.
5    *
6    * Java-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * Java-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with Java-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.application.state.impl;
21  
22  import java.awt.Component;
23  import java.awt.Container;
24  import java.awt.Frame;
25  import java.awt.Rectangle;
26  import java.io.IOException;
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.StringTokenizer;
31  
32  import com.cosylab.application.state.State;
33  import com.cosylab.application.state.StateFactory;
34  import com.cosylab.application.state.StateKeeper;
35  import com.cosylab.application.state.StateOriginator;
36  import com.cosylab.application.state.StateStorage;
37  
38  
39  /**
40   * This class is an implementation of the <code>StateKeeper</code> interface
41   * and stores application state by traversing through its AWT tree.
42   * Each component in the tree which implements <code>StateOriginator</code>
43   * is called to create a <code>State</code> object in the process of the
44   * aplication state saving. The created <code>State</code> will be marked
45   * with the full path to the component so later it can be delivered to it. 
46   * <br/>
47   * If the AWT hierarchy of the components at the time of restoring the state
48   * is different than at the time of saving, the state keeper might not be able  
49   * to deliver data to the <code>StateOriginator</code> these cases should be
50   * handled separately inside the application.
51   *
52   * @author dvitas
53   */
54  public class AWTStateKeeper implements StateKeeper
55  {
56  	protected static final char pathSeparator = '/';
57  	protected static final char indexSeparator = '#';
58  	protected static final String tokenizerDelimiter = "" + pathSeparator
59  		+ indexSeparator;
60  	protected Container rootContainer = null;
61  
62  	/**
63  		 *
64  		 */
65  	public AWTStateKeeper(Container rootContainer)
66  	{
67  		super();
68  		this.rootContainer = rootContainer;
69  	}
70  
71  	protected void fillState(Container container, String path, HashMap paths,
72  	    HashMap indexes, List states)
73  	{
74  		path = path + pathSeparator + container.getClass().getName();
75  
76  		Integer index = (Integer)paths.get(path);
77  
78  		if(index == null) {
79  			index = new Integer(0);
80  		} else {
81  			index = new Integer(index.intValue() + 1);
82  		}
83  
84  		paths.put(path, index);
85  		path = path + indexSeparator + index.intValue();
86  
87  		//System.out.println(">>> Querying '"+container.getName()+"' '"+path+"' '"+container+"'");
88  		Component[] components = container.getComponents();
89  		//System.out.println(">>> Component returned "+components.length);
90  
91  		for(int i = 0; i < components.length; i++) {
92  			if(components[i] instanceof StateOriginator) {
93  				StateOriginator so = (StateOriginator)components[i];
94  				State s = so.getState();
95  
96  				if(s != null) {
97  					String id = path + pathSeparator
98  						+ components[i].getClass().getName();
99  					index = (Integer)indexes.get(id);
100 
101 					if(index == null) {
102 						index = new Integer(0);
103 					} else {
104 						index = new Integer(index.intValue() + 1);
105 					}
106 
107 					indexes.put(id, index);
108 					id = id + indexSeparator + index.intValue();
109 					s.setID(id);
110 					states.add(s);
111 				}
112 			}
113 
114 			if(components[i] instanceof Container) {
115 				fillState((Container)components[i], path, paths, indexes, states);
116 			}
117 		}
118 	}
119 
120 	protected Object getObject(Container container, String name, int index)
121 	{
122 		int hitCount = -1;
123 		Component[] components = container.getComponents();
124 
125 		for(int i = 0; i < components.length; i++) {
126 			if(components[i].getClass().getName().equals(name)) {
127 				hitCount++;
128 			}
129 
130 			if(hitCount == index) {
131 				return components[i];
132 			}
133 		}
134 
135 		return null;
136 	}
137 
138 	protected StateOriginator getOriginator(Container root, String ID)
139 	{
140 		StateOriginator originator = null;
141 		int index;
142 
143 		StringTokenizer st = new StringTokenizer(ID, tokenizerDelimiter);
144 		Container container = root;
145 
146 		// check if this tree belongs to us
147 		if(st.hasMoreTokens()) {
148 			if(!root.getClass().getName().equals(st.nextToken())) {
149 				return null;
150 			}
151 
152 			st.nextToken(); // skip index
153 		}
154 
155 		Object object = null;
156 
157 		// go through the path to the component
158 		while(st.hasMoreTokens()) {
159 			String name = st.nextToken();
160 			index = Integer.parseInt(st.nextToken());
161 			object = getObject(container, name, index);
162 
163 			if(object == null) {
164 				return null;
165 			}
166 
167 			if(object instanceof Container) {
168 				container = (Container)object;
169 			}
170 		}
171 
172 		if(!(object instanceof StateOriginator)) {
173 			return null;
174 		}
175 
176 		return (StateOriginator)object;
177 	}
178 
179 	/* (non-Javadoc)
180 	 * @see com.cosylab.application.state.StateKeeper#restore()
181 	 */
182 	public void restore(final String filePath, final String applicationName)
183 		throws IOException
184 	{
185 		if(rootContainer == null )
186 			throw new IOException("Unable to restore state. The rootContainer is null!");
187 
188 		StateStorage ss = StateFactory.createStateStorage(filePath,
189 			    applicationName);
190 		ss.load(filePath, applicationName);
191 		List states = ss.getStates();
192 		setStates(states);
193 	}
194 
195 	protected void setStates(List states) {
196 		// Do root first so that others get the right treatment
197 		int extSt=Frame.NORMAL;
198 		
199 		State curState;
200 		String ID;
201 		for(int i = 0; i < states.size(); i++) {
202 			curState=(State)states.get(i);
203 			ID = curState.getID();
204 
205 			if(ID.equals("RootContainer")) {
206 				Rectangle bounds = curState.getRectangle("Bounds");
207 				rootContainer.setBounds(bounds);
208 				
209 				// Handle maximized state
210 				if (rootContainer instanceof Frame) {
211 					extSt=curState.getInt("extendedState",Frame.NORMAL);
212                     ((Frame)rootContainer).setExtendedState(extSt);
213                     ((Frame)rootContainer).validate();
214                     ((Frame)rootContainer).doLayout();
215 				}
216 				states.remove(i);
217 				break;
218 			}
219 		}
220 
221 		for(int i = 0; i < states.size(); i++) {
222 			curState=(State)states.get(i);
223 			ID = curState.getID();
224 
225 			StateOriginator originator = getOriginator(rootContainer, ID);
226 
227 			if(originator != null) {
228 				originator.setState(curState);
229 			} else {
230 				// The path has changed (probably Smart Toolkit) - these originators should be handled separately
231 				/*System.err.println("Unable to get the originator for path "
232 					+ ID);*/
233 			}
234 		}
235 	}
236 	
237 	protected List getStates() {
238 		List states = new ArrayList();
239 		// add a state for the rootContainer no matter if it is StateOriginator or not
240 		State state = StateFactory.createState();
241 		state.setID("RootContainer");
242 		state.putRectangle("Bounds", rootContainer.getBounds());
243 
244 		// in these maps we will keep track of components of the same class in a container 
245 		HashMap indexes = new HashMap();
246 		HashMap paths = new HashMap();
247 		fillState(rootContainer, "", paths, indexes, states);
248         
249         // Check extended state
250         // MUST COME IN THE END TO HANDLE LAYOUT PROPERLY!!!
251         if (rootContainer instanceof Frame) {
252             int extSt=((Frame)rootContainer).getExtendedState();
253             state.putInt("extendedState",extSt);
254             if (extSt!=Frame.NORMAL) { //Get normal bounds
255                 ((Frame)rootContainer).setExtendedState(Frame.NORMAL);
256                 ((Frame)rootContainer).validate();
257                 ((Frame)rootContainer).doLayout();
258                 state.putRectangle("Bounds", rootContainer.getBounds());
259             }
260         }
261         
262         states.add(state);
263 		return states;
264 	}
265 
266 	/* (non-Javadoc)
267 	 * @see com.cosylab.application.state.StateKeeper#save()
268 	 */
269 	public void save(final String filePath, final String applicationName)
270 		throws IOException
271 	{
272 		if(rootContainer == null )
273 			throw new IOException("Unable to save state. The rootContainer is null!");
274 
275 		StateStorage ss = StateFactory.createStateStorage();
276 		ss.getStates().addAll(getStates());
277 		ss.store(filePath, applicationName);
278 	}
279 	
280 }
281 
282 /* __oOo__ */