/*
* Copyright (C) 2012 Sony Mobile Communications AB
*
* This file is part of ApkAnalyser.
*
* 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 andreflect.gui.chart.shape;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Rectangle;
import mereflect.MEClass;
import mereflect.MEField;
import mereflect.MEMethod;
import analyser.gui.ClassTreeRenderer;
import analyser.gui.FlagIcon;
import andreflect.Util;
import andreflect.gui.chart.ClassComponent;
import andreflect.gui.chart.GraphPanel;
import com.mxgraph.canvas.mxGraphics2DCanvas;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.shape.mxBasicShape;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;
public class ClassShape extends mxBasicShape {
private int drawOneLine(int y, FlagIcon fIcon, String text, boolean align_center, mxGraphics2DCanvas canvas, mxCellState state) {
Rectangle tmp = state.getRectangle();
double scale = canvas.getScale();
int charspacing = (int) (GraphPanel.CHAR_SPACING * scale);
Rectangle fontRect = mxUtils.getSizeForString(text, mxUtils.getFont(state
.getStyle()), scale).getRectangle();
FlagIcon im = fIcon;
int imageheight = Math.min((int) (im.getIconHeight() * scale), fontRect.height);
int imagewidth = (int) (im.getIconWidth() * scale); //ratio is fixed it will changed in canvas.drawImage()
// workaround to caclulate font y
int fonty = y;
Font scaledFont = mxUtils.getFont(state.getStyle(), scale);
canvas.getGraphics().setFont(scaledFont);
int fontSize = mxUtils.getInt(state.getStyle(), mxConstants.STYLE_FONTSIZE,
mxConstants.DEFAULT_FONTSIZE);
FontMetrics fm = canvas.getGraphics().getFontMetrics();
int scaledFontSize = scaledFont.getSize();
double fontScaleFactor = ((double) scaledFontSize) / ((double) fontSize);
// This factor is the amount by which the font is smaller/
// larger than we expect for the given scale. 1 means it's
// correct, 0.8 means the font is 0.8 the size we expected
// when scaled, etc.
double fontScaleRatio = fontScaleFactor / scale;
// The y position has to be moved by (1 - ratio) * height / 2
fonty += 2 * fm.getMaxAscent() - fm.getHeight()
+ 5/*mxConstants.LABEL_INSET*/* scale;
double vertAlignProportion = 1.0; //0 top 1.0 bottom 0.5 middle align
fonty += (1.0 - fontScaleRatio) * fontRect.height * vertAlignProportion;
// end workaround
int dx = 0;
if (align_center)
{
int sw = fm.stringWidth(text) + charspacing * 2 + imagewidth;
dx = (tmp.width - sw) / 2;
}
Rectangle imagerect = new Rectangle(tmp.x + dx + charspacing, y + fontRect.height - imageheight, imagewidth, imageheight);
im.paintIcon(imagerect, canvas.getGraphics());
canvas.getGraphics().drawString(text, (tmp.x + dx + charspacing + imagewidth + charspacing), fonty);
return y + fontRect.height + mxConstants.LINESPACING;
}
private void draw(ClassComponent split, mxGraphics2DCanvas canvas, mxCellState state) {
Rectangle tmp = state.getRectangle();
int y = tmp.y;
MEClass clazz = split.getMEClass();
FlagIcon ci;
if (clazz.isInterface()) {
ci = ClassTreeRenderer.ICON_INTERFACE;
} else {
ci = ClassTreeRenderer.ICON_CLASS;
}
if (state.getView().getGraph().isCellCollapsed(state.getCell())) {
y = drawOneLine(y, ci, clazz.getClassName(), false, canvas, state);
return;
}
y = drawOneLine(y, ci, clazz.getClassName(), true, canvas, state);
canvas.getGraphics().drawLine(tmp.x, y, tmp.x + tmp.width, y);
boolean hasField = false;
for (MEField field : split.getFields()) {
hasField = true;
FlagIcon fIcon = null;
int flags = 0;
if (field.isPublic()) {
fIcon = ClassTreeRenderer.ICON_PUBLICFIELD;
} else if (field.isProtected()) {
fIcon = ClassTreeRenderer.ICON_PROTECTEDFIELD;
} else if (field.isPrivate()) {
fIcon = ClassTreeRenderer.ICON_PRIVATEFIELD;
} else {
fIcon = ClassTreeRenderer.ICON_PACKAGEMFIELD;
}
flags |= field.isStatic() ? FlagIcon.FLAG_STATIC : 0;
flags |= field.isFinal() ? FlagIcon.FLAG_FINAL : 0;
fIcon.setFlags(flags);
String text = field.getName() + " : " + Util.shortenClassName(field.getType().toString());
y = drawOneLine(y, fIcon, text, false, canvas, state);
}
if (!hasField) {
double scale = canvas.getScale();
y += (int) (GraphPanel.LINE_SPACING * scale);
}
boolean hasMethod = false;
canvas.getGraphics().drawLine(tmp.x, y, tmp.x + tmp.width, y);
for (MEMethod method : split.getMethods()) {
hasMethod = true;
FlagIcon fIcon = null;
int flags = 0;
if (method.isPublic()) {
fIcon = ClassTreeRenderer.ICON_PUBLICMETHOD;
} else if (method.isProtected()) {
fIcon = ClassTreeRenderer.ICON_PROTECTEDMETHOD;
} else if (method.isPrivate()) {
fIcon = ClassTreeRenderer.ICON_PRIVATEMETHOD;
} else {
fIcon = ClassTreeRenderer.ICON_PACKAGEMETHOD;
}
flags |= method.isNative() ? FlagIcon.FLAG_NATIVE : 0;
flags |= method.isConstructor() ? FlagIcon.FLAG_CONSTRUCTOR : 0;
flags |= method.isStatic() ? FlagIcon.FLAG_STATIC : 0;
flags |= method.isFinal() ? FlagIcon.FLAG_FINAL : 0;
fIcon.setFlags(flags);
String text = method.getFormattedName() + "(" + method.getArgumentsStringUml() + ") : " + Util.shortenClassName(method.getReturnClassString());
y = drawOneLine(y, fIcon, text, false, canvas, state);
}
if (!hasMethod) {
canvas.getGraphics().drawLine(tmp.x, y, tmp.x + tmp.width, y);
}
}
private static final Color COLOR_FOCUS = Color.CYAN.brighter().brighter();
private static final Color COLOR_DEPENDENCY = Color.ORANGE.brighter().brighter();
private static final Color COLOR_DEPSUPER = Color.GREEN.brighter().brighter();
@Override
public void paintShape(mxGraphics2DCanvas canvas, mxCellState state)
{
Rectangle tmp = state.getRectangle();
mxIGraphModel model = state.getView().getGraph().getModel();
Object val = model.getValue(state.getCell());
assert val instanceof ClassComponent;
ClassComponent split = (ClassComponent) val;
if (configureGraphics(canvas, state, true))
{
if (split.isFocus()) {
canvas.getGraphics().setColor(COLOR_FOCUS);
} else if (split.isDepSuper()) {
canvas.getGraphics().setColor(COLOR_DEPSUPER);
} else if (split.isDependency()) {
canvas.getGraphics().setColor(COLOR_DEPENDENCY);
}
canvas.fillShape(new Rectangle(tmp.x, tmp.y, tmp.width, tmp.height));
}
if (configureGraphics(canvas, state, false))
{
draw(split, canvas, state);
canvas.getGraphics().drawRect(tmp.x, tmp.y, tmp.width, tmp.height);
}
}
}