You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pp...@apache.org on 2010/05/14 04:14:32 UTC

svn commit: r944083 [2/4] - in /openjpa/trunk/openjpa-examples/openbooks/src: ./ main/ main/java/ main/java/jpa/ main/java/jpa/tools/ main/java/jpa/tools/swing/ main/java/openbook/ main/java/openbook/client/ main/java/openbook/domain/ main/java/openboo...

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PathFinder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PathFinder.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PathFinder.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PathFinder.java Fri May 14 02:14:30 2010
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import java.awt.Point;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A* Algorithm to find rectilinear path through a {@link Maze}.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class PathFinder {
+    private final Maze _maze;
+    
+    public PathFinder(Maze maze) {
+        _maze = maze;
+    }
+    
+    /**
+     * A* algorithm to find a path through a maze.
+     * The algorithm follows these steps
+     *   <LI> Add the starting square (or node) to the open list.
+     *   <LI> Repeat the following:
+     *   <LI> Current node is the lowest cost square on the open list
+     *   <LI> Move current node to the closed list.
+     *   <LI> For each of adjacent neighbor n to this current square …
+     *   <LI> If n is not {@link Maze#isReachable(int, int) reachable} or if n is on the closed list, ignore. 
+     *        Otherwise do the following.
+     *   <LI> If n is not on the open list, add it to the open list. Make the current square 
+     *        the parent of n. Record the cost of n. 
+     *   <LI> If n is on the open list already, replace if this path to n is lower cost. 
+     *   until the target node is added to the closed list, or fail to find the target square
+     *   i.e. the open list is empty.   
+     *
+     * @param x1 the x-coordinate of the starting point
+     * @param y1 the y-coordinate of the starting point
+     * @param x2 the x-coordinate of the target point
+     * @param y2 the y-coordinate of the target point
+     * @return a array of points in the form of x1,y1, x2,y2, ....
+     */
+    public List<Point> findPath(int x1, int y1, int x2, int y2) {
+        Node source = new Node(null, x1, y1);
+        Node target = new Node(null, x2, y2);
+        int maxCost = distance(source, target)*2;
+        LinkedList<Node> openList = new LinkedList<Node>();
+        List<Node> closedList = new ArrayList<Node>();
+        openList.add(source);
+        do {
+            Node current = openList.remove(0);
+            closedList.add(current);
+            if (current.f < maxCost) {
+                exploreNeighbours(current, target, openList, closedList);
+            }
+        } while (!openList.isEmpty() && findMatchingNode(x2, y2, closedList) == null);
+        target = findMatchingNode(x2, y2, closedList);
+        if (target == null) 
+            return traceBackPath(closedList.get(closedList.size()-1));
+        return traceBackPath(target);
+    }
+    
+    private void exploreNeighbours(Node current, Node target, List<Node> openList, List<Node> closedList) {
+        insertNeighbour(current, current.x+1, current.y, target, openList, closedList);
+        insertNeighbour(current, current.x-1, current.y, target, openList, closedList);
+        insertNeighbour(current, current.x,   current.y+1, target, openList, closedList);
+        insertNeighbour(current, current.x,   current.y-1, target, openList, closedList);
+        Collections.sort(openList);
+    }
+    
+    private Node insertNeighbour(Node n, int x, int y, Node target, List<Node> openList, List<Node> closedList) {
+        if (distance(x,y,target) != 0) {
+            if (!_maze.isReachable(x, y) || findMatchingNode(x, y, closedList) != null) {
+                return null;
+            }
+        }
+        Node m = findMatchingNode(x, y, openList);
+        if (m == null) {
+            m = new Node(n,x,y);
+            m.g = n.g + 1;
+            m.h = distance(target, m);
+            m.f = m.g + m.h;
+            openList.add(m);
+        } else if (m.g > n.g+1){
+            m.parent = n;
+            m.g = n.g + 1;
+            m.f = m.g + m.h;
+        }
+        return m;
+    }
+    
+    private Node findMatchingNode(int x, int y, List<Node> list) {
+        for (Node n : list) {
+            if (n.x == x && n.y == y)
+                return n;
+        }
+        return null;
+    }
+    
+    int distance(Node n, Node m) {
+        return Math.abs(n.x - m.x) + Math.abs(n.y - m.y);
+    }
+    int distance(int x, int y, Node m) {
+        return Math.abs(x - m.x) + Math.abs(y - m.y);
+    }
+    
+    List<Point> traceBackPath(Node target) {
+        LinkedList<Point> path = new LinkedList<Point>();
+        path.add(new Point(target.x, target.y));       
+        Node next = target.parent;
+        while (next != null) {
+            path.add(0,new Point(next.x, next.y));
+            next = next.parent;
+        }
+        return straighten(path);
+    }
+    
+    List<Point> straighten(List<Point> path) {
+        if (path.size() < 3)
+            return path;
+        List<Point> mids = new ArrayList<Point>();
+        Point prev = path.get(0);
+        Point mid  = path.get(1);
+        for (int i = 2; i < path.size(); i++) {
+            Point next = path.get(i);
+            if ((mid.x == prev.x && mid.x == next.x) || (mid.y == prev.y && mid.y == next.y)) {
+                mids.add(mid);
+            }
+            prev = mid;
+            mid = next;
+        }
+        path.removeAll(mids);
+        return path;
+    }
+    
+    private static class Node implements Comparable<Node> {
+        int f,g,h;
+        int x; int y;
+        Node parent;
+        
+        public Node(Node p, int x, int y) {
+            parent = p;
+            this.x = x;
+            this.y = y;
+        }
+        
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o instanceof Node) {
+                Node that = (Node)o;
+                return x == that.x && y == that.y;
+            }
+            return false;
+        }
+
+        @Override
+        public int compareTo(Node o) {
+            if (f == o.f) return 0;
+            return f > o.f ? 1 : -1;
+        }
+        
+        public String toString() {
+            return "(" + x + "," + y + ":" + g + ")";
+        }
+    }
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PathFinder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PowerPointViewer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PowerPointViewer.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PowerPointViewer.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PowerPointViewer.java Fri May 14 02:14:30 2010
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.Box;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import openbook.client.Images;
+
+/**
+ * Displays and navigates PowerPoint slides.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public class PowerPointViewer extends JPanel {
+    private List<WeakReference<ImageIcon>> _slides = new ArrayList<WeakReference<ImageIcon>>();
+    private int _total;      // Total number of slides in the deck.
+    private String _dir;
+    private List<URL> _slideURLs;
+    private JButton _prev, _next;
+    private JSpinner _goto;
+    private JButton[] _navButtons;
+    private int MAX_BUTTONS = 10; // Total number of navigation buttons excluding the PREVIOUS and NEXT button.
+    
+    // The slide
+    private JLabel _view;
+    
+    // The key for client property in the navigation buttons denoting the 0-based index of the slides. 
+    private static final String SLIDE_INDEX = "slide.index";
+    
+    /**
+     * Create a viewer with slides in the specified directory.
+     * 
+     * @param dir path to a directory containing PowerPoint slides.
+     * @param slides name of the slides
+     */
+    public PowerPointViewer(String dir, List<String> slides) {
+        super(true);
+        _dir = dir.endsWith("/") ? dir : dir + "/";
+        _slideURLs = validateSlides(_dir, slides);
+        _total  = _slideURLs.size();
+        for (int i = 0; i < _total; i++) {
+            _slides.add(null);
+        }
+        
+        setLayout(new BorderLayout());
+        _view = new JLabel(getSlideAt(0));
+        add(new JScrollPane(_view), BorderLayout.CENTER);
+        add(createNavigationButtons(), BorderLayout.SOUTH);
+    }
+    
+    /**
+     * Create buttons to navigate the slides.
+     * 
+     * @return a panel containing the navigation buttons.
+     */
+    private JPanel createNavigationButtons() {
+        JPanel buttons = new JPanel();
+        _navButtons = new JButton[Math.min(MAX_BUTTONS, _total)]; 
+        _prev = new JButton(Images.BACK);
+        buttons.add(_prev);
+        _prev.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                prevPage();
+            }
+        });
+        buttons.add(Box.createHorizontalGlue());
+        for (int i = 0; i < _navButtons.length; i++) {
+            if (i == _navButtons.length/2) {
+                JLabel gotoLabel = new JLabel("Go to ");
+                _goto = new JSpinner(new SpinnerNumberModel(1,1,_total,1));
+                buttons.add(gotoLabel);
+                buttons.add(_goto);
+                _goto.addChangeListener(new ChangeListener() {
+                    public void stateChanged(ChangeEvent e) {
+                        Integer page = (Integer)_goto.getValue();
+                        showPage(page-1);
+                    }
+                });
+            }
+            int slideIndex = i + 2;
+            _navButtons[i] = new JButton(""+slideIndex);
+            buttons.add(_navButtons[i]);
+            _navButtons[i].putClientProperty(SLIDE_INDEX, i+1);
+            _navButtons[i].addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    JButton button = (JButton)e.getSource();
+                    int index = (Integer)button.getClientProperty(SLIDE_INDEX);
+                    showPage(index);
+                }
+            });
+        }
+        _next = new JButton(Images.NEXT);
+        buttons.add(Box.createHorizontalGlue());
+        buttons.add(_next);
+        _next.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                nextPage();
+            }
+        });
+        return buttons;
+        
+    }
+    
+    /**
+     * Show the next page.
+     */
+    private void nextPage() {
+        int current = getCurrentPageIndex();
+        if (current >= _total-1)
+            return;
+        current += 1;
+        showPage(current);
+    }
+    
+    private void prevPage() {
+        int current = getCurrentPageIndex();
+        if (current <= 0)
+            return;
+        current -= 1;
+        showPage(current);
+    }
+    
+    int getCurrentPageIndex() {
+        return (Integer)_goto.getValue()-1;
+    }
+    
+    /**
+     * Shows the slide at the given index.
+     * 
+     * @param index 0-based index of the slides.
+     */
+    private void showPage(int index) {
+        _view.setIcon(getSlideAt(index));
+        updateButtons(index);
+    }
+    
+    /**
+     * Updates the buttons.
+     * 
+     * @param current 0-based index of the currently displayed slide.
+     */
+    private void updateButtons(int index) {
+        _goto.setValue(index+1);
+        
+        int last  = index + _navButtons.length;
+        if (last >= _total)
+            return;
+        
+        for (int i = 0; i < _navButtons.length; i++) {
+            int slideIndex = index+i+2;
+            _navButtons[i].setText(""+ slideIndex);
+            _navButtons[i].putClientProperty(SLIDE_INDEX, (index+i+1));
+        }
+    }
+    
+    public int getSlideCount() {
+        return _total;
+    }
+       
+    public ImageIcon getSlideAt(int index) {
+        WeakReference<ImageIcon> weak = _slides.get(index);
+        if (weak == null) {
+            return loadSlide(index);
+        }
+        return (weak.get() == null) ? loadSlide(index) : weak.get();
+    }
+    
+    ImageIcon loadSlide(int index) {
+        URL imgURL = _slideURLs.get(index);
+        ImageIcon icon = new ImageIcon(imgURL);
+        _slides.add(index, new WeakReference<ImageIcon>(icon));
+        return icon;
+    }
+    
+    List<URL> validateSlides(String dir, List<String> slides) {
+        List<URL> valid = new ArrayList<URL>();
+        for (String slide : slides) {
+            URL url = findResource(dir + slide);
+            if (url != null) {
+                valid.add(url);
+            }
+        }
+        return valid;
+    }
+    
+    private URL findResource(String path) {
+        if (path == null)
+            return null;
+        URL imgURL = Thread.currentThread().getContextClassLoader().getResource(path);
+        if (imgURL == null) {
+            imgURL = getClass().getResource(path);
+            if (imgURL == null) {
+                System.err.println("Couldn't find file: " + path);
+                return null;
+            }
+        }
+        return imgURL;
+    }
+    
+//    private ImageIcon createImageIcon(String path) {
+//        if (path == null)
+//            return null;
+//        URL imgURL = Thread.currentThread().getContextClassLoader().getResource(path);
+//        if (imgURL != null) {
+//            return new ImageIcon(imgURL);
+//        } else {
+//            imgURL = Images.class.getResource(path);
+//            if (imgURL != null) {
+//                return new ImageIcon(imgURL);
+//            } else {
+//                System.err.println("Couldn't find file: " + path);
+//                return null;
+//            }
+//        }
+//    }
+
+    
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PowerPointViewer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PreparedQueryViewer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PreparedQueryViewer.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PreparedQueryViewer.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PreparedQueryViewer.java Fri May 14 02:14:30 2010
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import javax.swing.JTable;
+
+
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
+
+/**
+ * A specialized table to display statistics of queries.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public class PreparedQueryViewer extends JTable {
+    
+    public PreparedQueryViewer(OpenJPAEntityManagerFactory emf) {
+        super(new QueryStatisticsModel(((OpenJPAEntityManagerFactorySPI)emf).getConfiguration()
+                .getQuerySQLCacheInstance().getStatistics()));
+    }
+
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/PreparedQueryViewer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/QueryDecorator.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/QueryDecorator.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/QueryDecorator.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/QueryDecorator.java Fri May 14 02:14:30 2010
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 Pinaki Poddar
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import javax.persistence.Query;
+
+/**
+ * Decorates an executable query.
+ * Concrete decorative action can be binding parameters, limiting the result range etc.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public interface QueryDecorator {
+    void decorate(Query q);
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/QueryDecorator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/QueryStatisticsModel.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/QueryStatisticsModel.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/QueryStatisticsModel.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/QueryStatisticsModel.java Fri May 14 02:14:30 2010
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import org.apache.openjpa.kernel.QueryStatistics;
+
+@SuppressWarnings("serial")
+class QueryStatisticsModel extends AbstractTableModel {
+    QueryStatistics<String> _stats;
+    private List<String> _keys = new ArrayList<String>();
+    
+    QueryStatisticsModel(QueryStatistics<String> stats) {
+        _stats = stats;
+        sortKeys(stats);
+    }
+    
+    @Override
+    public int getColumnCount() {
+        return 3;
+    }
+
+    @Override
+    public int getRowCount() {
+        sortKeys(_stats);
+        return _keys.size();
+    }
+    
+    @Override
+    public String getColumnName(int columnIndex) {
+        switch (columnIndex) {
+        case 0: return "Total";
+        case 1: return "Hit";
+        case 2: return "Query";
+        default : return "null";
+        }
+    }
+
+    @Override
+    public Object getValueAt(int rowIndex, int columnIndex) {
+        String query = _keys.get(rowIndex);
+        switch (columnIndex) {
+        case 0: return _stats.getExecutionCount(query);
+        case 1: return _stats.getHitCount(query);
+        case 2: return query;
+        default : return null;
+        }
+    }
+    
+    void sortKeys(QueryStatistics<String> stats) {
+        if (_stats.keys().size() != _keys.size()) {
+            _keys = new ArrayList<String>(_stats.keys());
+            if (_keys.size() > 1) {
+                Collections.sort(_keys);
+            }
+            fireTableDataChanged();
+        }
+    }
+
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/QueryStatisticsModel.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SQLLogger.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SQLLogger.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SQLLogger.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SQLLogger.java Fri May 14 02:14:30 2010
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import java.awt.Color;
+import java.io.PrintStream;
+
+import javax.swing.SwingUtilities;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+
+import jpa.tools.swing.GraphicOutputStream;
+
+
+
+import org.apache.openjpa.lib.jdbc.AbstractJDBCListener;
+import org.apache.openjpa.lib.jdbc.JDBCEvent;
+
+/**
+ * Logs SQL statement to a graphic console.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class SQLLogger extends AbstractJDBCListener {
+    private static PrintStream out = null;
+    private static AttributeSet red, green, blue, magenta;
+    static {
+        StyleContext ctx = StyleContext.getDefaultStyleContext();
+        red = ctx.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.RED);
+        green = ctx.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.GREEN);
+        blue = ctx.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.BLUE);
+        magenta = ctx.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.MAGENTA);
+    }
+    
+    public static void setOutput(GraphicOutputStream o) {
+        out = new PrintStream(o, true);
+        o.registerStyle("INSERT", green);
+        o.registerStyle("SELECT", blue);
+        o.registerStyle("UPDATE", magenta);
+        o.registerStyle("DELETE", red);
+    }
+    
+    @Override
+    public void beforeExecuteStatement(final JDBCEvent event) {
+        if (out == null)
+            return;
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                out.println(event.getSQL());
+            }
+        });
+    }
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SQLLogger.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/ScrollingTextPane.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/ScrollingTextPane.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/ScrollingTextPane.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/ScrollingTextPane.java Fri May 14 02:14:30 2010
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+import javax.swing.JTextPane;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+
+/**
+ * A TextPane where text that scrolls as new text is appended.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public class ScrollingTextPane extends JTextPane {
+    
+    public void appendText(String text) {
+        if (text == null)
+            return;
+        try {
+            Document doc = getDocument();
+            setCaretPosition(doc.getLength());
+            replaceSelection(text);
+            Rectangle r = modelToView(doc.getLength());
+            if (r != null) {
+                scrollRectToVisible(r);
+            }
+        } catch (Exception e) {
+            
+        }
+    }
+    
+    public static void main(String[] args) throws Exception {
+        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+        JFrame f = new JFrame();
+        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        
+        final ScrollingTextPane test = new ScrollingTextPane();
+        f.getContentPane().add(new JScrollPane(test));
+        f.pack();
+        f.setSize(600,450);
+        f.setVisible(true);
+        
+        Timer timer = new Timer(1000, new ActionListener() {
+            SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss");
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                test.appendText(fmt.format(new Date()) + "\r\n");
+            }
+        });
+        timer.start();
+    }
+
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/ScrollingTextPane.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SourceCodeViewer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SourceCodeViewer.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SourceCodeViewer.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SourceCodeViewer.java Fri May 14 02:14:30 2010
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.swing.Box;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JEditorPane;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.HTMLEditorKit;
+
+
+/**
+ * A viewer for source code.
+ * The input to this viewer is a set of *.html file names which may or may not have anchors.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public class SourceCodeViewer extends JPanel implements ActionListener {
+    private final JEditorPane _editor;
+    private final JComboBox   _bookmarks;
+    private Map<String, File> _files = new TreeMap<String, File>();
+    
+    public SourceCodeViewer(String dir) {
+        super(true);
+        setLayout(new BorderLayout());
+        
+        
+        _editor = new JEditorPane();
+        _editor.setEditorKit(new HTMLEditorKit());
+        _editor.setEditable(false);
+        
+        File root = getFile(dir);
+        if (root != null) {
+            List<File> files = new FileScanner("html", true).scan(root);
+            for (File f : files) {
+                String fileName = f.getAbsolutePath();
+                String key = fileName.substring(
+                        root.getAbsolutePath().length()+1, 
+                        fileName.length()- "html".length()-1)
+                        .replace(File.separatorChar,'.');
+                _files.put(key, f);
+            }
+        }
+        
+        DefaultComboBoxModel model = new DefaultComboBoxModel();
+        for (String key : _files.keySet()) {
+            model.addElement(key);
+        }
+        _bookmarks = new JComboBox(model);
+        _editor.setEditable(false);
+        _bookmarks.setEditable(false);
+        
+        _bookmarks.addActionListener(this);
+        
+        add(new JScrollPane(_editor), BorderLayout.CENTER);
+        JPanel topPanel = new JPanel();
+        ((FlowLayout)topPanel.getLayout()).setAlignment(FlowLayout.LEADING);
+        topPanel.add(new JLabel("Select File "));
+        topPanel.add(_bookmarks);
+        topPanel.add(Box.createHorizontalGlue());
+        add(topPanel, BorderLayout.NORTH);
+    }
+    
+    
+   
+    public void actionPerformed(ActionEvent e) {
+        String name = (String)_bookmarks.getSelectedItem();
+        try {
+            HTMLDocument doc = new HTMLDocument();
+            _editor.read(new FileInputStream(_files.get(name)), doc);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+    
+    /**
+     * Check the given string for a matching file. The string is first
+     * tested to see if it is an existing file path. If it does not
+     * represent an existing file, it is checked as a resource name of a
+     * file. 
+     * @param name the file path or resource name
+     */
+    private File getFile(String name) {
+        if (name == null)
+            return null;
+
+        File file = new File(name);
+        if (file.exists())
+            return file;
+
+        URL url = Thread.currentThread().getContextClassLoader().getResource(name); 
+        if (url == null) {
+            url = getClass().getClassLoader().getResource(name);
+        }
+        if (url != null) {
+            String urlFile = url.getFile();
+            if (urlFile != null) {
+                File rsrc = new File(URLDecoder.decode(urlFile));
+                if (rsrc.exists())
+                    return rsrc;
+            }
+        }
+
+        return null;
+    }
+
+    
+    InputStream getResource(String resource) {
+        InputStream stream = getClass().getResourceAsStream(resource);
+        if (stream == null) {
+            stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
+        }
+        return stream;
+    }
+    
+    public static void main(String[] args) throws Exception {
+        SourceCodeViewer viewer = new SourceCodeViewer(args.length == 0 ? "source" : args[0]);
+        JFrame frame = new JFrame("Source Code Viewer");
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.getContentPane().add(viewer);
+        frame.pack();
+        frame.setVisible(true);
+    }
+    
+    
+
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SourceCodeViewer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/StatusBar.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/StatusBar.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/StatusBar.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/StatusBar.java Fri May 14 02:14:30 2010
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import java.awt.Color;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.concurrent.TimeUnit;
+
+import javax.swing.Box;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.SwingWorker;
+import javax.swing.Timer;
+
+@SuppressWarnings("serial")
+public class StatusBar extends JPanel implements PropertyChangeListener {
+    private JProgressBar progressBar;
+    private JProgressBar memoryBar;
+    private JLabel messageText;
+    private TaskProgress task;
+    
+    public StatusBar() {
+        progressBar = new JProgressBar();
+        memoryBar = new JProgressBar();
+        
+        messageText = new JLabel();
+        
+        setLayout(new GridLayout(1,0));
+        add(messageText);
+        add(Box.createHorizontalGlue());
+        add(memoryBar);
+        add(new JLabel("Total " + (Runtime.getRuntime().maxMemory()/1000000) + "MB"));
+        add(progressBar);
+        MemoryDisplay memory = new MemoryDisplay(memoryBar);
+        new Timer(100, memory).start();
+    }
+    
+    public void showMessage(String text) {
+        messageText.setText(text);
+    }
+    
+    public void startTimer(long duration, int interval, TimeUnit unit) {
+        progressBar.setEnabled(true);
+        if (duration > 0) {
+            progressBar.setStringPainted(true);
+            progressBar.setMaximum(100);
+            progressBar.setMinimum(0);
+            task = new TaskProgress(unit.toMillis(duration), interval);
+            task.addPropertyChangeListener(this);
+            task.execute();
+        } else {
+            progressBar.setStringPainted(false);
+            progressBar.setIndeterminate(true);
+            task = new TaskProgress(duration, interval);
+            task.addPropertyChangeListener(this);
+            task.execute();
+        }
+    }
+    
+    /**
+     * Invoked when task's progress property changes.
+     */
+    public void propertyChange(PropertyChangeEvent evt) {
+        if ("progress".equals(evt.getPropertyName())) {
+            progressBar.setValue((Integer)evt.getNewValue());
+        } 
+    }
+
+    
+    public void stopTimer() {
+        if (task != null) {
+            task.cancel(true);
+            task = null;
+        }
+        progressBar.setIndeterminate(false);
+        progressBar.setString("");
+        progressBar.setEnabled(false);
+    }
+    
+    /*
+     * Emits progress property from a background thread.
+     */
+    class TaskProgress extends SwingWorker<Void, Integer> {
+        private long startTimeInMillis;
+        private long _total = 100;
+        private int _interval = 100;
+        
+        public TaskProgress(long total, int interval) {
+            _total    = Math.max(total, 1);
+            _interval = Math.max(interval, 1);
+        }
+        @Override
+        public Void doInBackground() {
+            startTimeInMillis = System.currentTimeMillis();
+            long endTimeInMillis = startTimeInMillis + _total;
+            long current = System.currentTimeMillis();
+            while (current < endTimeInMillis && !isCancelled()) {
+                try {
+                    current = System.currentTimeMillis();
+                    int pctComplete = (int)((100*(current - startTimeInMillis))/_total);
+                    setProgress(pctComplete);
+                    Thread.sleep(_interval);
+                } catch (InterruptedException ignore) {
+                }
+            }
+            return null;
+        }
+    }
+    
+    public class MemoryDisplay implements ActionListener {
+        JProgressBar bar;
+        public MemoryDisplay(JProgressBar bar) {
+            this.bar = bar;
+            bar.setStringPainted(true);
+            bar.setMinimum(0);
+            bar.setMaximum(100);
+        }
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            Runtime jvm = Runtime.getRuntime();
+            long totalMemory = jvm.totalMemory();
+            long usedMemory = totalMemory-jvm.freeMemory();
+            int usedPct = (int)((100*usedMemory)/totalMemory);
+            bar.setForeground(getColor(usedPct));
+            bar.setValue((int)usedPct);
+            bar.setString(usedPct + "% (" + mb(usedMemory) + "/" + mb(totalMemory) + "MB) ");
+        }
+        
+        private long mb(long m) {
+            return m/1000000;
+        }
+        
+        Color getColor(int pct) {
+            int red = 255*pct/100;
+            int green = 255*(100-pct)/100;
+            return new Color(red, green, 0);
+        }
+    }
+    
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/StatusBar.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SwingHelper.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SwingHelper.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SwingHelper.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SwingHelper.java Fri May 14 02:14:30 2010
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import java.awt.AWTException;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.Toolkit;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.concurrent.TimeUnit;
+
+import javax.imageio.ImageIO;
+import javax.swing.AbstractButton;
+import javax.swing.ButtonGroup;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.plaf.FontUIResource;
+
+
+/**
+ * Assorted utility methods.
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+public class SwingHelper {
+    /**
+     * Position the given component at the center of the given parent component or physical screen.
+     * 
+     * @param c the component to be positioned
+     * @param parent the component whose center will match the center of the given component.
+     * If null, the given component will match the screen center.
+     * 
+     */
+    public static void position(Component c, Component parent) {
+        Dimension d = c.getPreferredSize();
+        if (parent == null) {
+            Dimension s =  Toolkit.getDefaultToolkit().getScreenSize();
+            c.setLocation(s.width/2 - d.width/2,  s.height/2 - d.height/2);
+        } else {
+            Point p = parent.getLocationOnScreen();
+            int pw = parent.getWidth();
+            int ph = parent.getHeight();
+            c.setLocation(p.x + pw/2 - d.width/2, p.y + ph/2 - d.height/2);
+        }
+    }
+    
+    private static int[] factors = {1000, 1000, 1000, 1000, 60, 60, 24};
+    
+    public static String getTimeString(long value, TimeUnit unit) {
+        if (value <= 0)
+            return "";
+        int i = unit.ordinal();
+        TimeUnit[] units = TimeUnit.values();
+        TimeUnit next = null;
+        int factor = -1;
+        if (i < factors.length -1) {
+            next = units[i+1];
+            factor = factors[i+1];
+            long nextValue = value/factor;
+            if (nextValue > 0) 
+                return getTimeString(value/factor, next) + " " + getTimeString(value%factor, unit); 
+        } 
+        
+        return value + toString(unit);
+    }
+    
+    public static String toString(TimeUnit unit) {
+        switch (unit) {
+        case HOURS:
+        case DAYS:
+        case MINUTES:
+        case SECONDS:
+            return unit.toString().substring(0,1).toLowerCase();
+        case MILLISECONDS:
+            return "ms";
+        case MICROSECONDS:
+            return "micros";
+        case NANOSECONDS:
+            return "ns";
+        }
+        return "";
+    }
+    
+    public static void print(Component c, String format, File output) {
+        try {
+            Robot robot = new Robot();
+            Point root = c.getLocationOnScreen();
+            BufferedImage shot = robot.createScreenCapture(new Rectangle(root.x, root.y, c.getWidth(), c.getHeight()));
+            ImageIO.write(shot, format, output);
+        } catch (AWTException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+    
+    public static AbstractButton getSelectedButton(ButtonGroup group) {
+        Enumeration<AbstractButton> buttons = group.getElements();
+        while (buttons.hasMoreElements()) {
+            AbstractButton b = buttons.nextElement();
+            if (b.isSelected()) {
+                return b;
+            }
+        }
+        return null;
+    }
+    
+    public static void setLookAndFeel(int fontSize) throws Exception {
+        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+        UIDefaults defaults = UIManager.getDefaults();
+        Enumeration<Object> keys = defaults.keys();
+        while (keys.hasMoreElements()) {
+            Object key = keys.nextElement();
+
+            if ((key instanceof String) && (((String) key).endsWith(".font"))) {
+                FontUIResource font = (FontUIResource) UIManager.get(key);
+                defaults.put (key, new FontUIResource(font.getFontName(), font.getStyle(), fontSize));
+            }
+        }
+     }
+    
+
+
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/SwingHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/TextStyles.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/TextStyles.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/TextStyles.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/TextStyles.java Fri May 14 02:14:30 2010
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package jpa.tools.swing;
+
+import java.awt.Color;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+
+public class TextStyles {
+    public static AttributeSet KEYS, VALUES;
+    static {
+        StyleContext ctx = StyleContext.getDefaultStyleContext();
+        KEYS = ctx.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.BLUE);
+        KEYS = ctx.addAttribute(KEYS, StyleConstants.Bold, true);
+        KEYS = ctx.addAttribute(KEYS, StyleConstants.FontSize, 14);
+        KEYS = ctx.addAttribute(KEYS, StyleConstants.FontFamily, "Courier");
+        
+        Color indianRed = new Color(205, 92, 92);
+        VALUES = ctx.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, indianRed);
+        VALUES = ctx.addAttribute(VALUES, StyleConstants.Bold, true);
+        VALUES = ctx.addAttribute(VALUES, StyleConstants.FontFamily, "Courier");
+        VALUES = ctx.addAttribute(VALUES, StyleConstants.FontSize, 14);
+    }
+
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/TextStyles.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/package.html
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/package.html?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/package.html (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/package.html Fri May 14 02:14:30 2010
@@ -0,0 +1,5 @@
+<html>
+<body>
+Swing widgets to browse persistent entities using JPA 2.0 MetaModel API. 
+</body>
+</html>
\ No newline at end of file

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/jpa/tools/swing/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/client/BuyBookPage.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/client/BuyBookPage.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/client/BuyBookPage.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/client/BuyBookPage.java Fri May 14 02:14:30 2010
@@ -0,0 +1,459 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+*/
+package openbook.client;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.GroupLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingWorker;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import jpa.tools.swing.EntityDataModel;
+import jpa.tools.swing.EntityTable;
+import jpa.tools.swing.EntityTableView;
+import jpa.tools.swing.ErrorDialog;
+import openbook.domain.Author;
+import openbook.domain.Book;
+import openbook.domain.Customer;
+import openbook.domain.PurchaseOrder;
+import openbook.domain.ShoppingCart;
+import openbook.server.OpenBookService;
+import openbook.server.QueryDecorator;
+
+import org.apache.openjpa.lib.jdbc.SQLFormatter;
+
+/**
+ * A visual page coordinates the following functions of {@link OpenBookService} : 
+ * <li>query for books
+ * <li>choose one or more of the selected books
+ * <li>add them to Shopping Cart
+ * <li>place a purchase order of the books in the shopping cart.
+ * <p>
+ * Each interaction with the underlying service occurs in a background i.e. 
+ * a <em>non</em>-AWT event dispatching thread. The background threads are
+ * used via {@link SwingWorker} and hence each persistence operation is
+ * can be potentially handled by a different JPA persistence context.
+ * This threading model not only adheres to the good practice of responsive graphical 
+ * user interface design, it exercises the <em>remote</em> nature of JPA service 
+ * (even within this single process Swing application) where every operation
+ * on a persistence context results into a set of <em>detached</em> instances
+ * to the remote client.  
+ * 
+ * @author Pinaki Poddar
+ */
+@SuppressWarnings("serial")
+public final class BuyBookPage extends JPanel {
+    private final OpenBookService   _service;
+    private final Customer          _customer;
+    private final SearchPanel       _searchPanel;
+    private final BuyPanel          _buyPanel;
+    private final SelectBookPanel   _selectPanel;
+    private final ShoppingCartPanel _cartPanel;
+    
+    /**
+     * A Page with 2x2 Grid of panels each for one of the specific action.
+     * 
+     * @param service the OpenBooks service handle.
+     */
+    public BuyBookPage(OpenBookService service, Customer customer) {
+        super(true);
+        _service = service;
+        _customer = customer;
+
+        GridLayout gridLayout = new GridLayout(2, 2);
+        this.setLayout(gridLayout);
+
+        _searchPanel = new SearchPanel("Step 1: Search for Books");
+        _selectPanel = new SelectBookPanel("Step 2: Select Books to view details");
+        _buyPanel    = new BuyPanel("Step 3: Add Book to Shopping Cart");
+        _cartPanel   = new ShoppingCartPanel("Step 4: Purchase Books in the Shopping Cart");
+
+        add(_searchPanel);
+        add(_cartPanel);
+        add(_selectPanel);
+        add(_buyPanel);
+        
+        _selectPanel._selectedBooks.getTable()
+            .getSelectionModel()
+            .addListSelectionListener(_buyPanel);
+    }
+
+    /**
+     * A form like panel displays the different fields to search for books.
+     * Zero or more form fields can be filled in. Though the service level
+     * contract does not mandate how to form the exact query from the form
+     * field values, the actual implementation demonstrates how dynamic 
+     * query construction introduced via Criteria Query API in JPA 2.0
+     * can aid in such common user scenarios.
+     * <br>
+     * The object level query is displayed to demonstrate the ability of
+     * OpenJPA to express a dynamic Criteria Query is a human-readable,
+     * JPQL-like query string.
+     * 
+     * @author Pinaki Poddar
+     * 
+     */
+    class SearchPanel extends JPanel implements ActionListener {
+        private final JTextField _title       = new JTextField("", 20);
+        private final JTextField _author      = new JTextField("", 20);
+        private final JTextField _maxPrice    = new JTextField("", 6);
+        private final JTextField _minPrice    = new JTextField("", 6);
+        private final JTextArea  _queryView   = new JTextArea();
+        private final SQLFormatter _formatter = new SQLFormatter();
+        
+        SearchPanel(String title) {
+            super(true);
+            setBorder(BorderFactory.createTitledBorder(title));
+
+            JButton searchButton = new JButton("Search", Images.START);
+            searchButton.setHorizontalTextPosition(SwingConstants.LEADING);
+
+            JLabel titleLabel  = new JLabel("Title :", SwingConstants.RIGHT);
+            JLabel authorLabel = new JLabel("Author:", SwingConstants.RIGHT);
+            JLabel priceLabel  = new JLabel("Price :", SwingConstants.RIGHT);
+            JLabel fromLabel   = new JLabel("from ", SwingConstants.RIGHT);
+            JLabel toLabel     = new JLabel("to ", SwingConstants.RIGHT);
+
+            JPanel panel = new JPanel();
+            GroupLayout layout = new GroupLayout(panel);
+            panel.setLayout(layout);
+            layout.setAutoCreateContainerGaps(true);
+            layout.setAutoCreateGaps(true);
+            GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
+            hGroup.addGroup(layout.createParallelGroup()
+                                  .addComponent(titleLabel)
+                                  .addComponent(authorLabel)
+                                  .addComponent(priceLabel));
+            hGroup.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
+                                  .addComponent(_title)
+                                  .addComponent(_author)
+                                  .addGroup(layout.createSequentialGroup()
+                                          .addComponent(fromLabel)
+                                          .addComponent(_minPrice)
+                                          .addComponent(toLabel)
+                                          .addComponent(_maxPrice)));
+            
+            GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
+            vGroup.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
+                    .addComponent(titleLabel)
+                    .addComponent(_title));
+            vGroup.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
+                    .addComponent(authorLabel)
+                    .addComponent(_author));
+            vGroup.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
+                    .addComponent(priceLabel)
+                    .addComponent(fromLabel)
+                    .addComponent(_minPrice)
+                    .addComponent(toLabel)
+                    .addComponent(_maxPrice));
+            
+            layout.setHorizontalGroup(hGroup);
+            layout.setVerticalGroup(vGroup);
+
+            JPanel buttonPanel = new JPanel();
+            buttonPanel.add(Box.createHorizontalGlue());
+            buttonPanel.add(searchButton);
+            buttonPanel.add(Box.createHorizontalGlue());
+
+            BoxLayout box = new BoxLayout(this, BoxLayout.Y_AXIS);
+            setLayout(box);
+            add(panel);
+            add(Box.createVerticalGlue());
+            add(buttonPanel);
+            
+            _queryView.setBorder(BorderFactory.createTitledBorder("Criteria Query as CQL"));
+            _queryView.setWrapStyleWord(true);
+            _queryView.setEditable(false);
+            _queryView.setBackground(getBackground());
+            add(_queryView);
+            searchButton.addActionListener(this);
+        }
+        
+        /**
+         * Execute a query and displays the result onto {@linkplain SelectBookPanel}.
+         * 
+         * The query is executed in a background, non-AWT thread.
+         */
+        public void actionPerformed(ActionEvent e) {
+            new SwingWorker<List<Book>, Void>() {
+                private String queryString;
+                @Override
+                protected List<Book> doInBackground() throws Exception {
+                    queryString = _service.getQuery(_title.getText(), 
+                            asDouble(_minPrice), asDouble(_maxPrice), 
+                            _author.getText());
+                    return _service.select(_title.getText(), 
+                            asDouble(_minPrice), asDouble(_maxPrice), 
+                            _author.getText(), 
+                            (QueryDecorator[])null);
+                }
+
+                @Override
+                protected void done() {
+                    try {
+                        _queryView.setText(_formatter.prettyPrint(queryString).toString());
+                        List<Book> selectedBooks = get(1, TimeUnit.SECONDS);
+                        _selectPanel.updateDataModel(selectedBooks);
+                    } catch (Exception e) {
+                        new ErrorDialog(e).setVisible(true);
+                    }
+                }
+            }.execute();
+        }
+
+        boolean isEmpty(JTextField text) {
+            if (text == null)
+                return true;
+            String s = text.getText();
+            return s == null || s.isEmpty() || s.trim().isEmpty();
+        }
+
+        Double asDouble(JTextField field) {
+            if (isEmpty(field)) return null;
+            try {
+                return Double.parseDouble(field.getText());
+            } catch (NumberFormatException e) {
+                System.err.println("Can not convert [" + field.getText() + "] to a double");
+            }
+            return null;
+        }
+    }
+
+    /**
+     * A panel to display the selected books in a tabular format.
+     * 
+     * @author Pinaki Poddar
+     * 
+     */
+    class SelectBookPanel extends JPanel {
+        private final JLabel _bookCount;
+        private final EntityTableView<Book> _selectedBooks;
+        
+        SelectBookPanel(String title) {
+            setLayout(new BorderLayout());
+            setBorder(BorderFactory.createTitledBorder(title));
+
+            _selectedBooks = new EntityTableView<Book>(Book.class,
+                    EntityDataModel.BASIC_ATTR | EntityDataModel.ROW_COUNT, 
+                    _service.getUnit());
+            _bookCount = new JLabel();
+    
+            add(_bookCount, BorderLayout.NORTH);
+            add(_selectedBooks, BorderLayout.CENTER);
+        }
+        
+        void updateDataModel(List<Book> books) {
+            _bookCount.setText(books.size() + " Book selected");
+            _selectedBooks.getDataModel().updateData(books);
+        }
+    }
+
+    /**
+     * A panel to display the details of a single book and a button to add the
+     * book to cart. Listens to the selection in the selected books.
+     * 
+     * @author Pinaki Poddar
+     * 
+     */
+    class BuyPanel extends JPanel implements ListSelectionListener {
+        JLabel _bookTitle;
+        JLabel _bookAuthors;
+        JLabel _bookPrice;
+        JLabel _bookISBN;
+        JButton _addToCart;
+        JSpinner _quantity;
+        JPanel _quantityPanel;
+        
+        public BuyPanel(String title) {
+            super(true);
+
+            setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+            setBorder(BorderFactory.createTitledBorder(title));
+            
+            JPanel descPanel = new JPanel();
+            descPanel.setLayout(new BoxLayout(descPanel, BoxLayout.Y_AXIS));
+            _bookTitle   = new JLabel();
+            _bookAuthors = new JLabel();
+            _bookPrice   = new JLabel();
+            _bookISBN    = new JLabel();
+            descPanel.add(_bookTitle);
+            descPanel.add(_bookAuthors);
+            descPanel.add(_bookPrice);
+            descPanel.add(_bookISBN);
+            add(descPanel);
+
+            _quantityPanel = new JPanel();
+            SpinnerNumberModel spinnerModel = new SpinnerNumberModel(1, 1, 10, 1);
+            _quantity = new JSpinner(spinnerModel);
+            _quantityPanel.add(new JLabel("Quantity:"));
+            _quantity.setEnabled(false);
+            _quantityPanel.add(_quantity);
+            _quantityPanel.setVisible(false);
+            add(_quantityPanel);
+            
+            add(Box.createVerticalGlue());
+            
+            JPanel buttonPanel = new JPanel();
+            buttonPanel.add(Box.createHorizontalGlue());
+            _addToCart = new JButton("Add to Cart", Images.getIcon("images/Add2Cart.jpg", true));
+            _addToCart.setEnabled(false);
+            buttonPanel.add(_addToCart);
+            buttonPanel.add(Box.createHorizontalGlue());
+            add(buttonPanel);
+
+            _addToCart.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    _cartPanel.addBook((Book)_addToCart.getClientProperty("Book"), 
+                            (Integer)_quantity.getValue());
+                }
+            });
+        }
+
+        void showBookDetails(Book book) {
+            _bookTitle.setText(book.getTitle());
+            List<Author> authors = book.getAuthors();
+            if (authors != null && !authors.isEmpty()) {
+                StringBuilder names = new StringBuilder();
+                for (Author author : authors) {
+                    if (names.length() != 0) names.append(", ");
+                    names.append(author.getName());
+                }
+                _bookAuthors.setText("by " + names);
+            }
+            _bookPrice.setText("Price:" + book.getPrice());
+            _bookISBN.setText("ISBN: " + book.getISBN());
+            _addToCart.setEnabled(true);
+            _quantity.setEnabled(true);
+            _quantityPanel.setVisible(true);
+            _addToCart.putClientProperty("Book", book);
+        }
+
+        @Override
+        public void valueChanged(ListSelectionEvent e) {
+            EntityTable<Book> table = _selectPanel._selectedBooks.getTable();
+            int row = table.getSelectedRow();
+            if (row == -1)
+                return;
+            int col = table.getSelectedColumn();
+            if (col == -1)
+                return;
+            EntityDataModel<Book> model = (EntityDataModel<Book>) table.getModel();
+            Book book = model.getRow(row);
+            showBookDetails(book);
+        }
+    }
+
+    /**
+     * A panel to display the shopping cart.
+     * 
+     * @author Pinaki Poddar
+     * 
+     */
+    class ShoppingCartPanel extends JPanel implements ActionListener {
+        private static final int MAX_ITEMS = 10;
+        private final ShoppingCart _cart;
+        private final JButton _placeOrder;
+        private final JLabel[] _items = new JLabel[MAX_ITEMS];
+        
+        public ShoppingCartPanel(String title) {
+            _cart = _customer.newCart();
+            setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
+            setBorder(BorderFactory.createTitledBorder(title));
+            _placeOrder = new JButton("Place Order", Images.START);
+            _placeOrder.setHorizontalTextPosition(SwingConstants.LEADING);
+            
+            _placeOrder.setEnabled(false);
+            for (int i = 0; i < MAX_ITEMS; i++) {
+                _items[i] = new JLabel("");
+                add(_items[i]);
+            }
+            add(Box.createVerticalGlue());
+            JPanel buttonPanel = new JPanel();
+            buttonPanel.add(Box.createHorizontalGlue());
+            buttonPanel.add(_placeOrder);
+            buttonPanel.add(Box.createHorizontalGlue());
+            add(buttonPanel);
+            _placeOrder.addActionListener(this);
+        }
+        
+        /**
+         * Add the given book to the cart. Updates the display.
+         */
+        public void addBook(Book book, int quantity) {
+            _cart.addItem(book, quantity);
+            updateDisplay();
+        }
+        
+        void updateDisplay() {
+            Map<Book, Integer> items = _cart.getItems();
+            int i = 0;
+            for (Map.Entry<Book, Integer> entry : items.entrySet()) {
+                JLabel item = _items[i++];
+                int quantity = entry.getValue();
+                Book book = entry.getKey();
+                item.setText(quantity + (quantity == 1 ? " copy of " : " copies of ") + book.getTitle());
+                item.setIcon(Images.DONE);
+            }
+            _placeOrder.setEnabled(items.size()>0);
+            super.repaint();
+        }       
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            new SwingWorker<PurchaseOrder, Void>() {
+                @Override
+                protected PurchaseOrder doInBackground() throws Exception {
+                    return _service.placeOrder(_cart);
+                }
+
+                @Override
+                protected void done() {
+                    try {
+                        get(1, TimeUnit.SECONDS);
+                        _cart.clear();
+                        for (int i = 0; i < MAX_ITEMS; i++) {
+                            _items[i].setText("");
+                            _items[i].setIcon(null);
+                        }
+                        _placeOrder.setEnabled(false);
+                    } catch (Exception e) {
+                        new ErrorDialog(e).setVisible(true);
+                    }
+                }
+            }.execute();
+        }
+    }
+}
\ No newline at end of file

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/client/BuyBookPage.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/client/DeliveryPage.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/client/DeliveryPage.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/client/DeliveryPage.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/client/DeliveryPage.java Fri May 14 02:14:30 2010
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2010-2012 Pinaki Poddar
+ *
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package openbook.client;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.SwingWorker;
+
+import jpa.tools.swing.EntityDataModel;
+import jpa.tools.swing.EntityTableView;
+import jpa.tools.swing.ErrorDialog;
+import openbook.domain.PurchaseOrder;
+import openbook.server.OpenBookService;
+
+/**
+ * A page to control delivery of pending orders.
+ * 
+ * @author Pinaki Poddar
+ * 
+ */
+@SuppressWarnings("serial")
+public class DeliveryPage extends JPanel {
+    private final JButton _deliver;
+    private EntityTableView<PurchaseOrder> _orders;
+    private final OpenBookService _service;
+    private final JLabel _title;
+    private final JRadioButton _showPending;
+    private final JRadioButton _showDelivered;
+    private final JRadioButton _showBoth;
+
+    public DeliveryPage(final OpenBookService service) {
+        setLayout(new BorderLayout());
+
+        _service = service;
+
+        _orders = new EntityTableView<PurchaseOrder>(PurchaseOrder.class, getOrders(PurchaseOrder.Status.PENDING),
+                EntityDataModel.ALL_ATTR, service.getUnit());
+
+        _title = new JLabel(_orders.getDataModel().getRowCount() + " " + PurchaseOrder.Status.PENDING
+                + " PurchaseOrder");
+
+        _deliver = new JButton("Deliver Pending Orders");
+
+        JPanel statusPanel = new JPanel();
+        ButtonGroup statusSelection = new ButtonGroup();
+        _showPending = new JRadioButton("Pending");
+        _showDelivered = new JRadioButton("Delivered");
+        _showBoth = new JRadioButton("All");
+        _showPending.setSelected(true);
+
+        statusSelection.add(_showPending);
+        statusSelection.add(_showDelivered);
+        statusSelection.add(_showBoth);
+        statusPanel.add(_showBoth);
+        statusPanel.add(_showPending);
+        statusPanel.add(_showDelivered);
+
+        JPanel actionPanel = new JPanel();
+        actionPanel.add(_deliver);
+
+        add(statusPanel, BorderLayout.NORTH);
+        add(_orders, BorderLayout.CENTER);
+
+        add(actionPanel, BorderLayout.SOUTH);
+
+        _showBoth.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                List<PurchaseOrder> selected = getOrders(null);
+                _orders.getDataModel().updateData(selected);
+                _title.setText(selected.size() + " PurchaseOrder");
+            }
+        });
+        _showPending.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                List<PurchaseOrder> selected = getOrders(PurchaseOrder.Status.PENDING);
+                _orders.getDataModel().updateData(selected);
+                _title.setText(selected.size() + " Pending PurchaseOrder");
+            }
+        });
+        _showDelivered.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                List<PurchaseOrder> selected = getOrders(PurchaseOrder.Status.DELEVERED);
+                _orders.getDataModel().updateData(selected);
+                _title.setText(selected.size() + " Delivered PurchaseOrder");
+            }
+        });
+
+        _deliver.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                new SwingWorker<List<PurchaseOrder>, Void>() {
+                    @Override
+                    protected List<PurchaseOrder> doInBackground() throws Exception {
+                        List<PurchaseOrder> updated = new ArrayList<PurchaseOrder>();
+                        EntityDataModel<PurchaseOrder> orders = _orders.getDataModel();
+                        int n = orders.getRowCount();
+                        for (int i = 0; i < n; i++) {
+                            PurchaseOrder order = orders.getRow(i);
+                            if (order.getStatus() == PurchaseOrder.Status.PENDING) {
+                                updated.add(_service.deliver(order));
+                            }
+                        }
+                        return updated;
+                    }
+
+                    @Override
+                    public void done() {
+                        try {
+                            List<PurchaseOrder> updated = get(10, TimeUnit.SECONDS);
+                            _orders.getDataModel().updateData(updated);
+                            _title.setText(updated.size() + "" + " Updated PurchaseOrder");
+                        } catch (Exception e) {
+                            new ErrorDialog(e).setVisible(true);
+                        }
+                    }
+                }.execute();
+            }
+        });
+    }
+
+    /**
+     * Gets the orders in a background (i.e. not AWT event dispatch thread)
+     * thread. <br>
+     * But blocks painting anyway, because that is what is intended.
+     * 
+     */
+    private List<PurchaseOrder> getOrders(final PurchaseOrder.Status status) {
+        SwingWorker<List<PurchaseOrder>, Void> worker = new SwingWorker<List<PurchaseOrder>, Void>() {
+            @Override
+            protected List<PurchaseOrder> doInBackground() throws Exception {
+                return _service.getOrders(status);
+            }
+        };
+        worker.execute();
+        try {
+            return worker.get();
+        } catch (Exception e) {
+            new ErrorDialog(e).setVisible(true);
+            return Collections.emptyList();
+        }
+    }
+}

Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/client/DeliveryPage.java
------------------------------------------------------------------------------
    svn:eol-style = native