Modified extender application to use a proxy shape to defer object
creation.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@559539 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DefaultShape.java b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DefaultShape.java
new file mode 100644
index 0000000..e889fbb
--- /dev/null
+++ b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DefaultShape.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.felix.example.extenderbased.host;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import javax.swing.ImageIcon;
+import org.apache.felix.example.extenderbased.host.extension.SimpleShape;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This class is used as a proxy to defer object creation from shape
+ * provider bundles and also as a placeholder shape when previously
+ * used shapes are no longer available. These two purposes are
+ * actually orthogonal, but were combined into a single class to
+ * reduce the number of classes in the application. The proxy-related
+ * functionality is introduced as a way to lazily create shape
+ * objects in an effort to improve performance; this level of
+ * indirection could be removed if eager creation of objects is not
+ * a concern.
+**/
+class DefaultShape implements SimpleShape
+{
+    private SimpleShape m_shape;
+    private ImageIcon m_icon;
+    private BundleContext m_context;
+    private long m_bundleId;
+    private String m_className;
+
+    /**
+     * This constructs a placeholder shape that draws a default
+     * icon. It is used when a previously drawn shape is no longer
+     * available.
+    **/
+    public DefaultShape()
+    {
+        // Do nothing.
+    }
+
+    /**
+     * This constructs a proxy shape that lazily instantiates the
+     * shape class from the associated extension bundle.
+     * @param context The bundle context to use for retrieving the extension bundle.
+     * @param bundleId The bundle ID of the extension bundle.
+     * @param className The class name of the shape.
+    **/
+    public DefaultShape(BundleContext context, long bundleId, String className)
+    {
+        m_context = context;
+        m_bundleId = bundleId;
+        m_className = className;
+    }
+
+    /**
+     * Implements the <tt>SimpleShape</tt> interface method. When acting as
+     * a proxy, this method lazily loads and instantiates the shape class and
+     * then uses it to draw the shape. When acting as a placeholder shape,
+     * this method draws the default icon.
+     * @param g2 The graphics object used for painting.
+     * @param p The position to paint the triangle.
+    **/
+    public void draw(Graphics2D g2, Point p)
+    {
+        // If this is a proxy shape, instantiate the shape class
+        // and use it to draw the shape.
+        if (m_context != null)
+        {
+            try
+            {
+                if (m_shape == null)
+                {
+                    // Get the bundle.
+                    Bundle bundle = m_context.getBundle(m_bundleId);
+                    // Load the class and instantiate it.
+                    Class clazz = bundle.loadClass(m_className);
+                    m_shape = (SimpleShape) clazz.newInstance();
+                }
+                // Draw the shape.
+                m_shape.draw(g2, p);
+                // If everything was successful, then simply return.
+                return;
+            }
+            catch (Exception ex)
+            {
+                // This generally should not happen, but if it does then
+                // we can just fall through and paint the default icon.
+            }
+        }
+
+        // If the proxied shape could not be drawn for any reason or if
+        // this shape is simply a placeholder, then draw the default icon.
+        if (m_icon == null)
+        {
+            try
+            {
+                m_icon = new ImageIcon(this.getClass().getResource("underc.png"));
+            }
+            catch (Exception ex)
+            {
+                ex.printStackTrace();
+                g2.setColor(Color.red);
+                g2.fillRect(0, 0, 60, 60);
+                return;
+            }
+        }
+        g2.drawImage(m_icon.getImage(), 0, 0, null);
+    }
+}
\ No newline at end of file
diff --git a/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DrawingFrame.java b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DrawingFrame.java
index c17fcef..1b4ce60 100644
--- a/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DrawingFrame.java
+++ b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/DrawingFrame.java
@@ -26,6 +26,13 @@
 
 import org.apache.felix.example.extenderbased.host.extension.SimpleShape;
 
+/**
+ * This class represents the main application class, which is a JFrame subclass
+ * that manages a toolbar of shapes and a drawing canvas. This class does not
+ * directly interact with the underlying OSGi framework; instead, it is injected
+ * with the available <tt>SimpleShape</tt> instances to eliminate any
+ * dependencies on the OSGi application programming interfaces.
+**/
 public class DrawingFrame extends JFrame
     implements MouseListener, MouseMotionListener
 {
@@ -34,11 +41,14 @@
     private JToolBar m_toolbar;
     private String m_selected;
     private JPanel m_panel;
-    private Map m_shapes = new HashMap();
     private ShapeComponent m_selectedComponent;
+    private Map m_shapes = new HashMap();
     private SimpleShape m_defaultShape = new DefaultShape();
     private ActionListener m_reusableActionListener = new ShapeActionListener();
 
+    /**
+     * Default constructor that populates the main window.
+    **/
     public DrawingFrame()
     {
         super("Extender-Based Host");
@@ -55,11 +65,22 @@
         setSize(400, 400);
     }
 
+    /**
+     * This method sets the currently selected shape to be used for drawing
+     * on the canvas.
+     * @param name The name of the shape to use for drawing on the canvas.
+    **/
     public void selectShape(String name)
     {
         m_selected = name;
     }
 
+    /**
+     * Retrieves the available <tt>SimpleShape</tt> associated with the given name.
+     * @param name The name of the <tt>SimpleShape</tt> to retrieve.
+     * @return The corresponding <tt>SimpleShape</tt> instance if available or
+     *         <tt>null</tt>.
+    **/
     public SimpleShape getShape(String name)
     {
         ShapeInfo info = (ShapeInfo) m_shapes.get(name);
@@ -73,6 +94,12 @@
         }
     }
 
+    /**
+     * Injects an available <tt>SimpleShape</tt> into the drawing frame.
+     * @param name The name of the injected <tt>SimpleShape</tt>.
+     * @param icon The icon associated with the injected <tt>SimpleShape</tt>.
+     * @param shape The injected <tt>SimpleShape</tt> instance.
+    **/
     public void addShape(String name, Icon icon, SimpleShape shape)
     {
         m_shapes.put(name, new ShapeInfo(name, icon, shape));
@@ -90,6 +117,10 @@
         repaint();
     }
 
+    /**
+     * Removes a no longer available <tt>SimpleShape</tt> from the drawing frame.
+     * @param name The name of the <tt>SimpleShape</tt> to remove.
+    **/
     public void removeShape(String name)
     {
         m_shapes.remove(name);
@@ -118,6 +149,11 @@
         }
     }
 
+    /**
+     * Implements method for the <tt>MouseListener</tt> interface to
+     * draw the selected shape into the drawing canvas.
+     * @param evt The associated mouse event.
+    **/
     public void mouseClicked(MouseEvent evt)
     {
         if (m_selected == null)
@@ -135,14 +171,27 @@
         }
     }
 
+    /**
+     * Implements an empty method for the <tt>MouseListener</tt> interface.
+     * @param evt The associated mouse event.
+    **/
     public void mouseEntered(MouseEvent evt)
     {
     }
 
+    /**
+     * Implements an empty method for the <tt>MouseListener</tt> interface.
+     * @param evt The associated mouse event.
+    **/
     public void mouseExited(MouseEvent evt)
     {
     }
 
+    /**
+     * Implements method for the <tt>MouseListener</tt> interface to initiate
+     * shape dragging.
+     * @param evt The associated mouse event.
+    **/
     public void mousePressed(MouseEvent evt)
     {
         Component c = m_panel.getComponentAt(evt.getPoint());
@@ -155,6 +204,11 @@
         }
     }
 
+    /**
+     * Implements method for the <tt>MouseListener</tt> interface to complete
+     * shape dragging.
+     * @param evt The associated mouse event.
+    **/
     public void mouseReleased(MouseEvent evt)
     {
         if (m_selectedComponent != null)
@@ -168,45 +222,31 @@
         }
     }
 
+    /**
+     * Implements method for the <tt>MouseMotionListener</tt> interface to
+     * move a dragged shape.
+     * @param evt The associated mouse event.
+    **/
     public void mouseDragged(MouseEvent evt)
     {
         m_selectedComponent.setBounds(
             evt.getX() - BOX / 2, evt.getY() - BOX / 2, BOX, BOX);
     }
 
+    /**
+     * Implements an empty method for the <tt>MouseMotionListener</tt>
+     * interface.
+     * @param evt The associated mouse event.
+    **/
     public void mouseMoved(MouseEvent evt)
     {
     }
 
-    private class DefaultShape implements SimpleShape
-    {
-        private ImageIcon m_icon = null;
-
-        public void draw(Graphics2D g2, Point p)
-        {
-            if (m_icon == null)
-            {
-                try
-                {
-                    m_icon = new ImageIcon(this.getClass().getResource("underc.png"));
-                }
-                catch (Exception ex)
-                {
-                    ex.printStackTrace();
-                    g2.setColor(Color.red);
-                    g2.fillRect(0, 0, getWidth() - 1, getHeight() - 1);
-                    return;
-                }
-            }
-            g2.drawImage(m_icon.getImage(), 0, 0, null);
-        }
-
-        public String getName()
-        {
-            return "Default";
-        }
-    }
-
+    /**
+     * Simple action listener for shape tool bar buttons that sets
+     * the drawing frame's currently selected shape when receiving
+     * an action event.
+    **/
     private class ShapeActionListener implements ActionListener
     {
         public void actionPerformed(ActionEvent evt)
@@ -215,6 +255,10 @@
         }
     }
 
+    /**
+     * This class is used to record the various information pertaining to
+     * an available shape.
+    **/
     private static class ShapeInfo
     {
         public String m_name;
diff --git a/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/ShapeTracker.java b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/ShapeTracker.java
index 09d8f44..5f8390a 100644
--- a/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/ShapeTracker.java
+++ b/examples/extenderbased.host/src/main/java/org/apache/felix/example/extenderbased/host/ShapeTracker.java
@@ -38,6 +38,8 @@
     private static final int ADDED = 1;
     // Flag indicating a removed shape.
     private static final int REMOVED = 2;
+    // The bundle context used for tracking.
+    private BundleContext m_context;
     // The application object to notify.
     private DrawingFrame m_frame;
 
@@ -51,6 +53,7 @@
     public ShapeTracker(BundleContext context, DrawingFrame frame)
     {
         super(context);
+        m_context = context;
         m_frame = frame;
     }
 
@@ -126,16 +129,11 @@
                 String iconPath = (String) dict.get(SimpleShape.ICON_PROPERTY);
                 Icon icon = new ImageIcon(bundle.getResource(iconPath));
                 // Get the class of the extension.
-                String classPath = (String) dict.get(SimpleShape.CLASS_PROPERTY);
-                try
-                {
-                    Class clazz = bundle.loadClass(classPath);
-                    m_frame.addShape(name, icon, (SimpleShape) clazz.newInstance());
-                }
-                catch (Exception ex)
-                {
-                    // This should never happen in this example.
-                }
+                String className = (String) dict.get(SimpleShape.CLASS_PROPERTY);
+                m_frame.addShape(
+                    name,
+                    icon,
+                    new DefaultShape(m_context, bundle.getBundleId(), className));
                 break;
 
             case REMOVED: