001: /*
002: * Copyright 2006-2007 Luca Garulli (luca.garulli@assetdata.it)
003: *
004: * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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: */
016:
017: package org.romaframework.aspect.view.echo2.look;
018:
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.FilenameFilter;
022: import java.io.IOException;
023: import java.net.URL;
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.List;
027: import java.util.Map;
028:
029: import nextapp.echo2.app.Component;
030: import nextapp.echo2.app.Extent;
031: import nextapp.echo2.app.ImageReference;
032: import nextapp.echo2.app.Style;
033: import nextapp.echo2.app.StyleSheet;
034: import nextapp.echo2.app.componentxml.ComponentXmlException;
035: import nextapp.echo2.app.componentxml.StyleSheetLoader;
036:
037: import org.apache.commons.logging.Log;
038: import org.apache.commons.logging.LogFactory;
039: import org.romaframework.aspect.view.ViewAspect;
040: import org.romaframework.aspect.view.ViewException;
041: import org.romaframework.core.GlobalConstants;
042: import org.romaframework.core.Utility;
043: import org.romaframework.core.entity.ComposedEntity;
044: import org.romaframework.core.resource.AutoReloadManager;
045: import org.romaframework.core.schema.SchemaHelper;
046:
047: import echopointng.image.URLImageReference;
048:
049: /**
050: * Image, Style and StyleSheet manager. It handles all Look-and-feel information.
051: */
052: public class LookAndFeelManager {
053:
054: private List<String> imagePaths;
055: private Map<String, ImageReference> images;
056: private List<StyleSheet> styleSheets;
057:
058: private Map<String, StyleSheet> styleSheetPathsMapping;
059: private Map<String, String> imagePathsMapping;
060:
061: private StyleSheetReloader ssReloader;
062: private ImageReloader imageReloader;
063: private AutoReloadManager autoReloadManager;
064:
065: private static Log log = LogFactory
066: .getLog(LookAndFeelManager.class);
067:
068: public static final String DEFAULT_NAME = "default";
069: public static final String IMAGE_PACKAGE = ".image";
070: public static final String STYLE_PACKAGE = ".style";
071: public static final String SEPARATOR = ".";
072: public static final String STYLE_EXTENSION = ".stylesheet";
073:
074: public LookAndFeelManager(AutoReloadManager iAutoReloadManager) {
075: autoReloadManager = iAutoReloadManager;
076:
077: imagePaths = new ArrayList<String>(3);
078: images = new HashMap<String, ImageReference>(3);
079: styleSheets = new ArrayList<StyleSheet>(3);
080:
081: // SET MAP FOR AUTORELOADING
082: styleSheetPathsMapping = new HashMap<String, StyleSheet>(3);
083: imagePathsMapping = new HashMap<String, String>(3);
084:
085: // SET LISTENERS FOR AUTORELOADING
086: ssReloader = new StyleSheetReloader(this );
087: imageReloader = new ImageReloader(this );
088: }
089:
090: public void init() {
091: String viewAspectPath = Utility
092: .getApplicationAspectPackage(ViewAspect.ASPECT_NAME);
093:
094: // ADD DEFAULT IMAGE LOCATION
095: addImagePath(Utility.CLASSPATH_PREFIX + viewAspectPath
096: + IMAGE_PACKAGE);
097:
098: // ADD DEFAULT STYLE LOCATION
099: String stylesRelativeDir = Utility
100: .getResourcePath(viewAspectPath + STYLE_PACKAGE
101: + Utility.PATH_SEPARATOR);
102: String stylesDir = Utility.PATH_SEPARATOR_STRING
103: + LookAndFeelManager.class.getResource(
104: Utility.PATH_SEPARATOR_STRING
105: + stylesRelativeDir).getFile();
106: for (String fileName : getAllStylesheets(stylesDir)) {
107: addStyleSheet(stylesRelativeDir + fileName);
108: }
109: }
110:
111: /**
112: * returns all the file names of the stylesheets present in the directory
113: * @param stylesDir the directory that contains the stylesheets
114: * @return all the file names of the stylesheets present in the directory
115: */
116: private List<String> getAllStylesheets(String stylesDir) {
117: List<String> returnList = new ArrayList<String>();
118: try {
119: File dir = new File(stylesDir);
120: if (!dir.exists())
121: return returnList;
122: if (!dir.isDirectory())
123: return returnList;
124: FilenameFilter filter = new FilenameFilter() {
125: public boolean accept(File dir, String name) {
126: if (name != null && name.endsWith(STYLE_EXTENSION))
127: return true;
128: return false;
129: }
130: };
131: File[] dirFiles = dir.listFiles(filter);
132: for (File file : dirFiles) {
133: if (!file.isDirectory())
134: returnList.add(file.getName());
135: }
136: } catch (Exception e) {
137: }
138: return returnList;
139: }
140:
141: public void setImagePaths(List<String> iImagePaths) {
142: imagePaths.clear();
143: for (String pathName : iImagePaths) {
144: addImagePath(pathName);
145: }
146: }
147:
148: public void addImagePath(String iImagePath) {
149: imagePaths.add(Utility.getAbsoluteResourcePath(iImagePath));
150: }
151:
152: /**
153: * Return an image searching in the configured paths. First time the image is cached in memory until further reloading. The
154: * image's dimensions are passed by parameters.
155: *
156: * @param iImageName
157: * Name of icon without path
158: * @param iW
159: * Width of icon
160: * @param iH
161: * Height of icon
162: * @return
163: */
164: public ImageReference getImage(String iImageName, int iW, int iH) {
165: synchronized (images) {
166: ImageReference image = images.get(iImageName);
167: if (image != null)
168: return image;
169:
170: return loadImage(iImageName, iW, iH);
171: }
172: }
173:
174: /**
175: * Return an image searching in the configured paths. First time the image is cached in memory until further reloading. The
176: * image's dimension is take from image's real dimension
177: *
178: * @param iImageName
179: * Name of icon without path
180: * @return ImageReference instance if found, otherwise null
181: * @see loadImage
182: */
183: public ImageReference getImage(String iImageName) {
184: return getImage(iImageName, 0, 0);
185: }
186:
187: protected ImageReference loadImage(String iImageName, int iW, int iH) {
188: ImageReference image = null;
189:
190: File file = null;
191: FileInputStream fis = null;
192:
193: try {
194: // LOAD IMAGE FROM FILE SYSTEM
195: for (String imagePath : imagePaths) {
196: URL url = Utility.loadURL(imagePath
197: + Utility.PATH_SEPARATOR_STRING + iImageName);
198:
199: if (url == null)
200: continue;
201:
202: log.debug("[LookAndFeelManager.loadImage] Loading: "
203: + iImageName + " from " + url);
204:
205: file = new File(url.toURI());
206:
207: if (!file.exists())
208: continue;
209:
210: fis = new FileInputStream(file);
211: if (iW <= 0 && iH <= 0) {
212: java.awt.image.BufferedImage img = javax.imageio.ImageIO
213: .read(fis);
214: iW = img.getWidth();
215: iH = img.getHeight();
216: }
217: Extent w = new Extent(iW);
218: Extent h = new Extent(iH);
219:
220: image = new URLImageReference(url, w, h);
221:
222: // CACHE THE NEW IMAGE LOADED
223: images.put(iImageName, image);
224:
225: // ASSOCIATE FILE PATH WITH ICON NAME FOR FURTHER RELOADING
226: imagePathsMapping.put(file.getAbsolutePath(),
227: iImageName);
228:
229: break;
230: }
231: } catch (Exception e) {
232: log.error(
233: "[LookAndFeelManager.loadImage] Error on loading image: "
234: + iImageName, e);
235: throw new ViewException("LookAndFeelManager exception", e);
236: } finally {
237: if (fis != null)
238: try {
239: fis.close();
240: } catch (IOException e) {
241: e.printStackTrace();
242: }
243: if (file != null)
244: file = null;
245: }
246:
247: if (image == null) {
248: log
249: .error("[LookAndFeelManager.loadImage] Error on loading image: "
250: + iImageName);
251: return null;
252: }
253:
254: return image;
255: }
256:
257: protected void reloadImage(File iFile) {
258: log.info("[LookAndFeelManager] Reload Image " + iFile);
259:
260: synchronized (images) {
261: // RELOAD IMAGE IN MEMORY
262: String imageName = imagePathsMapping.get(iFile
263: .getAbsolutePath());
264: images.put(imageName, loadImage(imageName, 0, 0));
265: }
266: }
267:
268: /**
269: * Setup stylesheets at startup.
270: *
271: * @param iStylesheetPaths
272: * List of stylesheet paths
273: */
274: public void setStyleSheets(List<String> iStylesheetPaths) {
275: styleSheets.clear();
276: for (String style : iStylesheetPaths) {
277: addStyleSheet(style);
278: }
279: }
280:
281: public void addStyleSheet(String iStylesheetFilePath) {
282: try {
283: StyleSheet stylesheet = StyleSheetLoader.load(
284: iStylesheetFilePath, Thread.currentThread()
285: .getContextClassLoader());
286:
287: String fileName = LookAndFeelManager.class
288: .getResource(
289: Utility.PATH_SEPARATOR_STRING
290: + iStylesheetFilePath).getFile();
291:
292: File file = new File(fileName);
293:
294: // CONVERT IN OS DEPENDENT PATH
295: fileName = file.getAbsolutePath();
296:
297: synchronized (styleSheets) {
298: styleSheets.add(stylesheet);
299:
300: // SET THE STYLE SHEET FOR AUTOMATIC RELOADING
301: styleSheetPathsMapping.put(fileName, stylesheet);
302: }
303:
304: autoReloadManager.addResource(file, ssReloader);
305: } catch (ComponentXmlException e) {
306: log.error("Error on loading stylesheet from path: "
307: + iStylesheetFilePath, e);
308: }
309: }
310:
311: protected void reloadStyleSheet(File iFile) {
312: log
313: .warn("[LookAndFeelManager.reloadStyleSheet] Reloading StyleSheet: "
314: + iFile + "...");
315:
316: synchronized (styleSheets) {
317: // GET THE STYLE SHEET NAME BASED ON FILE PATH INFO
318: StyleSheet stylesheet = styleSheetPathsMapping.get(iFile
319: .getAbsolutePath());
320: int currPos = styleSheets.indexOf(stylesheet);
321: if (currPos == -1)
322: return;
323:
324: // RELOAD STYLESHEET IN MEMORY
325: try {
326: styleSheets.remove(currPos);
327: stylesheet = StyleSheetLoader.load(new FileInputStream(
328: iFile), Thread.currentThread()
329: .getContextClassLoader());
330: // UPDATE NEW STYLESHEET
331: styleSheets.add(currPos, stylesheet);
332: styleSheetPathsMapping.put(iFile.getAbsolutePath(),
333: stylesheet);
334: } catch (Exception e) {
335: log.error(
336: "[LookAndFeelManager.reloadStyleSheet] Error",
337: e);
338: }
339: }
340: }
341:
342: /**
343: * Search the style following stylesheet declaration.
344: *
345: * @param iComponentClass
346: * @param iStyleName
347: * @return Echo2 Style if found, otherwise null
348: */
349: public Style getStyle(Class<?> iComponentClass, String iStyleName) {
350: Style style;
351: for (StyleSheet stylesheet : styleSheets) {
352: style = stylesheet.getStyle(iComponentClass, iStyleName);
353: if (style != null)
354: return style;
355: }
356: return null;
357: }
358:
359: /*
360: * Assign dinamically the style to the component selected.
361: */
362: public void assignStyle(Component iComponent,
363: Class<?> iDomainClass, String iStyleName) {
364: Style style = getStyle(iComponent, iDomainClass, null,
365: iStyleName);
366: if (style != null)
367: iComponent.setStyle(style);
368: }
369:
370: /*
371: * Assign dinamically the style to the component selected.
372: */
373: public void assignStyle(Component iComponent,
374: Class<?> iDomainClass, String iFieldName, String iStyleName) {
375: Style style = getStyle(iComponent, iDomainClass, iFieldName,
376: iStyleName);
377: if (style != null)
378: iComponent.setStyle(style);
379: }
380:
381: /*
382: * Get dynamically the style to the component selected.
383: */
384: public Style getStyle(Component iComponent, Class<?> iDomainClass,
385: String iFieldName, String iStyleName) {
386: String styleName = iStyleName;
387:
388: // GET SUPERCLASS FOR ROMA FORM COMPONENTS
389: Class<?> componentClass = iComponent.getClass();
390:
391: Style style = null;
392: if (styleName != null)
393: // TRY TO GET -CUSTOM- STYLE IF DEFINED
394: style = getStyle(componentClass, styleName);
395:
396: if (style != null)
397: return style;
398:
399: if (iDomainClass != null) {
400: style = getStyleFollowingDomainInheritance(iComponent,
401: iDomainClass, iFieldName, componentClass);
402: if (style != null)
403: return style;
404: }
405:
406: style = getStyleFollowingComponentInheritance(iComponent,
407: iFieldName, componentClass);
408: if (style != null)
409: return style;
410:
411: if (iFieldName != null) {
412: // TRY TO GET ABSOLUTE -DEFAULT- STYLE + FIELD NAME
413: style = getStyle(componentClass, GlobalConstants.ROOT_CLASS
414: + "." + iFieldName);
415: if (style != null)
416: return style;
417: }
418:
419: if (iStyleName != null) {
420: // TRY TO GET ABSOLUTE -DEFAULT- STYLE + STYLE NAME
421: style = getStyle(componentClass, GlobalConstants.ROOT_CLASS
422: + "." + iStyleName);
423: if (style != null)
424: return style;
425: }
426:
427: // TRY TO GET ABSOLUTE -DEFAULT- STYLE
428: return getStyle(componentClass, GlobalConstants.ROOT_CLASS);
429: }
430:
431: private Style getStyleFollowingDomainInheritance(
432: Component iComponent, Class<?> iDomainClass,
433: String iFieldName, Class<?> componentClass) {
434: String styleName;
435: Style style;
436: // TRY TO GET -DOMAIN CLASS NAME- STYLE IF DEFINED, ITERATING ON ALL
437: // SUPER-CLASSES
438: Class<?> domainClass = iDomainClass;
439: do {
440: if (iFieldName == null)
441: styleName = domainClass.getSimpleName();
442: else
443: // APPEND FIELD NAME TO SEARCH IF ANY
444: styleName = domainClass.getSimpleName() + "."
445: + iFieldName;
446:
447: style = getStyle(componentClass, styleName);
448:
449: if (style == null && iFieldName != null)
450: // SEARCH WITHOUT FIELD NAME IF ANY
451: style = getStyle(componentClass, domainClass
452: .getSimpleName());
453:
454: if (style == null
455: && domainClass.getSuperclass() != null
456: && domainClass.getSuperclass().equals(Object.class)
457: && ComposedEntity.class
458: .isAssignableFrom(iDomainClass)) {
459: // FOLLOW INHERITANCE USING COMPOSITION PATTERN
460: domainClass = SchemaHelper.getGenericClass(iDomainClass
461: .getGenericSuperclass());
462: style = getStyle(iComponent, domainClass, iFieldName,
463: styleName);
464: break;
465: } else
466: domainClass = domainClass.getSuperclass();
467: } while (style == null && domainClass != null
468: && !domainClass.equals(Object.class));
469: return style;
470: }
471:
472: private Style getStyleFollowingComponentInheritance(
473: Component iComponent, String iFieldName,
474: Class<?> componentClass) {
475: String styleName;
476: Style style;
477: // TRY TO GET -DEFAULT- STYLE FOR COMPONENT CLASS RECURSIVELY FOLLOWING
478: // INHERITANCE OF COMPONENT
479: Class<?> formClass;
480: if (iComponent.getParent() == null)
481: formClass = iComponent.getClass();
482: else {
483: // GET THE HIGHER PARENT COMPONENT AS FORM CLASS
484: Component component = iComponent.getParent();
485: while (component.getParent() != null)
486: component = component.getParent();
487:
488: formClass = component.getClass();
489: }
490:
491: do {
492: if (iFieldName == null)
493: styleName = formClass.getSimpleName();
494: else
495: // APPEND FIELD NAME TO SEARCH IF ANY
496: styleName = formClass.getSimpleName() + "."
497: + iFieldName;
498:
499: style = getStyle(componentClass, styleName);
500:
501: if (style == null && iFieldName != null)
502: // SEARCH WITHOUT FIELD NAME IF ANY
503: style = getStyle(componentClass, formClass
504: .getSimpleName());
505:
506: formClass = formClass.getSuperclass();
507: } while (style == null && !formClass.equals(Object.class));
508: return style;
509: }
510:
511: /*
512: * Assign dynamically the style to the component selected.
513: */
514: public void assignStyle(Component iComponent, Class<?> iDomainClass) {
515: assignStyle(iComponent, iDomainClass, null);
516: }
517:
518: /*
519: * Assign dinamically the style to the component selected.
520: */
521: public void assignStyle(Component iComponent) {
522: assignStyle(iComponent, null, null);
523: }
524:
525: /*
526: * Assign dinamically the style to the component selected.
527: */
528: public void assignStyle(Component iComponent, String iStyleName) {
529: assignStyle(iComponent, null, iStyleName);
530: }
531: }
|