You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pp...@apache.org on 2010/05/14 04:14:32 UTC
svn commit: r944083 [4/4] - in
/openjpa/trunk/openjpa-examples/openbooks/src: ./ main/ main/java/
main/java/jpa/ main/java/jpa/tools/ main/java/jpa/tools/swing/
main/java/openbook/ main/java/openbook/client/ main/java/openbook/domain/
main/java/openboo...
Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/OpenBookServiceImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/OpenBookServiceImpl.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/OpenBookServiceImpl.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/OpenBookServiceImpl.java Fri May 14 02:14:30 2010
@@ -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();
+ }
+
+
+}
Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/OpenBookServiceImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/PersistenceService.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/PersistenceService.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/PersistenceService.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/PersistenceService.java Fri May 14 02:14:30 2010
@@ -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);
+ }
+ }
+}
Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/PersistenceService.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/QueryDecorator.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/QueryDecorator.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/QueryDecorator.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/QueryDecorator.java Fri May 14 02:14:30 2010
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 Pinaki Poddar
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+package 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);
+}
Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/QueryDecorator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/QueryParameterBinder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/QueryParameterBinder.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/QueryParameterBinder.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/QueryParameterBinder.java Fri May 14 02:14:30 2010
@@ -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");
+ }
+ }
+ }
+}
Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/QueryParameterBinder.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/ServiceFactory.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/ServiceFactory.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/ServiceFactory.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/ServiceFactory.java Fri May 14 02:14:30 2010
@@ -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;
+ }
+}
Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/server/ServiceFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/PropertyHelper.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/PropertyHelper.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/PropertyHelper.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/PropertyHelper.java Fri May 14 02:14:30 2010
@@ -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;
+ }
+}
Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/PropertyHelper.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/Randomizer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/Randomizer.java?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/Randomizer.java (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/Randomizer.java Fri May 14 02:14:30 2010
@@ -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;
+ }
+}
Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/Randomizer.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/package.html
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/package.html?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/package.html (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/package.html Fri May 14 02:14:30 2010
@@ -0,0 +1,5 @@
+<html>
+<body>
+Common utility classes used by both OpenBook service and client.
+</body>
+</html>
\ No newline at end of file
Propchange: openjpa/trunk/openjpa-examples/openbooks/src/main/java/openbook/util/package.html
------------------------------------------------------------------------------
svn:eol-style = native
Added: openjpa/trunk/openjpa-examples/openbooks/src/main/resources/source/jpa/tools/swing/AttributeLegendView.html
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-examples/openbooks/src/main/resources/source/jpa/tools/swing/AttributeLegendView.html?rev=944083&view=auto
==============================================================================
--- openjpa/trunk/openjpa-examples/openbooks/src/main/resources/source/jpa/tools/swing/AttributeLegendView.html (added)
+++ openjpa/trunk/openjpa-examples/openbooks/src/main/resources/source/jpa/tools/swing/AttributeLegendView.html Fri May 14 02:14:30 2010
@@ -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>
\ No newline at end of file