001: package org.wings;
002:
003: import java.util.ArrayList;
004: import java.util.Collections;
005: import java.util.Comparator;
006: import java.util.HashMap;
007: import java.util.HashSet;
008: import java.util.Iterator;
009: import java.util.LinkedHashSet;
010: import java.util.List;
011: import java.util.Map;
012: import java.util.Set;
013: import java.util.SortedMap;
014: import java.util.TreeMap;
015:
016: import org.apache.commons.logging.Log;
017: import org.apache.commons.logging.LogFactory;
018: import org.wings.plaf.ComponentCG;
019: import org.wings.plaf.Update;
020: import org.wings.util.SStringBuilder;
021:
022: /**
023: * Default implementation of the reload manager.
024: *
025: * @author Stephan Schuster
026: */
027: public class DefaultReloadManager implements ReloadManager {
028:
029: private static final long serialVersionUID = 2951486214657704539L;
030:
031: private final transient static Log log = LogFactory
032: .getLog(DefaultReloadManager.class);
033:
034: private int updateCount = 0;
035:
036: private boolean updateMode = false;
037:
038: private boolean suppressMode = false;
039:
040: private boolean acceptChanges = true;
041:
042: private final Map<SComponent, PotentialUpdate> fullReplaceUpdates = new HashMap<SComponent, PotentialUpdate>(
043: 256);
044:
045: private final Map<SComponent, Set<PotentialUpdate>> fineGrainedUpdates = new HashMap<SComponent, Set<PotentialUpdate>>(
046: 64);
047:
048: /**
049: * All the components to reload. A LinkedHashSet to have an ordered sequence that allows for fast lookup.
050: */
051: private final Set<SComponent> componentsToReload = new LinkedHashSet<SComponent>();
052:
053: public void reload(SComponent component) {
054: if (suppressMode) {
055: return;
056: }
057:
058: if (component == null)
059: throw new IllegalArgumentException(
060: "Component must not be null!");
061:
062: if (updateMode) {
063: addUpdate(component, null);
064: } else {
065: if (!componentsToReload.contains(component)) {
066: componentsToReload.add(component);
067: }
068: }
069: }
070:
071: public void addUpdate(SComponent component, Update update) {
072: if (component == null)
073: throw new IllegalArgumentException(
074: "Component must not be null!");
075:
076: if (update == null) {
077: update = component.getCG().getComponentUpdate(component);
078: if (update == null) {
079: SFrame frame = component.getParentFrame();
080: if (frame != null)
081: fullReplaceUpdates.put(frame, null);
082: return;
083: }
084: }
085:
086: component = update.getComponent();
087:
088: if (acceptChanges) {
089: PotentialUpdate potentialUpdate = new PotentialUpdate(
090: update);
091:
092: if ((update.getProperty() & Update.FULL_REPLACE_UPDATE) == Update.FULL_REPLACE_UPDATE) {
093: fullReplaceUpdates.put(component, potentialUpdate);
094: } else {
095: Set<PotentialUpdate> potentialUpdates = getFineGrainedUpdates(component);
096: potentialUpdates.remove(potentialUpdate);
097: potentialUpdates.add(potentialUpdate);
098: fineGrainedUpdates.put(component, potentialUpdates);
099: }
100: } else if (log.isDebugEnabled()) {
101: //log.debug("Component " + component + " changed after invalidation of frames.");
102: }
103: }
104:
105: @SuppressWarnings({"unchecked"})
106: public List<Update> getUpdates() {
107: if (!componentsToReload.isEmpty()) {
108: for (SComponent aComponentsToReload : componentsToReload) {
109: boolean tmp = acceptChanges;
110: acceptChanges = true;
111: addUpdate(aComponentsToReload, null);
112: acceptChanges = tmp;
113: }
114: }
115:
116: filterUpdates();
117:
118: List<PotentialUpdate> filteredUpdates = new ArrayList<PotentialUpdate>(
119: fullReplaceUpdates.values());
120: for (Set<PotentialUpdate> updates : fineGrainedUpdates.values()) {
121: filteredUpdates.addAll(updates);
122: }
123: Collections.sort(filteredUpdates, getUpdateComparator());
124:
125: return (List) filteredUpdates;
126: }
127:
128: public Set<SComponent> getDirtyComponents() {
129: final Set<SComponent> dirtyComponents = new HashSet<SComponent>();
130: dirtyComponents.addAll(fullReplaceUpdates.keySet());
131: dirtyComponents.addAll(fineGrainedUpdates.keySet());
132: dirtyComponents.addAll(componentsToReload);
133: return dirtyComponents;
134: }
135:
136: public Set<SFrame> getDirtyFrames() {
137: final Set<SFrame> dirtyFrames = new HashSet<SFrame>(5);
138: for (Object o : getDirtyComponents()) {
139: SFrame parentFrame = ((SComponent) o).getParentFrame();
140: if (parentFrame != null) {
141: dirtyFrames.add(parentFrame);
142: }
143: }
144: return dirtyFrames;
145: }
146:
147: public void invalidateFrames() {
148: Iterator i = getDirtyFrames().iterator();
149: while (i.hasNext()) {
150: ((SFrame) i.next()).invalidate();
151: i.remove();
152: }
153: acceptChanges = false;
154: }
155:
156: public void notifyCGs() {
157: for (SComponent component : getDirtyComponents()) {
158: ComponentCG componentCG = component.getCG();
159: if (componentCG != null) {
160: componentCG.componentChanged(component);
161: }
162: }
163: }
164:
165: public void clear() {
166: updateCount = 0;
167: updateMode = false;
168: acceptChanges = true;
169: fullReplaceUpdates.clear();
170: fineGrainedUpdates.clear();
171: componentsToReload.clear();
172: }
173:
174: public boolean isUpdateMode() {
175: return updateMode;
176: }
177:
178: public void setUpdateMode(boolean updateMode) {
179: this .updateMode = updateMode;
180: }
181:
182: public boolean isSuppressMode() {
183: return suppressMode;
184: }
185:
186: public void setSuppressMode(boolean suppressMode) {
187: this .suppressMode = suppressMode;
188: }
189:
190: public boolean isReloadRequired(SFrame frame) {
191: if (updateMode)
192: return fullReplaceUpdates.containsKey(frame);
193: else
194: return true;
195: }
196:
197: protected Set<PotentialUpdate> getFineGrainedUpdates(
198: SComponent component) {
199: Set<PotentialUpdate> potentialUpdates = fineGrainedUpdates
200: .get(component);
201: if (potentialUpdates == null) {
202: potentialUpdates = new HashSet<PotentialUpdate>(5);
203: }
204: return potentialUpdates;
205: }
206:
207: protected void filterUpdates() {
208: if (log.isDebugEnabled())
209: printAllUpdates("Potential updates:");
210:
211: fineGrainedUpdates.keySet().removeAll(
212: fullReplaceUpdates.keySet());
213:
214: SortedMap<String, SComponent> componentHierarchy = new TreeMap<String, SComponent>();
215:
216: for (SComponent component : getDirtyComponents()) {
217: if ((!component.isRecursivelyVisible() && !(component instanceof SMenu))
218: || component.getParentFrame() == null) {
219: fullReplaceUpdates.remove(component);
220: fineGrainedUpdates.remove(component);
221: } else {
222: componentHierarchy.put(getPath(component), component);
223: }
224: }
225:
226: for (Iterator i = componentHierarchy.keySet().iterator(); i
227: .hasNext();) {
228: final String topPath = (String) i.next();
229: final String comparePath = (topPath + "/").substring(1); // get rid of depth
230: if (fullReplaceUpdates.containsKey(componentHierarchy
231: .get(topPath))) {
232: while (i.hasNext()) {
233: final String subPath = (String) i.next();
234: if (subPath.substring(1).startsWith(comparePath)) {
235: fullReplaceUpdates.remove(componentHierarchy
236: .get(subPath));
237: fineGrainedUpdates.remove(componentHierarchy
238: .get(subPath));
239: i.remove();
240: }
241: }
242: }
243: i = componentHierarchy.tailMap(topPath + "\0").keySet()
244: .iterator();
245: }
246:
247: if (log.isDebugEnabled())
248: printAllUpdates("Effective updates:");
249: }
250:
251: /**
252: * Return the path of the component. The first character denotes the depth of the path.
253: */
254: private String getPath(SComponent component) {
255: return getPath(new SStringBuilder("0"), component).toString();
256: }
257:
258: private SStringBuilder getPath(SStringBuilder builder,
259: SComponent component) {
260: if (component == null)
261: return builder;
262: if (component.getClientProperty("drm:realParentComponent") != null) {
263: Object parent = component
264: .getClientProperty("drm:realParentComponent");
265: if (!(parent instanceof SComponent))
266: parent = null;
267: return getPath(builder, (SComponent) parent).append("/")
268: .append(component.getName());
269: } else {
270: builder.setCharAt(0, (char) (builder.charAt(0) + 1)); // increase depth
271: return getPath(builder, component.getParent()).append("/")
272: .append(component.getName());
273: }
274: }
275:
276: private void printAllUpdates(String header) {
277: log.debug(header);
278: int numberOfUpdates = 0;
279: SStringBuilder output = new SStringBuilder(512);
280: for (SComponent component : getDirtyComponents()) {
281: output.setLength(0);
282: output.append(" ").append(component + ":");
283: if (fullReplaceUpdates.containsKey(component)) {
284: output.append(" " + fullReplaceUpdates.get(component));
285: if (fullReplaceUpdates.get(component) == null) {
286: output
287: .append(" [no component update supported --> reload frame!!!]");
288: }
289: ++numberOfUpdates;
290: }
291: for (PotentialUpdate potentialUpdate : getFineGrainedUpdates(component)) {
292: output.append(" " + potentialUpdate);
293: ++numberOfUpdates;
294: }
295: log.debug(output.toString());
296: }
297: log.debug(" --> " + numberOfUpdates + " updates");
298: }
299:
300: private final class PotentialUpdate implements Update {
301:
302: private Update update;
303: private int position;
304:
305: public PotentialUpdate(Update update) {
306: this .update = update;
307: this .position = updateCount++;
308: }
309:
310: public SComponent getComponent() {
311: return update.getComponent();
312: }
313:
314: public Handler getHandler() {
315: return update.getHandler();
316: }
317:
318: public int getProperty() {
319: return update.getProperty();
320: }
321:
322: public int getPriority() {
323: return update.getPriority();
324: }
325:
326: public int getPosition() {
327: return position;
328: }
329:
330: @Override
331: public boolean equals(Object object) {
332: if (object == this )
333: return true;
334: if (object == null || object.getClass() != this .getClass())
335: return false;
336:
337: PotentialUpdate other = (PotentialUpdate) object;
338:
339: return update.equals(other.update);
340: }
341:
342: @Override
343: public int hashCode() {
344: return update.hashCode();
345: }
346:
347: @Override
348: public String toString() {
349: String clazz = update.getClass().getName();
350: int index = clazz.lastIndexOf("$");
351: if (index < 0)
352: index = clazz.lastIndexOf(".");
353: return clazz.substring(++index) + "[" + getPriority() + "|"
354: + getPosition() + "]";
355: }
356:
357: }
358:
359: private Comparator<PotentialUpdate> getUpdateComparator() {
360: return new CombinedComparator<PotentialUpdate>(
361: new InverseComparator<PotentialUpdate>(
362: new PriorityComparator()),
363: new PositionComparator());
364: }
365:
366: private static class PositionComparator implements
367: Comparator<PotentialUpdate> {
368:
369: public int compare(PotentialUpdate object1,
370: PotentialUpdate object2) {
371: if (object1.getPosition() < object2.getPosition())
372: return -1;
373: if (object1.getPosition() > object2.getPosition())
374: return 1;
375: return 0;
376: }
377:
378: }
379:
380: private static class PriorityComparator implements
381: Comparator<PotentialUpdate> {
382:
383: public int compare(PotentialUpdate object1,
384: PotentialUpdate object2) {
385: if (object1.getPriority() < object2.getPriority())
386: return -1;
387: if (object1.getPriority() > object2.getPriority())
388: return 1;
389: return 0;
390: }
391:
392: }
393:
394: private static class CombinedComparator<T> implements Comparator<T> {
395:
396: private Comparator<T> comparator1;
397: private Comparator<T> comparator2;
398:
399: public CombinedComparator(Comparator<T> c1, Comparator<T> c2) {
400: this .comparator1 = c1;
401: this .comparator2 = c2;
402: }
403:
404: public int compare(T object1, T object2) {
405: int result = comparator1.compare(object1, object2);
406: if (result == 0)
407: return comparator2.compare(object1, object2);
408: else
409: return result;
410: }
411: }
412:
413: private static class InverseComparator<T> implements Comparator<T> {
414:
415: private Comparator<T> comparator;
416:
417: public InverseComparator(Comparator<T> c) {
418: this .comparator = c;
419: }
420:
421: public int compare(T object1, T object2) {
422: return -comparator.compare(object1, object2);
423: }
424: }
425:
426: }
|