1 /* 
2  * Copyright 2005 Paul Hinds
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.tp23.antinstaller.runtime;
17
18import java.awt.GraphicsConfiguration;
19import java.io.ByteArrayOutputStream;
20import java.io.InputStream;
21import java.util.ArrayList;
22import java.util.List;
23import java.util.ResourceBundle;
24
25import javax.swing.ImageIcon;
26import javax.swing.JFrame;
27
28import org.tp23.antinstaller.InstallException;
29import org.tp23.antinstaller.Installer;
30import org.tp23.antinstaller.InstallerContext;
31import org.tp23.antinstaller.ValidationException;
32import org.tp23.antinstaller.antmod.FeedbackListener;
33import org.tp23.antinstaller.page.Page;
34import org.tp23.antinstaller.renderer.AntOutputRenderer;
35import org.tp23.antinstaller.renderer.RendererFactory;
36import org.tp23.antinstaller.renderer.swing.PageCompletionListener;
37import org.tp23.antinstaller.renderer.swing.SizeConstants;
38import org.tp23.antinstaller.renderer.swing.SwingInstallerContext;
39import org.tp23.antinstaller.renderer.swing.SwingMessageRenderer;
40import org.tp23.antinstaller.renderer.swing.SwingPageRenderer;
41
42/**
43 * <p>Runs the installer in a JFrame window </p>
44 * <p>This class uses the Installer object tree as its data source and renderers
45 * from the org.tp23.antinstaller.renderer.swing package </p>
46 * Runners must also create a MessageRenderer and make it available in the
47 * InstallerContext
48 * <p>Copyright: Copyright (c) 2004</p>
49 * <p>Company: tp23</p>
50 *
51 * @author Paul Hinds
52 * @version $Id: SwingRunner.java,v 1.11 2007/01/19 00:24:36 teknopaul Exp $
53 */
54public class SwingRunner extends AntRunner implements Runner, PageCompletionListener {
55
56    private static final ResourceBundle res = ResourceBundle.getBundle("org.tp23.antinstaller.renderer.Res");
57
58
59    protected SwingInstallerContext swingCtx = null;
60    private JFrame frame = new JFrame();
61    private List pageRenderers;
62    private volatile boolean doAnt = false;
63    protected Thread initialThread;
64    protected IfPropertyHelper ifHelper;
65    // context local property refs
66    protected InstallerContext ctx;
67    protected Logger logger;
68    protected Installer installer;
69
70    public SwingRunner(InstallerContext ctx) {
71        super(ctx);
72        swingCtx = new SwingInstallerContext(ctx, frame);
73
74        SwingMessageRenderer smr = new SwingMessageRenderer();
75        smr.setOwner(frame);
76        ctx.setMessageRenderer(smr);
77
78        ctx.setBuildListener(new FeedbackListener(swingCtx));
79
80        ifHelper = new IfPropertyHelper(ctx);
81        logger = ctx.getLogger();
82        installer = ctx.getInstaller();
83        this.ctx = ctx;
84    }
85
86    /**
87     * Renders the installer in a Swing GUI, this method blocks until
88     * the UI has finished
89     *
90     * @return boolean false implies user aborted
91     * @throws InstallException
92     */
93    public boolean runInstaller() throws InstallException {
94        try {
95            frame.setTitle(installer.getName());
96            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
97            frame.setSize(SizeConstants.PAGE_WIDTH, SizeConstants.PAGE_HEIGHT);
98            frame.getRootPane().setDoubleBuffered(true);
99            setLocation(frame);
00            setIcon(frame, installer);
01
02            preparePages(installer.getPages(), ctx);
03            showFirstPage();
04            // need to  block here until pages are complete
05            initialThread = Thread.currentThread();
06            try {
07                Thread.sleep(Long.MAX_VALUE);
08            }
09            catch (InterruptedException ex1) {
10
11            }
12            return doAnt;
13        }
14        catch (Exception ex) {
15            logger.log("Fatal exception: " + ex.getMessage());
16            if (ctx.getInstaller().isVerbose()) {
17                logger.log(ex);
18            }
19            ctx.getMessageRenderer().printMessage("Fatal exception: " + ex.getMessage());
20            throw new InstallException("", ex);
21        }
22    }
23
24    public void pageBack(Page page) {
25        if (page.isAbort()) {
26            abort();
27            return;
28        }
29        Page[] pages = installer.getPages();
30        for (int i = 0; i < pages.length; i++) {
31            if (pages[i] == page) {
32                // found current page
33                if (i > 0) {
34
35                    //skip pages if the ifTarget or ifProperty attributes exist and fail
36                    int nextIdx = i - 1;
37                    try {
38                        while (true) {
39                            if (!ifHelper.ifTarget(pages[nextIdx], pages) ||
40                                    !ifHelper.ifProperty(pages[nextIdx])) {
41                                //Continue looping
42                                --nextIdx;
43                            } else {
44                                break;
45                            }
46                        }
47                    }
48                    catch (InstallException instExc) {
49                        logger.log("InstallException rendering page:" + page.getName());
50                        logger.log(installer, instExc);
51                    }
52
53                    //for(;ifTargetSkip(pages[nextIdx], pages);nextIdx--);
54
55                    SwingPageRenderer renderer = (SwingPageRenderer) pageRenderers.get(nextIdx);
56                    ctx.setCurrentPage(pages[nextIdx]);
57                    try {
58                        renderNext(renderer);
59                    }
60                    catch (InstallException ex) {
61                        logger.log("InstallExcepiton rendering page:" + page.getName());
62                        logger.log(installer, ex);
63                    }
64                    catch (ClassNotFoundException ex) {
65                        logger.log("ClassNotFoundException rendering page:" + page.getName());
66                        logger.log(installer, ex);
67                    }
68                    return;
69                }
70            }
71        }
72    }
73
74    /**
75     * Called when a page is complete and the next button is pressed.
76     * This method is called by the event thread that looses exceptions so Throwable
77     * is caught
78     *
79     * @param page Page
80     */
81    public void pageComplete(Page page) {
82        try {
83            if (page.isAbort()) {
84                abort();
85                return;
86            }
87            runPost(page);
88            Page[] pages = installer.getPages();
89            SwingPageRenderer currentRenderer;
90            for (int i = 0; i < pages.length; i++) {
91                if (pages[i] == page) { // found current page
92                    currentRenderer = (SwingPageRenderer) pageRenderers.get(i);
93                    // check validation
94                    boolean validationPassed = false;
95                    try {
96                        currentRenderer.updateInputFields();
97                        validationPassed = currentRenderer.validateFields();
98                    } catch (ValidationException ve) {
99                        logger.log("ValidationException rendering page:" + page.getName());
00                        logger.log(installer, ve);
01                        return;
02                    }
03                    if (!validationPassed) {
04                        return;
05                    }
06
07
08                    if (i < pages.length - 1) {
09
10                        //more pages left
11
12                        // skip the page if the ifTarget or ifProperty dictate it
13                        int nextIdx = i + 1;
14                        while (true) {
15                            if (!ifHelper.ifTarget(pages[nextIdx], pages) ||
16                                    !ifHelper.ifProperty(pages[nextIdx])) {
17                                //Continue looping
18                                nextIdx++;
19                            } else {
20                                break;
21                            }
22                        }
23
24
25                        SwingPageRenderer renderer = (SwingPageRenderer) pageRenderers.get(nextIdx);
26                        ctx.setCurrentPage(pages[nextIdx]);
27                        try {
28                            renderNext(renderer);
29                        }
30                        catch (InstallException ex) {
31                            logger.log("InstallException rendering page:" + page.getName());
32                            logger.log(installer, ex);
33                        }
34                        catch (ClassNotFoundException ex) {
35                            logger.log("ClassNotFoundException rendering page:" + page.getName());
36                            logger.log(installer, ex);
37                        }
38                        return;
39                    }
40                    if (i == pages.length - 1) {
41                        // all done
42                        currentRenderer.getBackButton().setEnabled(false);
43                        currentRenderer.getNextButton().setEnabled(false);
44                        currentRenderer.getFinishButton().setEnabled(false);
45                        doAnt = true;
46                        initialThread.interrupt();
47                        return;
48                    }
49                }
50            }
51        }
52        catch (Throwable e) {
53            ctx.log("Throwable during page completion:" + e.getMessage());
54            if (ctx.getInstaller().isVerbose()) {
55                ctx.log(e);
56            }
57        }
58    }
59
60    protected void showFirstPage() throws Exception {
61        ctx.setCurrentPage(installer.getPages()[0]);
62        renderNext((SwingPageRenderer) pageRenderers.get(0));
63    }
64
65
66    private void preparePages(Page[] pages, InstallerContext ctx) throws Exception {
67        pageRenderers = new ArrayList();
68        for (int i = 0; i < pages.length; i++) {
69            SwingPageRenderer renderer = RendererFactory.getSwingPageRenderer(pages[i]);
70            if (i == 0) {
71                renderer.getBackButton().setEnabled(false);
72            }
73            renderer.setContext(swingCtx);
74            renderer.setPageCompletionListener(this);
75            renderer.setPage(pages[i]);
76            renderer.instanceInit();
77            pageRenderers.add(renderer);
78            if (renderer instanceof AntOutputRenderer) {
79                ctx.setAntOutputRenderer((AntOutputRenderer) renderer);
80            }
81        }
82    }
83
84    protected void renderNext(SwingPageRenderer renderer) throws ClassNotFoundException, InstallException {
85        renderer.updateDefaultValues();
86        frame.getContentPane().removeAll();
87        frame.getContentPane().add(renderer);
88        frame.getContentPane().repaint();
89        frame.show();
90        if (renderer.getNextButton().isEnabled()) {
91            renderer.getNextButton().requestFocus();
92        } else if (renderer.getFinishButton().isEnabled()) {
93            renderer.getFinishButton().requestFocus();
94        }
95    }
96
97    private void setLocation(JFrame frame) {
98        GraphicsConfiguration config = frame.getGraphicsConfiguration();
99        int x = (int) config.getBounds().getCenterX() - (SizeConstants.PAGE_WIDTH / 2);
00        int y = (int) config.getBounds().getCenterY() - (SizeConstants.PAGE_HEIGHT / 2);
01        frame.setLocation(x, y);
02        frame.setResizable(false);
03    }
04
05    private void setIcon(JFrame frame, Installer installer) {
06        String iconResource = installer.getWindowIcon();
07        try {
08            if (iconResource == null) {
09                return;
10            }
11            InputStream in = this.getClass().getResourceAsStream(iconResource);
12            ByteArrayOutputStream baos = new ByteArrayOutputStream();
13            byte[] buffer = new byte[256];
14            int read = 0; // The number of bytes read from the stream
15            for (read = in.read(buffer); read != -1; read = in.read(buffer)) {
16                baos.write(buffer, 0, read);
17            }
18            ImageIcon icon = new ImageIcon(baos.toByteArray());
19            //Image icon = Toolkit.getDefaultToolkit().createImage(baos.toByteArray());
20            frame.setIconImage(icon.getImage());
21        }
22        catch (Exception ex) {
23            // we can live with out an icon
24            logger.log("Can not load icon resource: " + iconResource);
25            logger.log(installer, ex);
26        }
27    }
28
29    public void antFinished() {
30        SwingPageRenderer renderer = (SwingPageRenderer) pageRenderers.get(pageRenderers.size() - 1);
31        renderer.getBackButton().setEnabled(false);
32        renderer.getNextButton().setEnabled(false);
33        renderer.getCancelButton().setEnabled(false);
34        renderer.getFinishButton().setText(res.getString("exit"));
35        renderer.getFinishButton().setEnabled(true);
36        renderer.getFinishButton().requestFocus();
37        renderer.getTitleLabel().setText(res.getString("complete"));
38        ctx.getAntOutputRenderer().getErr().flush();
39        ctx.getAntOutputRenderer().getOut().flush();
40        ctx.getMessageRenderer().printMessage(res.getString("finished"));
41    }
42
43    public void fatalError() {
44        List renderers = getPageRenderers();
45        if ((renderers != null) && (renderers.size() > 0)) {
46            SwingPageRenderer renderer = (SwingPageRenderer) renderers.get(renderers.size() - 1);
47            renderer.getBackButton().setEnabled(false);
48            renderer.getNextButton().setEnabled(false);
49            renderer.getCancelButton().setEnabled(false);
50            renderer.getFinishButton().setText(res.getString("exit"));
51            renderer.getFinishButton().setEnabled(true);
52            renderer.getFinishButton().requestFocus();
53            renderer.getTitleLabel().setText(res.getString("failed"));
54        }
55        // else - we're done here, or should we call abort()?
56    }
57
58    /**
59     * Returns a string representation of the object.
60     *
61     * @return a string representation of the object.
62     */
63    public String toString() {
64        return "SwingRunner";
65    }
66
67    private void abort() {
68        this.doAnt = false;
69        initialThread.interrupt();
70    }
71
72    /**
73     * @return Returns the frame.
74     */
75    public JFrame getFrame() {
76        return frame;
77    }
78
79    /**
80     * This method is only valid after the PageRenderers have been generated
81     *
82     * @return Returns the pageRenderers.
83     */
84    public List getPageRenderers() {
85        return pageRenderers;
86    }
87}
88