/*
* 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.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;
/**
* 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.
*/
@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.
*/
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;
}
}