001: /*
002: * Copyright 2005 Paul Hinds
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: package org.tp23.antinstaller.runtime;
017:
018: import java.awt.Component;
019: import java.awt.Graphics;
020: import java.awt.GraphicsConfiguration;
021: import java.awt.Image;
022: import java.awt.Insets;
023: import java.io.ByteArrayOutputStream;
024: import java.io.InputStream;
025: import java.util.ArrayList;
026: import java.util.List;
027:
028: import javax.swing.ImageIcon;
029: import javax.swing.JFrame;
030: import javax.swing.JPanel;
031: import javax.swing.border.Border;
032:
033: import org.tp23.antinstaller.InstallException;
034: import org.tp23.antinstaller.Installer;
035: import org.tp23.antinstaller.InstallerContext;
036: import org.tp23.antinstaller.ValidationException;
037: import org.tp23.antinstaller.antmod.FeedbackListener;
038: import org.tp23.antinstaller.page.Page;
039: import org.tp23.antinstaller.renderer.AIResourceBundle;
040: import org.tp23.antinstaller.renderer.AntOutputRenderer;
041: import org.tp23.antinstaller.renderer.RendererFactory;
042: import org.tp23.antinstaller.renderer.swing.PageCompletionListener;
043: import org.tp23.antinstaller.renderer.swing.SizeConstants;
044: import org.tp23.antinstaller.renderer.swing.SwingInstallerContext;
045: import org.tp23.antinstaller.renderer.swing.SwingMessageRenderer;
046: import org.tp23.antinstaller.renderer.swing.SwingPageRenderer;
047:
048: /**
049: * <p>Runs the installer in a JFrame window </p>
050: * <p>This class uses the Installer object tree as its data source and renderers
051: * from the org.tp23.antinstaller.renderer.swing package </p>
052: * Runners must also create a MessageRenderer and make it available in the
053: * InstallerContext
054: * <p>Copyright: Copyright (c) 2004</p>
055: * <p>Company: tp23</p>
056: *
057: * @author Paul Hinds
058: * @version $Id: SwingRunner.java,v 1.11 2007/01/19 00:24:36 teknopaul Exp $
059: */
060: public class SwingRunner extends AntRunner implements Runner,
061: PageCompletionListener {
062:
063: private static final AIResourceBundle res = new AIResourceBundle();
064:
065: protected SwingInstallerContext swingCtx = null;
066: protected JFrame frame = new JFrame();
067: private List pageRenderers = new ArrayList();
068: private volatile boolean doAnt = false;
069: protected Thread initialThread;
070: protected IfPropertyHelper ifHelper;
071:
072: protected Logger logger;
073: protected Installer installer;
074:
075: public SwingRunner(InstallerContext ctx) {
076: super (ctx);
077: swingCtx = new SwingInstallerContext(ctx, frame);
078:
079: SwingMessageRenderer smr = createMessageRenderer();
080: smr.setOwner(getFrame());
081: ctx.setMessageRenderer(smr);
082:
083: ctx.setBuildListener(new FeedbackListener(swingCtx));
084:
085: ifHelper = new IfPropertyHelper(ctx);
086: logger = ctx.getLogger();
087: installer = ctx.getInstaller();
088:
089: }
090:
091: /**
092: * Renders the installer in a Swing GUI, this method blocks until
093: * the UI has finished
094: *
095: * @return boolean false implies user aborted
096: * @throws InstallException
097: */
098: public boolean runInstaller() throws InstallException {
099: try {
100: getFrame().setTitle(installer.getName());
101: getFrame().setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
102: // this is a problem in that PAGE_HEIGHT and WIDTH do not incorporate OS border and title bar
103:
104: getFrame().getRootPane().setDoubleBuffered(true);
105: getFrame().pack();
106: setSize(getFrame());
107: setLocation(getFrame());
108: setIcon(getFrame(), installer);
109: setBackgrond(getFrame(), installer);
110:
111: preparePages(installer.getPages(), ctx);
112: showFirstPage();
113: getFrame().setVisible(true);
114: // need to block here until pages are complete
115: initialThread = Thread.currentThread();
116: try {
117: Thread.sleep(Long.MAX_VALUE);
118: } catch (InterruptedException ex1) {
119:
120: }
121: return doAnt;
122: } catch (Exception ex) {
123: logger.log("Fatal exception: " + ex.getMessage());
124: if (ctx.getInstaller().isVerbose()) {
125: logger.log(ex);
126: }
127: ctx.getMessageRenderer().printMessage(
128: "Fatal exception: " + ex.getMessage());
129: throw new InstallException("", ex);
130: }
131: }
132:
133: public void pageBack(Page page) {
134: if (page.isAbort()) {
135: abort();
136: return;
137: }
138: Page[] pages = installer.getPages();
139: for (int i = 0; i < pages.length; i++) {
140: if (pages[i] == page) {
141: // found current page
142: if (i > 0) {
143:
144: //skip pages if the ifTarget or ifProperty attributes exist and fail
145: int nextIdx = i - 1;
146: try {
147: while (true) {
148: if (!ifHelper.ifTarget(pages[nextIdx],
149: pages)
150: || !ifHelper
151: .ifProperty(pages[nextIdx])) {
152: //Continue looping
153: --nextIdx;
154: } else {
155: break;
156: }
157: }
158: } catch (InstallException instExc) {
159: logger.log("InstallException rendering page:"
160: + page.getName());
161: logger.log(installer, instExc);
162: }
163:
164: //for(;ifTargetSkip(pages[nextIdx], pages);nextIdx--);
165:
166: SwingPageRenderer renderer = (SwingPageRenderer) pageRenderers
167: .get(nextIdx);
168: ctx.setCurrentPage(pages[nextIdx]);
169: try {
170: renderNext(renderer);
171: } catch (InstallException ex) {
172: logger.log("InstallExcepiton rendering page:"
173: + page.getName());
174: logger.log(installer, ex);
175: } catch (ClassNotFoundException ex) {
176: logger
177: .log("ClassNotFoundException rendering page:"
178: + page.getName());
179: logger.log(installer, ex);
180: }
181: return;
182: }
183: }
184: }
185: }
186:
187: /**
188: * Called when a page is complete and the next button is pressed.
189: * This method is called by the event thread that looses exceptions so Throwable
190: * is caught
191: *
192: * @param page Page
193: */
194: public void pageComplete(Page page) {
195: try {
196: if (page.isAbort()) {
197: abort();
198: return;
199: }
200: Page[] pages = installer.getPages();
201: SwingPageRenderer currentRenderer;
202: for (int i = 0; i < pages.length; i++) {
203: if (pages[i] == page) { // found current page
204: currentRenderer = (SwingPageRenderer) pageRenderers
205: .get(i);
206: // check validation
207: boolean validationPassed = false;
208: try {
209: currentRenderer.updateInputFields();
210: validationPassed = currentRenderer
211: .validateFields();
212: } catch (ValidationException ve) {
213: logger
214: .log("ValidationException rendering page:"
215: + page.getName());
216: logger.log(installer, ve);
217: return;
218: }
219: if (!validationPassed) {
220: return;
221: }
222: runPost(page);
223:
224: if (i < pages.length - 1) {
225:
226: //more pages left
227:
228: // skip the page if the ifTarget or ifProperty dictate it
229: int nextIdx = i + 1;
230: while (true) {
231: if (!ifHelper.ifTarget(pages[nextIdx],
232: pages)
233: || !ifHelper
234: .ifProperty(pages[nextIdx])) {
235: //Continue looping
236: nextIdx++;
237: } else {
238: break;
239: }
240: }
241:
242: SwingPageRenderer renderer = (SwingPageRenderer) pageRenderers
243: .get(nextIdx);
244: ctx.setCurrentPage(pages[nextIdx]);
245: try {
246: renderNext(renderer);
247: } catch (InstallException ex) {
248: logger
249: .log("InstallException rendering page:"
250: + page.getName());
251: logger.log(installer, ex);
252: } catch (ClassNotFoundException ex) {
253: logger
254: .log("ClassNotFoundException rendering page:"
255: + page.getName());
256: logger.log(installer, ex);
257: }
258: return;
259: }
260: if (i == pages.length - 1) {
261: // all done
262: currentRenderer.getBackButton().setEnabled(
263: false);
264: currentRenderer.getNextButton().setEnabled(
265: false);
266: currentRenderer.getFinishButton().setEnabled(
267: false);
268: doAnt = true;
269: initialThread.interrupt();
270: return;
271: }
272: }
273: }
274: } catch (Throwable e) {
275: ctx.log("Throwable during page completion:"
276: + e.getMessage());
277: if (ctx.getInstaller().isVerbose()) {
278: ctx.log(e);
279: }
280: }
281: }
282:
283: protected void showFirstPage() throws Exception {
284: ctx.setCurrentPage(installer.getPages()[0]);
285: renderNext((SwingPageRenderer) pageRenderers.get(0));
286: }
287:
288: protected void preparePages(Page[] pages, InstallerContext ctx)
289: throws Exception {
290: for (int i = 0; i < pages.length; i++) {
291: SwingPageRenderer renderer = RendererFactory
292: .getSwingPageRenderer(pages[i]);
293: if (i == 0) {
294: renderer.getBackButton().setEnabled(false);
295: }
296: renderer.setContext(swingCtx);
297: renderer.setPageCompletionListener(this );
298: renderer.setPage(pages[i]);
299: renderer.instanceInit();
300: pageRenderers.add(renderer);
301: if (renderer instanceof AntOutputRenderer) {
302: ctx.setAntOutputRenderer((AntOutputRenderer) renderer);
303: }
304: }
305: }
306:
307: protected void renderNext(SwingPageRenderer renderer)
308: throws ClassNotFoundException, InstallException {
309: renderer.updateDefaultValues();
310: getFrame().getContentPane().removeAll();
311: getFrame().getContentPane().add(renderer);
312: getFrame().getContentPane().repaint();
313: getFrame().show();
314: if (renderer.getNextButton().isEnabled()) {
315: renderer.getNextButton().requestFocus();
316: } else if (renderer.getFinishButton().isEnabled()) {
317: renderer.getFinishButton().requestFocus();
318: }
319: }
320:
321: public static void setLocation(JFrame frame) {
322: GraphicsConfiguration config = frame.getGraphicsConfiguration();
323: int x = (int) config.getBounds().getCenterX()
324: - (SizeConstants.FULL_WIDTH / 2);
325: int y = (int) config.getBounds().getCenterY()
326: - (SizeConstants.PAGE_HEIGHT / 2);
327: frame.setLocation(x, y);
328: frame.setResizable(false);
329: }
330:
331: public static void setSize(JFrame frame) {
332: frame.setSize(SizeConstants.PAGE_WIDTH
333: + SizeConstants.IMAGE_PANEL_WIDTH,
334: SizeConstants.PAGE_HEIGHT);
335: }
336:
337: protected void setIcon(JFrame frame, Installer installer) {
338: String iconResource = installer.getWindowIcon();
339: try {
340: if (iconResource == null) {
341: return;
342: }
343: InputStream in = this .getClass().getResourceAsStream(
344: iconResource);
345: ByteArrayOutputStream baos = new ByteArrayOutputStream();
346: byte[] buffer = new byte[256];
347: int read = 0; // The number of bytes read from the stream
348: for (read = in.read(buffer); read != -1; read = in
349: .read(buffer)) {
350: baos.write(buffer, 0, read);
351: }
352: ImageIcon icon = new ImageIcon(baos.toByteArray());
353: //Image icon = Toolkit.getDefaultToolkit().createImage(baos.toByteArray());
354: frame.setIconImage(icon.getImage());
355: } catch (Exception ex) {
356: // we can live with out an icon
357: logger.log("Can not load icon resource: " + iconResource);
358: logger.log(installer, ex);
359: }
360: }
361:
362: private void setBackgrond(JFrame frame, Installer installer) {
363: String iconResource = installer.getBackground();
364: try {
365: if (iconResource == null) {
366: return;
367: }
368: InputStream in = this .getClass().getResourceAsStream(
369: iconResource);
370: if (in == null) { // bacround image is optional
371: return;
372: }
373: ByteArrayOutputStream baos = new ByteArrayOutputStream();
374: byte[] buffer = new byte[256];
375: int read = 0; // The number of bytes read from the stream
376: for (read = in.read(buffer); read != -1; read = in
377: .read(buffer)) {
378: baos.write(buffer, 0, read);
379: }
380: ImageIcon backgroundIcon = new ImageIcon(baos.toByteArray());
381: //Image icon = Toolkit.getDefaultToolkit().createImage(baos.toByteArray());
382: JPanel background = new BackgroundPanel(backgroundIcon
383: .getImage()); // JPanel();
384: background.setLayout(frame.getContentPane().getLayout());
385: //background.setBorder(new BackgroundBorder(backgroundIcon.getImage()));
386: frame.setContentPane(background);
387: //frame.getRootPane().setBorder(new BackgroundBorder(backgroundIcon.getImage()));
388: } catch (Exception ex) {
389: // we can live with out a background
390: logger.log("Can not load background resource: "
391: + iconResource);
392: logger.log(installer, ex);
393: }
394: }
395:
396: public void antFinished() {
397: SwingPageRenderer renderer = (SwingPageRenderer) pageRenderers
398: .get(pageRenderers.size() - 1);
399: renderer.getBackButton().setEnabled(false);
400: renderer.getNextButton().setEnabled(false);
401: renderer.getCancelButton().setEnabled(false);
402: renderer.getFinishButton()
403: .setText(res.getString("button.exit"));
404: renderer.getFinishButton().setEnabled(true);
405: renderer.getFinishButton().requestFocus();
406: renderer.getTitleLabel().setText(res.getString("complete"));
407: ctx.getAntOutputRenderer().getErr().flush();
408: ctx.getAntOutputRenderer().getOut().flush();
409: ctx.getMessageRenderer()
410: .printMessage(res.getString("finished"));
411: }
412:
413: public void fatalError() {
414: List renderers = getPageRenderers();
415: if ((renderers != null) && (renderers.size() > 0)) {
416: SwingPageRenderer renderer = (SwingPageRenderer) renderers
417: .get(renderers.size() - 1);
418: renderer.getBackButton().setEnabled(false);
419: renderer.getNextButton().setEnabled(false);
420: renderer.getCancelButton().setEnabled(false);
421: renderer.getFinishButton().setText(
422: res.getString("button.exit"));
423: renderer.getFinishButton().setEnabled(true);
424: renderer.getFinishButton().requestFocus();
425: renderer.getTitleLabel().setText(res.getString("failed"));
426: }
427: // else - we're done here, or should we call abort()?
428: }
429:
430: /**
431: * Returns a string representation of the object.
432: *
433: * @return a string representation of the object.
434: */
435: public String toString() {
436: return "SwingRunner";
437: }
438:
439: protected void abort() {
440: this .doAnt = false;
441: initialThread.interrupt();
442: }
443:
444: /**
445: * @return Returns the frame.
446: */
447: public JFrame getFrame() {
448: return frame;
449: }
450:
451: public SwingMessageRenderer createMessageRenderer() {
452: return new SwingMessageRenderer();
453: }
454:
455: /**
456: * This method is only valid after the PageRenderers have been generated
457: *
458: * @return Returns the pageRenderers.
459: */
460: public List getPageRenderers() {
461: return pageRenderers;
462: }
463:
464: private class BackgroundPanel extends JPanel {
465:
466: private Image img;
467:
468: public BackgroundPanel(Image img) {
469: this .img = img;
470: }
471:
472: public void paintComponent(Graphics g) {
473: // TODO not called and will need to pain ALL the clip since we calim to be opaque
474: g.drawImage(img, 0, 0, null);
475: }
476:
477: public boolean isOpaque() {
478: return true;
479: }
480:
481: }
482:
483: // TODO not working but at least paintComponent() gets called
484: public class BackgroundBorder implements Border {
485: private final Image image;
486:
487: public BackgroundBorder(Image image) {
488: this .image = image;
489: }
490:
491: public void paintBorder(Component c, Graphics g, int x, int y,
492: int width, int height) {
493: g.drawImage(image, 0, 0, null);
494: }
495:
496: public Insets getBorderInsets(Component c) {
497: return new Insets(0, 0, 0, 0);
498: }
499:
500: public boolean isBorderOpaque() {
501: return true;
502: }
503: }
504: }
|