001    /*
002      Copyright (C) 2002-2003 Laurent Martelli <laurent@aopsys.com>
003      
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public License
015      along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
017    
018    package org.objectweb.jac.aspects.gui.web;
019    
020    import java.awt.Dimension;
021    import java.io.File;
022    import java.io.IOException;
023    import java.io.PrintWriter;
024    import java.util.Arrays;
025    import java.util.Hashtable;
026    import java.util.Iterator;
027    import java.util.Map;
028    import org.apache.log4j.Logger;
029    import org.objectweb.jac.aspects.cache.MethodCache;
030    import org.objectweb.jac.aspects.gui.*;
031    import org.objectweb.jac.aspects.gui.web.html.*;
032    import org.objectweb.jac.aspects.timestamp.Timestamps;
033    import org.objectweb.jac.core.rtti.FieldItem;
034    import org.objectweb.jac.core.rtti.MethodItem;
035    import org.objectweb.jac.util.Images;
036    import org.objectweb.jac.util.ObjectArray;
037    import org.objectweb.jac.util.Strings;
038    
039    public abstract class AbstractView implements View {
040        static Logger logger = Logger.getLogger("web");
041        static Logger loggerClose = Logger.getLogger("gui.close");
042        static Logger loggerDisplay = Logger.getLogger("display");
043        static Logger loggerContext = Logger.getLogger("display.context");
044       
045        protected String label;
046        protected DisplayContext context;
047        protected int width;
048        protected int height;
049        ViewFactory factory;
050    
051        // style used to change display (css for web)
052        String style;
053    
054        Object[] parameters;
055        String type;
056    
057        /** row number, for TableCellViewer */
058        protected boolean isCellViewer = false;
059        protected int row;
060        protected int column;
061        protected View table;
062    
063        public AbstractView() {
064        }
065    
066        public AbstractView(ViewFactory factory, DisplayContext context) {
067            this.factory = factory;
068            this.context = context;
069        }
070    
071        Border viewBorder;
072       
073        /**
074         * Get the value of viewBorder.
075         * @return value of viewBorder.
076         */
077        public Border getViewBorder() {
078            return viewBorder;
079        }
080       
081        /**
082         * Set the value of viewBorder.
083         * @param v  Value to assign to viewBorder.
084         */
085        public void setViewBorder(Border  v) {
086            this.viewBorder = v;
087        }
088       
089        MethodItem message;
090       
091        /**
092         * Get the value of message.
093         * @return value of message.
094         */
095        public MethodItem getMessage() {
096            return message;
097        }
098       
099        /**
100         * Set the value of message.
101         * @param v  Value to assign to message.
102         */
103        public void setMessage(MethodItem  v) {
104            this.message = v;
105        }
106    
107        protected String description;
108       
109        /**
110         * Get the value of description.
111         * @return value of description.
112         */
113        public String getDescription() {
114            return description;
115        }
116       
117        /**
118         * Set the value of description.
119         * @param v  Value to assign to description.
120         */
121        public void setDescription(String  v) {
122            this.description = v;
123        }
124    
125        protected View parentView;
126       
127        /**
128         * Get the value of parent.
129         * @return value of parent.
130         */
131        public View getParentView() {
132            return parentView;
133        }
134       
135        /**
136         * Set the value of parent.
137         * @param v  Value to assign to parent.
138         */
139        public void setParentView(View  v) {
140            this.parentView = v;
141        }
142    
143        public View getRootView() {
144            if (parentView==null)
145                return this;
146            return parentView.getRootView();
147        }
148    
149        public boolean isDescendantOf(View ancestor) {
150            if (this==ancestor)
151                return true;
152            else if (parentView==null)
153                return false;
154            else
155                return parentView.isDescendantOf(ancestor);
156        }
157    
158        public void setContext(DisplayContext context) {
159            loggerContext.debug("setContext on "+this);
160            this.context = context;
161        }
162    
163        public DisplayContext getContext() {
164            return context;
165        }
166    
167        public void setFactory(ViewFactory factory) {
168            this.factory = factory;
169        }
170    
171        public ViewFactory getFactory() {
172            return factory;
173        }
174    
175        public void setLabel(String label) {
176            this.label = label;
177        }
178    
179        public String getLabel() {
180            return label;
181        }
182    
183        public void setWidth(int width) {
184            this.width = width;
185        }
186    
187        public void setHeight(int height) {
188            this.height = height;
189        }
190    
191        public void setType(String type) {
192            this.type = type;
193        }
194    
195        public String getType() {
196            return type;
197        }
198    
199        public void setStyle(String style) {
200            this.style = style;
201        }
202    
203        public String getStyle() {
204            return style;
205        }
206    
207        public void setParameters(Object[] parameters) {
208            this.parameters = parameters;
209        }
210       
211        public Object[] getParameters() {
212            return parameters;
213        }
214    
215        public void setFocus(FieldItem field, Object option) {
216        }
217    
218        public void close(boolean validate) {
219            loggerClose.debug("closing "+this);
220            closed = true;
221            ((WebDisplay)context.getDisplay()).unregisterView(this);
222        }
223    
224        boolean closed = false;
225    
226        public boolean isClosed() {
227            return closed;
228        }
229    
230        public boolean equalsView(ViewIdentity view) {
231            return 
232                ( ( type!=null && 
233                    type.equals(view.getType()) )
234                  || (type==null && view.getType()==null ) )
235                && ( ( parameters!=null && 
236                       Arrays.equals(parameters,view.getParameters()) ) 
237                     || (parameters==null && view.getParameters()==null) );
238        }
239    
240        public boolean equalsView(String type, Object[] parameters) {
241            return this.type.equals(type)
242                && Arrays.equals(this.parameters,parameters);
243        }
244    
245        /**
246         * Are we in a <FORM> element ?
247         */
248        protected boolean isInForm() {
249            return true;
250        }
251       
252        /**
253         * Build an HTML element for an event. It takes into account if we
254         * are in a form, and if the browser is MS-IE.
255         * @param text text to display for the link
256         * @param event the name of the event 
257         * @param params additional parameters for the link URL
258         * @return an HTML element */
259        protected Composite eventURL(String text, String event, String params) {
260            JacRequest request = WebDisplay.getRequest();
261            String parameters = "event="+event+"&source="+getId()+params;
262            if (request.isIEUserAgent()) {
263                // workaround for MSIE which does not handle <button> the way it should
264                logger.debug("user-agent: "+request.getUserAgent());
265                Link link = new Link(
266                    ((WebDisplay)context.getDisplay()).getServletName()+
267                    "?"+parameters,
268                    text);
269                link.attribute("onclick","return commitForm(this,'"+parameters+"')");
270                return link;
271            } else {
272                if (isInForm()) {
273                    Button button = 
274                        new Button("submit","eventAndAction",parameters);
275                    button.add(text);
276                    return button;
277                } else {
278                    return new Link(
279                        ((WebDisplay)context.getDisplay()).getServletName()+
280                        "?event="+event+"&source="+getId()+params,
281                        text);
282                }
283            } 
284        }
285    
286        /**
287         * Write HTML code for a button
288         *
289         * @param out where to write the HTML 
290         * @param icon resource name of an icon
291         * @param label text of the button
292         * @param event the event linked to the button
293         */
294        protected void showButton(PrintWriter out, String icon, String label, String event) {
295            JacRequest request = WebDisplay.getRequest();
296            if (request.isIEUserAgent()) {
297                out.println(
298                    "<table class=\"method\"><td>"+
299                    (icon!=null?iconElement(ResourceManager.getResource(icon),"").toString():"")+
300                    eventURL(label,event,"").toString()+
301                    "</td></table>");
302            } else {
303                out.println(
304                    eventURL(label,event,"")
305                    .add(0,(icon!=null?iconElement(ResourceManager.getResource(icon),"").toString():""))
306                    .cssClass("method")
307                    .toString());
308            }
309        }
310    
311        public String getOpenBorder() {
312            String s = "";
313            if (viewBorder==null) 
314                return s;
315            s += "<div class=\"BORDER_"+Border.i2aStyle(viewBorder.getStyle())+"\">";
316            if (viewBorder.hasTitle()) {
317                s += "<div class=\"label\">"+viewBorder.getTitle()+"</div>";
318            }
319            return s;
320        }
321    
322        public String getCloseBorder() {
323            if (viewBorder==null) 
324                return "";
325            return "</div>";
326        }
327    
328        protected String getBaseURL() {
329            return ((WebDisplay)context.getDisplay()).getServletName();
330        }
331    
332        /**
333         * Build the base URL for an event
334         * @param event the name of the event
335         */
336        protected String eventURL(String event) {
337            String base = getBaseURL()+
338                "?event="+event+"&source="+getId();
339            if (isCellViewer)
340                return base+"&tableEventSource="+getId(table)+
341                    "&row="+row+"&col="+column;
342            else
343                return base;
344        }
345    
346        /**
347         * Builds an <img> tag for an icon
348         *
349         * @param icon resource path of icon
350         * @param alt alt string for <img> HTML tag
351         */ 
352        protected Element iconElement(String icon, String alt) {
353            return iconElement(icon,alt,true);
354        }
355    
356        static MethodCache iconCache = new MethodCache(null/*new Timestamps()*/);
357    
358        /**
359         * Builds an <img> tag for an icon
360         *
361         * @param icon resource path of icon
362         * @param alt alt string for <img> HTML tag
363         * @param showAlt if true, return alt if icon is null
364         */ 
365        protected Element iconElement(String icon, String alt, boolean showAlt) {
366            if (Strings.isEmpty(icon)) {
367                return new Text(showAlt?alt:"");
368            } else {
369                Dimension size = null;
370                File file = new File(icon);
371                ObjectArray args = new ObjectArray(new Object[]{file});
372                MethodCache.Entry entry = iconCache.getEntry(args,null);
373                if (entry!=null) {
374                    size = (Dimension)entry.value;
375                } else {
376                    try {
377                        size = Images.getImageFileSize(file);
378                        logger.debug("size of "+icon+": "+size.width+"x"+size.height);
379                   } catch (Exception e) {
380                        logger.warn("Could not determine size of icon "+icon,e);
381                    }
382                }
383                if (size==null) {
384                    logger.warn("Could not determine size of icon "+icon);
385                } else {
386                     iconCache.putEntry(args,size);
387                }
388                return new Image("resources/"+icon,alt,size).cssClass("icon");
389            }
390        }
391    
392        protected String getId() {
393            return ((WebDisplay)context.getDisplay()).registerView(this);
394        }
395    
396        protected String getId(View view) {
397            return ((WebDisplay)context.getDisplay()).registerView(view);
398        }
399    
400        // HTMLViewer interface
401    
402        /* the style sheets */
403        String styleSheet = "resources/org/objectweb/jac/aspects/gui/web/style.css";
404        String styleSheetIE = "resources/org/objectweb/jac/aspects/gui/web/ie.css";
405        String styleSheetKonqueror = "resources/org/objectweb/jac/aspects/gui/web/konqueror.css";
406    
407        String javascript = "resources/org/objectweb/jac/aspects/gui/web/script.js";
408    
409        public void setStyleSheet(String styleSheet) {
410            this.styleSheet = styleSheet;
411        }
412    
413        /**
414         * Generate an HTML page, with full headers
415         * @see #genBody(PrintWriter)
416         */
417        protected void genPage(PrintWriter out) throws IOException {
418            out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">");
419            out.println("<html>");
420            out.println("  <head>");
421            out.println("  <title>"+label+"</title>");
422            out.println("  <meta name=\"Author\" content=\"JAC web-gui server\">" );
423            out.println("  <script type=\"text/javascript\" src=\""+javascript+"\"></script>");
424            genStyleSheets(out,context.getCustomizedView());
425            out.println("  </head>");
426            out.println("  <body>");
427            genBody(out);
428            out.println("  </body>");
429            out.println("</html>");
430        }
431    
432        protected void genStyleSheets(PrintWriter out, CustomizedView customized) {
433            out.println("  <link rel=\"stylesheet\" type=\"text/css\" "+
434                        "href=\""+styleSheet+"\" title=\"JAC\">");
435            JacRequest request = WebDisplay.getRequest();
436            if (request.isIEUserAgent()) {
437                out.println("  <link rel=\"stylesheet\" type=\"text/css\" "+
438                            "href=\""+styleSheetIE+"\">");
439            }
440            if (request.userAgentMatch("Konqueror")) {
441                out.println("  <link rel=\"stylesheet\" type=\"text/css\" "+
442                            "href=\""+styleSheetKonqueror+"\">");
443            }
444    
445            out.println("  <style type=\"text/css\">");
446            Iterator it;
447            if (customized!=null) {
448                it = customized.getCustomizedGUI().getStyleSheetURLs().iterator();
449                while (it.hasNext()) {
450                    String url = (String)it.next();
451                    out.println("    @import \""+url+"\";");         
452                }
453            }
454            it = GuiAC.getStyleSheetURLs().iterator();
455            while (it.hasNext()) {
456                String url = (String)it.next();
457                out.println("    @import \""+url+"\";");         
458            }
459            out.println("  </style>");
460        }
461    
462        /**
463         * Override this method to generate the body of an HTML page.
464         * @see #genPage(PrintWriter)
465         */
466        protected void genBody(PrintWriter out) throws IOException {
467            out.println("     <p>Empty page</p>");
468        }
469    
470        // TableCellViewer interface
471    
472        public void setTable(View table) {
473            this.table = table;
474        }
475    
476        public void setRow(int row) {
477            this.row = row;
478        }
479    
480        public void setColumn(int column) {
481            this.column = column;
482        }
483    
484        Hashtable attributes = new Hashtable();
485        public void setAttribute(String name, String value) {
486            attributes.put(name,value);
487        }
488        protected void printAttributes(PrintWriter out) {
489            Iterator it = attributes.entrySet().iterator();
490            while (it.hasNext()) {
491                Map.Entry entry = (Map.Entry)it.next();
492                out.write(" "+entry.getKey()+"=\""+entry.getValue()+"\"");
493            }
494        }
495    
496        protected void openForm(PrintWriter out) {
497            out.println("  <form action=\""+
498                        ((WebDisplay)context.getDisplay()).getServletName()+"\" "+
499                        "method=\"post\" accept-charset=\""+GuiAC.getEncoding()+"\" "+
500                        "enctype=\"multipart/form-data\">");
501        }
502    
503        protected void closeForm(PrintWriter out) {
504            out.println("  </form>");
505        }
506    
507        protected void showFormButtons(PrintWriter out, boolean dialog) {
508            out.println("  <div class=\"actions\">");
509            out.println("    <input type=\"hidden\" name=\"source\" value=\""+getId()+"\">");
510            if (dialog) {
511                showButton(out,null,GuiAC.getLabelOK(),"onOK");
512                showButton(out,null,GuiAC.getLabelCancel(),"onCancel");
513            } else {
514                showButton(out,null,GuiAC.getLabelClose(),"onOK");
515            }
516            if (context.hasEnabledEditor()) {
517                showButton(out,null,"Refresh","onRefresh");
518            }
519            out.println("  </div>");
520        }
521    
522        protected void showFormButtons(PrintWriter out) {
523            showFormButtons(out,true);
524        }
525    
526        protected void genEventAndActionButton(PrintWriter out, String event) {
527            showButton(out,null,"Refresh",event);
528        }
529    }