|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun * Microsystems, Inc. All Rights Reserved. */ /* * PropertySheetTest.java * * Created on August 24, 2001, 4:25 PM */ package org.openide.explorer.propertysheet; import java.awt.BorderLayout; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dialog; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.KeyboardFocusManager; import javax.swing.JFrame; import java.util.StringTokenizer; import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; import org.openide.explorer.propertysheet.PropertySheet; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import org.openide.*; import org.openide.nodes.*; import org.openide.explorer.propertysheet.*; import org.openide.explorer.propertysheet.editors.*; import java.beans.*; import java.lang.ref.WeakReference; import java.lang.reflect.*; import javax.swing.*; import javax.swing.ImageIcon; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import junit.framework.*; import junit.textui.TestRunner; import org.netbeans.junit.*; import org.openide.ErrorManager; import org.openide.NotifyDescriptor; import org.openide.util.Lookup; /** Tests that PropertyPanel honors Escape and Enter keys when * in a dialog */ public class PropertyPanelInDialogTest extends NbTestCase { static { org.netbeans.core.NonGui.registerPropertyEditors(); } public PropertyPanelInDialogTest(String name) { super(name); } public static void main(String args[]) { TestRunner.run(suite ()); } public static Test suite() { return new PropertyPanelInDialogSuite (); } private static class PropertyPanelInDialogSuite extends NbTestSuite { public PropertyPanelInDialogSuite () { super (PropertyPanelInDialogTest.class); } public void run (final TestResult tr) { super.run (tr); } } /* * This test creates a Property, Editor and Node. First test checks if initialized * editor contains the same value as property. The second checks if the property * value is changed if the same change will be done in the editor. */ PropertyPanel basicRen; PropertyPanel tagsRen1; PropertyPanel tagsRen2; PropertyPanel tagsRen3; PropertyPanel boolRen; PropertyPanel custRen; PropertyPanel custRen2; PropertyPanel exRen; PropertyPanel numRen; PropertyPanel edRen; private TNode tn; private BasicProperty basicProp; private TagsProperty tags1; private TagsProperty tags2; private TagsProperty tags3; private BooleanProperty booleanProp; private EditorCustom ec; private CustomProperty customProp; private CustomProperty customProp2; private BasicEditor te; private boolean setup=false; private JFrame jf=null; private JPanel jp=null; private int SLEEP_LENGTH=200; PropertyPanel[] renderers; JButton launcher; protected void tearDown() { if (jf != null) { jf.hide(); jf.dispose(); } } protected void setUp() throws Exception { // UIManager.setLookAndFeel(new com.sun.java.swing.plaf.windows.WindowsLookAndFeel()); // UIManager.setLookAndFeel(new com.sun.java.swing.plaf.gtk.GTKLookAndFeel()); if (setup) return; // Create new TesBasicProperty basicProp= new BasicProperty("basicProp", true); tags1 = new TagsProperty("tags1", true, new String[] {"What","is","the","meaning","of","life"}); tags2 = new TagsProperty("tags2", true, new String[] {"NetBeans","can be ","really","cool"}); tags3 = new TagsProperty("tags3", true, new String[] {"Behold","the","power","of","cheese"}); booleanProp = new BooleanProperty("booleanProp", true); customProp = new CustomProperty("CustomProp", true); customProp2 = new CustomProperty("CustomProp2", true); ExceptionProperty exProp = new ExceptionProperty("Exception prop", true); NumProperty numProp = new NumProperty("Int prop", true); EditableNumProperty edProp = new EditableNumProperty("Editable", true); // Create new BasicEditor te = new BasicEditor(); ec = new EditorCustom(); // Create new TNode tn = new TNode(); jf = new JFrame(); jf.getContentPane().setLayout(new BorderLayout()); jp = new JPanel(); jp.setLayout(new FlowLayout()); jf.getContentPane().add(jp, BorderLayout.CENTER); jf.setLocation (20,20); jf.setSize (600, 200); basicRen = new PropertyPanel(basicProp); tagsRen1 = new PropertyPanel(tags1); tagsRen2 = new PropertyPanel(tags2); tagsRen3 = new PropertyPanel(tags3); boolRen = new PropertyPanel(booleanProp); custRen = new PropertyPanel(customProp); custRen2 = new PropertyPanel(customProp2); exRen = new PropertyPanel(exProp); numRen = new PropertyPanel(numProp); edRen = new PropertyPanel(edProp); tagsRen2.putClientProperty("radioButtonMax", new Integer(10)); renderers = new PropertyPanel[] { basicRen, tagsRen1, tagsRen2, boolRen, custRen, edRen, numRen }; launcher = new JButton("Invoke dialog"); launcher.addActionListener (new ActionListener() { public void actionPerformed(ActionEvent ae) { invokeDlg(); } }); jf.getContentPane().add(launcher); new WaitWindow(jf); //block until window open jf.toFront(); ExtTestCase.requestFocus(launcher); sleep(); Thread.currentThread().sleep(300); sleep(); currRen = basicRen; setup = true; } public void testBooleanEditor() throws Exception { currRen = boolRen; checkOneRenderer(); } public void testStringEditor() throws Exception { currRen = basicRen; checkOneRenderer(); } public void testComboEditor() throws Exception { currRen = tagsRen1; checkOneRenderer(); } public void testEditableCombo() throws Exception { currRen = edRen; checkOneRenderer(); } public void testRadioEditor() throws Exception { currRen = tagsRen2; checkOneRenderer(); } PropertyPanel currRen=null; NotifyDescriptor not = null; Object notifyResult=null; private void invokeDlg() { NotifyDescriptor not = new NotifyDescriptor(currRen, "Boo!", NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.QUESTION_MESSAGE, new Object[] {NotifyDescriptor.OK_OPTION, NotifyDescriptor.CANCEL_OPTION}, null); notifyResult = DialogDisplayer.getDefault().notify(not); try { Thread.currentThread().sleep(300); } catch (Exception e){} ExtTestCase.waitForDialog(); } static Boolean functioning = null; private static final boolean canBeRun() { if (functioning == null) { functioning = ExtTestCase.canSafelyRunFocusTests() ? Boolean.TRUE : Boolean.FALSE; if (Boolean.FALSE.equals(functioning)) { System.err.println("Platform focus behavior not sane. Not " + "running PropertyPanelInDialogTest"); } } return functioning.booleanValue(); } public void checkOneRenderer() throws Exception { if (!canBeRun()) { return; } SwingUtilities.invokeLater (new Runnable() { public void run() { invokeDlg(); } }); sleep(); sleep(); ExtTestCase.waitForDialog(); while (!currRen.isVisible()) { System.currentTimeMillis(); } if (currRen.getProperty() != tags2) { currRen.requestFocus(); } else { clickOn(currRen, 80, 25); } if (currRen == boolRen) { clickOn(currRen, 40, 13); } sleep(); ExtTestCase.waitForComponentOrChildToGetFocus(currRen); Component focus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); assertTrue ("After showing dialog, focus owner should be child of renderer, but it is " + focus, currRen == focus || currRen.isAncestorOf(focus)); Container anc = null; if (focus instanceof JComponent) { JComponent jcp = (JComponent) focus; anc = jcp.getTopLevelAncestor(); } assertTrue (anc != null); pressKey(currRen, KeyEvent.VK_ESCAPE); releaseKey(currRen, KeyEvent.VK_ESCAPE); typeKey(currRen, KeyEvent.VK_ESCAPE); if (focus instanceof JComboBox) { //do a second set, the first should just close the popup pressKey(currRen, KeyEvent.VK_ESCAPE); releaseKey(currRen, KeyEvent.VK_ESCAPE); typeKey(currRen, KeyEvent.VK_ESCAPE); } int ct = 0; //#47044 while (anc.isShowing()) { System.err.println("Iter"); sleep(); if (ct++ > 1000) { return; } } sleep(); sleep(); sleep(); ExtTestCase.waitForAnythingToGetFocus(); sleep(); focus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); assertTrue("After closing dialog, focus should not be null.", focus != null); ExtTestCase.waitForFrame(); assertTrue("After closing dialog, focus should be on the button in the main frame - perhaps the dialog did not close?" + focus, focus == launcher); SwingUtilities.invokeLater (new Runnable() { public void run() { invokeDlg(); } }); while (!currRen.isVisible()) { System.err.println("Waiting"); } //clickOn(currRen); if (currRen.getProperty() != tags2) { clickOn(currRen); } else { clickOn(currRen, 80, 25); } if (currRen == boolRen) { clickOn(currRen, 40, 13); } sleep(); sleep(); ExtTestCase.waitForComponentOrChildToGetFocus(currRen); focus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); assertTrue ("After showing dialog, focus owner should be child of renderer, but it is " + focus, currRen == focus || currRen.isAncestorOf(focus)); anc = null; if (focus instanceof JComponent) { JComponent jcp = (JComponent) focus; anc = jcp.getTopLevelAncestor(); } assertTrue (anc != null); pressKey(currRen, KeyEvent.VK_ENTER); releaseKey(currRen, KeyEvent.VK_ENTER); typeKey(currRen, KeyEvent.VK_ENTER); if (focus instanceof JComboBox) { //do a second set, the first should just close the popup pressKey(currRen, KeyEvent.VK_ENTER); releaseKey(currRen, KeyEvent.VK_ENTER); typeKey(currRen, KeyEvent.VK_ENTER); } ct = 0; //#47044 while (anc.isShowing()) { System.err.println("Iter2"); sleep(); if (ct++ > 1000) { return; } } sleep(); ExtTestCase.waitForAnythingToGetFocus(); focus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); assertTrue("After closing dialog, focus should not be null.", focus != null); ExtTestCase.waitForFrame(); focus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); assertTrue("After closing dialog, focus should be on the button in the main frame - perhaps the dialog did not close?" + focus, focus == launcher); } private class FL implements FocusListener { private FocusEvent gainedEvent=null; private FocusEvent lostEvent=null; private int gainedCount=0; private int lostCount=0; public void assertGained() { assertNotNull ("No focus gained received after clicking on an editable renderer", gainedEvent); assertTrue("Received wrong number of focus gained events for a single click on a renderer " + gainedCount, gainedCount == 1); } public void assertLost() { assertNotNull ("No focus lost event received after clicking away from a focused, editable renderer", lostEvent); assertTrue("Received wrong number of focus lost events for a single click away from a focused renderer" + lostCount, lostCount == 1); } public void focusGained(java.awt.event.FocusEvent e) { gainedEvent = e; gainedCount++; } public void focusLost(java.awt.event.FocusEvent e) { lostEvent = e; lostCount++; } } private class CL implements ChangeListener { private ChangeEvent e; public void assertEvent(String msg) { sleep(); //give the event time to happen assertNotNull (msg, e); e = null; } public void assertNoEvent(String msg) { sleep(); assertNull (e); e = null; } public void stateChanged(ChangeEvent e) { this.e = e; } } private static class TestGCVal extends Object { public String toString() { return "TestGCVal"; } } private static class WaitWindow extends WindowAdapter { boolean shown=false; public WaitWindow (JFrame f) { f.addWindowListener(this); f.show(); if (!shown) { synchronized(this) { try { //System.err.println("Waiting for window"); wait(5000); } catch (Exception e) {} } } } public void windowOpened(WindowEvent e) { shown = true; synchronized(this) { //System.err.println("window opened"); notifyAll(); ((JFrame) e.getSource()).removeWindowListener(this); } } } private void sleep() { //useful when running interactively try { Thread.currentThread().sleep(SLEEP_LENGTH); } catch (InterruptedException ie) { //go away } //runs faster -uncomment for production use try { //jf.getTreeLock().wait(); SwingUtilities.invokeAndWait(new Runnable() { public void run() { System.currentTimeMillis(); } }); //jf.getTreeLock().wait(); SwingUtilities.invokeAndWait(new Runnable() { public void run() { System.currentTimeMillis(); } }); } catch (Exception e) { } } private void changeProperty (PropertyPanel ren, Node.Property newProp) { ren.setProperty(newProp); } private void clickOn (final PropertyPanel ren, final int fromRight, final int fromTop) throws Exception { SwingUtilities.invokeAndWait (new Runnable() { public void run() { Point toClick = new Point(ren.getWidth() - fromRight, fromTop); Component target=ren.getComponentAt(toClick); if (target == null) { target = ren; } toClick = SwingUtilities.convertPoint(ren, toClick, target); // System.err.println("Target component is " + target.getClass().getName() + " - " + target + " clicking at " + toClick); MouseEvent me = new MouseEvent (target, MouseEvent.MOUSE_PRESSED, System.currentTimeMillis(), MouseEvent.BUTTON1_MASK, toClick.x, toClick.y, 2, false); target.dispatchEvent(me); me = new MouseEvent (target, MouseEvent.MOUSE_RELEASED, System.currentTimeMillis(), MouseEvent.BUTTON1_MASK, toClick.x, toClick.y, 2, false); target.dispatchEvent(me); me = new MouseEvent (target, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), MouseEvent.BUTTON1_MASK, toClick.x, toClick.y, 2, false); } }); sleep(); } private void clickOn (final PropertyPanel ren) throws Exception { SwingUtilities.invokeAndWait (new Runnable() { public void run() { Point toClick = new Point(5,5); Component target=ren.getComponentAt(toClick); MouseEvent me = new MouseEvent (target, MouseEvent.MOUSE_PRESSED, System.currentTimeMillis(), MouseEvent.BUTTON1_MASK, toClick.x, toClick.y, 2, false); target.dispatchEvent(me); } }); sleep(); } private void releaseKey (final Component target, final int key) throws Exception { SwingUtilities.invokeAndWait(new Runnable() { public void run() { KeyEvent ke = new KeyEvent (target, KeyEvent.KEY_RELEASED, System.currentTimeMillis(), 0, key, (char) key); target.dispatchEvent(ke); } }); sleep(); } private void pressKey (final Component target, final int key) throws Exception { SwingUtilities.invokeAndWait(new Runnable() { public void run() { KeyEvent ke = new KeyEvent (target, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), 0, key, (char) key); target.dispatchEvent(ke); } }); sleep(); } private void shiftPressKey (final Component target, final int key) throws Exception { SwingUtilities.invokeAndWait(new Runnable() { public void run() { KeyEvent ke = new KeyEvent (target, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), KeyEvent.SHIFT_MASK, key, (char) key); target.dispatchEvent(ke); } }); sleep(); } private void typeKey (final Component target, final int key) throws Exception { SwingUtilities.invokeAndWait(new Runnable() { public void run() { KeyEvent ke = new KeyEvent (target, KeyEvent.KEY_TYPED, System.currentTimeMillis(), 0, KeyEvent.VK_UNDEFINED, (char) key); System.err.println("Typing key to " + target); target.dispatchEvent(ke); } }); sleep(); } //Node definition public class TNode extends AbstractNode { //create Node public TNode() { super (Children.LEAF); setName("TNode"); // or, super.setName if needed setDisplayName("TNode"); createSheet(); } //clone existing Node public Node cloneNode() { return new TNode(); } public void addProp (Node.Property p) { props.put(p); this.firePropertyChange(PROP_PROPERTY_SETS, null, null); this.firePropertySetsChange(null, null); } Sheet sheet=null; Sheet.Set props=null; // Create a property sheet: protected Sheet createSheet() { sheet = super.createSheet(); // Make sure there is a "Properties" set: props = sheet.get(Sheet.PROPERTIES); if (props == null) { props = Sheet.createPropertiesSet(); sheet.put(props); } props.put(basicProp); props.put(tags1); props.put(tags2); props.put(tags3); props.put(booleanProp); props.put(customProp); return sheet; } // Method firing changes public void fireMethod(String s, Object o1, Object o2) { firePropertyChange(s,o1,o2); } } // Property definition public class BasicProperty extends PropertySupport { private Object myValue = "Value"; // Create new Property public BasicProperty(String name, boolean isWriteable) { super(name, Object.class, name, "", true, isWriteable); } // get property value public Object getValue() { return myValue; } // set property value public void setValue(Object value) throws IllegalArgumentException,IllegalAccessException, InvocationTargetException { Object oldVal = myValue; myValue = value; tn.fireMethod(getName(), oldVal, myValue); } // get the property editor public PropertyEditor getPropertyEditor() { return te; } } // Editor definition public class BasicEditor extends PropertyEditorSupport implements ExPropertyEditor { PropertyEnv env; // Create new BasicEditor public BasicEditor() { } /* * This method is called by the IDE to pass * the environment to the property editor. */ public void attachEnv(PropertyEnv env) { this.env = env; } // Set that this Editor doesn't support custom Editor public boolean supportsCustomEditor() { return false; } // Set the Property value threw the Editor public void setValue(Object newValue) { super.setValue(newValue); } public String getAsText() { return getValue() == null ? "null" : getValue().toString(); } } private static class PseudoWindowsLookAndFeel extends com.sun.java.swing.plaf.windows.WindowsLookAndFeel { public boolean isSupportedLookAndFeel() { return true; } } public class TagsEditor extends PropertyEditorSupport implements ExPropertyEditor { PropertyEnv env; String[] tags; public TagsEditor(String[] tags) { this.tags = tags; } public String[] getTags() { return tags; } public void attachEnv(PropertyEnv env) { this.env = env; } public boolean supportsCustomEditor() { return false; } public void setValue(Object newValue) { super.setValue(newValue); } } // Property definition public class TagsProperty extends PropertySupport { private Object myValue = "Value"; private String[] tags; // Create new Property public TagsProperty(String name, boolean isWriteable, String[] tags) { super(name, Object.class, name, "", true, isWriteable); this.tags = tags; } // get property value public Object getValue() { return myValue; } // set property value public void setValue(Object value) throws IllegalArgumentException,IllegalAccessException, InvocationTargetException { Object oldVal = myValue; myValue = value; tn.fireMethod(getName(), oldVal, myValue); } // get the property editor public PropertyEditor getPropertyEditor() { return new TagsEditor(tags); } public String getShortDescription() { return "I have tags!"; } } // Property definition public class BooleanProperty extends PropertySupport { private Boolean myValue = Boolean.FALSE; // Create new Property public BooleanProperty(String name, boolean isWriteable) { super(name, Boolean.class, name, "", true, isWriteable); } // get property value public Object getValue() { return myValue; } // set property value public void setValue(Object value) throws IllegalArgumentException,IllegalAccessException, InvocationTargetException { Object oldVal = myValue; myValue = (Boolean) value; tn.fireMethod(getName(), oldVal, myValue); } } public class CustomProperty extends PropertySupport { private Object myValue = "Value"; // Create new Property public CustomProperty(String name, boolean isWriteable) { super(name, Object.class, name, "", true, isWriteable); } // get property value public Object getValue() { return myValue; } // set property value public void setValue(Object value) throws IllegalArgumentException,IllegalAccessException, InvocationTargetException { Object oldVal = myValue; myValue = value; tn.fireMethod(getName(), oldVal, myValue); } // get the property editor public PropertyEditor getPropertyEditor() { return ec; } } public class ExceptionProperty extends PropertySupport { private Object myValue = "Value"; // Create new Property public ExceptionProperty(String name, boolean isWriteable) { super(name, Object.class, name, "", true, isWriteable); } // get property value public Object getValue() { return myValue; } // set property value public void setValue(Object value) throws IllegalArgumentException,IllegalAccessException, InvocationTargetException { Object oldVal = myValue; myValue = value; tn.fireMethod(getName(), oldVal, myValue); } // get the property editor public PropertyEditor getPropertyEditor() { return exed; } } private ExEditor exed = new ExEditor(); public static class ExEditor extends PropertyEditorSupport { private Object myVal="Value"; public ExEditor() {} public void setAsText(String val) { //System.err.println("SetAsText"); if (val.equals("Value")) { myVal = val; } else { IllegalArgumentException iae = new IllegalArgumentException ("No!"); ErrorManager.getDefault().annotate(iae, ErrorManager.USER, "NoNo!", "Localized message",null, null); throw iae; } } public void setValue(Object newValue) { myVal = newValue; firePropertyChange(); } public Object getValue() { return "Value"; } } // Editor definition public class EditorCustom extends PropertyEditorSupport implements ExPropertyEditor { PropertyEnv env; // Create new BasicEditor public EditorCustom() { } /* * This method is called by the IDE to pass * the environment to the property editor. */ public void attachEnv(PropertyEnv env) { this.env = env; } // Set that this Editor doesn't support custom Editor public boolean supportsCustomEditor() { return true; } // Set the Property value threw the Editor public void setValue(Object newValue) { super.setValue(newValue); } public String getAsText() { return getValue() == null ? "null" : getValue().toString(); } public Component getCustomEditor() { return new JPanel(); } } public class NumProperty extends PropertySupport { private Integer myValue = new Integer(4); // Create new Property public NumProperty(String name, boolean isWriteable) { super(name, Integer.class, name, "", true, isWriteable); } // get property value public Object getValue() { return myValue; } // set property value public void setValue(Object value) throws IllegalArgumentException,IllegalAccessException, InvocationTargetException { if (!(value instanceof Integer)) { throw new IllegalArgumentException("Not an integer - " + value); } Object oldVal = myValue; myValue = (Integer) value; tn.fireMethod(getName(), oldVal, myValue); } // get the property editor public PropertyEditor getPropertyEditor() { return new NumberedTagsEditor(); } public Object getValue(String key) { if ("canEditAsText".equals(key)) { return Boolean.TRUE; } else { return super.getValue(key); } } } public class EditableNumProperty extends TagsProperty { public EditableNumProperty(String name, boolean isWriteable) { super (name, isWriteable, new String[]{"boo"}); } public PropertyEditor getPropertyEditor() { return new PropertyPanelInDialogTest.EditableTagsEditor(); } } // Combo must display text, not numbers public class NumberedTagsEditor extends PropertyEditorSupport { private int val=3; // Create new BasicEditor public NumberedTagsEditor() { } public String[] getTags() { return new String[] {"zero","one","two","three","four","five","six","seven"}; } // Set the Property value threw the Editor public void setValue(Object newValue) { val = ((Integer) newValue).intValue(); firePropertyChange(); } public String getAsText() { return getTags()[((Integer) getValue()).intValue()]; } public void setAsText(String txt) { String[] t = getTags(); for (int i=0; i < t.length; i++) { if (txt.trim().equals(t[i])) { setValue(new Integer(i)); return; } } IllegalArgumentException iae = new IllegalArgumentException(txt); ErrorManager.getDefault().annotate(iae, ErrorManager.USER, txt, txt + " is not a valid value", null, null); } public Object getValue() { return new Integer(val); } public Component getCustomEditor() { return new JPanel(); } } public class EditableTagsEditor extends TagsEditor implements ExPropertyEditor { private Object val="woof"; public EditableTagsEditor() { super (new String[] {"miaou","woof","moo","quack"}); } public void attachEnv (PropertyEnv env) { env.getFeatureDescriptor().setValue("canEditAsText", Boolean.TRUE); } public void setAsText(String s) { setValue(s); } public void setValue(Object val) { this.val = val; } public Object getValue() { return val; } public String getAsText() { return val.toString(); } } } |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.