mirror of
https://github.com/apache/openjpa.git
synced 2025-02-21 01:15:30 +00:00
Adding initial files
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@944083 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
b0993e01cd
commit
3887eb414e
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.image.BufferedImage;
|
||||
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
/**
|
||||
* Displays color codes of each attribute type.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AttributeLegendView extends JPanel {
|
||||
|
||||
public AttributeLegendView() {
|
||||
super(true);
|
||||
setBorder(BorderFactory.createTitledBorder("Attribute Legends"));
|
||||
setLayout(new GridLayout(0,3));
|
||||
add(createColoredLabel("IDENTITY", Color.RED));
|
||||
add(createColoredLabel("VERSION", Color.DARK_GRAY));
|
||||
for (Attribute.PersistentAttributeType type : Attribute.PersistentAttributeType.values()) {
|
||||
add(createColoredLabel(type.toString().replace('_', ' '), MetamodelHelper.getColor(type)));
|
||||
}
|
||||
}
|
||||
|
||||
JComponent createColoredLabel(String text, Color c) {
|
||||
int width = 40;
|
||||
int height = 20;
|
||||
BufferedImage bimage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
for (int i = 0; i < width; i++)
|
||||
for (int j = 0; j < height; j++)
|
||||
bimage.setRGB(i, j, c.getRGB());
|
||||
JLabel label = new JLabel(text, new ImageIcon(bimage), JLabel.LEADING);
|
||||
return label;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.persistence.metamodel.Attribute;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
/**
|
||||
* View of a persistent attribute as a JLabel.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AttributeView extends JLabel {
|
||||
private final Attribute<?,?> _attr;
|
||||
|
||||
public AttributeView(Attribute<?,?> attr) {
|
||||
_attr = attr;
|
||||
Color color = MetamodelHelper.getColor(attr);
|
||||
setForeground(color);
|
||||
setText(MetamodelHelper.getDisplayName(attr));
|
||||
}
|
||||
|
||||
public Attribute<?,?> getAttribute() {
|
||||
return _attr;
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollBar;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextPane;
|
||||
|
||||
/**
|
||||
* Displays Properties.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ConfigurationViewer extends JTextPane {
|
||||
private static final char SPACE = ' ';
|
||||
|
||||
public ConfigurationViewer(String title, Map<String, Object> config) {
|
||||
super();
|
||||
setBorder(BorderFactory.createTitledBorder(title));
|
||||
TreeSet<String> sortedKeys = new TreeSet<String>(config.keySet());
|
||||
int L = getMaxLength(sortedKeys);
|
||||
for (String key : sortedKeys) {
|
||||
setCaretPosition(getDocument().getLength());
|
||||
setCharacterAttributes(TextStyles.KEYS, true);
|
||||
replaceSelection(key + pad(L - key.length()) + ":");
|
||||
setCaretPosition(getDocument().getLength());
|
||||
setCharacterAttributes(TextStyles.VALUES, true);
|
||||
replaceSelection(toString(config.get(key)) + "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
String toString(Object value) {
|
||||
if (value == null)
|
||||
return "null";
|
||||
if (value.getClass().isArray()) {
|
||||
return Arrays.toString((Object[])value);
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
private int getMaxLength(Set<String> keys) {
|
||||
int len = 1;
|
||||
for (String s : keys) {
|
||||
len = Math.max(s.length(), len);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
private String pad(int n) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (int i = 0; i < n; i++)
|
||||
buf.append(SPACE);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
import javax.persistence.metamodel.PluralAttribute;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
/**
|
||||
* A data model for a tabular view of a list of persistent entities.
|
||||
* The data supplied by this model can be filtered to display field values of
|
||||
* basic type or single-valued or multi-valued relationships.
|
||||
* <br>
|
||||
* The meta-information about the attributes of the entity are supplied by
|
||||
* newly defined {@link Metamodel meta-model API} of JPA 2.0 specification.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EntityDataModel<T> extends AbstractTableModel implements TableModel, Iterable<T> {
|
||||
/**
|
||||
* Constant designates to include non-relation fields.
|
||||
*/
|
||||
public static int BASIC_ATTR = 1 << 0;
|
||||
/**
|
||||
* Constant designates to include single-valued relation fields.
|
||||
*/
|
||||
public static int ASSOCIATION_ATTR = 1 << 1;
|
||||
/**
|
||||
* Constant designates to include multi-valued relation fields.
|
||||
*/
|
||||
public static int PLURAL_ATTR = 1 << 2;
|
||||
/**
|
||||
* Constant designates to include all fields.
|
||||
*/
|
||||
public static int ALL_ATTR = BASIC_ATTR | ASSOCIATION_ATTR | PLURAL_ATTR;
|
||||
/**
|
||||
* Constant designates to show a row count field at the first column.
|
||||
*/
|
||||
public static int ROW_COUNT = 1 << 3;
|
||||
|
||||
private List<String> columnNames;
|
||||
private List<Class<?>> columnClasses;
|
||||
private List<Method> methods;
|
||||
private List<T> data;
|
||||
private List<Attribute<? super T,?>> attributes;
|
||||
private static Object[] EMPTY_ARGS = null;
|
||||
private static Class<?>[] EMPTY_CLASSES = null;
|
||||
|
||||
private boolean showsRowCount;
|
||||
private boolean showsBasicAttr;
|
||||
private boolean showsSingularAttr;
|
||||
private boolean showsPluralAttr;
|
||||
|
||||
/**
|
||||
* Attributes of the entity are reordered with basic attributes, followed by singular
|
||||
* association followed by the many-valued attributes.
|
||||
*
|
||||
* @param cls
|
||||
* @param data
|
||||
* @param meta
|
||||
*/
|
||||
public EntityDataModel(Class<T> cls, List<T> data, Metamodel meta, int styleBits) {
|
||||
super();
|
||||
this.data = data;
|
||||
EntityType<T> entityType = meta.entity(cls);
|
||||
|
||||
columnNames = new ArrayList<String>();
|
||||
columnClasses = new ArrayList<Class<?>>();
|
||||
attributes = new ArrayList<Attribute<? super T,?>>();
|
||||
methods = new ArrayList<Method>();
|
||||
|
||||
showsRowCount = (styleBits & ROW_COUNT) != 0;
|
||||
showsBasicAttr = (styleBits & BASIC_ATTR) != 0;
|
||||
showsSingularAttr = (styleBits & ASSOCIATION_ATTR) != 0;
|
||||
showsPluralAttr = (styleBits & PLURAL_ATTR) != 0;
|
||||
|
||||
Set<SingularAttribute<? super T, ?>> sAttrs = entityType.getSingularAttributes();
|
||||
if (showsBasicAttr) {
|
||||
for (SingularAttribute<? super T, ?> attr : sAttrs) {
|
||||
if (!attr.isAssociation()) {
|
||||
attributes.add(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showsSingularAttr) {
|
||||
for (SingularAttribute<? super T, ?> attr : sAttrs) {
|
||||
if (attr.isAssociation()) {
|
||||
attributes.add(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showsPluralAttr) {
|
||||
Set<PluralAttribute<? super T, ?, ?>> pAttrs = entityType.getPluralAttributes();
|
||||
for (PluralAttribute<? super T, ?, ?> attr : pAttrs) {
|
||||
attributes.add(attr);
|
||||
}
|
||||
}
|
||||
Collections.sort(attributes, new MetamodelHelper.AttributeComparator());
|
||||
for (Attribute<? super T, ?> attr : attributes) {
|
||||
populate(cls, attr);
|
||||
}
|
||||
if (showsRowCount) {
|
||||
columnNames.add(0,"Row");
|
||||
columnClasses.add(0, Long.class);
|
||||
attributes.add(0, null);
|
||||
methods.add(0, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void populate(Class<T> cls, Attribute<?,?> attr) {
|
||||
columnNames.add(attr.getName());
|
||||
columnClasses.add(wrap(attr.getJavaType()));
|
||||
methods.add(getMethod(cls, attr.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the attribute at a given column index.
|
||||
* Can be null for 0-th index if row count is being shown.
|
||||
*/
|
||||
public Attribute<?,?> getAttribute(int columnIndex) {
|
||||
return attributes.get(columnIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity represented in the given row.
|
||||
*/
|
||||
public T getRow(int row) {
|
||||
return data.get(row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
return columnClasses.get(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnNames.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
return columnNames.get(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
Method method = methods.get(columnIndex);
|
||||
if (method == null) {
|
||||
return columnIndex == 0 && showsRowCount ? rowIndex+1 : "?";
|
||||
}
|
||||
Object row = data.get(rowIndex);
|
||||
Object val = getValueByReflection(rowIndex, row, columnIndex, method);
|
||||
return val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object value, int rowIndex, int columnIndex) {
|
||||
// should never be called
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value by reflection.
|
||||
* @param o
|
||||
* @param p
|
||||
* @return
|
||||
*/
|
||||
Object getValueByReflection(int rowIndex, Object o, int columnIndex, Method m) {
|
||||
if (o == null) {
|
||||
System.err.println("Row " + rowIndex + " is null");
|
||||
return null;
|
||||
}
|
||||
if (m == null) {
|
||||
System.err.println("Column " + columnIndex + ":" + getColumnName(columnIndex) + " method is null");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return m.invoke(o, EMPTY_ARGS);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Can not extract value at [" + rowIndex + "," + columnIndex + "] due to " + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> wrap(Class<?> c) {
|
||||
if (c == null || c.isInterface() || c.isArray())
|
||||
return Object.class;
|
||||
if (c.isPrimitive()) {
|
||||
if (c == boolean.class) return Boolean.class;
|
||||
if (c == byte.class) return Byte.class;
|
||||
if (c == char.class) return Character.class;
|
||||
if (c == double.class) return Double.class;
|
||||
if (c == float.class) return Float.class;
|
||||
if (c == int.class) return Integer.class;
|
||||
if (c == long.class) return Long.class;
|
||||
if (c == short.class) return Short.class;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private Method getMethod(Class<T> type, String p) {
|
||||
try {
|
||||
String getter = "get" + Character.toUpperCase(p.charAt(0))+p.substring(1);
|
||||
return type.getMethod(getter, EMPTY_CLASSES);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public void updateData(List<T> newData) {
|
||||
data = newData;
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return data.iterator();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.PersistenceUnitUtil;
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.swing.DefaultCellEditor;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import org.apache.openjpa.enhance.PersistenceCapable;
|
||||
import org.apache.openjpa.enhance.StateManager;
|
||||
|
||||
|
||||
/**
|
||||
* A specialized Swing table to display a list of persistent entity instances.
|
||||
* The data for the table is supplied via a specialized {@linkplain EntityDataModel data model}
|
||||
* that is supplied at construction.
|
||||
* <br>
|
||||
* The table view uses specialized cell renderer to display single-valued and multi-valued
|
||||
* association.
|
||||
*
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EntityTable<T> extends JTable {
|
||||
private InstanceCellRenderer instanceCellRenderer;
|
||||
private CollectionCellRenderer collectionCellRenderer;
|
||||
|
||||
public EntityTable(Class<T> cls, List<T> data, int styleBits, EntityManagerFactory unit) {
|
||||
super();
|
||||
instanceCellRenderer = new InstanceCellRenderer(unit.getPersistenceUnitUtil());
|
||||
collectionCellRenderer = new CollectionCellRenderer();
|
||||
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
|
||||
setModel(new EntityDataModel<T>(cls, data, unit.getMetamodel(), styleBits));
|
||||
getModel().addTableModelListener(this);
|
||||
initColumnSizes();
|
||||
setFillsViewportHeight(true);
|
||||
}
|
||||
|
||||
|
||||
public InstanceCellRenderer getInstanceRenderer() {
|
||||
return instanceCellRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the special renderer for single- and multi-valued association.
|
||||
* Otherwise uses the super classes' renderer defined by the field type.
|
||||
*/
|
||||
public TableCellRenderer getCellRenderer(int row, int column) {
|
||||
Attribute<?,?> attr = ((EntityDataModel)getModel()).getAttribute(column);
|
||||
TableCellRenderer renderer = null;
|
||||
if (attr == null) {
|
||||
renderer = super.getCellRenderer(row, column);
|
||||
} else if (attr.isAssociation()) {
|
||||
renderer = instanceCellRenderer;
|
||||
} else if (attr.isCollection()) {
|
||||
renderer = collectionCellRenderer;
|
||||
} else {
|
||||
renderer = super.getCellRenderer(row, column);
|
||||
}
|
||||
if (renderer instanceof DefaultTableCellRenderer) {
|
||||
((DefaultTableCellRenderer)renderer).setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public TableCellEditor getCellEditor(int row, int column) {
|
||||
Attribute<?,?> attr = ((EntityDataModel)getModel()).getAttribute(column);
|
||||
if (attr == null)
|
||||
return super.getCellEditor(row, column);
|
||||
if (attr.isCollection())
|
||||
return new DefaultCellEditor((JComboBox)getCellRenderer(row, column));
|
||||
return super.getCellEditor(row, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks good column sizes.
|
||||
* If all column heads are wider than the column's cells'
|
||||
* contents, then you can just use column.sizeWidthToFit().
|
||||
*/
|
||||
private void initColumnSizes() {
|
||||
TableModel model = getModel();
|
||||
TableColumn column = null;
|
||||
Component comp = null;
|
||||
int headerWidth = 0;
|
||||
|
||||
TableCellRenderer headerRenderer = getTableHeader().getDefaultRenderer();
|
||||
|
||||
for (int columnIndex = 0; columnIndex < model.getColumnCount(); columnIndex++) {
|
||||
int cellWidth = 0;
|
||||
column = getColumnModel().getColumn(columnIndex);
|
||||
|
||||
comp = headerRenderer.getTableCellRendererComponent(
|
||||
null, column.getHeaderValue(),
|
||||
false, false, 0, 0);
|
||||
headerWidth = comp.getPreferredSize().width;
|
||||
TableCellRenderer renderer = getCellRenderer(0, columnIndex);
|
||||
int rowCount = Math.min(model.getRowCount(), 10);
|
||||
for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
||||
Object value = model.getValueAt(rowIndex, columnIndex);
|
||||
comp = renderer.getTableCellRendererComponent(
|
||||
this, value,
|
||||
false, false, rowIndex, columnIndex);
|
||||
cellWidth = Math.max(comp.getPreferredSize().width, cellWidth);
|
||||
}
|
||||
column.setPreferredWidth(Math.max(headerWidth, cellWidth));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the value of a persistent entity in a table column as the persistent identifier.
|
||||
* The persistent identifier is extracted by the new {@link PersistenceUnitUtil utility} feature
|
||||
* of JPA 2.0 API.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class InstanceCellRenderer extends DefaultTableCellRenderer {
|
||||
private final PersistenceUnitUtil util;
|
||||
|
||||
public InstanceCellRenderer(PersistenceUnitUtil util) {
|
||||
super();
|
||||
this.util = util;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stringified persistence identifier of the given instance.
|
||||
*
|
||||
*/
|
||||
public String renderAsString(Object instance) {
|
||||
if (instance == null) {
|
||||
return "null";
|
||||
}
|
||||
Object id = util.getIdentifier(instance);
|
||||
if (id != null)
|
||||
return id.toString();
|
||||
if (instance instanceof PersistenceCapable) {
|
||||
PersistenceCapable pc = (PersistenceCapable)instance;
|
||||
StateManager sm = pc.pcGetStateManager();
|
||||
id = sm == null ? null : sm.fetchObjectId();
|
||||
}
|
||||
return id == null ? "null" : id.toString();
|
||||
}
|
||||
|
||||
public void setValue(Object instance) {
|
||||
setForeground(Color.BLUE);
|
||||
setText(renderAsString(instance));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders a many-valued attribute as simply three dots.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class CollectionCellRenderer extends DefaultTableCellRenderer {
|
||||
public CollectionCellRenderer() {
|
||||
setPreferredSize(new Dimension(10,20));
|
||||
}
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value,
|
||||
boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
return super.getTableCellRendererComponent(table, value == null ? null : "...",
|
||||
isSelected, hasFocus, row, column);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
import javax.persistence.metamodel.ManagedType;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import jpa.tools.swing.EntityTable.InstanceCellRenderer;
|
||||
|
||||
|
||||
/**
|
||||
* An entity table view consists of a JTable and optionally another table for many-valued associations.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EntityTableView<T> extends JPanel implements ListSelectionListener {
|
||||
private EntityTable<T> _table;
|
||||
private JTextArea _details;
|
||||
|
||||
public EntityTableView(Class<T> cls, int styleBits, EntityManagerFactory unit) {
|
||||
this(cls, (List<T>)Collections.EMPTY_LIST, styleBits, unit);
|
||||
}
|
||||
|
||||
public EntityTableView(Class<T> cls, List<T> data, int styleBits, EntityManagerFactory unit) {
|
||||
super(true);
|
||||
_table = new EntityTable<T>(cls, data, styleBits, unit);
|
||||
_table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
|
||||
if ((styleBits & EntityDataModel.PLURAL_ATTR) != 0) {
|
||||
_table.getSelectionModel().addListSelectionListener(this);
|
||||
_details = new JTextArea("Click many-valued columns for display");
|
||||
_details.setForeground(Color.LIGHT_GRAY);
|
||||
_details.setBorder(BorderFactory.createTitledBorder("Many-valued association"));
|
||||
_details.setEditable(false);
|
||||
}
|
||||
setBorder(BorderFactory.createTitledBorder(_table.getModel().getRowCount() + " " +
|
||||
unit.getMetamodel().entity(cls).getName()));
|
||||
|
||||
setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
|
||||
add(new JScrollPane(_table,
|
||||
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
|
||||
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS));
|
||||
if (_details != null)
|
||||
add(_details);
|
||||
}
|
||||
|
||||
public EntityTable<T> getTable() {
|
||||
return _table;
|
||||
}
|
||||
|
||||
public EntityDataModel<T> getDataModel() {
|
||||
return (EntityDataModel<T>)_table.getModel();
|
||||
}
|
||||
|
||||
public void updateTitle(String txt) {
|
||||
Border border = getBorder();
|
||||
if (border instanceof TitledBorder) {
|
||||
((TitledBorder)border).setTitle(txt);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Notified when a cell in the table is selected.
|
||||
* If the selected cell is a many-valued attribute then displays its details in the details pane.
|
||||
*/
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
int row = _table.getSelectedRow();
|
||||
if (row == -1)
|
||||
return;
|
||||
int col = _table.getSelectedColumn();
|
||||
if (col == -1)
|
||||
return;
|
||||
EntityDataModel<?> model = (EntityDataModel<?>)_table.getModel();
|
||||
Attribute<?,?> attr = model.getAttribute(col);
|
||||
if (attr.isCollection()) {
|
||||
Object val = model.getValueAt(row, col);
|
||||
showDetails(attr, val);
|
||||
}
|
||||
}
|
||||
|
||||
private void showDetails(Attribute<?,?> attr, Object val) {
|
||||
_details.setText(null);
|
||||
ManagedType<?> owner = attr.getDeclaringType();
|
||||
String title = (owner instanceof EntityType)
|
||||
? ((EntityType<?>)owner).getName() + "." + attr.getName()
|
||||
: owner.getJavaType().getSimpleName() + "." + attr.getName();
|
||||
TitledBorder border = (TitledBorder)_details.getBorder();
|
||||
|
||||
if (val == null) {
|
||||
border.setTitle(title + " (null)");
|
||||
} else {
|
||||
Collection<?> coll = (Collection<?>)val;
|
||||
if (attr.getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION) {
|
||||
border.setTitle(title + " (" + coll.size() + " values)");
|
||||
_details.setForeground(Color.BLACK);
|
||||
for (Object e : coll) {
|
||||
_details.append(_table.getInstanceRenderer().renderAsString(e) + "\r\n");
|
||||
_details.append(e + "\r\n");
|
||||
}
|
||||
} else {
|
||||
border.setTitle(title + " (" + coll.size() + " instances)");
|
||||
_details.setForeground(Color.BLUE);
|
||||
InstanceCellRenderer renderer = _table.getInstanceRenderer();
|
||||
for (Object e : coll) {
|
||||
_details.append(renderer.renderAsString(e) + "\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Point;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
/**
|
||||
* View of a persistent entity type as a JPanel.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EntityTypeView<T> extends JPanel {
|
||||
final EntityType<T> type;
|
||||
final int hgap = 4;
|
||||
final int vgap = 4;
|
||||
|
||||
public EntityTypeView(EntityType<T> type) {
|
||||
this.type = type;
|
||||
GridLayout layout = new GridLayout(0,1);
|
||||
setLayout(layout);
|
||||
layout.setVgap(vgap);
|
||||
layout.setHgap(hgap);
|
||||
|
||||
setBackground(Color.WHITE);
|
||||
setBorder(BorderFactory.createTitledBorder(MetamodelHelper.getDisplayName(type)));
|
||||
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
List<Attribute<? super T,?>> attrs = MetamodelHelper.getAttributes(type);
|
||||
for (Attribute<? super T,?> attr : attrs) {
|
||||
JComponent c = new AttributeView(attr);
|
||||
add(c);
|
||||
w = Math.max(w, c.getPreferredSize().width);
|
||||
h += c.getPreferredSize().height + 2*vgap;
|
||||
}
|
||||
// setPreferredSize(new Dimension(w,h));
|
||||
}
|
||||
|
||||
public EntityType<T> getEntityType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the top left position of the attribute label relative to this entity view.
|
||||
* @param attr
|
||||
* @return
|
||||
*/
|
||||
public Point getPosition(Attribute<?,?> attr) {
|
||||
for (Component c : getComponents()) {
|
||||
if (c instanceof AttributeView) {
|
||||
if (((AttributeView)c).getAttribute().equals(attr)) {
|
||||
return new Point(c.getLocation().x, c.getLocation().y + c.getHeight()/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Point(0,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
import javax.swing.text.StyleContext;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ErrorDialog extends JDialog {
|
||||
private static List<String> filters = Arrays.asList("java.awt.", "javax.swing.", "sun.reflect.");
|
||||
private static AttributeSet red, black;
|
||||
static {
|
||||
StyleContext ctx = StyleContext.getDefaultStyleContext();
|
||||
red = ctx.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.RED);
|
||||
red = ctx.addAttribute(red, StyleConstants.Bold, true);
|
||||
red = ctx.addAttribute(red, StyleConstants.FontSize, 12);
|
||||
red = ctx.addAttribute(red, StyleConstants.FontFamily, "Courier");
|
||||
black = ctx.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.BLACK);
|
||||
black = ctx.addAttribute(black, StyleConstants.Bold, false);
|
||||
black = ctx.addAttribute(black, StyleConstants.FontFamily, "Courier");
|
||||
}
|
||||
|
||||
public ErrorDialog(JComponent owner, Throwable t) {
|
||||
super();
|
||||
setModal(true);
|
||||
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||
// Icon icon = Images.ERROR;
|
||||
// setIconImage(((ImageIcon)icon).getImage());
|
||||
addException(t);
|
||||
pack();
|
||||
SwingHelper.position(this, owner);
|
||||
}
|
||||
|
||||
public ErrorDialog(Throwable t) {
|
||||
this(null, t);
|
||||
}
|
||||
|
||||
void addException(Throwable t) {
|
||||
setTitle("Error");
|
||||
String txt = t.getClass().getName() + ":" + t.getLocalizedMessage();
|
||||
JTextArea message = new JTextArea(txt);
|
||||
message.setLineWrap(true);
|
||||
message.setWrapStyleWord(true);
|
||||
message.setForeground(Color.RED);
|
||||
message.setText(txt);
|
||||
message.setEditable(false);
|
||||
|
||||
JTextPane window = new JTextPane();
|
||||
printStackTrace(t, window);
|
||||
window.setEditable(false);
|
||||
JScrollPane pane = new JScrollPane(window,
|
||||
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
|
||||
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
pane.setPreferredSize(new Dimension(400, 200));
|
||||
pane.setBorder(BorderFactory.createTitledBorder("Stacktrace"));
|
||||
pane.setPreferredSize(new Dimension(400, 200));
|
||||
JPanel main = new JPanel();
|
||||
main.setLayout(new BorderLayout());
|
||||
main.add(message, BorderLayout.NORTH);
|
||||
main.add(pane, BorderLayout.CENTER);
|
||||
JPanel buttonPanel = new JPanel();
|
||||
JButton ok = new JButton("OK");
|
||||
ok.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
ErrorDialog.this.dispose();
|
||||
}
|
||||
});
|
||||
buttonPanel.add(ok);
|
||||
main.add(buttonPanel, BorderLayout.SOUTH);
|
||||
getContentPane().add(main);
|
||||
}
|
||||
static String NEWLINE = "\r\n";
|
||||
StringBuilder printStackTrace(Throwable t, JTextPane text) {
|
||||
String message = t.getClass().getName() + ": " + t.getMessage() + NEWLINE;
|
||||
text.setCaretPosition(text.getDocument().getLength());
|
||||
text.setCharacterAttributes(red, false);
|
||||
text.replaceSelection(message);
|
||||
StackTraceElement[] traces = t.getStackTrace();
|
||||
text.setCharacterAttributes(black, false);
|
||||
for (StackTraceElement e : traces) {
|
||||
if (!isFiltered(e.getClassName())) {
|
||||
String str = " " + e.toString() + NEWLINE;
|
||||
text.setCaretPosition(text.getDocument().getLength());
|
||||
text.replaceSelection(str);
|
||||
}
|
||||
}
|
||||
|
||||
Throwable cause = t.getCause();
|
||||
if (cause !=null && cause != t) {
|
||||
printStackTrace(cause, text);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder toString(StackTraceElement[] traces) {
|
||||
StringBuilder error = new StringBuilder();
|
||||
for (StackTraceElement e : traces) {
|
||||
if (!isFiltered(e.getClassName())) {
|
||||
String str = e.toString();
|
||||
error.append(str).append("\r\n");
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
private boolean isFiltered(String className) {
|
||||
for (String s : filters) {
|
||||
if (className.startsWith(s))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
new ErrorDialog(new IllegalArgumentException(
|
||||
"This is test error with very long line of error message that should not be in a single line"))
|
||||
.setVisible(true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package jpa.tools.swing;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FileScanner {
|
||||
private String ext;
|
||||
boolean recursive;
|
||||
|
||||
public FileScanner(String ext, boolean recurse) {
|
||||
this.ext = ext;
|
||||
this.recursive = recurse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the given
|
||||
* @param root
|
||||
* @return
|
||||
*/
|
||||
public List<File> scan(File dir) {
|
||||
List<File> bag = new ArrayList<File>();
|
||||
scan(dir, bag);
|
||||
return bag;
|
||||
}
|
||||
|
||||
private void scan(File dir, List<File> bag) {
|
||||
if (dir == null || !dir.exists() || !dir.isDirectory())
|
||||
return;
|
||||
File[] all = dir.listFiles();
|
||||
for (File f : all) {
|
||||
if (ext == null || f.getName().endsWith(ext)) {
|
||||
bag.add(f);
|
||||
}
|
||||
if (recursive && f.isDirectory()) {
|
||||
scan(f, bag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package jpa.tools.swing;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* Searches for a given class in the set of directories recursively.
|
||||
* If a given directory contains jar file then searches inside the jar.
|
||||
*
|
||||
* Usage
|
||||
* $ java find.Finder class dir1 dir2...
|
||||
* where
|
||||
* class is fully qualified class name
|
||||
* dir name of a file system directory
|
||||
*
|
||||
* Example
|
||||
* $ java find.Finder org.eclipse.ui.plugin.AbstractUIPlugin c:\eclipse\plugins
|
||||
* will print
|
||||
* org.eclipse.ui.plugin.AbstractUIPlugin found in c:\eclipse\plugins\org.eclipse.ui.workbench_3.4.1.M20080827-0800a.jar
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class Finder {
|
||||
private static final String CLASS_SUFFIX = ".class";
|
||||
private static final String JAR_SUFFIX = ".jar";
|
||||
private static final char DOT = '.';
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static void usage() {
|
||||
System.err.println("Searches a given class in the given directories."
|
||||
+ "\r\nIf a given directory contains jar then searches within the jar."
|
||||
+ "\r\nIf a given directory contains other directories then searches "
|
||||
+ "recursively.\r\n");
|
||||
System.err.println("\r\n Usage:");
|
||||
System.err.println(" $ java find.Finder class dir [dir...]");
|
||||
System.err.println(" where");
|
||||
System.err.println(" class fully-qualified class name");
|
||||
System.err.println(" dir name of a directory");
|
||||
System.err.println("\r\n Example:");
|
||||
System.err.println(" $ java find.Finder java.lang.Object c:\\java");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length < 2) {
|
||||
usage();
|
||||
System.exit(0);
|
||||
}
|
||||
Finder finder = new Finder();
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
File root = new File(args[i]);
|
||||
finder.scan(root, args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void scan(File dir, String cls) throws IOException {
|
||||
File[] classes = dir.listFiles(new FileFilter() {
|
||||
public boolean accept(File path) {
|
||||
return path.getName().endsWith(CLASS_SUFFIX);
|
||||
}
|
||||
});
|
||||
String clsName = cls+CLASS_SUFFIX;
|
||||
for (File c : classes) {
|
||||
String name = c.getName().replace(File.separatorChar, DOT);
|
||||
if (name.endsWith(clsName))
|
||||
System.err.println(cls + " found in " + c.getAbsolutePath());
|
||||
}
|
||||
|
||||
File[] jars = dir.listFiles(new FileFilter() {
|
||||
public boolean accept(File path) {
|
||||
return path.getName().endsWith(JAR_SUFFIX);
|
||||
}
|
||||
});
|
||||
|
||||
for (File jar : jars) {
|
||||
JarFile jarFile = new JarFile(jar);
|
||||
scan(jarFile, cls);
|
||||
}
|
||||
|
||||
File[] dirs = dir.listFiles(new FileFilter() {
|
||||
public boolean accept(File path) {
|
||||
return path.isDirectory();
|
||||
}
|
||||
});
|
||||
for (File cdir : dirs)
|
||||
scan(cdir, cls);
|
||||
}
|
||||
|
||||
private void scan(JarFile jar, String cls) {
|
||||
String clsName = cls.replace('.', '/') + CLASS_SUFFIX;
|
||||
debug("Scanning " + jar.getName() + " for [" + clsName + "]");
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String entryName = entry.getName();
|
||||
if (entryName.endsWith(CLASS_SUFFIX))
|
||||
debug(" " + entryName);
|
||||
if (entryName.equals(clsName))
|
||||
System.err.println(cls + " found in " + jar.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void debug(String s) {
|
||||
if (DEBUG)
|
||||
System.err.println(s);
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.StyleContext;
|
||||
|
||||
/**
|
||||
* An output stream that uses a {@link ScrollingTextPane} as its sink.
|
||||
* Flushes the buffer at line end.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class GraphicOutputStream extends OutputStream {
|
||||
ScrollingTextPane _sink;
|
||||
private char[] buffer = new char[1024];
|
||||
private int count;
|
||||
private Map<String, AttributeSet> _styles = new HashMap<String, AttributeSet>();
|
||||
private static AttributeSet _defaultStyle = StyleContext.getDefaultStyleContext()
|
||||
.getStyle(StyleContext.DEFAULT_STYLE);
|
||||
|
||||
public GraphicOutputStream(ScrollingTextPane delegate) {
|
||||
_sink = delegate;
|
||||
}
|
||||
|
||||
public void registerStyle(String pattern, AttributeSet style) {
|
||||
_styles.put(pattern, style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
buffer[count++] = (char)b;
|
||||
if (count > buffer.length || b == '\r' || b == '\n') {
|
||||
flushBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
private void flushBuffer() {
|
||||
String txt = new String(buffer, 0, count);
|
||||
count = 0;
|
||||
AttributeSet style = getStyle(txt);
|
||||
_sink.setCharacterAttributes(style, true);
|
||||
_sink.appendText(txt);
|
||||
}
|
||||
|
||||
AttributeSet getStyle(String txt) {
|
||||
if (txt == null || txt.isEmpty())
|
||||
return _defaultStyle;
|
||||
for (String pattern : _styles.keySet()) {
|
||||
if (txt.startsWith(pattern))
|
||||
return _styles.get(pattern);
|
||||
}
|
||||
return _defaultStyle;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.TexturePaint;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import javax.swing.border.AbstractBorder;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ImageBorder extends AbstractBorder {
|
||||
|
||||
Image image;
|
||||
String title;
|
||||
|
||||
// Image topCenterImage, topLeftImage, topRight;
|
||||
//
|
||||
// Image leftCenterImage, rightCenterImage;
|
||||
//
|
||||
// Image bottomCenterImage, bottomLeftImage, bottomRightImage;
|
||||
|
||||
Insets insets;
|
||||
|
||||
public ImageBorder(Image img, String title) {
|
||||
image = img;
|
||||
this.title = title;
|
||||
}
|
||||
// public ImageBorder(Image top_left, Image top_center, Image top_right, Image left_center,
|
||||
// Image right_center, Image bottom_left, Image bottom_center, Image bottom_right) {
|
||||
//
|
||||
// this.topLeftImage = top_left;
|
||||
// this.topCenterImage = top_center;
|
||||
// this.topRight = top_right;
|
||||
// this.leftCenterImage = left_center;
|
||||
// this.rightCenterImage = right_center;
|
||||
// this.bottomLeftImage = bottom_left;
|
||||
// this.bottomCenterImage = bottom_center;
|
||||
// this.bottomRightImage = bottom_right;
|
||||
// }
|
||||
|
||||
public void setInsets(Insets insets) {
|
||||
this.insets = insets;
|
||||
}
|
||||
|
||||
public Insets getBorderInsets(Component c) {
|
||||
if (insets != null) {
|
||||
return insets;
|
||||
} else {
|
||||
// return new Insets(topCenterImage.getHeight(null), leftCenterImage.getWidth(null), bottomCenterImage
|
||||
// .getHeight(null), rightCenterImage.getWidth(null));
|
||||
return new Insets(image.getHeight(null), 100,10,100);
|
||||
}
|
||||
}
|
||||
|
||||
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
|
||||
g.setColor(Color.white);
|
||||
g.fillRect(x, y, width, height);
|
||||
g.setColor(Color.black);
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
int tlw = image.getWidth(null);
|
||||
int tlh = image.getHeight(null);
|
||||
fillTexture(g2, image, x, y, tlw, tlh);
|
||||
g2.drawString(title, tlw, 10);
|
||||
|
||||
// int tlw = topLeftImage.getWidth(null);
|
||||
// int tlh = topLeftImage.getHeight(null);
|
||||
// int tcw = topCenterImage.getWidth(null);
|
||||
// int tch = topCenterImage.getHeight(null);
|
||||
// int trw = topRight.getWidth(null);
|
||||
// int trh = topRight.getHeight(null);
|
||||
//
|
||||
// int lcw = leftCenterImage.getWidth(null);
|
||||
// int lch = leftCenterImage.getHeight(null);
|
||||
// int rcw = rightCenterImage.getWidth(null);
|
||||
// int rch = rightCenterImage.getHeight(null);
|
||||
//
|
||||
// int blw = bottomLeftImage.getWidth(null);
|
||||
// int blh = bottomLeftImage.getHeight(null);
|
||||
// int bcw = bottomCenterImage.getWidth(null);
|
||||
// int bch = bottomCenterImage.getHeight(null);
|
||||
// int brw = bottomRightImage.getWidth(null);
|
||||
// int brh = bottomRightImage.getHeight(null);
|
||||
|
||||
// fillTexture(g2, topLeftImage, x, y, tlw, tlh);
|
||||
// fillTexture(g2, topCenterImage, x + tlw, y, width - tlw - trw, tch);
|
||||
// fillTexture(g2, topRight, x + width - trw, y, trw, trh);
|
||||
//
|
||||
// fillTexture(g2, leftCenterImage, x, y + tlh, lcw, height - tlh - blh);
|
||||
// fillTexture(g2, rightCenterImage, x + width - rcw, y + trh, rcw, height - trh - brh);
|
||||
//
|
||||
// fillTexture(g2, bottomLeftImage, x, y + height - blh, blw, blh);
|
||||
// fillTexture(g2, bottomCenterImage, x + blw, y + height - bch, width - blw - brw, bch);
|
||||
// fillTexture(g2, bottomRightImage, x + width - brw, y + height - brh, brw, brh);
|
||||
}
|
||||
|
||||
public void fillTexture(Graphics2D g2, Image img, int x, int y, int w, int h) {
|
||||
BufferedImage buff = createBufferedImage(img);
|
||||
Rectangle anchor = new Rectangle(x, y, img.getWidth(null), img.getHeight(null));
|
||||
TexturePaint paint = new TexturePaint(buff, anchor);
|
||||
g2.setPaint(paint);
|
||||
g2.fillRect(x, y, w, h);
|
||||
}
|
||||
|
||||
public BufferedImage createBufferedImage(Image img) {
|
||||
BufferedImage buff = new BufferedImage(img.getWidth(null), img.getHeight(null),
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics gfx = buff.createGraphics();
|
||||
gfx.drawImage(img, 0, 0, null);
|
||||
gfx.dispose();
|
||||
return buff;
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.List;
|
||||
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class InstanceDataModel<T> extends AbstractTableModel {
|
||||
List<Attribute<? super T, ?>> attributes;
|
||||
private T _instance;
|
||||
|
||||
public InstanceDataModel(EntityType<T> type, T instance) {
|
||||
super();
|
||||
attributes = MetamodelHelper.getAttributes(type);
|
||||
this._instance = instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
return columnIndex == 0 ? String.class : Object.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
return columnIndex == 0 ? "Properties" : "Value";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return attributes.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
Attribute<? super T, ?> attr = attributes.get(rowIndex);
|
||||
return columnIndex == 0 ? attr.getName() : MetamodelHelper.getValue(attr, _instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object value, int rowIndex, int columnIndex) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.persistence.metamodel.EntityType;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JTable;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class InstanceView<T> extends JTable {
|
||||
public InstanceView(EntityType<T> type, T instance) {
|
||||
super(new InstanceDataModel<T>(type, instance));
|
||||
setShowHorizontalLines(false);
|
||||
setShowVerticalLines(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface Maze {
|
||||
boolean isReachable(int x, int y);
|
||||
}
|
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.CollectionAttribute;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
import javax.persistence.metamodel.MapAttribute;
|
||||
import javax.persistence.metamodel.PluralAttribute;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import javax.persistence.metamodel.Type;
|
||||
|
||||
/**
|
||||
* Static utility for analyzing persistent metadata model using JPA 2.0.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class MetamodelHelper {
|
||||
public static final int ATTR_ID = 0;
|
||||
public static final int ATTR_VERSION = 1;
|
||||
public static final int ATTR_BASIC = 2;
|
||||
public static final int ATTR_SINGULAR_RELATION = 3;
|
||||
public static final int ATTR_PLURAL_RELATION = 4;
|
||||
|
||||
public static final Color MIDNIGHT_BLUE = new Color(25,25,112);
|
||||
public static final Color DARK_GREEN = new Color(0,100,0);
|
||||
public static final Color KHAKI = new Color(240, 230, 140);
|
||||
|
||||
public static String getDisplayName(Type<?> type) {
|
||||
if (type instanceof EntityType) {
|
||||
return getDisplayName((EntityType<?>)type);
|
||||
}
|
||||
return getDisplayName(type.getJavaType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the displayed name of a given entity type.
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static String getDisplayName(EntityType<?> type) {
|
||||
return type.getName();
|
||||
}
|
||||
|
||||
public static String getDisplayName(Class<?> cls) {
|
||||
String fullName = cls.getName();
|
||||
if (fullName.startsWith("java.") || fullName.startsWith("openbook.domain.")) {
|
||||
int i = fullName.lastIndexOf('.');
|
||||
return fullName.substring(i+1);
|
||||
}
|
||||
return fullName;
|
||||
}
|
||||
|
||||
|
||||
public static String getDisplayName(Attribute<?,?> attr) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(getDisplayName(attr.getJavaType()));
|
||||
if (attr instanceof MapAttribute) {
|
||||
buffer.append("<").append(getDisplayName(((MapAttribute)attr).getKeyType())).append(",")
|
||||
.append(getDisplayName(((MapAttribute)attr).getElementType())).append(">");
|
||||
} else if (attr instanceof PluralAttribute) {
|
||||
buffer.append("<").append(getDisplayName(((PluralAttribute)attr).getElementType())).append(">");
|
||||
}
|
||||
buffer.append(" ").append(attr.getName());
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
|
||||
public static <T> List<Attribute<? super T,?>> getAttributes(EntityType<T> type) {
|
||||
List<Attribute<? super T,?>> list = new ArrayList<Attribute<? super T,?>>(type.getAttributes());
|
||||
Collections.sort(list, new AttributeComparator());
|
||||
return list;
|
||||
}
|
||||
|
||||
public static int getAttributeType(Attribute<?, ?> a) {
|
||||
if (a instanceof SingularAttribute) {
|
||||
SingularAttribute<?, ?> sa = (SingularAttribute<?, ?>)a;
|
||||
if (sa.isId()) return ATTR_ID;
|
||||
if (sa.isVersion()) return ATTR_VERSION;
|
||||
if (sa.isAssociation()) return ATTR_SINGULAR_RELATION;
|
||||
if (sa.isCollection()) return ATTR_PLURAL_RELATION;
|
||||
} else {
|
||||
if (a.isAssociation()) return ATTR_SINGULAR_RELATION;
|
||||
if (a.isCollection()) return ATTR_PLURAL_RELATION;
|
||||
}
|
||||
return ATTR_BASIC;
|
||||
|
||||
}
|
||||
|
||||
public static <T> Set<SingularAttribute<? super T, ?>> getIdAttributes(EntityType<T> type) {
|
||||
Set<SingularAttribute<? super T,?>> attrs = type.getSingularAttributes();
|
||||
Set<SingularAttribute<? super T,?>> idAttrs = new HashSet<SingularAttribute<? super T,?>>();
|
||||
for (SingularAttribute<? super T,?> attr : attrs) {
|
||||
if (attr.isId()) {
|
||||
idAttrs.add(attr);
|
||||
}
|
||||
}
|
||||
return idAttrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the derived target of the given type, if any. Otherwise null.
|
||||
*/
|
||||
public static <T> EntityType<?> derivedTarget(EntityType<T> type) {
|
||||
Set<SingularAttribute<? super T,?>> ids = getIdAttributes(type);
|
||||
for (SingularAttribute<? super T,?> id : ids) {
|
||||
EntityType<?> derived = getParentType(id);
|
||||
if (derived != null) {
|
||||
return derived;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static EntityType<?> getParentType(SingularAttribute<?,?> id) {
|
||||
if (id.getType() instanceof EntityType) {
|
||||
return (EntityType<?>)id.getType();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isId(Attribute<?,?> a) {
|
||||
if (a instanceof SingularAttribute)
|
||||
return ((SingularAttribute<?,?>)a).isId();
|
||||
return false;
|
||||
}
|
||||
public static boolean isVersion(Attribute<?,?> a) {
|
||||
if (a instanceof SingularAttribute)
|
||||
return ((SingularAttribute<?,?>)a).isVersion();
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Color getColor(Attribute<?,?> attr) {
|
||||
if (isId(attr))
|
||||
return Color.RED;
|
||||
if (isVersion(attr))
|
||||
return Color.DARK_GRAY;
|
||||
return getColor(attr.getPersistentAttributeType());
|
||||
}
|
||||
|
||||
public static Color getColor(Attribute.PersistentAttributeType type) {
|
||||
switch (type) {
|
||||
case BASIC : return Color.BLACK;
|
||||
case EMBEDDED: return Color.GRAY;
|
||||
case ONE_TO_ONE: return Color.BLUE;
|
||||
case MANY_TO_ONE: return MIDNIGHT_BLUE;
|
||||
case ONE_TO_MANY: return DARK_GREEN;
|
||||
case MANY_TO_MANY: return Color.PINK;
|
||||
case ELEMENT_COLLECTION: return KHAKI;
|
||||
default: return Color.BLACK;
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer getAttributeTypeCode(Attribute<?,?> attr) {
|
||||
if (isId(attr))
|
||||
return 0;
|
||||
if (isVersion(attr))
|
||||
return 1;
|
||||
|
||||
switch (attr.getPersistentAttributeType()) {
|
||||
case BASIC :
|
||||
case EMBEDDED:
|
||||
return 2;
|
||||
case ONE_TO_ONE:
|
||||
case MANY_TO_ONE:
|
||||
return 3;
|
||||
case ONE_TO_MANY:
|
||||
case MANY_TO_MANY:
|
||||
case ELEMENT_COLLECTION: return 4;
|
||||
default: return 5;
|
||||
}
|
||||
}
|
||||
private static Map<Attribute<?,?>, Method> members = new HashMap<Attribute<?,?>, Method>();
|
||||
private static Object[] EMPTY_ARGS = null;
|
||||
private static Class<?>[] EMPTY_CLASSES = null;
|
||||
/**
|
||||
* Gets the value of the given persistent attribute for the given instance.
|
||||
* @param attr
|
||||
* @param instance
|
||||
* @return
|
||||
*/
|
||||
public static Object getValue(Attribute<?,?> attr, Object instance) {
|
||||
Member member = attr.getJavaMember();
|
||||
Method getter = null;
|
||||
if (member instanceof Method) {
|
||||
getter = (Method)member;
|
||||
} else if (members.containsKey(attr)) {
|
||||
getter = members.get(attr);
|
||||
} else {
|
||||
getter = getMethod(attr.getDeclaringType().getJavaType(), attr.getName());
|
||||
members.put(attr, getter);
|
||||
}
|
||||
if (getter == null)
|
||||
return null;
|
||||
try {
|
||||
return getter.invoke(instance, EMPTY_ARGS);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Method getMethod(Class<?> type, String p) {
|
||||
try {
|
||||
String getter = "get" + Character.toUpperCase(p.charAt(0))+p.substring(1);
|
||||
return type.getMethod(getter, EMPTY_CLASSES);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares EntityType by their dependency of derived targets.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public static class EntityComparator implements Comparator<EntityType<?>> {
|
||||
@Override
|
||||
public int compare(EntityType<?> o1, EntityType<?> o2) {
|
||||
if (derivedTarget(o1) == o2)
|
||||
return 1;
|
||||
if (derivedTarget(o2) == o1)
|
||||
return -1;
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares attribute by their qualification.
|
||||
* Identity
|
||||
* Version
|
||||
* Basic
|
||||
* Singular association
|
||||
* Plural association
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public static class AttributeComparator implements Comparator<Attribute<?,?>> {
|
||||
@Override
|
||||
public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
|
||||
Integer t1 = getAttributeTypeCode(a1);
|
||||
Integer t2 = getAttributeTypeCode(a2);
|
||||
if (t1.equals(t2)) {
|
||||
return a1.getName().compareTo(a2.getName());
|
||||
} else {
|
||||
return t1.compareTo(t2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.Component;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
|
||||
/**
|
||||
* Graphical View of a JPA 2.0 Metamodel.
|
||||
* The view is isomorphic to the {@link Metamodel meta-model} defined by JPA 2.0 specification.
|
||||
* Hence the view is organized in terms of corresponding views of {@link EntityTypeView entity}
|
||||
* and their {@link AttributeView attributes}.
|
||||
* <br>
|
||||
* This view also draws linkage with A*-algorithm between the derived primary key attributes that
|
||||
* reference other entity types.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MetamodelView extends JPanel implements Maze {
|
||||
|
||||
private static final int GRID = 8;
|
||||
int hgap = 40;
|
||||
int vgap = 40;
|
||||
|
||||
/**
|
||||
* Creates a panel where each {@link EntityTypeView} is placed in a FlowLayout.
|
||||
* Only the entities of the model are ordered based on their primary key
|
||||
* dependencies, if any.
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public MetamodelView(Metamodel model) {
|
||||
super(true);
|
||||
FlowLayout layout = (FlowLayout)getLayout();
|
||||
layout.setHgap(80);
|
||||
layout.setVgap(80);
|
||||
// getInsets(new Insets(100,100,100,100));
|
||||
Set<EntityType<?>> types = model.getEntities();
|
||||
List<EntityType<?>> sorted = new ArrayList<EntityType<?>>(types);
|
||||
Collections.sort(sorted, new MetamodelHelper.EntityComparator());
|
||||
for (EntityType type : sorted) {
|
||||
EntityTypeView view = new EntityTypeView(type);
|
||||
add(view);
|
||||
}
|
||||
}
|
||||
|
||||
EntityTypeView<?> findViewByType(EntityType<?> type) {
|
||||
if (type == null)
|
||||
return null;
|
||||
for (Component c : getComponents()) {
|
||||
if (c instanceof EntityTypeView) {
|
||||
EntityTypeView<?> view = (EntityTypeView<?>)c;
|
||||
if (view.getEntityType().equals(type)) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
super.paint(g);
|
||||
for (Component c : getComponents()) {
|
||||
if (c instanceof EntityTypeView == false) {
|
||||
continue;
|
||||
}
|
||||
EntityTypeView<?> view = (EntityTypeView<?>)c;
|
||||
for (SingularAttribute<?,?> id : MetamodelHelper.getIdAttributes(view.getEntityType())) {
|
||||
EntityTypeView<?> target = findViewByType(MetamodelHelper.getParentType(id));
|
||||
if (target == null)
|
||||
continue;
|
||||
|
||||
PathFinder runner = new PathFinder(this);
|
||||
Point start = getConnectLocation(view, id);
|
||||
Point finish = target.getLocation();
|
||||
List<Point> path = runner.findPath(start.x/GRID, start.y/GRID,
|
||||
finish.x/GRID, finish.y/GRID);
|
||||
if (path.isEmpty())
|
||||
continue;
|
||||
Point p1 = path.get(0);
|
||||
Point p2 = null;
|
||||
for (int i = 1; i < path.size(); i++) {
|
||||
p2 = path.get(i);
|
||||
g.drawLine(p1.x*GRID, p1.y*GRID, p2.x*GRID, p2.y*GRID);
|
||||
p1 = p2;
|
||||
}
|
||||
g.setColor(Color.BLACK);
|
||||
int r = 4;
|
||||
g.fillOval(p1.x*GRID -r, p1.y*GRID - r, 2*r, 2*r);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the attribute in the entity view relative to this panel.
|
||||
* @param a
|
||||
* @param attr
|
||||
* @return
|
||||
*/
|
||||
Point getConnectLocation(EntityTypeView<?> a, Attribute<?,?> attr) {
|
||||
Point p1 = a.getLocation();
|
||||
Point p2 = a.getPosition(attr);
|
||||
return new Point(p1.x + p2.x, p1.y + p2.y);
|
||||
}
|
||||
|
||||
// contract for the maze
|
||||
@Override
|
||||
public boolean isReachable(int x, int y) {
|
||||
for (Component view : getComponents()) {
|
||||
Rectangle r = view.getBounds();
|
||||
int px = x*GRID;
|
||||
int py = y*GRID;
|
||||
if (r.contains(px, py))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -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 + ")";
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Swing widgets to browse persistent entities using JPA 2.0 MetaModel API.
|
||||
</body>
|
||||
</html>
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,634 @@
|
||||
/*
|
||||
* 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.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.event.TreeSelectionEvent;
|
||||
import javax.swing.event.TreeSelectionListener;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
|
||||
import jpa.tools.swing.AttributeLegendView;
|
||||
import jpa.tools.swing.ConfigurationViewer;
|
||||
import jpa.tools.swing.ErrorDialog;
|
||||
import jpa.tools.swing.GraphicOutputStream;
|
||||
import jpa.tools.swing.MetamodelView;
|
||||
import jpa.tools.swing.PowerPointViewer;
|
||||
import jpa.tools.swing.PreparedQueryViewer;
|
||||
import jpa.tools.swing.ScrollingTextPane;
|
||||
import jpa.tools.swing.SourceCodeViewer;
|
||||
import jpa.tools.swing.StatusBar;
|
||||
import jpa.tools.swing.SwingHelper;
|
||||
import openbook.domain.Customer;
|
||||
import openbook.server.OpenBookService;
|
||||
import openbook.server.ServiceFactory;
|
||||
import openbook.util.PropertyHelper;
|
||||
|
||||
import org.apache.openjpa.conf.OpenJPAVersion;
|
||||
import org.apache.openjpa.persistence.OpenJPAPersistence;
|
||||
|
||||
/**
|
||||
* A graphical user interface based client of OpenBooks for demonstration.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class Demo extends JFrame implements Thread.UncaughtExceptionHandler {
|
||||
private static final Dimension TAB_VIEW = new Dimension(1400,800);
|
||||
private static final Dimension OUT_VIEW = new Dimension(1400,200);
|
||||
private static final Dimension NAV_VIEW = new Dimension(400,1000);
|
||||
|
||||
/**
|
||||
* The actions invoked by this sample demonstration.
|
||||
*/
|
||||
private Action _root;
|
||||
private Action _about;
|
||||
private Action _buyBook;
|
||||
private Action _deliver;
|
||||
private Action _supply;
|
||||
private Action _viewConfig;
|
||||
private Action _viewDomain;
|
||||
private Action _viewData;
|
||||
private Action _viewSource;
|
||||
private Action _viewQuery;
|
||||
|
||||
/**
|
||||
* The primary graphic widgets used to invoke and display the results of the actions.
|
||||
*/
|
||||
private JToolBar _toolBar;
|
||||
private JTree _navigator;
|
||||
private JTabbedPane _tabbedPane;
|
||||
private JTabbedPane _outputPane;
|
||||
private StatusBar _statusBar;
|
||||
private ScrollingTextPane _sqlLog;
|
||||
public static final Icon LOGO = Images.getIcon("images/OpenBooks.jpg");
|
||||
|
||||
private boolean _debug = Boolean.getBoolean("openbook.debug");
|
||||
|
||||
/**
|
||||
* The handle to the service.
|
||||
*/
|
||||
private OpenBookService _service;
|
||||
private Customer _customer;
|
||||
private Map<String, Object> _config;
|
||||
|
||||
/**
|
||||
* Runs the demo.
|
||||
* @param args
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
SwingHelper.setLookAndFeel(14);
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
Demo demo = new Demo();
|
||||
demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
demo.pack();
|
||||
SwingHelper.position(demo, null);
|
||||
demo.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private Demo() {
|
||||
Thread.currentThread().setUncaughtExceptionHandler(this);
|
||||
_config = PropertyHelper.load(System.getProperty("openbook.client.config", "demo.properties"));
|
||||
|
||||
setTitle("OpenBooks: A Sample JPA 2.0 Application");
|
||||
|
||||
_root = new WelcomeAction("OpenBooks", "images/OpenBooks.jpg", "OpenBooks");
|
||||
_about = new AboutAction("About OpenBooks", "images/OpenBooks.jpg", "About OpenBooks");
|
||||
_buyBook = new BuyBookAction("Buy", "images/Add2Cart.jpg", "Browse and Buy Books");
|
||||
_deliver = new DeliveryAction("Deliver", "images/Deliver.jpg", "Deliver Pending Orders");
|
||||
_supply = new SupplyAction("Supply", "images/Supply.jpg", "Supply Books");
|
||||
_viewConfig = new ViewConfigAction("Configuration", "images/browse.png", "View Configuration");
|
||||
_viewDomain = new ViewDomainAction("Domain", "images/DomainModel.jpg", "View Domain Model");
|
||||
_viewData = new ViewDataAction("Data", "images/DataModel.jpg", "View Instances");
|
||||
_viewSource = new ViewSourceAction("Source", "images/SourceCode.jpg", "View Source Code");
|
||||
_viewQuery = new ViewQueryCacheAction("Query", "images/DataModel.jpg", "View Queries");
|
||||
|
||||
_toolBar = createToolBar();
|
||||
_navigator = createNavigator();
|
||||
_tabbedPane = createTabbedView();
|
||||
_outputPane = createOutputView();
|
||||
_statusBar = createStatusBar();
|
||||
|
||||
JSplitPane horizontalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
||||
horizontalSplitPane.setContinuousLayout(true);
|
||||
horizontalSplitPane.setDividerSize(1);
|
||||
JScrollPane scrollPane = new JScrollPane(_navigator);
|
||||
scrollPane.setMinimumSize(new Dimension(NAV_VIEW.width/4, NAV_VIEW.height));
|
||||
scrollPane.setPreferredSize(NAV_VIEW);
|
||||
horizontalSplitPane.add(scrollPane);
|
||||
|
||||
JSplitPane verticalSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
||||
verticalSplitPane.setContinuousLayout(true);
|
||||
verticalSplitPane.setDividerSize(1);
|
||||
verticalSplitPane.add(_tabbedPane);
|
||||
verticalSplitPane.add(_outputPane);
|
||||
horizontalSplitPane.add(verticalSplitPane);
|
||||
|
||||
Container content = getContentPane();
|
||||
content.add(_toolBar, BorderLayout.PAGE_START);
|
||||
content.add(horizontalSplitPane, BorderLayout.CENTER);
|
||||
content.add(_statusBar, BorderLayout.SOUTH);
|
||||
|
||||
_root.actionPerformed(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the handle to OpenBooks service.
|
||||
*/
|
||||
public OpenBookService getService() {
|
||||
if (_service == null) {
|
||||
final String unitName = PropertyHelper.getString(_config, "openbook.unit",
|
||||
OpenBookService.DEFAULT_UNIT_NAME);
|
||||
|
||||
SwingWorker<OpenBookService, Void> getService = new SwingWorker<OpenBookService, Void> () {
|
||||
@Override
|
||||
protected OpenBookService doInBackground() throws Exception {
|
||||
return ServiceFactory.getService(unitName);
|
||||
}
|
||||
|
||||
};
|
||||
getService.execute();
|
||||
try {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
_service = getService.get(10, TimeUnit.SECONDS);
|
||||
} catch (Exception t) {
|
||||
new ErrorDialog(t).setVisible(true);
|
||||
} finally {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
}
|
||||
return _service;
|
||||
}
|
||||
|
||||
public Customer getCustomer() {
|
||||
if (_customer == null) {
|
||||
SwingWorker<Customer, Void> task = new SwingWorker<Customer, Void> () {
|
||||
@Override
|
||||
protected Customer doInBackground() throws Exception {
|
||||
return getService().login("guest");
|
||||
}
|
||||
|
||||
};
|
||||
task.execute();
|
||||
try {
|
||||
_customer = task.get(1, TimeUnit.SECONDS);
|
||||
} catch (Exception t) {
|
||||
new ErrorDialog(t).setVisible(true);
|
||||
}
|
||||
}
|
||||
return _customer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
new ErrorDialog(e);
|
||||
} else {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private JToolBar createToolBar() {
|
||||
JToolBar toolBar = new JToolBar();
|
||||
toolBar.add(_buyBook);
|
||||
toolBar.add(_deliver);
|
||||
toolBar.add(_supply);
|
||||
Dimension d = new Dimension(40, 32);
|
||||
toolBar.addSeparator(d);
|
||||
|
||||
toolBar.add(_viewConfig);
|
||||
toolBar.add(_viewDomain);
|
||||
toolBar.add(_viewData);
|
||||
toolBar.add(_viewSource);
|
||||
toolBar.add(_viewQuery);
|
||||
|
||||
toolBar.addSeparator(d);
|
||||
|
||||
toolBar.add(Box.createHorizontalGlue());
|
||||
toolBar.add(_about);
|
||||
toolBar.add(Box.createHorizontalStrut(2));
|
||||
return toolBar;
|
||||
}
|
||||
|
||||
private StatusBar createStatusBar() {
|
||||
return new StatusBar();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Abstract root of all Action objects helps to locate/configure visual action parameters such as
|
||||
* tooltip text or image.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public abstract class OpenBookAction extends AbstractAction {
|
||||
public OpenBookAction(Map<String,Object> props, String key) {
|
||||
this(PropertyHelper.getString(props, key + "name", ""),
|
||||
PropertyHelper.getString(props, key + "icon", null),
|
||||
PropertyHelper.getString(props, key + "tooltip", ""),
|
||||
PropertyHelper.getString(props, key + "help", ""));
|
||||
}
|
||||
|
||||
public OpenBookAction(String name, String iconLocation, String tooltip) {
|
||||
this(name, iconLocation, tooltip, tooltip);
|
||||
}
|
||||
|
||||
public OpenBookAction(String name, String iconLocation, String tooltip, String helpText) {
|
||||
putValue(Action.NAME, name);
|
||||
putValue(Action.SHORT_DESCRIPTION, tooltip);
|
||||
putValue(Action.LONG_DESCRIPTION, helpText);
|
||||
|
||||
Icon icon = Images.getIcon(iconLocation, true);
|
||||
putValue(Action.SMALL_ICON, icon);
|
||||
}
|
||||
}
|
||||
|
||||
public class BuyBookAction extends OpenBookAction {
|
||||
BuyBookPage _buyBookPage;
|
||||
public BuyBookAction(String name, String iconLocation, String tooltip) {
|
||||
super(name, iconLocation, tooltip);
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (_buyBookPage == null) {
|
||||
_buyBookPage = new BuyBookPage(getService(), getCustomer());
|
||||
}
|
||||
showTab(_tabbedPane, "Buy Books", _buyBookPage);
|
||||
switchTab(_outputPane, _sqlLog);
|
||||
}
|
||||
|
||||
}
|
||||
public class DeliveryAction extends OpenBookAction {
|
||||
DeliveryPage _deliveryPage;
|
||||
public DeliveryAction(String name, String iconLocation, String tooltip) {
|
||||
super(name, iconLocation, tooltip);
|
||||
}
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (_deliveryPage == null) {
|
||||
_deliveryPage = new DeliveryPage(getService());
|
||||
}
|
||||
showTab(_tabbedPane, "Deliver Books", _deliveryPage);
|
||||
switchTab(_outputPane, _sqlLog);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SupplyAction extends OpenBookAction {
|
||||
SupplyPage _supplyPage;
|
||||
public SupplyAction(String name, String iconLocation, String tooltip) {
|
||||
super(name, iconLocation, tooltip);
|
||||
}
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (_supplyPage == null) {
|
||||
_supplyPage = new SupplyPage(getService());
|
||||
}
|
||||
showTab(_tabbedPane, "Supply Books", _supplyPage);
|
||||
switchTab(_outputPane, _sqlLog);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ViewConfigAction extends OpenBookAction {
|
||||
ConfigurationViewer _configView;
|
||||
public ViewConfigAction(String name, String iconLocation, String tooltip) {
|
||||
super(name, iconLocation, tooltip);
|
||||
}
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (_configView == null) {
|
||||
_configView = new ConfigurationViewer("Unit Configuration", getService().getUnit().getProperties());
|
||||
showTab(_tabbedPane, "Configuration", new JScrollPane(_configView));
|
||||
} else {
|
||||
showTab(_tabbedPane, "Configuration", _configView);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ViewDomainAction extends OpenBookAction {
|
||||
MetamodelView _domainView;
|
||||
AttributeLegendView _legends;
|
||||
public ViewDomainAction(String name, String iconLocation, String tooltip) {
|
||||
super(name, iconLocation, tooltip);
|
||||
}
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (_domainView == null) {
|
||||
_domainView = new MetamodelView(getService().getUnit().getMetamodel());
|
||||
_legends = new AttributeLegendView();
|
||||
showTab(_outputPane, "Legends", new JScrollPane(_legends));
|
||||
}
|
||||
showTab(_tabbedPane, "Domain Model", _domainView);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ViewDataAction extends OpenBookAction {
|
||||
public ViewDataAction(String name, String iconLocation, String tooltip) {
|
||||
super(name, iconLocation, tooltip);
|
||||
}
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
showTab(_tabbedPane, "Buy Books", null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ViewQueryCacheAction extends OpenBookAction {
|
||||
PreparedQueryViewer _queryView;
|
||||
public ViewQueryCacheAction(String name, String iconLocation, String tooltip) {
|
||||
super(name, iconLocation, tooltip);
|
||||
}
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (_queryView == null) {
|
||||
_queryView = new PreparedQueryViewer(OpenJPAPersistence.cast(getService().getUnit()));
|
||||
showTab(_tabbedPane, "JPQL Query", new JScrollPane(_queryView));
|
||||
}
|
||||
showTab(_tabbedPane, "JPQL Queries", _queryView);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ViewSourceAction extends OpenBookAction {
|
||||
SourceCodeViewer _sourceViewer;
|
||||
|
||||
public ViewSourceAction(String name, String iconLocation, String tooltip) {
|
||||
super(name, iconLocation, tooltip);
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (_sourceViewer == null) {
|
||||
_sourceViewer = new SourceCodeViewer("source");
|
||||
}
|
||||
showTab(_tabbedPane, "Source Code", _sourceViewer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the "welcome" page.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class WelcomeAction extends OpenBookAction {
|
||||
PowerPointViewer _powerpoint;
|
||||
JLabel _logoLabel = new JLabel(LOGO);
|
||||
boolean _showPresentation = true;
|
||||
|
||||
public WelcomeAction(String name, String iconLocation, String tooltip) {
|
||||
super(name, iconLocation, tooltip);
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (_powerpoint == null && _showPresentation) {
|
||||
String dir = PropertyHelper.getString(_config, "openbook.slides.dir", "slides/");
|
||||
String[] defaultSlides = {
|
||||
"Slide1.JPG",
|
||||
"Slide2.JPG",
|
||||
"Slide3.JPG",
|
||||
"Slide4.JPG",
|
||||
"Slide5.JPG",
|
||||
"Slide6.JPG",
|
||||
"Slide7.JPG",
|
||||
"Slide8.JPG",
|
||||
"Slide9.JPG",
|
||||
"Slide10.JPG",
|
||||
"Slide11.JPG",
|
||||
"Slide12.JPG",
|
||||
"Slide13.JPG",
|
||||
"Slide14.JPG",
|
||||
"Slide15.JPG"};
|
||||
List<String> slides = PropertyHelper.getStringList(_config, "openbook.slides.list",
|
||||
Arrays.asList(defaultSlides));
|
||||
try {
|
||||
_powerpoint = new PowerPointViewer(dir, slides);
|
||||
} catch (Exception e1) {
|
||||
_showPresentation = false;
|
||||
System.err.println("Error while opening slide deck at " + dir + ". \r\n"+ e1);
|
||||
}
|
||||
}
|
||||
showTab(_tabbedPane, "Home", _powerpoint != null ? _powerpoint : _logoLabel);
|
||||
}
|
||||
|
||||
}
|
||||
public class AboutAction extends OpenBookAction {
|
||||
AboutDialog _dialog;
|
||||
|
||||
public AboutAction(String name, String iconLocation, String tooltip) {
|
||||
super(name, iconLocation, tooltip);
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (_dialog == null) {
|
||||
_dialog = new AboutDialog(LOGO);
|
||||
SwingHelper.position(_dialog, Demo.this);
|
||||
}
|
||||
_dialog.setVisible(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the given tab in the given pane.
|
||||
* @param pane the tabbed pane
|
||||
* @param title title of the tab component
|
||||
* @param tab the component to show
|
||||
*/
|
||||
void showTab(JTabbedPane pane, String title, Component tab) {
|
||||
if (tab == null)
|
||||
return;
|
||||
Component c = locateTab(pane, tab);
|
||||
if (c == null) {
|
||||
pane.addTab(title, tab);
|
||||
pane.setSelectedComponent(tab);
|
||||
} else {
|
||||
pane.setSelectedComponent(c);
|
||||
}
|
||||
}
|
||||
|
||||
void switchTab(JTabbedPane pane, Component tab) {
|
||||
if (tab == null)
|
||||
return;
|
||||
Component c = locateTab(pane, tab);
|
||||
if (c == null) {
|
||||
pane.setSelectedComponent(c);
|
||||
}
|
||||
}
|
||||
|
||||
Component locateTab(JTabbedPane pane, Component tab) {
|
||||
int index = pane.indexOfComponent(tab);
|
||||
if (index == -1) {
|
||||
Component[] components = pane.getComponents();
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
if (components[i] instanceof JScrollPane
|
||||
&& (((JScrollPane)components[i]).getViewport().getView() == tab)) {
|
||||
return components[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return pane.getComponentAt(index);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private JTabbedPane createTabbedView() {
|
||||
JTabbedPane pane = new JTabbedPane();
|
||||
pane.setPreferredSize(TAB_VIEW);
|
||||
pane.setMinimumSize(new Dimension(TAB_VIEW.width, TAB_VIEW.height));
|
||||
return pane;
|
||||
}
|
||||
|
||||
private JTabbedPane createOutputView() {
|
||||
JTabbedPane pane = new JTabbedPane();
|
||||
pane.setPreferredSize(OUT_VIEW);
|
||||
_sqlLog = new ScrollingTextPane();
|
||||
GraphicOutputStream stream = new GraphicOutputStream(_sqlLog);
|
||||
_sqlLog.setPreferredSize(TAB_VIEW);
|
||||
SQLLogger.setOutput(stream);
|
||||
pane.addTab("SQL Log", new JScrollPane(_sqlLog));
|
||||
ScrollingTextPane consoleLog = new ScrollingTextPane();
|
||||
GraphicOutputStream console = new GraphicOutputStream(consoleLog);
|
||||
System.setErr(new PrintStream(console, true));
|
||||
pane.addTab("Console", new JScrollPane(consoleLog));
|
||||
return pane;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the navigation tree and adds the tree nodes. Each tree node is attached with an action
|
||||
* that fires when the node is selected.
|
||||
*/
|
||||
private JTree createNavigator() {
|
||||
ActionTreeNode root = new ActionTreeNode(_root);
|
||||
DefaultMutableTreeNode app = new DefaultMutableTreeNode("Application WorkFlows");
|
||||
DefaultMutableTreeNode views = new DefaultMutableTreeNode("Views");
|
||||
root.add(app);
|
||||
root.add(views);
|
||||
|
||||
|
||||
app.add(new ActionTreeNode(_buyBook));
|
||||
app.add(new ActionTreeNode(_deliver));
|
||||
app.add(new ActionTreeNode(_supply));
|
||||
|
||||
views.add(new ActionTreeNode(_viewConfig));
|
||||
views.add(new ActionTreeNode(_viewDomain));
|
||||
views.add(new ActionTreeNode(_viewQuery));
|
||||
views.add(new ActionTreeNode(_viewData));
|
||||
views.add(new ActionTreeNode(_viewSource));
|
||||
|
||||
|
||||
JTree tree = new JTree(root);
|
||||
tree.setShowsRootHandles(true);
|
||||
|
||||
tree.addTreeSelectionListener(new TreeSelectionListener() {
|
||||
public void valueChanged(TreeSelectionEvent e) {
|
||||
Object treeNode = _navigator.getLastSelectedPathComponent();
|
||||
if (treeNode instanceof ActionTreeNode) {
|
||||
((ActionTreeNode)treeNode)._action.actionPerformed(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
tree.setCellRenderer(new TypedTreeCellRenderer());
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tree node which may have an associated action.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public static class ActionTreeNode extends DefaultMutableTreeNode {
|
||||
private final Action _action;
|
||||
public ActionTreeNode(Action action) {
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return _action.getValue(Action.SHORT_DESCRIPTION).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class TypedTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||
@Override
|
||||
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
|
||||
boolean leaf, int row, boolean hasFocus) {
|
||||
return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AboutDialog extends JDialog {
|
||||
public AboutDialog(Icon logo) {
|
||||
setModal(true);
|
||||
setLayout(new BorderLayout());
|
||||
JButton button = new JButton("<html>"
|
||||
+ "<b>OpenBooks</b> "
|
||||
+ "<br> using OpenJPA version " + OpenJPAVersion.MAJOR_RELEASE + "." + OpenJPAVersion.MINOR_RELEASE
|
||||
+ "<br> by JPA Team, SWG"
|
||||
+ "<br>IBM Corporation"
|
||||
+ "<p>"
|
||||
+ "</html>");
|
||||
button.setIcon(logo);
|
||||
button.setHorizontalTextPosition(SwingConstants.RIGHT);
|
||||
button.setEnabled(true);
|
||||
button.setBorderPainted(false);
|
||||
add(button, BorderLayout.CENTER);
|
||||
add(new JLabel(Images.getIcon("images/websphere.png")), BorderLayout.SOUTH);
|
||||
setTitle("About OpenBooks");
|
||||
setAlwaysOnTop(true);
|
||||
setResizable(false);
|
||||
pack();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.Image;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
/**
|
||||
* Utility to load and cache images.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class Images {
|
||||
|
||||
private static Map<String, Icon> images = new HashMap<String, Icon>();
|
||||
public static Icon NEXT = getIcon("images/nav_forward.gif");
|
||||
public static Icon BACK = getIcon("images/nav_backward.gif");
|
||||
public static Icon DONE = getIcon("images/done.png");
|
||||
public static Icon CANCEL = getIcon("images/cancel.png");
|
||||
public static Icon ERROR = getIcon("images/error.png");
|
||||
public static Icon BROWSE = getIcon("images/browse.png");
|
||||
public static Icon START = getIcon("images/start_task.gif");
|
||||
public static Icon MONITOR = getIcon("images/console_view.gif");
|
||||
|
||||
public static Icon getIcon(String name) {
|
||||
Icon icon = images.get(name);
|
||||
if (icon == null) {
|
||||
icon = createImageIcon(name);
|
||||
images.put(name, icon);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static Icon getIcon(String name, boolean scale) {
|
||||
return getIcon(name, 32, -1);
|
||||
}
|
||||
|
||||
public static Icon getIcon(String name, int width, int height) {
|
||||
Icon icon = getIcon(name);
|
||||
if (icon == null) {
|
||||
return null;
|
||||
}
|
||||
icon = new ImageIcon(((ImageIcon)icon).getImage().getScaledInstance(32, -1, Image.SCALE_SMOOTH));
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ImageIcon, or null if the path was invalid.
|
||||
*
|
||||
**/
|
||||
protected static 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 openbook.client;
|
||||
|
||||
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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingWorker;
|
||||
|
||||
import jpa.tools.swing.EntityDataModel;
|
||||
import jpa.tools.swing.EntityTableView;
|
||||
import jpa.tools.swing.ErrorDialog;
|
||||
import openbook.domain.Book;
|
||||
import openbook.domain.Inventory;
|
||||
import openbook.server.OpenBookService;
|
||||
|
||||
/**
|
||||
* A page to view and supply low inventory items.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SupplyPage extends JPanel {
|
||||
private final OpenBookService _service;
|
||||
|
||||
private final EntityTableView<Inventory> _lowInventories;
|
||||
|
||||
private final JButton _supply;
|
||||
private final JButton _view;
|
||||
private final JLabel _title;
|
||||
|
||||
private static int REORDER_LIMIT = 10;
|
||||
private static int REORDER_QUANTITY = 40;
|
||||
|
||||
public SupplyPage(final OpenBookService service) {
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
_service = service;
|
||||
|
||||
_title = new JLabel(REORDER_LIMIT + " lowest inventory items");
|
||||
_view = new JButton("Show " + REORDER_LIMIT + " lowest inventory items");
|
||||
_supply = new JButton("Supply " + REORDER_QUANTITY + " to each item");
|
||||
|
||||
List<Inventory> orders = getInventory(REORDER_LIMIT);
|
||||
_lowInventories = new EntityTableView<Inventory>(Inventory.class,
|
||||
orders,
|
||||
EntityDataModel.BASIC_ATTR | EntityDataModel.ASSOCIATION_ATTR,
|
||||
service.getUnit());
|
||||
|
||||
add(_title, BorderLayout.NORTH);
|
||||
add(_lowInventories, BorderLayout.CENTER);
|
||||
JPanel buttons = new JPanel();
|
||||
buttons.add(_view);
|
||||
buttons.add(_supply);
|
||||
add(buttons, BorderLayout.SOUTH);
|
||||
|
||||
|
||||
_view.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
_lowInventories.getDataModel().updateData(getInventory(REORDER_LIMIT));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Supplies each inventory displayed.
|
||||
*/
|
||||
_supply.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
new SwingWorker<List<Inventory>, Void>() {
|
||||
@Override
|
||||
protected List<Inventory> doInBackground() throws Exception {
|
||||
EntityDataModel<Inventory> invs = _lowInventories.getDataModel();
|
||||
List<Inventory> updated = new ArrayList<Inventory>();
|
||||
for (Inventory inv : invs) {
|
||||
Book supplied = _service.supply(inv.getBook(), REORDER_QUANTITY);
|
||||
updated.add(supplied.getInventory());
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
public void done() {
|
||||
try {
|
||||
_lowInventories.getDataModel().updateData(get(1, TimeUnit.SECONDS));
|
||||
} 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<Inventory> getInventory(final Integer limit) {
|
||||
SwingWorker<List<Inventory>, Void> worker = new SwingWorker<List<Inventory>, Void>() {
|
||||
@Override
|
||||
protected List<Inventory> doInBackground() throws Exception {
|
||||
return _service.getReorderableBooks(REORDER_LIMIT);
|
||||
}
|
||||
};
|
||||
worker.execute();
|
||||
try {
|
||||
return worker.get();
|
||||
} catch (Exception e) {
|
||||
new ErrorDialog(e).setVisible(true);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 openbook.domain;
|
||||
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Version;
|
||||
|
||||
/**
|
||||
* A persistent entity to represent an author of one or more Book.
|
||||
* <br>
|
||||
* <b>Notes</b>: No setter for identity value.
|
||||
* <br>
|
||||
* <LI><b>Identity</b>:Generated value as identity.
|
||||
* <LI><b>Mapping</b>:Many-to-Many mapping to Books.
|
||||
* <LI><b>Version</b>: Yes.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
public class Author {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToMany(mappedBy="authors")
|
||||
private Set<Book> books;
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Book> getBooks() {
|
||||
return books;
|
||||
}
|
||||
|
||||
public void addBook(Book book) {
|
||||
if (books == null)
|
||||
books = new HashSet<Book>();
|
||||
books.add(book);
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Generated by OpenJPA MetaModel Generator Tool.
|
||||
**/
|
||||
|
||||
package openbook.domain;
|
||||
|
||||
import javax.persistence.metamodel.SetAttribute;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
@javax.persistence.metamodel.StaticMetamodel
|
||||
(value=openbook.domain.Author.class)
|
||||
@javax.annotation.Generated
|
||||
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Thu May 13 20:18:23 CDT 2010")
|
||||
public class Author_ {
|
||||
public static volatile SetAttribute<Author,Book> books;
|
||||
public static volatile SingularAttribute<Author,Long> id;
|
||||
public static volatile SingularAttribute<Author,String> name;
|
||||
public static volatile SingularAttribute<Author,Integer> version;
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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 openbook.domain;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Version;
|
||||
|
||||
/**
|
||||
* An immutable persistent entity represents a Book.
|
||||
* <br>
|
||||
* The mutable properties of the book such as number of items in stock etc.
|
||||
* are factored out in a separate {@link Inventory} instance.
|
||||
* <br>
|
||||
* The state of inventory is mutable, but the relation to inventory is immutable.
|
||||
*
|
||||
* <LI><b>Identity</b>: Application-defined identity.
|
||||
* <LI><b>Mapping</b>: One-to-One bi-directional, immutable mapping to {@link Inventory}.
|
||||
* Many-to-Many bi-directional mapping to {@linkplain Author}.
|
||||
* <LI><b>Version</b>: No.
|
||||
* <p>
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
public class Book implements Serializable {
|
||||
@Id
|
||||
private String ISBN;
|
||||
|
||||
private String title;
|
||||
|
||||
private double price;
|
||||
|
||||
@OneToOne(mappedBy="book",
|
||||
fetch=FetchType.LAZY,
|
||||
cascade=CascadeType.ALL,
|
||||
optional=false,
|
||||
orphanRemoval=true)
|
||||
private Inventory inventory;
|
||||
|
||||
@ManyToMany(fetch=FetchType.EAGER)
|
||||
private List<Author> authors;
|
||||
|
||||
/**
|
||||
* A no-arg constructor is required for JPA Specification.
|
||||
*/
|
||||
public Book() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a book with given parameters.
|
||||
*
|
||||
* @param ISBN primary identity of this Book
|
||||
* @param title Title of the book.
|
||||
* @param price price of the book.
|
||||
* @param initialSupply initial inventory quantity.
|
||||
*/
|
||||
public Book(String ISBN, String title, double price, int initialSupply) {
|
||||
this.ISBN = ISBN;
|
||||
this.title = title;
|
||||
this.price = price;
|
||||
inventory = new Inventory(this, initialSupply);
|
||||
}
|
||||
|
||||
public String getISBN() {
|
||||
return ISBN;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public List<Author> getAuthors() {
|
||||
return authors;
|
||||
}
|
||||
|
||||
public void addAuthor(Author...authors) {
|
||||
if (this.authors == null)
|
||||
this.authors = new ArrayList<Author>();
|
||||
for (Author a : authors) {
|
||||
if (!this.authors.contains(a))
|
||||
this.authors.add(a);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAuthors(List<Author> authors) {
|
||||
this.authors = authors;
|
||||
}
|
||||
|
||||
public Inventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Generated by OpenJPA MetaModel Generator Tool.
|
||||
**/
|
||||
|
||||
package openbook.domain;
|
||||
|
||||
import javax.persistence.metamodel.ListAttribute;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
@javax.persistence.metamodel.StaticMetamodel
|
||||
(value=openbook.domain.Book.class)
|
||||
@javax.annotation.Generated
|
||||
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Thu May 13 20:18:23 CDT 2010")
|
||||
public class Book_ {
|
||||
public static volatile SingularAttribute<Book,String> ISBN;
|
||||
public static volatile ListAttribute<Book,Author> authors;
|
||||
public static volatile SingularAttribute<Book,Inventory> inventory;
|
||||
public static volatile SingularAttribute<Book,Double> price;
|
||||
public static volatile SingularAttribute<Book,String> title;
|
||||
public static volatile SingularAttribute<Book,Integer> version;
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 openbook.domain;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
/**
|
||||
* A persistent entity represents a Customer.
|
||||
*
|
||||
* <br><b>Persistent Identity</b>: auto-generated identity.
|
||||
* <br><b>Mapping</b>:
|
||||
*
|
||||
* <br><b>Design Notes</b>: No setter for identity value.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
public class Customer implements Serializable {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
private String name;
|
||||
private String email;
|
||||
|
||||
public Customer() {
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link ShoppingCart} for this customer.
|
||||
* @return
|
||||
*/
|
||||
public ShoppingCart newCart() {
|
||||
return new ShoppingCart(this);
|
||||
}
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Generated by OpenJPA MetaModel Generator Tool.
|
||||
**/
|
||||
|
||||
package openbook.domain;
|
||||
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
@javax.persistence.metamodel.StaticMetamodel
|
||||
(value=openbook.domain.Customer.class)
|
||||
@javax.annotation.Generated
|
||||
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Thu May 13 20:18:23 CDT 2010")
|
||||
public class Customer_ {
|
||||
public static volatile SingularAttribute<Customer,String> email;
|
||||
public static volatile SingularAttribute<Customer,Long> id;
|
||||
public static volatile SingularAttribute<Customer,String> name;
|
||||
public static volatile SingularAttribute<Customer,Integer> version;
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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 openbook.domain;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Version;
|
||||
|
||||
/**
|
||||
* A mutable persistent entity.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
public class Inventory implements Serializable {
|
||||
@Id
|
||||
@OneToOne(fetch=FetchType.EAGER, optional=false)
|
||||
private Book book;
|
||||
|
||||
private int supplied;
|
||||
|
||||
private int sold;
|
||||
|
||||
protected Inventory() {
|
||||
|
||||
}
|
||||
/**
|
||||
* Construct with the given Book and initial inventory count.
|
||||
* Package protected because only a Book can create its own inventory.
|
||||
*
|
||||
* @param book non-null Book.
|
||||
* @param initialSupply must be greater than zero.
|
||||
*/
|
||||
Inventory(Book book, int initialSupply) {
|
||||
if (book == null)
|
||||
throw new NullPointerException("Can not create inventory for null Book");
|
||||
if (initialSupply < 1)
|
||||
throw new IllegalArgumentException("Can not create inventory " + initialSupply + " for " + book +
|
||||
" Initial inventory must be greater than zero.");
|
||||
this.book = book;
|
||||
increment(initialSupply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Book that this inventory represents.
|
||||
*
|
||||
* @return non-null Book.
|
||||
*/
|
||||
public Book getBook() {
|
||||
return book;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the available quantity.
|
||||
* This is an <em>in-flight</em> value representing the difference between the quantity supplied and quantity sold
|
||||
* so far.
|
||||
*/
|
||||
public int getInStock() {
|
||||
return supplied - sold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the quantity supplied so far.
|
||||
*
|
||||
* @return a monotonically increasing positive number.
|
||||
*/
|
||||
public int getSupplied() {
|
||||
return supplied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the quantity sold so far.
|
||||
*
|
||||
* @return a monotonically increasing positive number.
|
||||
*/
|
||||
public int getSold() {
|
||||
return sold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment this inventory by the given quantity.
|
||||
*
|
||||
* @param supplied must be positive.
|
||||
*/
|
||||
public void increment(int supplied) {
|
||||
if (supplied < 1)
|
||||
throw new IllegalArgumentException("Can not add " + supplied + " supplies to " + this +
|
||||
" Suuplied quanlity must be greater than zero.");
|
||||
this.supplied += supplied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement this inventory by the given quantity.
|
||||
*
|
||||
* @param sold must be positive.
|
||||
*/
|
||||
public void decrement(int sold) {
|
||||
if (sold < 1)
|
||||
throw new IllegalArgumentException("can not sell " + sold + "quantity to " + this
|
||||
+ "Sold quantity Must be greater than zero.");
|
||||
this.sold += sold;
|
||||
}
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Generated by OpenJPA MetaModel Generator Tool.
|
||||
**/
|
||||
|
||||
package openbook.domain;
|
||||
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
@javax.persistence.metamodel.StaticMetamodel
|
||||
(value=openbook.domain.Inventory.class)
|
||||
@javax.annotation.Generated
|
||||
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Thu May 13 20:18:23 CDT 2010")
|
||||
public class Inventory_ {
|
||||
public static volatile SingularAttribute<Inventory,Book> book;
|
||||
public static volatile SingularAttribute<Inventory,Integer> sold;
|
||||
public static volatile SingularAttribute<Inventory,Integer> supplied;
|
||||
public static volatile SingularAttribute<Inventory,Integer> version;
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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 openbook.domain;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.OrderColumn;
|
||||
|
||||
/**
|
||||
* An immutable persistent entity with complex primary key.
|
||||
* The primary key is combination of the primary identity of {@linkplain PurchaseOrder} and
|
||||
* an 1-based integer index.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
@IdClass(LineItem.LineItemId.class)
|
||||
public class LineItem implements Serializable {
|
||||
@Id
|
||||
@OneToOne
|
||||
private PurchaseOrder order;
|
||||
|
||||
@Id
|
||||
@OrderColumn
|
||||
@Column(name="IDX") // index is keyword
|
||||
private int index;
|
||||
|
||||
@ManyToOne(optional=false)
|
||||
private Book book;
|
||||
|
||||
private int quantity;
|
||||
|
||||
protected LineItem() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructed as a line item for the given PurchaseOrder for the given Book for the given quantity.
|
||||
* Package protected because only the PurchaseOrder can create its own LineItem.
|
||||
*
|
||||
* @param order non-null PurchaseOrder
|
||||
* @param i the 1-based index of this line item in its parent PurchaseOrder
|
||||
* @param book non-null Book of this line item
|
||||
* @param quantity no. of books must be greater than zero.
|
||||
*/
|
||||
LineItem(PurchaseOrder order, int i, Book book, int quantity) {
|
||||
if (order == null)
|
||||
throw new NullPointerException("Can not create LineItem for null PurchaseOrder");
|
||||
if (i < 1)
|
||||
throw new IllegalArgumentException("Can not create LineItem with index " + i + ". Must be > 0");
|
||||
if (book == null)
|
||||
throw new NullPointerException("Can not create LineItem for null Book");
|
||||
if (quantity < 1)
|
||||
throw new IllegalArgumentException("Can not create LineItem with quantity " + i + ". Must be > 0");
|
||||
|
||||
this.order = order;
|
||||
this.index = i;
|
||||
this.book = book;
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Book for this line item.
|
||||
* @return non-null Book.
|
||||
*/
|
||||
public Book getBook() {
|
||||
return book;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the quantity of the book for this line item.
|
||||
* @return a positive number.
|
||||
*/
|
||||
public int getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent PurchaseOrder of this line item.
|
||||
* @return non-null PurchaseOrder.
|
||||
*/
|
||||
public PurchaseOrder getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 1-based index this line item in its parent PurchaseOrder.
|
||||
* @return index must be greater than zero.
|
||||
*/
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Separate identity class.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public static class LineItemId implements Serializable {
|
||||
long order;
|
||||
int index;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == this)
|
||||
return true;
|
||||
if (other instanceof LineItemId) {
|
||||
LineItemId that = (LineItemId)other;
|
||||
return order == that.order && index == that.index;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (31 ^ order + index);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Generated by OpenJPA MetaModel Generator Tool.
|
||||
**/
|
||||
|
||||
package openbook.domain;
|
||||
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
@javax.persistence.metamodel.StaticMetamodel
|
||||
(value=openbook.domain.LineItem.class)
|
||||
@javax.annotation.Generated
|
||||
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Thu May 13 20:18:23 CDT 2010")
|
||||
public class LineItem_ {
|
||||
public static volatile SingularAttribute<LineItem,Book> book;
|
||||
public static volatile SingularAttribute<LineItem,Integer> index;
|
||||
public static volatile SingularAttribute<LineItem,PurchaseOrder> order;
|
||||
public static volatile SingularAttribute<LineItem,Integer> quantity;
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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 openbook.domain;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Time;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
/**
|
||||
* A persistent entity.
|
||||
* Auto-generated identity.
|
||||
* Enum and Date type persistent attribute.
|
||||
* One-to-One uni-directional mapping to Customer.
|
||||
* One-to-Many bi-directional mapping to LineItem.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
public class PurchaseOrder implements Serializable {
|
||||
public enum Status {PENDING, DELEVERED};
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@OneToOne(optional=false)
|
||||
private Customer customer;
|
||||
|
||||
private Status status;
|
||||
|
||||
@OneToMany(mappedBy="order", cascade=CascadeType.ALL, orphanRemoval=true)
|
||||
private List<LineItem> items;
|
||||
|
||||
private int total;
|
||||
|
||||
@Temporal(TemporalType.TIME)
|
||||
private Date placedOn;
|
||||
|
||||
@Temporal(TemporalType.TIME)
|
||||
private Date deliveredOn;
|
||||
|
||||
protected PurchaseOrder() {}
|
||||
|
||||
/**
|
||||
* Constructed by transferring the content of the given {@linkplain ShoppingCart}.
|
||||
* @param cart
|
||||
*/
|
||||
public PurchaseOrder(ShoppingCart cart) {
|
||||
customer = cart.getCustomer();
|
||||
status = Status.PENDING;
|
||||
placedOn = new Time(System.currentTimeMillis());
|
||||
Map<Book, Integer> items = cart.getItems();
|
||||
for (Map.Entry<Book, Integer> entry : items.entrySet()) {
|
||||
addItem(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setDelivered() {
|
||||
if (this.status == Status.DELEVERED)
|
||||
throw new IllegalStateException(this + " has been delivered");
|
||||
this.status = Status.DELEVERED;
|
||||
this.deliveredOn = new Time(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public List<LineItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
void addItem(Book book, int quantity) {
|
||||
if (items == null)
|
||||
items = new ArrayList<LineItem>();
|
||||
items.add(new LineItem(this, items.size()+1, book, quantity));
|
||||
total += (book.getPrice() * quantity);
|
||||
}
|
||||
|
||||
public double getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public Date getPlacedOn() {
|
||||
return placedOn;
|
||||
}
|
||||
|
||||
public Date getDeliveredOn() {
|
||||
return deliveredOn;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Generated by OpenJPA MetaModel Generator Tool.
|
||||
**/
|
||||
|
||||
package openbook.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import javax.persistence.metamodel.ListAttribute;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import openbook.domain.PurchaseOrder.Status;
|
||||
|
||||
@javax.persistence.metamodel.StaticMetamodel
|
||||
(value=openbook.domain.PurchaseOrder.class)
|
||||
@javax.annotation.Generated
|
||||
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Thu May 13 20:18:23 CDT 2010")
|
||||
public class PurchaseOrder_ {
|
||||
public static volatile SingularAttribute<PurchaseOrder,Customer> customer;
|
||||
public static volatile SingularAttribute<PurchaseOrder,Date> deliveredOn;
|
||||
public static volatile SingularAttribute<PurchaseOrder,Long> id;
|
||||
public static volatile ListAttribute<PurchaseOrder,LineItem> items;
|
||||
public static volatile SingularAttribute<PurchaseOrder,Date> placedOn;
|
||||
public static volatile SingularAttribute<PurchaseOrder,Status> status;
|
||||
public static volatile SingularAttribute<PurchaseOrder,Integer> total;
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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.domain;
|
||||
|
||||
|
||||
/**
|
||||
* A simple numeric range.
|
||||
* Minimum value is included, maximum value is excluded.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class Range<N extends Number> {
|
||||
private N min;
|
||||
private N max;
|
||||
private final Class<N> type;
|
||||
|
||||
public Range(Object min, Object max) {
|
||||
this((N)min, (N)max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a range. Empty range i.e. where minimum equals maximum is allowed.
|
||||
*
|
||||
* @param min non-null minimum value.
|
||||
* @param max non-null maximum value.
|
||||
*/
|
||||
public Range(N min, N max) {
|
||||
if (min == null || max == null)
|
||||
throw new IllegalArgumentException("Supplied Min or Max is null");
|
||||
if (max.doubleValue() < min.doubleValue())
|
||||
throw new IllegalArgumentException("Invalid range (" + min + "," + max + ")");
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
type = (Class<N>)min.getClass();
|
||||
}
|
||||
|
||||
public Class<N> type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given value is within this range.
|
||||
* Minimum is included, maximum is excluded.
|
||||
*
|
||||
* @param x a non-null value
|
||||
* @return true if the given value is greater than or equals to minimum and less than the maximum.
|
||||
*/
|
||||
public boolean contains(Number x) {
|
||||
return x != null && x.doubleValue() >= min.doubleValue() && x.doubleValue() < max.doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given range is within this range.
|
||||
* Minimum is included, maximum is excluded.
|
||||
*
|
||||
* @param x a non-null value
|
||||
* @return true if the given value is greater than or equals to minimum and less than the maximum.
|
||||
*/
|
||||
public <X extends Number> boolean contains(Range<X> r) {
|
||||
return r != null && r.getMinimum().doubleValue() >= min.doubleValue() &&
|
||||
r.getMaximum().doubleValue() <= max.doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum value.
|
||||
*/
|
||||
public N getMinimum() {
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum value.
|
||||
*/
|
||||
public N getMaximum() {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts this range by the given number.
|
||||
*
|
||||
* @param x a non-null value.
|
||||
*
|
||||
* @return if this range is adjusted by this value.
|
||||
*/
|
||||
public boolean adjust(N x) {
|
||||
if (x == null)
|
||||
return false;
|
||||
if (x.doubleValue() < min.doubleValue()) {
|
||||
min = x;
|
||||
return true;
|
||||
}
|
||||
if (x.doubleValue() > max.doubleValue()) {
|
||||
max = x;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((max == null) ? 0 : max.hashCode());
|
||||
result = prime * result + ((min == null) ? 0 : min.hashCode());
|
||||
result = prime * result + ((type == null) ? 0 : type.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Range other = (Range) obj;
|
||||
if (max == null) {
|
||||
if (other.max != null)
|
||||
return false;
|
||||
} else if (!max.equals(other.max))
|
||||
return false;
|
||||
if (min == null) {
|
||||
if (other.min != null)
|
||||
return false;
|
||||
} else if (!min.equals(other.min))
|
||||
return false;
|
||||
if (type == null) {
|
||||
if (other.type != null)
|
||||
return false;
|
||||
} else if (!type.equals(other.type))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 openbook.domain;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A non-persistent entity holds the content of a shopping session for a {@linkplain Customer}.
|
||||
* Used to create a persistent PurchaseOrder.
|
||||
* Books can be added or removed from this cart.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ShoppingCart implements Serializable {
|
||||
private Customer customer;
|
||||
private Map<Book, Integer> items;
|
||||
|
||||
/**
|
||||
* Construct a cart for the given Customer.
|
||||
*
|
||||
* @param c non-null Customer.
|
||||
*/
|
||||
ShoppingCart(Customer c) {
|
||||
customer = c;
|
||||
items = new HashMap<Book, Integer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Customer who owns this cart.
|
||||
*
|
||||
* @return non-null Customer.
|
||||
*/
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the books with their corresponding quantity in this cart.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<Book, Integer> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given book with the given quantity in the cart.
|
||||
* If the book already exists then the quantity is added to the existing quantity.
|
||||
*
|
||||
* @param book non-null Book
|
||||
* @param quantity a positive quantity.
|
||||
*/
|
||||
public void addItem(Book book, int quantity) {
|
||||
if (book == null)
|
||||
throw new NullPointerException("Can not add null Book to " + this);
|
||||
if (quantity < 1)
|
||||
throw new IllegalArgumentException("Can not add " + quantity + " " + book + " to " + this +
|
||||
" Added qunatity must be greater than zero");
|
||||
int current = items.containsKey(book) ? items.get(book) : 0;
|
||||
items.put(book, current + quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the quantity of the given book by the given delta.
|
||||
*
|
||||
* @param book a non-null Book that must exist in this cart.
|
||||
* @param delta no. of quantity to change. Can be positive or negative.
|
||||
* If the resultant quantity becomes zero of negative, the book is removed from the cart.
|
||||
*/
|
||||
public void changeQuantity(Book book, int delta) {
|
||||
if (book == null)
|
||||
throw new NullPointerException("Can not change quantity for null Book in " + this);
|
||||
if (!items.containsKey(book))
|
||||
throw new IllegalArgumentException("Can not change quantity for " + book + " becuase the book does not " +
|
||||
"exist in " + this);
|
||||
int current = items.containsKey(book) ? items.get(book) : 0;
|
||||
if (current + delta <= 0) {
|
||||
items.remove(book);
|
||||
} else {
|
||||
items.put(book, current + delta);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given book from this cart.
|
||||
*
|
||||
* @param book book a non-null Book that must exist in this cart.
|
||||
*/
|
||||
public void remove(Book book) {
|
||||
if (book == null)
|
||||
throw new NullPointerException("Can not remove null Book from " + this);
|
||||
if (!items.containsKey(book))
|
||||
throw new IllegalArgumentException("Can not remove " + book + " becuase the book does not " +
|
||||
"exist in " + this);
|
||||
items.remove(book);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
items.clear();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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 openbook.server;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
|
||||
import openbook.domain.Book;
|
||||
import openbook.domain.Customer;
|
||||
import openbook.domain.Inventory;
|
||||
import openbook.domain.PurchaseOrder;
|
||||
import openbook.domain.ShoppingCart;
|
||||
|
||||
/**
|
||||
* A simple service to select Books, purchase them and manage their inventory.
|
||||
* A service handle can be obtained from {@link ServiceFactory#getService(String)
|
||||
* Service Factory}.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public interface OpenBookService {
|
||||
public static final String DEFAULT_UNIT_NAME = "OpenBooks";
|
||||
|
||||
/**
|
||||
* Starts a session for the given named Customer.
|
||||
* If no record of the given name exists, a new Customer record is created.
|
||||
*
|
||||
* @param name name of a possibly existing customer. Or a new one.
|
||||
*
|
||||
* @return a Customer
|
||||
*/
|
||||
public Customer login(String name);
|
||||
|
||||
/**
|
||||
* Selects a list of Books matching the given conditions.
|
||||
* Each of the conditional parameter can be null.
|
||||
* A null parameter implies that the resultant query should ignore that parameter.
|
||||
*
|
||||
* @param title title of the Book
|
||||
* @param min minimum price
|
||||
* @param max maximum price
|
||||
* @param author name of author
|
||||
* @param decorators to modify the executable query such as its range.
|
||||
* @return
|
||||
*/
|
||||
public List<Book> select(String title,
|
||||
Double min, Double max,
|
||||
String author,
|
||||
QueryDecorator...decorators);
|
||||
/**
|
||||
* Gets the query String for the given parameters.
|
||||
* Each of the conditional parameter can be null.
|
||||
* A null parameter implies that the resultant query should ignore that parameter.
|
||||
*
|
||||
* @param title title of the Book
|
||||
* @param min minimum price
|
||||
* @param max maximum price
|
||||
* @param author name of author
|
||||
* @param decorators to modify the executable query such as its range.
|
||||
* @return
|
||||
*/
|
||||
public String getQuery(String title,
|
||||
Double min, Double max,
|
||||
String author);
|
||||
|
||||
/**
|
||||
* Runs an arbitrary JPQL query to return a list of result.
|
||||
*
|
||||
* @param jpql a valid JPQL query string.
|
||||
* @param resultClass type of the result.
|
||||
* @param decorators zero or more QueryDecorators to be applied before Query is executed.
|
||||
* @return the selected instances.
|
||||
*/
|
||||
<T> List<T> query(String jpql, Class<T> resultClass, QueryDecorator...decorators);
|
||||
<T> List<T> getExtent(Class<T> entity);
|
||||
|
||||
/**
|
||||
* Buys the content of the given cart.
|
||||
*
|
||||
* @param cart a non-empty cart.
|
||||
* @return a PurchaseOrder for the content of the cart.
|
||||
*/
|
||||
public PurchaseOrder placeOrder(ShoppingCart cart);
|
||||
|
||||
/**
|
||||
* Delivers the given order. Delivery changes the status of the order, decrements
|
||||
* inventory and finally removes the line items.
|
||||
*
|
||||
* @param order a PENDING order to be delivered.
|
||||
* @return the PurchaseOrder after delivery.
|
||||
*
|
||||
*/
|
||||
public PurchaseOrder deliver(PurchaseOrder order);
|
||||
|
||||
/**
|
||||
* Add inventory of the given Book by the given quantity.
|
||||
*
|
||||
* @param b a Book whose inventory is to be incremented
|
||||
* @param quantity positive number.
|
||||
*
|
||||
* @return the Book after incrementing its inventory.
|
||||
*/
|
||||
public Book supply(Book b, int quantity);
|
||||
|
||||
/**
|
||||
* Gets the list of orders of given status.
|
||||
*
|
||||
* @param status status of the orders. null implies all orders.
|
||||
*
|
||||
* @return list of orders sorted by their placement dates.
|
||||
*/
|
||||
public List<PurchaseOrder> getOrders(PurchaseOrder.Status status);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the list of Books whose inventory is lower than the given limit.
|
||||
*
|
||||
* @param limit reorder limit. null implies all inventory.
|
||||
*
|
||||
* @return list of Books with inventory lower than the given limit.
|
||||
*/
|
||||
public List<Inventory> getReorderableBooks(int limit);
|
||||
|
||||
/**
|
||||
* Count the number of instances of the given persistent type.
|
||||
*
|
||||
* @param cls a persistent type.
|
||||
* @return number of persistent entity of the given type.
|
||||
*/
|
||||
public long count(Class<?> cls);
|
||||
|
||||
/**
|
||||
* Populates the underlying data repository with sample values, only if
|
||||
* the data repository is empty.
|
||||
*
|
||||
* @param loadParameters control the number of Books etc. to be created.
|
||||
* null is allowed.
|
||||
*
|
||||
* @return true if the repository is initialized by this invocation.
|
||||
*/
|
||||
public boolean initialize(Map<String,Object> loadParameters);
|
||||
public void clean();
|
||||
/**
|
||||
* Gets the underlying persistence unit.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public EntityManagerFactory getUnit();
|
||||
|
||||
/**
|
||||
* Gets the name of the underlying persistence unit.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getUnitName();
|
||||
|
||||
/**
|
||||
* Affirms if the transaction on this persistence unit is managed by a container.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isManaged();
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,402 @@
|
||||
/*
|
||||
* 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 openbook.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.ParameterExpression;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
|
||||
import openbook.domain.Author;
|
||||
import openbook.domain.Author_;
|
||||
import openbook.domain.Book;
|
||||
import openbook.domain.Book_;
|
||||
import openbook.domain.Customer;
|
||||
import openbook.domain.Customer_;
|
||||
import openbook.domain.Inventory;
|
||||
import openbook.domain.Inventory_;
|
||||
import openbook.domain.LineItem;
|
||||
import openbook.domain.PurchaseOrder;
|
||||
import openbook.domain.PurchaseOrder_;
|
||||
import openbook.domain.Range;
|
||||
import openbook.domain.ShoppingCart;
|
||||
import openbook.util.PropertyHelper;
|
||||
import openbook.util.Randomizer;
|
||||
|
||||
import org.apache.openjpa.persistence.criteria.OpenJPACriteriaBuilder;
|
||||
|
||||
/**
|
||||
* A demonstrative example of a transaction service with persistent entity using Java Persistence API.
|
||||
* <br>
|
||||
* This example service operates on a persistent domain model to browse {@linkplain Book books},
|
||||
* occasionally {@linkplain #placeOrder(ShoppingCart) placing} {@linkplain PurchaseOrder purchase orders},
|
||||
* while {@linkplain Inventory inventory} gets updated either by {@linkplain #deliver() delivery} or
|
||||
* by {@linkplain #supply() supply}.
|
||||
* <br>
|
||||
* The operational model as well as the persistent domain model is influenced by the fact that
|
||||
* a JPA based application can benefit from
|
||||
* <LI>Mostly Immutable Persistent Data Model
|
||||
* <LI>Optimistic Transaction Model
|
||||
* <br>for better scalability and throughput.
|
||||
* <br>
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class OpenBookServiceImpl extends PersistenceService implements OpenBookService {
|
||||
|
||||
public static final int CUSTOMER_COUNT = 10;
|
||||
public static final int BOOK_COUNT = 100;
|
||||
public static final int AUTHOR_COUNT = 40;
|
||||
public static final int AUTHOR_PER_BOOK = 3;
|
||||
|
||||
/**
|
||||
* Range of number of queries executed for a {@linkplain #shop() shopping} trip.
|
||||
*/
|
||||
public static final Range<Double> PRICE_RANGE = new Range<Double>(4.99, 120.99);
|
||||
public static final Range<Integer> STOCK_RANGE = new Range<Integer>(5, 50);
|
||||
public static final int REORDER_LEVEL = 10;
|
||||
|
||||
OpenBookServiceImpl(String unit, EntityManagerFactory emf, boolean managed,
|
||||
PersistenceContextType scope) {
|
||||
super(unit, emf, managed, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize service by populating inventory of Books and Customers.
|
||||
* If the inventory exists, then returns immediately without creating any new inventory.
|
||||
*
|
||||
* @return true if new inventory is created. false otherwise.
|
||||
*/
|
||||
public boolean initialize(Map<String,Object> config) {
|
||||
if (isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
EntityManager em = begin();
|
||||
if (config == null) {
|
||||
config = Collections.EMPTY_MAP;
|
||||
}
|
||||
int nCustomer = PropertyHelper.getInteger(config, "openbook.Customer.Count", CUSTOMER_COUNT);
|
||||
int nBook = PropertyHelper.getInteger(config, "openbook.Book.Count", BOOK_COUNT);
|
||||
int nAuthor = PropertyHelper.getInteger(config, "openbook.Author.Count", AUTHOR_COUNT);
|
||||
int nAuthorPerBook = PropertyHelper.getInteger(config, "openbook.Book.Author.Count", AUTHOR_PER_BOOK);
|
||||
|
||||
Double priceMax = PropertyHelper.getDouble(config, "openbook.Book.Price.Max", PRICE_RANGE.getMaximum());
|
||||
Double priceMin = PropertyHelper.getDouble(config, "openbook.Book.Price.Min", PRICE_RANGE.getMinimum());
|
||||
|
||||
Integer stockMax = PropertyHelper.getInteger(config, "openbook.Inventory.Max", STOCK_RANGE.getMaximum());
|
||||
Integer stockMin = PropertyHelper.getInteger(config, "openbook.Inventory.Min", STOCK_RANGE.getMinimum());
|
||||
|
||||
System.err.println("Creating " + nCustomer + " new Customer");
|
||||
for (int i = 1; i < nCustomer; i++) {
|
||||
Customer customer = new Customer();
|
||||
customer.setName("Customer-"+i);
|
||||
em.persist(customer);
|
||||
}
|
||||
|
||||
List<Author> allAuthors = new ArrayList<Author>();
|
||||
System.err.println("Creating " + nAuthor + " new Authors");
|
||||
for (int i = 1; i <= nAuthor; i++) {
|
||||
Author author = new Author();
|
||||
author.setName("Author-"+i);
|
||||
allAuthors.add(author);
|
||||
em.persist(author);
|
||||
}
|
||||
System.err.println("Creating " + nBook + " new Books");
|
||||
System.err.println("Linking at most " + nAuthorPerBook + " Authors per Book");
|
||||
for (int i = 1; i <= nBook; i++) {
|
||||
Book book = new Book(Randomizer.randomString(4,2),
|
||||
"Book-" + i,
|
||||
Randomizer.random(priceMin, priceMax),
|
||||
Randomizer.random(stockMin, stockMax));
|
||||
List<Author> authors = Randomizer.selectRandom(allAuthors,
|
||||
Math.max(1, Randomizer.random(nAuthorPerBook)));
|
||||
for (Author author : authors) {
|
||||
author.addBook(book);
|
||||
book.addAuthor(author);
|
||||
}
|
||||
em.persist(book);
|
||||
}
|
||||
|
||||
|
||||
commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms whether the database is loaded with some records.
|
||||
*/
|
||||
public boolean isInitialized() {
|
||||
return count(Book.class) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a name and email to login a Customer.
|
||||
* If such a customer exists, return it. Otherwise creates a new one.
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public Customer login(String name) {
|
||||
EntityManager em = begin();
|
||||
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<Customer> q = cb.createQuery(Customer.class);
|
||||
Customer customer = null;
|
||||
Root<Customer> root = q.from(Customer.class);
|
||||
ParameterExpression<String> pName = cb.parameter(String.class);
|
||||
q.where(cb.equal(root.get(Customer_.name), pName));
|
||||
List<Customer> customers = em.createQuery(q)
|
||||
.setParameter(pName, name)
|
||||
.getResultList();
|
||||
if (customers.isEmpty()) {
|
||||
Customer newCustomer = new Customer();
|
||||
newCustomer.setName(name);
|
||||
em.persist(newCustomer);
|
||||
customer = newCustomer;
|
||||
} else {
|
||||
customer = customers.get(0);
|
||||
}
|
||||
commit();
|
||||
return customer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find books that match title and price range.
|
||||
* @param title
|
||||
* @param minPrice
|
||||
* @param maxPrice
|
||||
* @param author
|
||||
* @return
|
||||
*/
|
||||
|
||||
public List<Book> select(String title, Double minPrice, Double maxPrice, String author,
|
||||
QueryDecorator...decorators) {
|
||||
CriteriaQuery<Book> q = buildQuery(title, minPrice, maxPrice, author);
|
||||
EntityManager em = begin();
|
||||
TypedQuery<Book> query = em.createQuery(q);
|
||||
List<Book> result = query.getResultList();
|
||||
commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getQuery(String title, Double minPrice, Double maxPrice, String author) {
|
||||
CriteriaQuery<Book> q = buildQuery(title, minPrice, maxPrice, author);
|
||||
return q.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Query based on the values of the user input form.
|
||||
* The user may or may not have filled a value for each form field
|
||||
* and accordingly the query will be different.<br>
|
||||
* This is typical of a form-based query. To account for all possible
|
||||
* combinations of field values to build a String-based JPQL can be
|
||||
* a daunting exercise. This method demonstrates how such dynamic,
|
||||
* conditional be alternatively developed using {@link CriteriaQuery}
|
||||
* introduced in JPA version 2.0.
|
||||
* <br>
|
||||
*
|
||||
*
|
||||
* @return a typed query
|
||||
*/
|
||||
|
||||
private CriteriaQuery<Book> buildQuery(String title,
|
||||
Double minPrice, Double maxPrice,
|
||||
String author) {
|
||||
CriteriaBuilder cb = getUnit().getCriteriaBuilder();
|
||||
CriteriaQuery<Book> q = cb.createQuery(Book.class);
|
||||
Root<Book> book = q.from(Book.class);
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
if (title != null && title.trim().length() > 0) {
|
||||
Predicate matchTitle = cb.like(book.get(Book_.title), title);
|
||||
predicates.add(matchTitle);
|
||||
}
|
||||
if (minPrice != null && maxPrice != null) {
|
||||
Predicate matchPrice = cb.between(book.get(Book_.price), minPrice, maxPrice);
|
||||
predicates.add(matchPrice);
|
||||
} else if (minPrice != null && maxPrice == null) {
|
||||
Predicate matchPrice = cb.ge(book.get(Book_.price), minPrice);
|
||||
predicates.add(matchPrice);
|
||||
} else if (minPrice == null && maxPrice != null) {
|
||||
Predicate matchPrice = cb.le(book.get(Book_.price), maxPrice);
|
||||
predicates.add(matchPrice);
|
||||
}
|
||||
if (author != null && author.trim().length() > 0) {
|
||||
Predicate matchAuthor = cb.like(book.join(Book_.authors).get(Author_.name), "%"+author+"%");
|
||||
predicates.add(matchAuthor);
|
||||
}
|
||||
|
||||
q.select(book);
|
||||
if (!predicates.isEmpty())
|
||||
q.where(predicates.toArray(new Predicate[predicates.size()]));
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deliver pending orders.
|
||||
* Queries for pending PurchaseOrders and attempts to deliver each in a separate
|
||||
* transaction. Some of the transactions may fail because of concurrent modification
|
||||
* on the inventory by the supplier.
|
||||
*/
|
||||
public PurchaseOrder deliver(PurchaseOrder o) {
|
||||
EntityManager em = begin();
|
||||
o = em.merge(o);
|
||||
o.setDelivered();
|
||||
for (LineItem item : o.getItems()) {
|
||||
item.getBook().getInventory().decrement(item.getQuantity());
|
||||
}
|
||||
commit();
|
||||
return o;
|
||||
}
|
||||
|
||||
public List<PurchaseOrder> getOrders(PurchaseOrder.Status status) {
|
||||
EntityManager em = begin();
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<PurchaseOrder> q = cb.createQuery(PurchaseOrder.class);
|
||||
Root<PurchaseOrder> order = q.from(PurchaseOrder.class);
|
||||
q.select(order);
|
||||
if (status != null) {
|
||||
q.where(cb.equal(order.get(PurchaseOrder_.status), status));
|
||||
}
|
||||
q.orderBy(cb.desc(order.get(PurchaseOrder_.placedOn)));
|
||||
|
||||
TypedQuery<PurchaseOrder> query = em.createQuery(q);
|
||||
List<PurchaseOrder> result = query.getResultList();
|
||||
commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@linkplain PurchaseOrder} from the content of the given {@linkplain ShoppingCart}.
|
||||
* <br>
|
||||
* The transaction is not expected to fail because the inventory is
|
||||
* not modified by placing an order.
|
||||
*
|
||||
* @param cart a non-null Shopping cart.
|
||||
*/
|
||||
public PurchaseOrder placeOrder(ShoppingCart cart) {
|
||||
EntityManager em = begin();
|
||||
PurchaseOrder order = new PurchaseOrder(cart);
|
||||
em.persist(order);
|
||||
commit();
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supply books that have low inventory.
|
||||
* <br>
|
||||
* Queries for books with low inventory and supply each book in separate
|
||||
* transaction. Some of the transactions may fail due to concurrent modification on
|
||||
* the {@linkplain Inventory} by the {@linkplain #deliver() delivery} process.
|
||||
*/
|
||||
public Book supply(Book b, int quantity) {
|
||||
EntityManager em = begin();
|
||||
b = em.merge(b);
|
||||
b.getInventory().increment(quantity);
|
||||
commit();
|
||||
return b;
|
||||
}
|
||||
|
||||
public List<Inventory> getReorderableBooks(int limit) {
|
||||
EntityManager em = begin();
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<Inventory> q = cb.createQuery(Inventory.class);
|
||||
Root<Inventory> inv = q.from(Inventory.class);
|
||||
q.select(inv);
|
||||
Expression<Integer> inStock = cb.diff(
|
||||
inv.get(Inventory_.supplied),
|
||||
inv.get(Inventory_.sold));
|
||||
q.orderBy(cb.asc(inStock));
|
||||
|
||||
List<Inventory> result = em.createQuery(q)
|
||||
.setMaxResults(limit)
|
||||
.getResultList();
|
||||
commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
public long count(Class<?> cls) {
|
||||
EntityManager em = getEntityManager();
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> c = cb.createQuery(Long.class);
|
||||
Root<?> from = c.from(cls);
|
||||
c.select(cb.count(from));
|
||||
return em.createQuery(c).getSingleResult();
|
||||
}
|
||||
|
||||
public List<Book> selectByExample(Book b, QueryDecorator...decorators) {
|
||||
return queryByTemplate(Book.class, b);
|
||||
}
|
||||
|
||||
private <T> List<T> queryByTemplate(Class<T> type, T template) {
|
||||
EntityManager em = begin();
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<T> c = cb.createQuery(type);
|
||||
c.where(((OpenJPACriteriaBuilder)cb).qbe(c.from(type), template));
|
||||
List<T> result = em.createQuery(c).getResultList();
|
||||
commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
public <T> List<T> getExtent(Class<T> entityClass) {
|
||||
EntityManager em = begin();
|
||||
CriteriaQuery<T> c = em.getCriteriaBuilder().createQuery(entityClass);
|
||||
c.from(entityClass);
|
||||
List<T> result = em.createQuery(c).getResultList();
|
||||
commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
public <T> List<T> query(String jpql, Class<T> resultClass, QueryDecorator... decorators) {
|
||||
EntityManager em = begin();
|
||||
TypedQuery<T> query = em.createQuery(jpql, resultClass);
|
||||
if (decorators != null) {
|
||||
for (QueryDecorator decorator : decorators) {
|
||||
decorator.decorate(query);
|
||||
}
|
||||
}
|
||||
List<T> result = query.getResultList();
|
||||
commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void clean() {
|
||||
EntityManager em = begin();
|
||||
Set<EntityType<?>> entities = em.getMetamodel().getEntities();
|
||||
for (EntityType<?> type : entities) {
|
||||
System.err.println("Deleting all instances of " + type.getName());
|
||||
em.createQuery("delete from " + type.getName() + " p").executeUpdate();
|
||||
}
|
||||
commit();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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 openbook.server;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
|
||||
/**
|
||||
* An abstract utility for JPA based service.
|
||||
* This thin wrapper over a {@link EntityManagerFactory Persistence Unit} maintains
|
||||
* <LI>per-thread persistence context
|
||||
* <LI>relinquishes direct transaction control under a managed environment
|
||||
*
|
||||
* @see #getEntityManager()
|
||||
* @see #newEntityManager()
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
abstract class PersistenceService implements Serializable {
|
||||
private final EntityManagerFactory emf;
|
||||
private final String unitName;
|
||||
private final boolean isManaged;
|
||||
private final PersistenceContextType scope;
|
||||
|
||||
private ThreadLocal<EntityManager> thread = new ThreadLocal<EntityManager>();
|
||||
private ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
protected PersistenceService(String unit, EntityManagerFactory emf, boolean managed,
|
||||
PersistenceContextType scope) {
|
||||
this.unitName = unit;
|
||||
this.emf = emf;
|
||||
this.isManaged = managed;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public final EntityManagerFactory getUnit() {
|
||||
return emf;
|
||||
}
|
||||
|
||||
public final String getUnitName() {
|
||||
return unitName;
|
||||
}
|
||||
|
||||
public final boolean isManaged() {
|
||||
return isManaged;
|
||||
}
|
||||
|
||||
public final PersistenceContextType getContextType() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entity manager associated with the current thread. If the
|
||||
* current thread is not associated with any entity manager or the
|
||||
* associated entity manager has been closed, creates a new entity manager
|
||||
* and associates with the current thread.
|
||||
*
|
||||
* @return an entity manager associated with the current thread.
|
||||
*/
|
||||
protected EntityManager getEntityManager() {
|
||||
try {
|
||||
lock.lock();
|
||||
EntityManager em = thread.get();
|
||||
if (em == null || !em.isOpen()) {
|
||||
em = emf.createEntityManager();
|
||||
thread.set(em);
|
||||
}
|
||||
return em;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entity manager. The entity manager is not associated with
|
||||
* the current thread.
|
||||
*/
|
||||
protected EntityManager newEntityManager() {
|
||||
return emf.createEntityManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins a transaction on the current thread. If the thread is associated
|
||||
* with a persistence context, then a transaction is started if necessary.
|
||||
* If the thread is not associated with a persistence context, then a new
|
||||
* context is created, associated with the thread, new transaction is
|
||||
* started.
|
||||
*
|
||||
* @see #getEntityManager()
|
||||
*/
|
||||
protected EntityManager begin() {
|
||||
try {
|
||||
lock.lock();
|
||||
EntityManager em = getEntityManager();
|
||||
if (isManaged) {
|
||||
em.joinTransaction();
|
||||
} else {
|
||||
if (!em.getTransaction().isActive()) {
|
||||
em.getTransaction().begin();
|
||||
}
|
||||
}
|
||||
return em;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a transaction on the current thread.
|
||||
*/
|
||||
protected void commit() {
|
||||
try {
|
||||
lock.lock();
|
||||
EntityManager em = getEntityManager();
|
||||
if (isManaged) {
|
||||
em.flush();
|
||||
} else {
|
||||
assertActive();
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
if (scope == PersistenceContextType.TRANSACTION) {
|
||||
em.clear();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolls back a transaction on the current thread.
|
||||
*/
|
||||
protected void rollback() {
|
||||
try {
|
||||
lock.lock();
|
||||
EntityManager em = getEntityManager();
|
||||
if (isManaged) {
|
||||
|
||||
} else {
|
||||
em.getTransaction().rollback();
|
||||
}
|
||||
if (scope == PersistenceContextType.TRANSACTION) {
|
||||
em.clear();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert current thread is associated with an active transaction.
|
||||
*/
|
||||
protected void assertActive() {
|
||||
EntityManager em = thread.get();
|
||||
String thread = Thread.currentThread().getName();
|
||||
assertTrue("No persistent context is associated with " + thread, em != null);
|
||||
assertTrue("Persistent context " + em + " associated with " + thread + " has been closed", em.isOpen());
|
||||
if (!isManaged) {
|
||||
assertTrue("Persistent context " + em + " associated with " + thread + " has no active transaction",
|
||||
em.getTransaction().isActive());
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertTrue(String s, boolean p) {
|
||||
if (!p) {
|
||||
System.err.println(s);
|
||||
throw new RuntimeException(s);
|
||||
}
|
||||
}
|
||||
}
|
@ -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 openbook.server;
|
||||
|
||||
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);
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.server;
|
||||
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
/**
|
||||
* Decorates a query by binding parameters.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class QueryParameterBinder implements QueryDecorator {
|
||||
private final Object[] params;
|
||||
|
||||
/**
|
||||
* Construct a parameter binder with the given parameters.
|
||||
*
|
||||
* @param params
|
||||
*/
|
||||
public QueryParameterBinder(Object...params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decorate(Query query) {
|
||||
if (params == null)
|
||||
return;
|
||||
for (int i = 0; i < params.length; i += 2) {
|
||||
if (params[i] instanceof Integer) {
|
||||
query.setParameter((Integer)params[i], params[i+1]);
|
||||
} else if (params[i] instanceof String) {
|
||||
query.setParameter((String)params[i], params[i+1]);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Parameter index " + params[i] +
|
||||
" is neither and integer nor String");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 openbook.server;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
|
||||
/**
|
||||
* A container of persistence units. Typically a JEE container will create, manage and inject
|
||||
* the persistence units to the user artifacts.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class ServiceFactory {
|
||||
private static final Map<String, OpenBookService> _services =
|
||||
new HashMap<String, OpenBookService>();
|
||||
|
||||
/**
|
||||
* Creates a persistence unit of given name configured with the given
|
||||
* name-value parameters.
|
||||
*
|
||||
* @param unit name of the persistence unit. A <code>META-INF/persistence.xml</code> must be
|
||||
* available with the same unit name in the classpath.
|
||||
*/
|
||||
public synchronized static OpenBookService getService(String unit) {
|
||||
OpenBookService service = _services.get(unit);
|
||||
if (service == null) {
|
||||
EntityManagerFactory emf = Persistence.createEntityManagerFactory(unit);
|
||||
service = new OpenBookServiceImpl(unit, emf, false, PersistenceContextType.TRANSACTION);
|
||||
_services.put(unit, service);
|
||||
}
|
||||
return service;
|
||||
}
|
||||
}
|
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A set of static utility functions to read properties from file, manage properties with multi-part keys,
|
||||
* array style properties etc.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class PropertyHelper {
|
||||
/**
|
||||
* Filter the properties by the given name.
|
||||
*
|
||||
* @param name a part of the key
|
||||
* @param prefix if true, property key must begin with the given name. Otherwise, the key merely contains the
|
||||
* name to qualify.
|
||||
* @param
|
||||
*
|
||||
* @return key-value pairs that match.
|
||||
*/
|
||||
public static Map<String,Object> filter(Map<String,Object> props, String name, boolean prefix,
|
||||
boolean includeArrays) {
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
for (String key : props.keySet()) {
|
||||
if (key == null)
|
||||
continue;
|
||||
boolean match = prefix ? key.startsWith(name) : (key.indexOf(name) != -1);
|
||||
if (match && !isArray(key)) {
|
||||
result.put(key, props.get(key));
|
||||
}
|
||||
}
|
||||
if (includeArrays) {
|
||||
Map<String,List<Object>> arrayProperties = filterArrayKeys(props, name, prefix);
|
||||
result.putAll(arrayProperties);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select only those property keys which ends with an array marker such as <code>openjpa.DataCache[1]</code>.
|
||||
* The multiple values of the property is inserted into the resultant map as a List of object against the
|
||||
* original key.
|
||||
* <br>
|
||||
* For example, if the original map had three key-value pairs as
|
||||
* <LI><code>openjpa.DataCache[1]=true</code>
|
||||
* <LI><code>openjpa.DataCache[2]=false</code>
|
||||
* <LI><code>openjpa.DataCache[3]=default</code>
|
||||
* <br>
|
||||
* Then that will result into a single entry in the resultant Map under the key <code>openjpa.DataCache</code>
|
||||
* with a value as a List of three elements namely <code>{true, false, default}</code>. The array index values
|
||||
* are not significant other than they must all be different for the same base key.
|
||||
*
|
||||
* @param name part of the property key
|
||||
* @param prefix does the name must appear as a prefix?
|
||||
*
|
||||
* @return key-value pairs that match.
|
||||
*/
|
||||
public static Map<String,List<Object>> filterArrayKeys(Map<String,Object> props, String name, boolean prefix) {
|
||||
Map<String, List<Object>> result = new HashMap<String, List<Object>>();
|
||||
for (String key : props.keySet()) {
|
||||
boolean match = prefix ? key.startsWith(name) : (key.indexOf(name) != -1);
|
||||
if (match && isArray(key)) {
|
||||
String realKey = removeArray(key);
|
||||
List<Object> values = result.get(realKey);
|
||||
if (values == null) {
|
||||
values = new ArrayList<Object>();
|
||||
result.put(realKey, values);
|
||||
}
|
||||
values.add(props.get(key));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load properties from the given name resource.
|
||||
* The given named resource is first looked up as resource on the current thread's context
|
||||
* and if not found as a file input.
|
||||
*
|
||||
* @param resource name a of resource.
|
||||
*
|
||||
* @return empty properties if no resource found.
|
||||
*/
|
||||
public static Map<String,Object> load(String resource) {
|
||||
Properties p = new Properties();
|
||||
try {
|
||||
InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
|
||||
if (stream == null) {
|
||||
stream = new FileInputStream(resource);
|
||||
}
|
||||
p.load(stream);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error reading " + resource + " due to " + e);
|
||||
}
|
||||
return toMap(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirm if the given resource is available either as a resource in the current thread's context classpath
|
||||
* or as a file.
|
||||
*
|
||||
*/
|
||||
public static boolean canFind(String resource) {
|
||||
if (resource == null)
|
||||
return false;
|
||||
InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
|
||||
if (stream != null)
|
||||
return true;
|
||||
return new File(resource).exists();
|
||||
}
|
||||
|
||||
public static Map<String, Object> toMap(Properties p) {
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
for (Object k : p.keySet()) {
|
||||
result.put(k.toString(), p.get(k));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites any key-value pair in the given map for which a System property is available
|
||||
* @param original properties to be overwritten
|
||||
* @return the original property overwritten with System properties
|
||||
*/
|
||||
public static Map<String, Object> overwriteWithSystemProperties(Map<String, Object> original) {
|
||||
Properties properties = System.getProperties();
|
||||
for (Object syskey : properties.keySet()) {
|
||||
if (original.containsKey(syskey)) {
|
||||
original.put(syskey.toString(), properties.get(syskey));
|
||||
}
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
|
||||
public static int getInteger(Map<String,Object> props, String key, int def) {
|
||||
int result = def;
|
||||
try {
|
||||
Object value = props.get(key);
|
||||
if (value != null)
|
||||
result = Integer.parseInt(value.toString());
|
||||
} catch (NumberFormatException nfe) {
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static double getDouble(Map<String,Object> props, String key, double def) {
|
||||
double result = def;
|
||||
try {
|
||||
Object value = props.get(key);
|
||||
if (value != null)
|
||||
result = Double.parseDouble(value.toString());
|
||||
} catch (NumberFormatException nfe) {
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getString(Map<String,Object> props, String key, String def) {
|
||||
Object value = props.get(key);
|
||||
if (value != null)
|
||||
return value.toString();
|
||||
return def;
|
||||
}
|
||||
|
||||
public static List<String> getStringList(Map<String,Object> props, String key) {
|
||||
return getStringList(props, key, Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
public static List<String> getStringList(Map<String,Object> props, String key, List<String> def) {
|
||||
Object value = props.get(key);
|
||||
if (value != null)
|
||||
return Arrays.asList(value.toString().split("\\,"));
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given string using array [] symbol at the end.
|
||||
*
|
||||
* @param key a string to check for array symbol.
|
||||
*/
|
||||
private static boolean isArray(String key) {
|
||||
if (key == null || key.length() < 3 || !key.endsWith("]"))
|
||||
return false;
|
||||
int i = key.indexOf("[");
|
||||
if (i == -1 || i != key.lastIndexOf("["))
|
||||
return false;
|
||||
String index = key.substring(i+1,key.length()-1);
|
||||
try {
|
||||
Integer.parseInt(index);
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("Bad index " + index + " in " + key);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String removeArray(String key) {
|
||||
int i = key.indexOf("[");
|
||||
return key.substring(0,i);
|
||||
}
|
||||
|
||||
public static Set<String> getSubsectionKeys(Set<String> keys, String section) {
|
||||
String prefix = asPrefix(section);
|
||||
Set<String> subsections = new HashSet<String>();
|
||||
for (String key : keys) {
|
||||
if (key.startsWith(prefix)) {
|
||||
subsections.add(prefix + getPrefix(key.substring(prefix.length())));
|
||||
}
|
||||
}
|
||||
return subsections;
|
||||
}
|
||||
|
||||
private static final String DOT = ".";
|
||||
private static String asPrefix(String s) {
|
||||
if (s.endsWith(DOT)) return s;
|
||||
return s + DOT;
|
||||
}
|
||||
public static String getPrefix(String s) {
|
||||
int i = s.indexOf(DOT);
|
||||
return (i == -1) ? s : s.substring(0, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the portion of the given map whose key has the given section at prefix.
|
||||
*
|
||||
* @param props a set of name-value pair
|
||||
* @param section a string representing section of a key
|
||||
*
|
||||
* @return a new map with only the keys that starts with the given section.
|
||||
*/
|
||||
public static Map<String,Object> getSection(Map<String,Object> props, String section) {
|
||||
return getSection(props, section, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the portion of the given map whose key has the given section at prefix.
|
||||
*
|
||||
* @param props a set of name-value pair
|
||||
* @param section a string representing section of a key
|
||||
* @param retain if true the key of resultant map is same as the original map. Otherwise
|
||||
* the resultant map keys are without the section prefix.
|
||||
*
|
||||
* @return the map with only the keys that starts with the given section.
|
||||
*/
|
||||
public static Map<String,Object> getSection(Map<String,Object> props, String section, boolean retain) {
|
||||
Map<String,Object> result = new HashMap<String, Object>(props);
|
||||
Set<String> keys = props.keySet();
|
||||
String prefix = asPrefix(section);
|
||||
for (String key : keys) {
|
||||
if (key.startsWith(prefix)) {
|
||||
String newKey = retain ? key : key.substring(prefix.length());
|
||||
result.put(newKey, props.get(key));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package openbook.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* A set of static utility functions for simulating pseudo-randomness.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class Randomizer {
|
||||
private static final Random rng = new Random(System.currentTimeMillis());
|
||||
private static final int MIN_ALPHA = (int)'A';
|
||||
private static final int MAX_ALPHA = (int)'Z';
|
||||
|
||||
/**
|
||||
* Returns true with a probability of p.
|
||||
*/
|
||||
public static boolean probability(double p) {
|
||||
return rng.nextDouble() < p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks a random number between 0 (inclusive) and N (exclusive).
|
||||
*/
|
||||
public static int random(int n) {
|
||||
return rng.nextInt(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks a uniformly distributed random integer within the given range.
|
||||
*/
|
||||
public static int random(int min, int max) {
|
||||
return min + rng.nextInt(max-min);
|
||||
}
|
||||
public static double random(double min, double max) {
|
||||
return min + rng.nextDouble()*(max-min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random alphanumeric String with each segment separated by a dash.
|
||||
*/
|
||||
public static String randomString(int...segments) {
|
||||
StringBuffer tmp = new StringBuffer();
|
||||
for (int s : segments) {
|
||||
tmp.append(tmp.length() == 0 ? (char)random(MIN_ALPHA, MAX_ALPHA) : '-');
|
||||
for (int j = 0; j < s; j++)
|
||||
tmp.append(random(10));
|
||||
}
|
||||
return tmp.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks a random element from the given list.
|
||||
*/
|
||||
public static <T> T selectRandom(List<T> list) {
|
||||
if (list == null || list.isEmpty())
|
||||
return null;
|
||||
if (list.size() == 1)
|
||||
return list.get(0);
|
||||
return list.get(random(list.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects n elements randomly from the given list.
|
||||
* @param <T>
|
||||
* @param list
|
||||
* @param n
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> selectRandom(List<T> list, int n) {
|
||||
if (list == null || list.isEmpty())
|
||||
return Collections.emptyList();
|
||||
Set<Integer> indices = new HashSet<Integer>();
|
||||
List<T> selected = new ArrayList<T>();
|
||||
int m = list.size();
|
||||
if (n >= m) {
|
||||
selected.addAll(list);
|
||||
} else {
|
||||
while (indices.size() < n) {
|
||||
indices.add(random(m));
|
||||
}
|
||||
for (Integer index : indices) {
|
||||
selected.add(list.get(index));
|
||||
}
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Common utility classes used by both OpenBook service and client.
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,92 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html><head>
|
||||
<title></title>
|
||||
<style type="text/css">
|
||||
<!--code { font-family: Courier New, Courier; font-size: 14pt; margin: 0px; }-->
|
||||
</style>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<!-- ======================================================== -->
|
||||
<!-- = Java Sourcecode to HTML automatically converted code = -->
|
||||
<!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = -->
|
||||
<!-- = Further information: http://www.java2html.de = -->
|
||||
<div align="left" class="java">
|
||||
<table border="0" cellpadding="3" cellspacing="0" bgcolor="#ffffff">
|
||||
<tr>
|
||||
<!-- start source code -->
|
||||
<td nowrap="nowrap" valign="top" align="left">
|
||||
<code>
|
||||
<font color="#808080">01</font> <font color="#3f7f5f">/*</font><br />
|
||||
<font color="#808080">02</font> <font color="#ffffff"> </font><font color="#3f7f5f">* Copyright 2010-2012 Pinaki Poddar</font><br />
|
||||
<font color="#808080">03</font> <font color="#ffffff"> </font><font color="#3f7f5f">*</font><br />
|
||||
<font color="#808080">04</font> <font color="#ffffff"> </font><font color="#3f7f5f">*</font><br />
|
||||
<font color="#808080">05</font> <font color="#ffffff"> </font><font color="#3f7f5f">* Licensed under the Apache License, Version 2.0 (the "License");</font><br />
|
||||
<font color="#808080">06</font> <font color="#ffffff"> </font><font color="#3f7f5f">* you may not use this file except in compliance with the License.</font><br />
|
||||
<font color="#808080">07</font> <font color="#ffffff"> </font><font color="#3f7f5f">* You may obtain a copy of the License at</font><br />
|
||||
<font color="#808080">08</font> <font color="#ffffff"> </font><font color="#3f7f5f">*</font><br />
|
||||
<font color="#808080">09</font> <font color="#ffffff"> </font><font color="#3f7f5f">* http://www.apache.org/licenses/LICENSE-2.0</font><br />
|
||||
<font color="#808080">10</font> <font color="#ffffff"> </font><font color="#3f7f5f">*</font><br />
|
||||
<font color="#808080">11</font> <font color="#ffffff"> </font><font color="#3f7f5f">* Unless required by applicable law or agreed to in writing, software</font><br />
|
||||
<font color="#808080">12</font> <font color="#ffffff"> </font><font color="#3f7f5f">* distributed under the License is distributed on an "AS IS" BASIS,</font><br />
|
||||
<font color="#808080">13</font> <font color="#ffffff"> </font><font color="#3f7f5f">* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</font><br />
|
||||
<font color="#808080">14</font> <font color="#ffffff"> </font><font color="#3f7f5f">* See the License for the specific language governing permissions and</font><br />
|
||||
<font color="#808080">15</font> <font color="#ffffff"> </font><font color="#3f7f5f">* limitations under the License.</font><br />
|
||||
<font color="#808080">16</font> <font color="#3f7f5f">*/</font><br />
|
||||
<font color="#808080">17</font> <font color="#7f0055"><b>package </b></font><font color="#000000">jpa.tools.swing;</font><br />
|
||||
<font color="#808080">18</font> <font color="#ffffff"></font><br />
|
||||
<font color="#808080">19</font> <font color="#7f0055"><b>import </b></font><font color="#000000">java.awt.Color;</font><br />
|
||||
<font color="#808080">20</font> <font color="#7f0055"><b>import </b></font><font color="#000000">java.awt.GridLayout;</font><br />
|
||||
<font color="#808080">21</font> <font color="#7f0055"><b>import </b></font><font color="#000000">java.awt.image.BufferedImage;</font><br />
|
||||
<font color="#808080">22</font> <font color="#ffffff"></font><br />
|
||||
<font color="#808080">23</font> <font color="#7f0055"><b>import </b></font><font color="#000000">javax.persistence.metamodel.Attribute;</font><br />
|
||||
<font color="#808080">24</font> <font color="#7f0055"><b>import </b></font><font color="#000000">javax.swing.BorderFactory;</font><br />
|
||||
<font color="#808080">25</font> <font color="#7f0055"><b>import </b></font><font color="#000000">javax.swing.ImageIcon;</font><br />
|
||||
<font color="#808080">26</font> <font color="#7f0055"><b>import </b></font><font color="#000000">javax.swing.JComponent;</font><br />
|
||||
<font color="#808080">27</font> <font color="#7f0055"><b>import </b></font><font color="#000000">javax.swing.JLabel;</font><br />
|
||||
<font color="#808080">28</font> <font color="#7f0055"><b>import </b></font><font color="#000000">javax.swing.JPanel;</font><br />
|
||||
<font color="#808080">29</font> <font color="#ffffff"></font><br />
|
||||
<font color="#808080">30</font> <font color="#3f5fbf">/**</font><br />
|
||||
<font color="#808080">31</font> <font color="#ffffff"> </font><font color="#3f5fbf">* Displays color codes of each attribute type.</font><br />
|
||||
<font color="#808080">32</font> <font color="#ffffff"> </font><font color="#3f5fbf">* </font><br />
|
||||
<font color="#808080">33</font> <font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@author </font><font color="#3f5fbf">Pinaki Poddar</font><br />
|
||||
<font color="#808080">34</font> <font color="#ffffff"> </font><font color="#3f5fbf">*</font><br />
|
||||
<font color="#808080">35</font> <font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br />
|
||||
<font color="#808080">36</font> <font color="#646464">@SuppressWarnings</font><font color="#000000">(</font><font color="#2a00ff">"serial"</font><font color="#000000">)</font><br />
|
||||
<font color="#808080">37</font> <font color="#7f0055"><b>public class </b></font><font color="#000000">AttributeLegendView </font><font color="#7f0055"><b>extends </b></font><font color="#000000">JPanel </font><font color="#000000">{</font><br />
|
||||
<font color="#808080">38</font> <font color="#ffffff"> </font><br />
|
||||
<font color="#808080">39</font> <font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#000000">AttributeLegendView</font><font color="#000000">() {</font><br />
|
||||
<font color="#808080">40</font> <font color="#ffffff"> </font><font color="#7f0055"><b>super</b></font><font color="#000000">(</font><font color="#7f0055"><b>true</b></font><font color="#000000">)</font><font color="#000000">;</font><br />
|
||||
<font color="#808080">41</font> <font color="#ffffff"> </font><font color="#000000">setBorder</font><font color="#000000">(</font><font color="#000000">BorderFactory.createTitledBorder</font><font color="#000000">(</font><font color="#2a00ff">"Attribute Legends"</font><font color="#000000">))</font><font color="#000000">;</font><br />
|
||||
<font color="#808080">42</font> <font color="#ffffff"> </font><font color="#000000">setLayout</font><font color="#000000">(</font><font color="#7f0055"><b>new </b></font><font color="#000000">GridLayout</font><font color="#000000">(</font><font color="#990000">0</font><font color="#000000">,</font><font color="#990000">3</font><font color="#000000">))</font><font color="#000000">;</font><br />
|
||||
<font color="#808080">43</font> <font color="#ffffff"> </font><font color="#000000">add</font><font color="#000000">(</font><font color="#000000">createColoredLabel</font><font color="#000000">(</font><font color="#2a00ff">"IDENTITY"</font><font color="#000000">, Color.RED</font><font color="#000000">))</font><font color="#000000">;</font><br />
|
||||
<font color="#808080">44</font> <font color="#ffffff"> </font><font color="#000000">add</font><font color="#000000">(</font><font color="#000000">createColoredLabel</font><font color="#000000">(</font><font color="#2a00ff">"VERSION"</font><font color="#000000">, Color.DARK_GRAY</font><font color="#000000">))</font><font color="#000000">;</font><br />
|
||||
<font color="#808080">45</font> <font color="#ffffff"> </font><font color="#7f0055"><b>for </b></font><font color="#000000">(</font><font color="#000000">Attribute.PersistentAttributeType type : Attribute.PersistentAttributeType.values</font><font color="#000000">()) {</font><br />
|
||||
<font color="#808080">46</font> <font color="#ffffff"> </font><font color="#000000">add</font><font color="#000000">(</font><font color="#000000">createColoredLabel</font><font color="#000000">(</font><font color="#000000">type.toString</font><font color="#000000">()</font><font color="#000000">.replace</font><font color="#000000">(</font><font color="#990000">'_'</font><font color="#000000">, </font><font color="#990000">' '</font><font color="#000000">)</font><font color="#000000">, MetamodelHelper.getColor</font><font color="#000000">(</font><font color="#000000">type</font><font color="#000000">)))</font><font color="#000000">;</font><br />
|
||||
<font color="#808080">47</font> <font color="#ffffff"> </font><font color="#000000">} </font><br />
|
||||
<font color="#808080">48</font> <font color="#ffffff"> </font><font color="#000000">}</font><br />
|
||||
<font color="#808080">49</font> <font color="#ffffff"> </font><br />
|
||||
<font color="#808080">50</font> <font color="#ffffff"> </font><font color="#000000">JComponent createColoredLabel</font><font color="#000000">(</font><font color="#000000">String text, Color c</font><font color="#000000">) {</font><br />
|
||||
<font color="#808080">51</font> <font color="#ffffff"> </font><font color="#7f0055"><b>int </b></font><font color="#000000">width = </font><font color="#990000">40</font><font color="#000000">;</font><br />
|
||||
<font color="#808080">52</font> <font color="#ffffff"> </font><font color="#7f0055"><b>int </b></font><font color="#000000">height = </font><font color="#990000">20</font><font color="#000000">;</font><br />
|
||||
<font color="#808080">53</font> <font color="#ffffff"> </font><font color="#000000">BufferedImage bimage = </font><font color="#7f0055"><b>new </b></font><font color="#000000">BufferedImage</font><font color="#000000">(</font><font color="#000000">width, height, BufferedImage.TYPE_INT_RGB</font><font color="#000000">)</font><font color="#000000">; </font><br />
|
||||
<font color="#808080">54</font> <font color="#ffffff"> </font><font color="#7f0055"><b>for </b></font><font color="#000000">(</font><font color="#7f0055"><b>int </b></font><font color="#000000">i = </font><font color="#990000">0</font><font color="#000000">; i < width; i++</font><font color="#000000">)</font><br />
|
||||
<font color="#808080">55</font> <font color="#ffffff"> </font><font color="#7f0055"><b>for </b></font><font color="#000000">(</font><font color="#7f0055"><b>int </b></font><font color="#000000">j = </font><font color="#990000">0</font><font color="#000000">; j < height; j++</font><font color="#000000">)</font><br />
|
||||
<font color="#808080">56</font> <font color="#ffffff"> </font><font color="#000000">bimage.setRGB</font><font color="#000000">(</font><font color="#000000">i, j, c.getRGB</font><font color="#000000">())</font><font color="#000000">;</font><br />
|
||||
<font color="#808080">57</font> <font color="#ffffff"> </font><font color="#000000">JLabel label = </font><font color="#7f0055"><b>new </b></font><font color="#000000">JLabel</font><font color="#000000">(</font><font color="#000000">text, </font><font color="#7f0055"><b>new </b></font><font color="#000000">ImageIcon</font><font color="#000000">(</font><font color="#000000">bimage</font><font color="#000000">)</font><font color="#000000">, JLabel.LEADING</font><font color="#000000">)</font><font color="#000000">;</font><br />
|
||||
<font color="#808080">58</font> <font color="#ffffff"> </font><font color="#7f0055"><b>return </b></font><font color="#000000">label;</font><br />
|
||||
<font color="#808080">59</font> <font color="#ffffff"> </font><font color="#000000">}</font><br />
|
||||
<font color="#808080">60</font> <font color="#ffffff"></font><br />
|
||||
<font color="#808080">61</font> <font color="#000000">}</font></code>
|
||||
|
||||
</td>
|
||||
<!-- end source code -->
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- = END of automatically generated HTML code = -->
|
||||
<!-- ======================================================== -->
|
||||
|
||||
|
||||
</body></html>
|
Loading…
x
Reference in New Issue
Block a user