001: /* *************************************************************************
002:
003: Millstone(TM)
004: Open Sourced User Interface Library for
005: Internet Development with Java
006:
007: Millstone is a registered trademark of IT Mill Ltd
008: Copyright (C) 2000-2005 IT Mill Ltd
009:
010: *************************************************************************
011:
012: This library is free software; you can redistribute it and/or
013: modify it under the terms of the GNU Lesser General Public
014: license version 2.1 as published by the Free Software Foundation.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: *************************************************************************
026:
027: For more information, contact:
028:
029: IT Mill Ltd phone: +358 2 4802 7180
030: Ruukinkatu 2-4 fax: +358 2 4802 7181
031: 20540, Turku email: info@itmill.com
032: Finland company www: www.itmill.com
033:
034: Primary source for MillStone information and releases: www.millstone.org
035:
036: ********************************************************************** */
037:
038: package org.millstone.webadapter;
039:
040: import java.io.BufferedWriter;
041: import java.io.File;
042: import java.io.FileNotFoundException;
043: import java.io.FileOutputStream;
044: import java.io.IOException;
045: import java.io.OutputStreamWriter;
046: import java.text.DateFormat;
047: import java.text.SimpleDateFormat;
048: import java.util.Collection;
049: import java.util.Date;
050: import java.util.HashMap;
051: import java.util.Iterator;
052: import java.util.LinkedList;
053:
054: import javax.servlet.http.HttpSession;
055:
056: import org.millstone.base.Application;
057: import org.millstone.base.data.util.BeanItem;
058: import org.millstone.base.data.util.MethodProperty;
059: import org.millstone.base.terminal.FileResource;
060: import org.millstone.base.ui.*;
061:
062: /**
063: * This class provides a debugging window where one may view the UIDL of
064: * the current window, or in a tabset the UIDL of an active frameset.
065: *
066: * It is primarily inteded for creating and debugging themes.
067: *
068: * @author IT Mill Ltd.
069: * @version 3.1.1
070: * @since 3.0
071: */
072: public class DebugWindow extends Window {
073:
074: protected static String WINDOW_NAME = "debug";
075:
076: private Application debuggedApplication;
077: private HashMap rawUIDL = new HashMap();
078: private WebAdapterServlet servlet;
079: private HttpSession session;
080:
081: private TabSheet tabs = new TabSheet();
082: private Select themeSelector;
083: private Label applicationInfo = new Label("", Label.CONTENT_XHTML);
084:
085: /**Create new debug window for an application.
086: * @param debuggedApplication Application to be debugged.
087: * @param session Session to be debugged.
088: * @param servlet Servlet to be debugged.
089: */
090: protected DebugWindow(Application debuggedApplication,
091: HttpSession session, WebAdapterServlet servlet) {
092:
093: super ("Debug window");
094: setName(WINDOW_NAME);
095: setServlet(servlet);
096: setSession(session);
097: setBorder(Window.BORDER_NONE);
098:
099: // Create control buttons
100: OrderedLayout controls = new OrderedLayout(
101: OrderedLayout.ORIENTATION_HORIZONTAL);
102: controls.addComponent(new Button("Restart Application", this ,
103: "restartApplication"));
104: controls.addComponent(new Button("Clear Session", this ,
105: "clearSession"));
106: Collection themes = servlet.getThemeSource().getThemes();
107: Collection names = new LinkedList();
108: for (Iterator i = themes.iterator(); i.hasNext();) {
109: names.add(((Theme) i.next()).getName());
110: }
111:
112: // Create theme selector
113: themeSelector = new Select("Application Theme", names);
114: themeSelector.setWriteThrough(false);
115:
116: // Terminal type editor
117: Label terminal = new Label("<h2>Terminal Information</h2> ",
118: Label.CONTENT_XHTML);
119: Form browser = new Form();
120: browser.setItemDataSource(new BeanItem(WebBrowserProbe
121: .getTerminalType(session)));
122: browser.removeItemProperty("class");
123: browser.replaceWithSelect("javaScriptVersion",
124: WebBrowser.JAVASCRIPT_VERSIONS,
125: WebBrowser.JAVASCRIPT_VERSIONS);
126: browser.replaceWithSelect("markupVersion",
127: WebBrowser.MARKUP_VERSIONS, WebBrowser.MARKUP_VERSIONS);
128: browser.setWriteThrough(false);
129: Button setbrowser = new Button("Set terminal information",
130: browser, "commit");
131: setbrowser.dependsOn(browser);
132:
133: // Arrange the UI in tabsheet
134: TabSheet infoTabs = new TabSheet();
135: addComponent(infoTabs);
136:
137: OrderedLayout appInfo = new OrderedLayout();
138: infoTabs.addTab(appInfo, "Application", null);
139: appInfo.addComponent(applicationInfo);
140: appInfo.addComponent(controls);
141: appInfo.addComponent(themeSelector);
142: appInfo.addComponent(new Button("Change theme", this ,
143: "commitTheme"));
144:
145: OrderedLayout winInfo = new OrderedLayout();
146: infoTabs.addTab(winInfo, "Windows", null);
147: winInfo.addComponent(tabs);
148: winInfo.addComponent(new Button("Save UIDL", this , "saveUIDL"));
149:
150: OrderedLayout termInfo = new OrderedLayout();
151: infoTabs.addTab(termInfo, "Terminal", null);
152: termInfo.addComponent(terminal);
153: termInfo.addComponent(browser);
154: termInfo.addComponent(setbrowser);
155:
156: // Set the debugged application
157: setDebuggedApplication(debuggedApplication);
158:
159: }
160:
161: protected Select createSelect(String caption, Object[] keys,
162: String[] names) {
163: Select s = new Select(caption);
164: s.addContainerProperty("name", String.class, "");
165: for (int i = 0; i < keys.length; i++) {
166: s.addItem(keys[i]).getItemProperty("name").setValue(
167: names[i]);
168: }
169: s.setItemCaptionPropertyId("name");
170: return s;
171: }
172:
173: public void saveUIDL() {
174:
175: synchronized (rawUIDL) {
176:
177: String currentUIDL = (String) rawUIDL.get(tabs
178: .getSelectedTab());
179:
180: if (currentUIDL == null)
181: return;
182:
183: DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
184: File file = new File("/uidl-debug"
185: + df.format(new Date(System.currentTimeMillis()))
186: + ".xml");
187: try {
188: BufferedWriter out = new BufferedWriter(
189: new OutputStreamWriter(new FileOutputStream(
190: file)));
191: out.write(currentUIDL);
192: out.close();
193:
194: //Open the UIDL also
195: open(new FileResource(file, this .getApplication()));
196: Log.info("UIDL written to file " + file);
197: } catch (FileNotFoundException e) {
198: Log.info("Failed to write debug to " + file + ": " + e);
199: } catch (IOException e) {
200: Log.info("Failed to write debug to " + file + ": " + e);
201: }
202: }
203: }
204:
205: public void commitTheme() {
206: themeSelector.commit();
207: }
208:
209: public void clearSession() {
210: session.invalidate();
211: }
212:
213: public void restartApplication() {
214: if (debuggedApplication != null)
215: debuggedApplication.close();
216: }
217:
218: protected void setWindowUIDL(Window window, String uidl) {
219: String caption = "UIDL:" + window.getName();
220: synchronized (tabs) {
221: for (Iterator i = tabs.getComponentIterator(); i.hasNext();) {
222: Component c = (Component) i.next();
223: if (tabs.getTabCaption(c).equals(caption)) {
224: ((Label) c).setValue(getHTMLFormattedUIDL(caption,
225: uidl));
226: ((Label) c).setContentMode(Label.CONTENT_XHTML);
227: rawUIDL.put(c, uidl);
228: caption = null;
229: }
230: }
231:
232: // Add new tab
233: if (caption != null) {
234: Label l = new Label(getHTMLFormattedUIDL(caption, uidl));
235: l.setContentMode(Label.CONTENT_XHTML);
236: rawUIDL.put(l, uidl);
237: tabs.addTab(l, caption, null);
238: }
239: }
240: }
241:
242: protected String getHTMLFormattedUIDL(String caption, String uidl) {
243: StringBuffer sb = new StringBuffer();
244:
245: // Print formatted UIDL with errors embedded
246: //Perl5Util util = new Perl5Util();
247:
248: int row = 0;
249: int prev = 0;
250: int index = 0;
251: boolean lastLineWasEmpty = false;
252:
253: sb
254: .append("<TABLE WIDTH=\"100%\" STYLE=\"border-left: 1px solid black; "
255: + "border-right: 1px solid black; border-bottom: "
256: + "1px solid black; border-top: 1px solid black\""
257: + " cellpadding=\"0\" cellspacing=\"0\" BORDER=\"0\">");
258:
259: if (caption != null)
260: sb.append("<TR><TH BGCOLOR=\"#ddddff\" COLSPAN=\"2\">"
261: + "<FONT SIZE=\"+2\">" + caption
262: + "</FONT></TH></TR>\n");
263:
264: boolean unfinished = true;
265: while (unfinished) {
266: row++;
267:
268: // Get individual line
269: index = uidl.indexOf('\n', prev);
270: String line;
271: if (index < 0) {
272: unfinished = false;
273: line = uidl.substring(prev);
274: } else {
275: line = uidl.substring(prev, index);
276: prev = index + 1;
277: }
278:
279: // Escape the XML
280: line = WebPaintTarget.escapeXML(line);
281:
282: // Code beautification : Comment lines
283: line = replaceAll(line, "<!--",
284: "<SPAN STYLE = \"color: #00dd00\"><!--");
285: line = replaceAll(line, "-->", "--></SPAN>");
286:
287: while (line.length() > 0 && line.charAt(0) == ' ') {
288: line = line.substring(1);
289: }
290: boolean isEmpty = (line.length() == 0 || line.equals("\r"));
291: line = " " + line;
292:
293: if (!(isEmpty && lastLineWasEmpty))
294: sb
295: .append("<TR"
296: + ((row % 10) > 4 ? " BGCOLOR=\"#eeeeff\""
297: : "")
298: + ">"
299: + "<TD VALIGN=\"top\" ALIGN=\"rigth\" STYLE=\"border-right: 1px solid gray\"> "
300: + String.valueOf(row) + " </TD><TD>"
301: + line + "</TD></TR>\n");
302:
303: lastLineWasEmpty = isEmpty;
304:
305: }
306:
307: sb.append("</TABLE>\n");
308:
309: return sb.toString();
310: }
311:
312: /**
313: * Replaces the characters in a substring of this <code>String</code>
314: * with characters in the specified <code>String</code>. The substring
315: * begins at the specified <code>start</code> and extends to the character
316: * at index <code>end - 1</code> or to the end of the
317: * <code>String</code> if no such character exists. First the
318: * characters in the substring are removed and then the specified
319: * <code>String</code> is inserted at <code>start</code>. (The
320: * <code>StringBuffer</code> will be lengthened to accommodate the
321: * specified String if necessary.)
322: * <p>
323: * NOTE: This operation is slow.
324: * </p>
325: *
326: * @param start The beginning index, inclusive.
327: * @param end The ending index, exclusive.
328: * @param str String that will replace previous contents.
329: * @return This string buffer.
330: * @exception StringIndexOutOfBoundsException if <code>start</code>
331: * is negative, greater than <code>length()</code>, or
332: * greater than <code>end</code>.
333: */
334: protected static String replace(String text, int start, int end,
335: String str) {
336: return new StringBuffer(text).replace(start, end, str)
337: .toString();
338: }
339:
340: protected static String replaceAll(String text, String oldStr,
341: String newStr) {
342: StringBuffer sb = new StringBuffer(text);
343:
344: int newStrLen = newStr.length();
345: int oldStrLen = oldStr.length();
346: if (oldStrLen <= 0)
347: return text;
348:
349: int i = 0;
350: while (i <= sb.length() - oldStrLen) {
351: if (sb.substring(i, i + oldStrLen).equals(oldStr)) {
352: sb.replace(i, i + oldStrLen, newStr);
353: i += newStrLen;
354: } else {
355: i++;
356: }
357: }
358: return sb.toString();
359: }
360:
361: /**
362: * Sets the application.
363: * @param application The application to set
364: */
365: protected void setDebuggedApplication(Application application) {
366: this .debuggedApplication = application;
367: if (application != null) {
368: applicationInfo.setValue("<h2>Application Class</h2> "
369: + application.getClass().getName());
370: themeSelector.setPropertyDataSource(new MethodProperty(
371: application, "theme"));
372: }
373: }
374:
375: /**
376: * Returns the servlet.
377: * @return WebAdapterServlet
378: */
379: protected WebAdapterServlet getServlet() {
380: return servlet;
381: }
382:
383: /**
384: * Returns the session.
385: * @return HttpSession
386: */
387: protected HttpSession getSession() {
388: return session;
389: }
390:
391: /**
392: * Sets the servlet.
393: * @param servlet The servlet to set
394: */
395: protected void setServlet(WebAdapterServlet servlet) {
396: this .servlet = servlet;
397: }
398:
399: /**
400: * Sets the session.
401: * @param session The session to set
402: */
403: protected void setSession(HttpSession session) {
404: this.session = session;
405: }
406:
407: }
|